1fd4e5da5Sopenharmony_ci// Copyright (C) 2019 Google Inc.
2fd4e5da5Sopenharmony_ci//
3fd4e5da5Sopenharmony_ci// Licensed under the Apache License, Version 2.0 (the "License");
4fd4e5da5Sopenharmony_ci// you may not use this file except in compliance with the License.
5fd4e5da5Sopenharmony_ci// You may obtain a copy of the License at
6fd4e5da5Sopenharmony_ci//
7fd4e5da5Sopenharmony_ci//      http://www.apache.org/licenses/LICENSE-2.0
8fd4e5da5Sopenharmony_ci//
9fd4e5da5Sopenharmony_ci// Unless required by applicable law or agreed to in writing, software
10fd4e5da5Sopenharmony_ci// distributed under the License is distributed on an "AS IS" BASIS,
11fd4e5da5Sopenharmony_ci// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fd4e5da5Sopenharmony_ci// See the License for the specific language governing permissions and
13fd4e5da5Sopenharmony_ci// limitations under the License.
14fd4e5da5Sopenharmony_ci
15fd4e5da5Sopenharmony_ci// langsvr implements a Language Server for the SPIRV assembly language.
16fd4e5da5Sopenharmony_cipackage main
17fd4e5da5Sopenharmony_ci
18fd4e5da5Sopenharmony_ciimport (
19fd4e5da5Sopenharmony_ci	"context"
20fd4e5da5Sopenharmony_ci	"fmt"
21fd4e5da5Sopenharmony_ci	"io"
22fd4e5da5Sopenharmony_ci	"io/ioutil"
23fd4e5da5Sopenharmony_ci	"log"
24fd4e5da5Sopenharmony_ci	"os"
25fd4e5da5Sopenharmony_ci	"path"
26fd4e5da5Sopenharmony_ci	"sort"
27fd4e5da5Sopenharmony_ci	"strings"
28fd4e5da5Sopenharmony_ci	"sync"
29fd4e5da5Sopenharmony_ci	"unicode/utf8"
30fd4e5da5Sopenharmony_ci
31fd4e5da5Sopenharmony_ci	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/parser"
32fd4e5da5Sopenharmony_ci	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/schema"
33fd4e5da5Sopenharmony_ci
34fd4e5da5Sopenharmony_ci	"github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/jsonrpc2"
35fd4e5da5Sopenharmony_ci	lsp "github.com/KhronosGroup/SPIRV-Tools/utils/vscode/src/lsp/protocol"
36fd4e5da5Sopenharmony_ci)
37fd4e5da5Sopenharmony_ci
38fd4e5da5Sopenharmony_ciconst (
39fd4e5da5Sopenharmony_ci	enableDebugLogging = false
40fd4e5da5Sopenharmony_ci)
41fd4e5da5Sopenharmony_ci
42fd4e5da5Sopenharmony_ci// rSpy is a reader 'spy' that wraps an io.Reader, and logs all data that passes
43fd4e5da5Sopenharmony_ci// through it.
44fd4e5da5Sopenharmony_citype rSpy struct {
45fd4e5da5Sopenharmony_ci	prefix string
46fd4e5da5Sopenharmony_ci	r      io.Reader
47fd4e5da5Sopenharmony_ci}
48fd4e5da5Sopenharmony_ci
49fd4e5da5Sopenharmony_cifunc (s rSpy) Read(p []byte) (n int, err error) {
50fd4e5da5Sopenharmony_ci	n, err = s.r.Read(p)
51fd4e5da5Sopenharmony_ci	log.Printf("%v %v", s.prefix, string(p[:n]))
52fd4e5da5Sopenharmony_ci	return n, err
53fd4e5da5Sopenharmony_ci}
54fd4e5da5Sopenharmony_ci
55fd4e5da5Sopenharmony_ci// wSpy is a reader 'spy' that wraps an io.Writer, and logs all data that passes
56fd4e5da5Sopenharmony_ci// through it.
57fd4e5da5Sopenharmony_citype wSpy struct {
58fd4e5da5Sopenharmony_ci	prefix string
59fd4e5da5Sopenharmony_ci	w      io.Writer
60fd4e5da5Sopenharmony_ci}
61fd4e5da5Sopenharmony_ci
62fd4e5da5Sopenharmony_cifunc (s wSpy) Write(p []byte) (n int, err error) {
63fd4e5da5Sopenharmony_ci	n, err = s.w.Write(p)
64fd4e5da5Sopenharmony_ci	log.Printf("%v %v", s.prefix, string(p))
65fd4e5da5Sopenharmony_ci	return n, err
66fd4e5da5Sopenharmony_ci}
67fd4e5da5Sopenharmony_ci
68fd4e5da5Sopenharmony_ci// main entry point.
69fd4e5da5Sopenharmony_cifunc main() {
70fd4e5da5Sopenharmony_ci	log.SetOutput(ioutil.Discard)
71fd4e5da5Sopenharmony_ci	if enableDebugLogging {
72fd4e5da5Sopenharmony_ci		// create a log file in the executable's directory.
73fd4e5da5Sopenharmony_ci		if logfile, err := os.Create(path.Join(path.Dir(os.Args[0]), "log.txt")); err == nil {
74fd4e5da5Sopenharmony_ci			defer logfile.Close()
75fd4e5da5Sopenharmony_ci			log.SetOutput(logfile)
76fd4e5da5Sopenharmony_ci		}
77fd4e5da5Sopenharmony_ci	}
78fd4e5da5Sopenharmony_ci
79fd4e5da5Sopenharmony_ci	log.Println("language server started")
80fd4e5da5Sopenharmony_ci
81fd4e5da5Sopenharmony_ci	stream := jsonrpc2.NewHeaderStream(rSpy{"IDE", os.Stdin}, wSpy{"LS", os.Stdout})
82fd4e5da5Sopenharmony_ci	s := server{
83fd4e5da5Sopenharmony_ci		files: map[string]*file{},
84fd4e5da5Sopenharmony_ci	}
85fd4e5da5Sopenharmony_ci	s.ctx, s.conn, s.client = lsp.NewServer(context.Background(), stream, &s)
86fd4e5da5Sopenharmony_ci	if err := s.conn.Run(s.ctx); err != nil {
87fd4e5da5Sopenharmony_ci		log.Panicln(err)
88fd4e5da5Sopenharmony_ci		os.Exit(1)
89fd4e5da5Sopenharmony_ci	}
90fd4e5da5Sopenharmony_ci
91fd4e5da5Sopenharmony_ci	log.Println("language server stopped")
92fd4e5da5Sopenharmony_ci}
93fd4e5da5Sopenharmony_ci
94fd4e5da5Sopenharmony_citype server struct {
95fd4e5da5Sopenharmony_ci	ctx    context.Context
96fd4e5da5Sopenharmony_ci	conn   *jsonrpc2.Conn
97fd4e5da5Sopenharmony_ci	client lsp.Client
98fd4e5da5Sopenharmony_ci
99fd4e5da5Sopenharmony_ci	files      map[string]*file
100fd4e5da5Sopenharmony_ci	filesMutex sync.Mutex
101fd4e5da5Sopenharmony_ci}
102fd4e5da5Sopenharmony_ci
103fd4e5da5Sopenharmony_ci// file represents a source file
104fd4e5da5Sopenharmony_citype file struct {
105fd4e5da5Sopenharmony_ci	fullRange parser.Range
106fd4e5da5Sopenharmony_ci	res       parser.Results
107fd4e5da5Sopenharmony_ci}
108fd4e5da5Sopenharmony_ci
109fd4e5da5Sopenharmony_ci// tokAt returns the parser token at the given position lp
110fd4e5da5Sopenharmony_cifunc (f *file) tokAt(lp lsp.Position) *parser.Token {
111fd4e5da5Sopenharmony_ci	toks := f.res.Tokens
112fd4e5da5Sopenharmony_ci	p := parser.Position{Line: int(lp.Line) + 1, Column: int(lp.Character) + 1}
113fd4e5da5Sopenharmony_ci	i := sort.Search(len(toks), func(i int) bool { return p.LessThan(toks[i].Range.End) })
114fd4e5da5Sopenharmony_ci	if i == len(toks) {
115fd4e5da5Sopenharmony_ci		return nil
116fd4e5da5Sopenharmony_ci	}
117fd4e5da5Sopenharmony_ci	if toks[i].Range.Contains(p) {
118fd4e5da5Sopenharmony_ci		return toks[i]
119fd4e5da5Sopenharmony_ci	}
120fd4e5da5Sopenharmony_ci	return nil
121fd4e5da5Sopenharmony_ci}
122fd4e5da5Sopenharmony_ci
123fd4e5da5Sopenharmony_cifunc (s *server) DidChangeWorkspaceFolders(ctx context.Context, p *lsp.DidChangeWorkspaceFoldersParams) error {
124fd4e5da5Sopenharmony_ci	log.Println("server.DidChangeWorkspaceFolders()")
125fd4e5da5Sopenharmony_ci	return nil
126fd4e5da5Sopenharmony_ci}
127fd4e5da5Sopenharmony_cifunc (s *server) Initialized(ctx context.Context, p *lsp.InitializedParams) error {
128fd4e5da5Sopenharmony_ci	log.Println("server.Initialized()")
129fd4e5da5Sopenharmony_ci	return nil
130fd4e5da5Sopenharmony_ci}
131fd4e5da5Sopenharmony_cifunc (s *server) Exit(ctx context.Context) error {
132fd4e5da5Sopenharmony_ci	log.Println("server.Exit()")
133fd4e5da5Sopenharmony_ci	return nil
134fd4e5da5Sopenharmony_ci}
135fd4e5da5Sopenharmony_cifunc (s *server) DidChangeConfiguration(ctx context.Context, p *lsp.DidChangeConfigurationParams) error {
136fd4e5da5Sopenharmony_ci	log.Println("server.DidChangeConfiguration()")
137fd4e5da5Sopenharmony_ci	return nil
138fd4e5da5Sopenharmony_ci}
139fd4e5da5Sopenharmony_cifunc (s *server) DidOpen(ctx context.Context, p *lsp.DidOpenTextDocumentParams) error {
140fd4e5da5Sopenharmony_ci	log.Println("server.DidOpen()")
141fd4e5da5Sopenharmony_ci	return s.processFile(ctx, p.TextDocument.URI, p.TextDocument.Text)
142fd4e5da5Sopenharmony_ci}
143fd4e5da5Sopenharmony_cifunc (s *server) DidChange(ctx context.Context, p *lsp.DidChangeTextDocumentParams) error {
144fd4e5da5Sopenharmony_ci	log.Println("server.DidChange()")
145fd4e5da5Sopenharmony_ci	return s.processFile(ctx, p.TextDocument.URI, p.ContentChanges[0].Text)
146fd4e5da5Sopenharmony_ci}
147fd4e5da5Sopenharmony_cifunc (s *server) DidClose(ctx context.Context, p *lsp.DidCloseTextDocumentParams) error {
148fd4e5da5Sopenharmony_ci	log.Println("server.DidClose()")
149fd4e5da5Sopenharmony_ci	return nil
150fd4e5da5Sopenharmony_ci}
151fd4e5da5Sopenharmony_cifunc (s *server) DidSave(ctx context.Context, p *lsp.DidSaveTextDocumentParams) error {
152fd4e5da5Sopenharmony_ci	log.Println("server.DidSave()")
153fd4e5da5Sopenharmony_ci	return nil
154fd4e5da5Sopenharmony_ci}
155fd4e5da5Sopenharmony_cifunc (s *server) WillSave(ctx context.Context, p *lsp.WillSaveTextDocumentParams) error {
156fd4e5da5Sopenharmony_ci	log.Println("server.WillSave()")
157fd4e5da5Sopenharmony_ci	return nil
158fd4e5da5Sopenharmony_ci}
159fd4e5da5Sopenharmony_cifunc (s *server) DidChangeWatchedFiles(ctx context.Context, p *lsp.DidChangeWatchedFilesParams) error {
160fd4e5da5Sopenharmony_ci	log.Println("server.DidChangeWatchedFiles()")
161fd4e5da5Sopenharmony_ci	return nil
162fd4e5da5Sopenharmony_ci}
163fd4e5da5Sopenharmony_cifunc (s *server) Progress(ctx context.Context, p *lsp.ProgressParams) error {
164fd4e5da5Sopenharmony_ci	log.Println("server.Progress()")
165fd4e5da5Sopenharmony_ci	return nil
166fd4e5da5Sopenharmony_ci}
167fd4e5da5Sopenharmony_cifunc (s *server) SetTraceNotification(ctx context.Context, p *lsp.SetTraceParams) error {
168fd4e5da5Sopenharmony_ci	log.Println("server.SetTraceNotification()")
169fd4e5da5Sopenharmony_ci	return nil
170fd4e5da5Sopenharmony_ci}
171fd4e5da5Sopenharmony_cifunc (s *server) LogTraceNotification(ctx context.Context, p *lsp.LogTraceParams) error {
172fd4e5da5Sopenharmony_ci	log.Println("server.LogTraceNotification()")
173fd4e5da5Sopenharmony_ci	return nil
174fd4e5da5Sopenharmony_ci}
175fd4e5da5Sopenharmony_cifunc (s *server) Implementation(ctx context.Context, p *lsp.ImplementationParams) ([]lsp.Location, error) {
176fd4e5da5Sopenharmony_ci	log.Println("server.Implementation()")
177fd4e5da5Sopenharmony_ci	return nil, nil
178fd4e5da5Sopenharmony_ci}
179fd4e5da5Sopenharmony_cifunc (s *server) TypeDefinition(ctx context.Context, p *lsp.TypeDefinitionParams) ([]lsp.Location, error) {
180fd4e5da5Sopenharmony_ci	log.Println("server.TypeDefinition()")
181fd4e5da5Sopenharmony_ci	return nil, nil
182fd4e5da5Sopenharmony_ci}
183fd4e5da5Sopenharmony_cifunc (s *server) DocumentColor(ctx context.Context, p *lsp.DocumentColorParams) ([]lsp.ColorInformation, error) {
184fd4e5da5Sopenharmony_ci	log.Println("server.DocumentColor()")
185fd4e5da5Sopenharmony_ci	return nil, nil
186fd4e5da5Sopenharmony_ci}
187fd4e5da5Sopenharmony_cifunc (s *server) ColorPresentation(ctx context.Context, p *lsp.ColorPresentationParams) ([]lsp.ColorPresentation, error) {
188fd4e5da5Sopenharmony_ci	log.Println("server.ColorPresentation()")
189fd4e5da5Sopenharmony_ci	return nil, nil
190fd4e5da5Sopenharmony_ci}
191fd4e5da5Sopenharmony_cifunc (s *server) FoldingRange(ctx context.Context, p *lsp.FoldingRangeParams) ([]lsp.FoldingRange, error) {
192fd4e5da5Sopenharmony_ci	log.Println("server.FoldingRange()")
193fd4e5da5Sopenharmony_ci	return nil, nil
194fd4e5da5Sopenharmony_ci}
195fd4e5da5Sopenharmony_cifunc (s *server) Declaration(ctx context.Context, p *lsp.DeclarationParams) ([]lsp.DeclarationLink, error) {
196fd4e5da5Sopenharmony_ci	log.Println("server.Declaration()")
197fd4e5da5Sopenharmony_ci	return nil, nil
198fd4e5da5Sopenharmony_ci}
199fd4e5da5Sopenharmony_cifunc (s *server) SelectionRange(ctx context.Context, p *lsp.SelectionRangeParams) ([]lsp.SelectionRange, error) {
200fd4e5da5Sopenharmony_ci	log.Println("server.SelectionRange()")
201fd4e5da5Sopenharmony_ci	return nil, nil
202fd4e5da5Sopenharmony_ci}
203fd4e5da5Sopenharmony_cifunc (s *server) Initialize(ctx context.Context, p *lsp.ParamInitia) (*lsp.InitializeResult, error) {
204fd4e5da5Sopenharmony_ci	log.Println("server.Initialize()")
205fd4e5da5Sopenharmony_ci	res := lsp.InitializeResult{
206fd4e5da5Sopenharmony_ci		Capabilities: lsp.ServerCapabilities{
207fd4e5da5Sopenharmony_ci			TextDocumentSync: lsp.TextDocumentSyncOptions{
208fd4e5da5Sopenharmony_ci				OpenClose: true,
209fd4e5da5Sopenharmony_ci				Change:    lsp.Full, // TODO: Implement incremental
210fd4e5da5Sopenharmony_ci			},
211fd4e5da5Sopenharmony_ci			HoverProvider:              true,
212fd4e5da5Sopenharmony_ci			DefinitionProvider:         true,
213fd4e5da5Sopenharmony_ci			ReferencesProvider:         true,
214fd4e5da5Sopenharmony_ci			RenameProvider:             true,
215fd4e5da5Sopenharmony_ci			DocumentFormattingProvider: true,
216fd4e5da5Sopenharmony_ci		},
217fd4e5da5Sopenharmony_ci	}
218fd4e5da5Sopenharmony_ci	return &res, nil
219fd4e5da5Sopenharmony_ci}
220fd4e5da5Sopenharmony_cifunc (s *server) Shutdown(ctx context.Context) error {
221fd4e5da5Sopenharmony_ci	log.Println("server.Shutdown()")
222fd4e5da5Sopenharmony_ci	return nil
223fd4e5da5Sopenharmony_ci}
224fd4e5da5Sopenharmony_cifunc (s *server) WillSaveWaitUntil(ctx context.Context, p *lsp.WillSaveTextDocumentParams) ([]lsp.TextEdit, error) {
225fd4e5da5Sopenharmony_ci	log.Println("server.WillSaveWaitUntil()")
226fd4e5da5Sopenharmony_ci	return nil, nil
227fd4e5da5Sopenharmony_ci}
228fd4e5da5Sopenharmony_cifunc (s *server) Completion(ctx context.Context, p *lsp.CompletionParams) (*lsp.CompletionList, error) {
229fd4e5da5Sopenharmony_ci	log.Println("server.Completion()")
230fd4e5da5Sopenharmony_ci	return nil, nil
231fd4e5da5Sopenharmony_ci}
232fd4e5da5Sopenharmony_cifunc (s *server) Resolve(ctx context.Context, p *lsp.CompletionItem) (*lsp.CompletionItem, error) {
233fd4e5da5Sopenharmony_ci	log.Println("server.Resolve()")
234fd4e5da5Sopenharmony_ci	return nil, nil
235fd4e5da5Sopenharmony_ci}
236fd4e5da5Sopenharmony_cifunc (s *server) Hover(ctx context.Context, p *lsp.HoverParams) (*lsp.Hover, error) {
237fd4e5da5Sopenharmony_ci	log.Println("server.Hover()")
238fd4e5da5Sopenharmony_ci	f := s.getFile(p.TextDocument.URI)
239fd4e5da5Sopenharmony_ci	if f == nil {
240fd4e5da5Sopenharmony_ci		return nil, fmt.Errorf("Unknown file")
241fd4e5da5Sopenharmony_ci	}
242fd4e5da5Sopenharmony_ci
243fd4e5da5Sopenharmony_ci	if tok := f.tokAt(p.Position); tok != nil {
244fd4e5da5Sopenharmony_ci		sb := strings.Builder{}
245fd4e5da5Sopenharmony_ci		switch v := f.res.Mappings[tok].(type) {
246fd4e5da5Sopenharmony_ci		default:
247fd4e5da5Sopenharmony_ci			sb.WriteString(fmt.Sprintf("<Unhandled type '%T'>", v))
248fd4e5da5Sopenharmony_ci		case *parser.Instruction:
249fd4e5da5Sopenharmony_ci			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Opcode.Opname))
250fd4e5da5Sopenharmony_ci		case *parser.Identifier:
251fd4e5da5Sopenharmony_ci			sb.WriteString(fmt.Sprintf("```\n%v\n```", v.Definition.Range.Text(f.res.Lines)))
252fd4e5da5Sopenharmony_ci		case *parser.Operand:
253fd4e5da5Sopenharmony_ci			if v.Name != "" {
254fd4e5da5Sopenharmony_ci				sb.WriteString(strings.Trim(v.Name, `'`))
255fd4e5da5Sopenharmony_ci				sb.WriteString("\n\n")
256fd4e5da5Sopenharmony_ci			}
257fd4e5da5Sopenharmony_ci
258fd4e5da5Sopenharmony_ci			switch v.Kind.Category {
259fd4e5da5Sopenharmony_ci			case schema.OperandCategoryBitEnum:
260fd4e5da5Sopenharmony_ci			case schema.OperandCategoryValueEnum:
261fd4e5da5Sopenharmony_ci				sb.WriteString("```\n")
262fd4e5da5Sopenharmony_ci				sb.WriteString(strings.Trim(v.Kind.Kind, `'`))
263fd4e5da5Sopenharmony_ci				sb.WriteString("\n```")
264fd4e5da5Sopenharmony_ci			case schema.OperandCategoryID:
265fd4e5da5Sopenharmony_ci				if s := tok.Text(f.res.Lines); s != "" {
266fd4e5da5Sopenharmony_ci					if id, ok := f.res.Identifiers[s]; ok && id.Definition != nil {
267fd4e5da5Sopenharmony_ci						sb.WriteString("```\n")
268fd4e5da5Sopenharmony_ci						sb.WriteString(id.Definition.Range.Text(f.res.Lines))
269fd4e5da5Sopenharmony_ci						sb.WriteString("\n```")
270fd4e5da5Sopenharmony_ci					}
271fd4e5da5Sopenharmony_ci				}
272fd4e5da5Sopenharmony_ci			case schema.OperandCategoryLiteral:
273fd4e5da5Sopenharmony_ci			case schema.OperandCategoryComposite:
274fd4e5da5Sopenharmony_ci			}
275fd4e5da5Sopenharmony_ci		case nil:
276fd4e5da5Sopenharmony_ci		}
277fd4e5da5Sopenharmony_ci
278fd4e5da5Sopenharmony_ci		if sb.Len() > 0 {
279fd4e5da5Sopenharmony_ci			res := lsp.Hover{
280fd4e5da5Sopenharmony_ci				Contents: lsp.MarkupContent{
281fd4e5da5Sopenharmony_ci					Kind:  "markdown",
282fd4e5da5Sopenharmony_ci					Value: sb.String(),
283fd4e5da5Sopenharmony_ci				},
284fd4e5da5Sopenharmony_ci			}
285fd4e5da5Sopenharmony_ci			return &res, nil
286fd4e5da5Sopenharmony_ci		}
287fd4e5da5Sopenharmony_ci	}
288fd4e5da5Sopenharmony_ci
289fd4e5da5Sopenharmony_ci	return nil, nil
290fd4e5da5Sopenharmony_ci}
291fd4e5da5Sopenharmony_cifunc (s *server) SignatureHelp(ctx context.Context, p *lsp.SignatureHelpParams) (*lsp.SignatureHelp, error) {
292fd4e5da5Sopenharmony_ci	log.Println("server.SignatureHelp()")
293fd4e5da5Sopenharmony_ci	return nil, nil
294fd4e5da5Sopenharmony_ci}
295fd4e5da5Sopenharmony_cifunc (s *server) Definition(ctx context.Context, p *lsp.DefinitionParams) ([]lsp.Location, error) {
296fd4e5da5Sopenharmony_ci	log.Println("server.Definition()")
297fd4e5da5Sopenharmony_ci	if f := s.getFile(p.TextDocument.URI); f != nil {
298fd4e5da5Sopenharmony_ci		if tok := f.tokAt(p.Position); tok != nil {
299fd4e5da5Sopenharmony_ci			if s := tok.Text(f.res.Lines); s != "" {
300fd4e5da5Sopenharmony_ci				if id, ok := f.res.Identifiers[s]; ok {
301fd4e5da5Sopenharmony_ci					loc := lsp.Location{
302fd4e5da5Sopenharmony_ci						URI:   p.TextDocument.URI,
303fd4e5da5Sopenharmony_ci						Range: rangeToLSP(id.Definition.Range),
304fd4e5da5Sopenharmony_ci					}
305fd4e5da5Sopenharmony_ci					return []lsp.Location{loc}, nil
306fd4e5da5Sopenharmony_ci				}
307fd4e5da5Sopenharmony_ci			}
308fd4e5da5Sopenharmony_ci		}
309fd4e5da5Sopenharmony_ci	}
310fd4e5da5Sopenharmony_ci	return nil, nil
311fd4e5da5Sopenharmony_ci}
312fd4e5da5Sopenharmony_cifunc (s *server) References(ctx context.Context, p *lsp.ReferenceParams) ([]lsp.Location, error) {
313fd4e5da5Sopenharmony_ci	log.Println("server.References()")
314fd4e5da5Sopenharmony_ci	if f := s.getFile(p.TextDocument.URI); f != nil {
315fd4e5da5Sopenharmony_ci		if tok := f.tokAt(p.Position); tok != nil {
316fd4e5da5Sopenharmony_ci			if s := tok.Text(f.res.Lines); s != "" {
317fd4e5da5Sopenharmony_ci				if id, ok := f.res.Identifiers[s]; ok {
318fd4e5da5Sopenharmony_ci					locs := make([]lsp.Location, len(id.References))
319fd4e5da5Sopenharmony_ci					for i, r := range id.References {
320fd4e5da5Sopenharmony_ci						locs[i] = lsp.Location{
321fd4e5da5Sopenharmony_ci							URI:   p.TextDocument.URI,
322fd4e5da5Sopenharmony_ci							Range: rangeToLSP(r.Range),
323fd4e5da5Sopenharmony_ci						}
324fd4e5da5Sopenharmony_ci					}
325fd4e5da5Sopenharmony_ci					return locs, nil
326fd4e5da5Sopenharmony_ci				}
327fd4e5da5Sopenharmony_ci			}
328fd4e5da5Sopenharmony_ci		}
329fd4e5da5Sopenharmony_ci	}
330fd4e5da5Sopenharmony_ci	return nil, nil
331fd4e5da5Sopenharmony_ci}
332fd4e5da5Sopenharmony_cifunc (s *server) DocumentHighlight(ctx context.Context, p *lsp.DocumentHighlightParams) ([]lsp.DocumentHighlight, error) {
333fd4e5da5Sopenharmony_ci	log.Println("server.DocumentHighlight()")
334fd4e5da5Sopenharmony_ci	return nil, nil
335fd4e5da5Sopenharmony_ci}
336fd4e5da5Sopenharmony_cifunc (s *server) DocumentSymbol(ctx context.Context, p *lsp.DocumentSymbolParams) ([]lsp.DocumentSymbol, error) {
337fd4e5da5Sopenharmony_ci	log.Println("server.DocumentSymbol()")
338fd4e5da5Sopenharmony_ci	return nil, nil
339fd4e5da5Sopenharmony_ci}
340fd4e5da5Sopenharmony_cifunc (s *server) CodeAction(ctx context.Context, p *lsp.CodeActionParams) ([]lsp.CodeAction, error) {
341fd4e5da5Sopenharmony_ci	log.Println("server.CodeAction()")
342fd4e5da5Sopenharmony_ci	return nil, nil
343fd4e5da5Sopenharmony_ci}
344fd4e5da5Sopenharmony_cifunc (s *server) Symbol(ctx context.Context, p *lsp.WorkspaceSymbolParams) ([]lsp.SymbolInformation, error) {
345fd4e5da5Sopenharmony_ci	log.Println("server.Symbol()")
346fd4e5da5Sopenharmony_ci	return nil, nil
347fd4e5da5Sopenharmony_ci}
348fd4e5da5Sopenharmony_cifunc (s *server) CodeLens(ctx context.Context, p *lsp.CodeLensParams) ([]lsp.CodeLens, error) {
349fd4e5da5Sopenharmony_ci	log.Println("server.CodeLens()")
350fd4e5da5Sopenharmony_ci	return nil, nil
351fd4e5da5Sopenharmony_ci}
352fd4e5da5Sopenharmony_cifunc (s *server) ResolveCodeLens(ctx context.Context, p *lsp.CodeLens) (*lsp.CodeLens, error) {
353fd4e5da5Sopenharmony_ci	log.Println("server.ResolveCodeLens()")
354fd4e5da5Sopenharmony_ci	return nil, nil
355fd4e5da5Sopenharmony_ci}
356fd4e5da5Sopenharmony_cifunc (s *server) DocumentLink(ctx context.Context, p *lsp.DocumentLinkParams) ([]lsp.DocumentLink, error) {
357fd4e5da5Sopenharmony_ci	log.Println("server.DocumentLink()")
358fd4e5da5Sopenharmony_ci	return nil, nil
359fd4e5da5Sopenharmony_ci}
360fd4e5da5Sopenharmony_cifunc (s *server) ResolveDocumentLink(ctx context.Context, p *lsp.DocumentLink) (*lsp.DocumentLink, error) {
361fd4e5da5Sopenharmony_ci	log.Println("server.ResolveDocumentLink()")
362fd4e5da5Sopenharmony_ci	return nil, nil
363fd4e5da5Sopenharmony_ci}
364fd4e5da5Sopenharmony_cifunc (s *server) Formatting(ctx context.Context, p *lsp.DocumentFormattingParams) ([]lsp.TextEdit, error) {
365fd4e5da5Sopenharmony_ci	log.Println("server.Formatting()")
366fd4e5da5Sopenharmony_ci	if f := s.getFile(p.TextDocument.URI); f != nil {
367fd4e5da5Sopenharmony_ci		// Start by measuring the distance from the start of each line to the
368fd4e5da5Sopenharmony_ci		// first opcode on that line.
369fd4e5da5Sopenharmony_ci		lineInstOffsets, maxInstOffset, instOffset, curOffset := []int{}, 0, 0, -1
370fd4e5da5Sopenharmony_ci		for _, t := range f.res.Tokens {
371fd4e5da5Sopenharmony_ci			curOffset++ // whitespace between tokens
372fd4e5da5Sopenharmony_ci			switch t.Type {
373fd4e5da5Sopenharmony_ci			case parser.Ident:
374fd4e5da5Sopenharmony_ci				if _, isInst := schema.Opcodes[t.Text(f.res.Lines)]; isInst && instOffset == 0 {
375fd4e5da5Sopenharmony_ci					instOffset = curOffset
376fd4e5da5Sopenharmony_ci					continue
377fd4e5da5Sopenharmony_ci				}
378fd4e5da5Sopenharmony_ci			case parser.Newline:
379fd4e5da5Sopenharmony_ci				lineInstOffsets = append(lineInstOffsets, instOffset)
380fd4e5da5Sopenharmony_ci				if instOffset > maxInstOffset {
381fd4e5da5Sopenharmony_ci					maxInstOffset = instOffset
382fd4e5da5Sopenharmony_ci				}
383fd4e5da5Sopenharmony_ci				curOffset, instOffset = -1, 0
384fd4e5da5Sopenharmony_ci			default:
385fd4e5da5Sopenharmony_ci				curOffset += utf8.RuneCountInString(t.Text(f.res.Lines))
386fd4e5da5Sopenharmony_ci			}
387fd4e5da5Sopenharmony_ci		}
388fd4e5da5Sopenharmony_ci		lineInstOffsets = append(lineInstOffsets, instOffset)
389fd4e5da5Sopenharmony_ci
390fd4e5da5Sopenharmony_ci		// Now rewrite each of the lines, adding padding at the start of the
391fd4e5da5Sopenharmony_ci		// line for alignment.
392fd4e5da5Sopenharmony_ci		sb, newline := strings.Builder{}, true
393fd4e5da5Sopenharmony_ci		for _, t := range f.res.Tokens {
394fd4e5da5Sopenharmony_ci			if newline {
395fd4e5da5Sopenharmony_ci				newline = false
396fd4e5da5Sopenharmony_ci				indent := maxInstOffset - lineInstOffsets[0]
397fd4e5da5Sopenharmony_ci				lineInstOffsets = lineInstOffsets[1:]
398fd4e5da5Sopenharmony_ci				switch t.Type {
399fd4e5da5Sopenharmony_ci				case parser.Newline, parser.Comment:
400fd4e5da5Sopenharmony_ci				default:
401fd4e5da5Sopenharmony_ci					for s := 0; s < indent; s++ {
402fd4e5da5Sopenharmony_ci						sb.WriteRune(' ')
403fd4e5da5Sopenharmony_ci					}
404fd4e5da5Sopenharmony_ci				}
405fd4e5da5Sopenharmony_ci			} else if t.Type != parser.Newline {
406fd4e5da5Sopenharmony_ci				sb.WriteString(" ")
407fd4e5da5Sopenharmony_ci			}
408fd4e5da5Sopenharmony_ci
409fd4e5da5Sopenharmony_ci			sb.WriteString(t.Text(f.res.Lines))
410fd4e5da5Sopenharmony_ci			if t.Type == parser.Newline {
411fd4e5da5Sopenharmony_ci				newline = true
412fd4e5da5Sopenharmony_ci			}
413fd4e5da5Sopenharmony_ci		}
414fd4e5da5Sopenharmony_ci
415fd4e5da5Sopenharmony_ci		formatted := sb.String()
416fd4e5da5Sopenharmony_ci
417fd4e5da5Sopenharmony_ci		// Every good file ends with a single new line.
418fd4e5da5Sopenharmony_ci		formatted = strings.TrimRight(formatted, "\n") + "\n"
419fd4e5da5Sopenharmony_ci
420fd4e5da5Sopenharmony_ci		return []lsp.TextEdit{
421fd4e5da5Sopenharmony_ci			{
422fd4e5da5Sopenharmony_ci				Range:   rangeToLSP(f.fullRange),
423fd4e5da5Sopenharmony_ci				NewText: formatted,
424fd4e5da5Sopenharmony_ci			},
425fd4e5da5Sopenharmony_ci		}, nil
426fd4e5da5Sopenharmony_ci	}
427fd4e5da5Sopenharmony_ci	return nil, nil
428fd4e5da5Sopenharmony_ci}
429fd4e5da5Sopenharmony_cifunc (s *server) RangeFormatting(ctx context.Context, p *lsp.DocumentRangeFormattingParams) ([]lsp.TextEdit, error) {
430fd4e5da5Sopenharmony_ci	log.Println("server.RangeFormatting()")
431fd4e5da5Sopenharmony_ci	return nil, nil
432fd4e5da5Sopenharmony_ci}
433fd4e5da5Sopenharmony_cifunc (s *server) OnTypeFormatting(ctx context.Context, p *lsp.DocumentOnTypeFormattingParams) ([]lsp.TextEdit, error) {
434fd4e5da5Sopenharmony_ci	log.Println("server.OnTypeFormatting()")
435fd4e5da5Sopenharmony_ci	return nil, nil
436fd4e5da5Sopenharmony_ci}
437fd4e5da5Sopenharmony_cifunc (s *server) Rename(ctx context.Context, p *lsp.RenameParams) (*lsp.WorkspaceEdit, error) {
438fd4e5da5Sopenharmony_ci	log.Println("server.Rename()")
439fd4e5da5Sopenharmony_ci	if f := s.getFile(p.TextDocument.URI); f != nil {
440fd4e5da5Sopenharmony_ci		if tok := f.tokAt(p.Position); tok != nil {
441fd4e5da5Sopenharmony_ci			if s := tok.Text(f.res.Lines); s != "" {
442fd4e5da5Sopenharmony_ci				if id, ok := f.res.Identifiers[s]; ok {
443fd4e5da5Sopenharmony_ci					changes := make([]lsp.TextEdit, len(id.References))
444fd4e5da5Sopenharmony_ci					for i, r := range id.References {
445fd4e5da5Sopenharmony_ci						changes[i].Range = rangeToLSP(r.Range)
446fd4e5da5Sopenharmony_ci						changes[i].NewText = p.NewName
447fd4e5da5Sopenharmony_ci					}
448fd4e5da5Sopenharmony_ci					m := map[string][]lsp.TextEdit{}
449fd4e5da5Sopenharmony_ci					m[p.TextDocument.URI] = changes
450fd4e5da5Sopenharmony_ci					return &lsp.WorkspaceEdit{Changes: &m}, nil
451fd4e5da5Sopenharmony_ci				}
452fd4e5da5Sopenharmony_ci			}
453fd4e5da5Sopenharmony_ci		}
454fd4e5da5Sopenharmony_ci	}
455fd4e5da5Sopenharmony_ci	return nil, nil
456fd4e5da5Sopenharmony_ci}
457fd4e5da5Sopenharmony_cifunc (s *server) PrepareRename(ctx context.Context, p *lsp.PrepareRenameParams) (*lsp.Range, error) {
458fd4e5da5Sopenharmony_ci	log.Println("server.PrepareRename()")
459fd4e5da5Sopenharmony_ci	return nil, nil
460fd4e5da5Sopenharmony_ci}
461fd4e5da5Sopenharmony_cifunc (s *server) ExecuteCommand(ctx context.Context, p *lsp.ExecuteCommandParams) (interface{}, error) {
462fd4e5da5Sopenharmony_ci	log.Println("server.ExecuteCommand()")
463fd4e5da5Sopenharmony_ci	return nil, nil
464fd4e5da5Sopenharmony_ci}
465fd4e5da5Sopenharmony_ci
466fd4e5da5Sopenharmony_cifunc (s *server) processFile(ctx context.Context, uri, source string) error {
467fd4e5da5Sopenharmony_ci	log.Println("server.DidOpen()")
468fd4e5da5Sopenharmony_ci	res, err := parser.Parse(source)
469fd4e5da5Sopenharmony_ci	if err != nil {
470fd4e5da5Sopenharmony_ci		return err
471fd4e5da5Sopenharmony_ci	}
472fd4e5da5Sopenharmony_ci	fullRange := parser.Range{
473fd4e5da5Sopenharmony_ci		Start: parser.Position{Line: 1, Column: 1},
474fd4e5da5Sopenharmony_ci		End:   parser.Position{Line: len(res.Lines), Column: utf8.RuneCountInString(res.Lines[len(res.Lines)-1]) + 1},
475fd4e5da5Sopenharmony_ci	}
476fd4e5da5Sopenharmony_ci
477fd4e5da5Sopenharmony_ci	s.filesMutex.Lock()
478fd4e5da5Sopenharmony_ci	s.files[uri] = &file{
479fd4e5da5Sopenharmony_ci		fullRange: fullRange,
480fd4e5da5Sopenharmony_ci		res:       res,
481fd4e5da5Sopenharmony_ci	}
482fd4e5da5Sopenharmony_ci	s.filesMutex.Unlock()
483fd4e5da5Sopenharmony_ci
484fd4e5da5Sopenharmony_ci	dp := lsp.PublishDiagnosticsParams{URI: uri, Diagnostics: make([]lsp.Diagnostic, len(res.Diagnostics))}
485fd4e5da5Sopenharmony_ci	for i, d := range res.Diagnostics {
486fd4e5da5Sopenharmony_ci		dp.Diagnostics[i] = diagnosticToLSP(d)
487fd4e5da5Sopenharmony_ci	}
488fd4e5da5Sopenharmony_ci	s.client.PublishDiagnostics(ctx, &dp)
489fd4e5da5Sopenharmony_ci	return nil
490fd4e5da5Sopenharmony_ci}
491fd4e5da5Sopenharmony_ci
492fd4e5da5Sopenharmony_cifunc (s *server) getFile(uri string) *file {
493fd4e5da5Sopenharmony_ci	s.filesMutex.Lock()
494fd4e5da5Sopenharmony_ci	defer s.filesMutex.Unlock()
495fd4e5da5Sopenharmony_ci	return s.files[uri]
496fd4e5da5Sopenharmony_ci}
497fd4e5da5Sopenharmony_ci
498fd4e5da5Sopenharmony_cifunc diagnosticToLSP(d parser.Diagnostic) lsp.Diagnostic {
499fd4e5da5Sopenharmony_ci	return lsp.Diagnostic{
500fd4e5da5Sopenharmony_ci		Range:    rangeToLSP(d.Range),
501fd4e5da5Sopenharmony_ci		Severity: severityToLSP(d.Severity),
502fd4e5da5Sopenharmony_ci		Message:  d.Message,
503fd4e5da5Sopenharmony_ci	}
504fd4e5da5Sopenharmony_ci}
505fd4e5da5Sopenharmony_ci
506fd4e5da5Sopenharmony_cifunc severityToLSP(s parser.Severity) lsp.DiagnosticSeverity {
507fd4e5da5Sopenharmony_ci	switch s {
508fd4e5da5Sopenharmony_ci	case parser.SeverityError:
509fd4e5da5Sopenharmony_ci		return lsp.SeverityError
510fd4e5da5Sopenharmony_ci	case parser.SeverityWarning:
511fd4e5da5Sopenharmony_ci		return lsp.SeverityWarning
512fd4e5da5Sopenharmony_ci	case parser.SeverityInformation:
513fd4e5da5Sopenharmony_ci		return lsp.SeverityInformation
514fd4e5da5Sopenharmony_ci	case parser.SeverityHint:
515fd4e5da5Sopenharmony_ci		return lsp.SeverityHint
516fd4e5da5Sopenharmony_ci	default:
517fd4e5da5Sopenharmony_ci		log.Panicf("Invalid severity '%d'", int(s))
518fd4e5da5Sopenharmony_ci		return lsp.SeverityError
519fd4e5da5Sopenharmony_ci	}
520fd4e5da5Sopenharmony_ci}
521fd4e5da5Sopenharmony_ci
522fd4e5da5Sopenharmony_cifunc rangeToLSP(r parser.Range) lsp.Range {
523fd4e5da5Sopenharmony_ci	return lsp.Range{
524fd4e5da5Sopenharmony_ci		Start: positionToLSP(r.Start),
525fd4e5da5Sopenharmony_ci		End:   positionToLSP(r.End),
526fd4e5da5Sopenharmony_ci	}
527fd4e5da5Sopenharmony_ci}
528fd4e5da5Sopenharmony_ci
529fd4e5da5Sopenharmony_cifunc positionToLSP(r parser.Position) lsp.Position {
530fd4e5da5Sopenharmony_ci	return lsp.Position{
531fd4e5da5Sopenharmony_ci		Line:      float64(r.Line - 1),
532fd4e5da5Sopenharmony_ci		Character: float64(r.Column - 1),
533fd4e5da5Sopenharmony_ci	}
534fd4e5da5Sopenharmony_ci}
535