firefly-compiler 0.5.39 → 0.5.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +158 -157
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +275 -275
  6. package/compiler/Compiler.ff +234 -234
  7. package/compiler/Dependencies.ff +186 -186
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/JsEmitter.ff +1437 -1437
  10. package/compiler/LspHook.ff +202 -202
  11. package/compiler/ModuleCache.ff +178 -178
  12. package/compiler/Workspace.ff +88 -88
  13. package/core/.firefly/include/package.json +5 -5
  14. package/core/.firefly/package.ff +2 -2
  15. package/core/Any.ff +25 -25
  16. package/core/Array.ff +298 -298
  17. package/core/Atomic.ff +63 -63
  18. package/core/Box.ff +7 -7
  19. package/core/BrowserSystem.ff +40 -40
  20. package/core/BuildSystem.ff +156 -156
  21. package/core/Crypto.ff +94 -94
  22. package/core/Equal.ff +41 -41
  23. package/core/Error.ff +25 -25
  24. package/core/HttpClient.ff +142 -142
  25. package/core/Instant.ff +24 -24
  26. package/core/Js.ff +305 -305
  27. package/core/JsSystem.ff +135 -135
  28. package/core/Json.ff +423 -423
  29. package/core/List.ff +482 -482
  30. package/core/Lock.ff +108 -108
  31. package/core/NodeSystem.ff +198 -198
  32. package/core/Ordering.ff +160 -160
  33. package/core/Path.ff +377 -378
  34. package/core/Queue.ff +90 -90
  35. package/core/Random.ff +140 -140
  36. package/core/RbMap.ff +216 -216
  37. package/core/Show.ff +44 -44
  38. package/core/SourceLocation.ff +68 -68
  39. package/core/Task.ff +165 -165
  40. package/experimental/benchmarks/ListGrab.ff +23 -23
  41. package/experimental/benchmarks/ListGrab.java +55 -55
  42. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  43. package/experimental/benchmarks/Pyrotek45.java +64 -64
  44. package/experimental/bidirectional/Bidi.ff +88 -88
  45. package/experimental/lines/Main.ff +40 -40
  46. package/experimental/random/Index.ff +53 -53
  47. package/experimental/random/Process.ff +120 -120
  48. package/experimental/random/RunLength.ff +65 -65
  49. package/experimental/random/Scrape.ff +51 -51
  50. package/experimental/random/Symbols.ff +73 -73
  51. package/experimental/random/Tensor.ff +52 -52
  52. package/experimental/random/Units.ff +36 -36
  53. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  54. package/experimental/s3/S3TestPut.ff +16 -16
  55. package/experimental/tests/TestJson.ff +26 -26
  56. package/firefly.sh +0 -0
  57. package/fireflysite/.firefly/package.ff +4 -4
  58. package/fireflysite/CommunityOverview.ff +20 -20
  59. package/fireflysite/CountingButtonDemo.ff +58 -58
  60. package/fireflysite/DocumentParser.ff +325 -325
  61. package/fireflysite/ExamplesOverview.ff +40 -40
  62. package/fireflysite/FrontPage.ff +344 -344
  63. package/fireflysite/GettingStarted.ff +45 -45
  64. package/fireflysite/Guide.ff +456 -456
  65. package/fireflysite/Main.ff +163 -163
  66. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  67. package/fireflysite/PackagesOverview.ff +49 -49
  68. package/fireflysite/PostgresqlDemo.ff +34 -34
  69. package/fireflysite/ReferenceAll.ff +18 -18
  70. package/fireflysite/ReferenceIntroduction.ff +11 -11
  71. package/fireflysite/Styles.ff +567 -567
  72. package/fireflysite/Test.ff +121 -121
  73. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
  74. package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
  75. package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
  76. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
  77. package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -235
  78. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
  79. package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
  80. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
  81. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
  82. package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
  83. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
  84. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
  85. package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
  86. package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
  87. package/lsp/.firefly/package.ff +1 -1
  88. package/lsp/CompletionHandler.ff +827 -827
  89. package/lsp/Handler.ff +714 -714
  90. package/lsp/HoverHandler.ff +79 -79
  91. package/lsp/LanguageServer.ff +272 -272
  92. package/lsp/SignatureHelpHandler.ff +55 -55
  93. package/lsp/SymbolHandler.ff +181 -181
  94. package/lsp/TestReferences.ff +17 -17
  95. package/lsp/TestReferencesCase.ff +7 -7
  96. package/lsp/stderr.txt +1 -1
  97. package/lsp/stdout.txt +34 -34
  98. package/lux/.firefly/package.ff +1 -1
  99. package/lux/Css.ff +648 -648
  100. package/lux/CssTest.ff +48 -48
  101. package/lux/Lux.ff +608 -608
  102. package/lux/LuxEvent.ff +79 -79
  103. package/lux/Main.ff +123 -123
  104. package/lux/Main2.ff +143 -143
  105. package/lux/TestDry.ff +28 -28
  106. package/output/js/ff/compiler/Builder.mjs +36 -36
  107. package/output/js/ff/core/Path.mjs +0 -2
  108. package/package.json +1 -1
  109. package/rpc/.firefly/package.ff +1 -1
  110. package/rpc/Rpc.ff +70 -70
  111. package/s3/.firefly/package.ff +1 -1
  112. package/s3/S3.ff +92 -92
  113. package/vscode/LICENSE.txt +21 -21
  114. package/vscode/Prepublish.ff +15 -15
  115. package/vscode/README.md +16 -16
  116. package/vscode/client/package-lock.json +544 -544
  117. package/vscode/client/package.json +22 -22
  118. package/vscode/client/src/extension.ts +104 -104
  119. package/vscode/icons/firefly-icon.svg +10 -10
  120. package/vscode/language-configuration.json +61 -61
  121. package/vscode/package-lock.json +3623 -3623
  122. package/vscode/package.json +1 -1
  123. package/vscode/snippets.json +241 -241
  124. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  125. package/webserver/.firefly/include/package.json +5 -5
  126. package/webserver/.firefly/package.ff +2 -2
  127. package/webserver/WebServer.ff +647 -647
  128. package/websocket/.firefly/package.ff +1 -1
  129. package/websocket/WebSocket.ff +100 -100
package/lsp/Handler.ff CHANGED
@@ -1,714 +1,714 @@
1
- import JsEmitter from ff:compiler
2
- import Builder from ff:compiler
3
- import Syntax from ff:compiler
4
- import Tokenizer from ff:compiler
5
- import Parser from ff:compiler
6
- import Token from ff:compiler
7
- import Environment from ff:compiler
8
- import Unification from ff:compiler
9
- import LspHook from ff:compiler
10
- import ModuleCache from ff:compiler
11
- import DependencyLock from ff:compiler
12
- import HoverHandler
13
- import CompletionHandler
14
- import SignatureHelpHandler
15
- import SymbolHandler
16
-
17
- capability Handler(
18
- fireflyPath: Path
19
- mutable rootPath: Option[Path]
20
- mutable virtualFiles: Map[String, String]
21
- mutable cancelledRequests: Set[MessageId]
22
- mutable responseCache: Map[TokenRequestCacheKey, ResultOrError]
23
- mutable fileSymbolsCache: Map[String, List[DocumentSymbol]]
24
- moduleCache: ModuleCache
25
- dependencyLock: DependencyLock
26
- )
27
-
28
- data TokenRequestCacheKey(
29
- method: String
30
- targetAt: Location
31
- includeDeclaration: Bool
32
- )
33
-
34
- data MessageId {
35
- MessageIdInt(id: Int)
36
- MessageIdString(id: String)
37
- }
38
-
39
- data ResultOrError {
40
- Result(result: String)
41
- Error(code: Int, message: String)
42
- }
43
-
44
- data LanguageServerException(message: String)
45
-
46
- data TokenLocation(
47
- file: String
48
- startLine: Int
49
- startColumn: Int
50
- endLine: Int
51
- endColumn: Int
52
- raw: String
53
- followedByLeftBracket: Bool
54
- )
55
-
56
- data Definition(at: Location, name: String, local: Bool)
57
-
58
-
59
- extend self: Handler {
60
-
61
- handleNotification(system: NodeSystem, method: String, parameters: Map[String, Json], version: Int): List[Pair[String, Json]] {
62
- method.{
63
- | "initialized" =>
64
- []
65
- | "textDocument/didOpen" =>
66
- self.handleDidOpen(system, parameters)
67
- []
68
- | "textDocument/didChange" =>
69
- self.handleDidChange(system, parameters)
70
- let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
71
- [Pair("textDocument/publishDiagnostics", diagnostics)]
72
- | "textDocument/didClose" =>
73
- self.handleDidClose(system, parameters)
74
- let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
75
- [Pair("textDocument/publishDiagnostics", diagnostics)]
76
- | "custom/focusDocument" =>
77
- let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
78
- [Pair("textDocument/publishDiagnostics", diagnostics)]
79
- | "workspace/didRenameFiles" =>
80
- let diagnostics = self.handleClearDiagnostic(system, parameters)
81
- diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
82
- | "workspace/didDeleteFiles" =>
83
- let diagnostics = self.handleClearDiagnostic(system, parameters)
84
- diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
85
- | "workspace/didChangeWatchedFiles" =>
86
- self.handleDidChangeWatched(system, parameters)
87
- | "exit" =>
88
- system.exit(0)
89
- | _ =>
90
- []
91
- }
92
- }
93
-
94
- handleRequest(system: NodeSystem, method: String, parameters: Map[String, Json], version: Int): ResultOrError {
95
- method.{
96
- | "initialize" => self.handleInitialize(system, parameters)
97
- //| "textDocument/diagnostic" => self.handleDiagnostic(system, parameters)
98
- | "textDocument/documentSymbol" => self.handleDocumentSymbol(system, parameters)
99
- | "textDocument/completion" => self.handleCompletion(system, parameters, version)
100
- | "textDocument/signatureHelp" => self.handleSignatureHelp(system, parameters, version)
101
- | "textDocument/hover" =>
102
- self.handleTokenRequestWithCache(system, method, parameters) {key =>
103
- self.handleHover(system, key.targetAt, goToDefinition = False, version)
104
- }
105
- | "textDocument/definition" =>
106
- self.handleTokenRequestWithCache(system, method, parameters) {key =>
107
- self.handleHover(system, key.targetAt, goToDefinition = True, version)
108
- }
109
- | "textDocument/references" =>
110
- self.handleTokenRequestWithCache(system, method, parameters) {key =>
111
- self.handleReferences(system, key.targetAt, key.includeDeclaration, local = False, version)
112
- }
113
- | "textDocument/rename" => self.handleRename(system, parameters, version)
114
- | "textDocument/documentHighlight" =>
115
- self.handleTokenRequestWithCache(system, method, parameters) {key =>
116
- self.handleReferences(system, key.targetAt, includeDeclaration = True, local = True, version)
117
- }
118
- | "workspace/symbol" =>
119
- self.printTime(system.mainTask(), "handleWorkspaceSymbol") {
120
- self.handleWorkspaceSymbol(system, parameters)
121
- }
122
- | "shutdown" => Result(Json.null().write())
123
- | _ => self.handleUnsupported()
124
- }
125
- }
126
-
127
- handleTokenRequestWithCache(
128
- system: NodeSystem
129
- method: String
130
- parameters: Map[String, Json]
131
- handle: TokenRequestCacheKey => ResultOrError
132
- ): ResultOrError {
133
- let targetAt = self.findTokenFromParameters(system, parameters)
134
- let includeDeclaration = parameters.get("context").{
135
- | Some(context) {context.field("includeDeclaration") | includeDeclaration} {includeDeclaration.getBool() | Some(b)} => b
136
- | _ => True
137
- }
138
- let cacheKey = TokenRequestCacheKey(method, targetAt, includeDeclaration)
139
- self.responseCache.get(cacheKey).{
140
- | Some(hit) => hit
141
- | None =>
142
- let result = handle(cacheKey)
143
- self.responseCache = self.responseCache.add(cacheKey, result)
144
- result
145
- }
146
- }
147
-
148
- handleUnsupported(): ResultOrError {
149
- Error(1234, "Unsupported method")
150
- }
151
-
152
- handleInitialize(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
153
- self.rootPath = parameters.grab("rootUri").getString().map {system.pathFromUrl(_)}
154
-
155
- let anyFireflyFile = Json.object()
156
- .with("filters", [
157
- Json.object()
158
- .with("pattern", Json.object()
159
- .with("glob", "**/*.ff")
160
- .with("matches", "file")
161
- )
162
- ])
163
-
164
- let o = Json.object()
165
- .with("capabilities", Json.object()
166
- .with("textDocumentSync", Json.object()
167
- .with("openClose", True)
168
- .with("change", 1 /* TextDocumentSyncKind.Full */)
169
- )
170
- .with("hoverProvider", True)
171
- .with("definitionProvider", True)
172
- .with("documentHighlightProvider", True)
173
- /*.with("diagnosticProvider", Json.object()
174
- .with("interFileDependencies", False)
175
- .with("workspaceDiagnostics", False)
176
- )*/
177
- .with("documentSymbolProvider", True)
178
- .with("completionProvider", Json.object()
179
- .with("triggerCharacters", [".", " ", "("])
180
- .with("allCommitCharacters", [";"])
181
- )
182
- .with("signatureHelpProvider", Json.object()
183
- .with("triggerCharacters", ["("])
184
- .with("retriggerCharacters", [","])
185
- )
186
- .with("referencesProvider", True)
187
- .with("workspaceSymbolProvider", True)
188
- .with("workspace", Json.object()
189
- .with("fileOperations", Json.object()
190
- .with("didRename", anyFireflyFile)
191
- .with("didDelete", anyFireflyFile)
192
- )
193
- )
194
- /*
195
- .with("workspaceFolders", js.object()
196
- .with("supported", True)
197
- .with("changeNotifications", True)
198
- )
199
- .with("fileOperations", js.object()
200
- .with("didCreate", anyFireflyFile)
201
- .with("didRename", anyFireflyFile)
202
- .with("didDelete", anyFireflyFile)
203
- )
204
- )*/
205
- .with("renameProvider", True)
206
- )
207
- .with("serverInfo", Json.object()
208
- .with("name", "Firefly Language Server")
209
- .with("version", "0.0.0")
210
- )
211
- Result(o.write())
212
- }
213
-
214
- /*handleDiagnostic(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
215
- let js = system.js()
216
- let uri = parameters.grab("textDocument").field("uri").grabString()
217
- let path = system.pathFromUrl(uri)
218
- let fireflyPath = system.path(".")
219
- let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, LspHook.disabled(), True)
220
- let diagnostics = errors.map {| CompileError(at, message) =>
221
- let tokenLocation = self.findToken(system, at)
222
- Json.object()
223
- .with("range", self.tokenLocationToLspRange(tokenLocation))
224
- .with("severity", 1)
225
- .with("message", message)
226
- }
227
- let o = Json.object()
228
- .with("kind", "full")
229
- .with("items", diagnostics)
230
- Result(o.write())
231
- }*/
232
-
233
- handleFocusDiagnostic(system: NodeSystem, parameters: Map[String, Json], version: Int): Json {
234
- let js = system.js()
235
- let uri = parameters.grab("textDocument").field("uri").grabString()
236
- let path = system.pathFromUrl(uri)
237
- let fireflyPath = system.path(".")
238
- let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, LspHook.disabled(), True)
239
- let diagnostics = errors.filter {_.at.file == path.absolute()}.map {| CompileError(at, message) =>
240
- let tokenLocation = self.findToken(system, at)
241
- Json.object()
242
- .with("range", self.tokenLocationToLspRange(tokenLocation))
243
- .with("severity", 1)
244
- .with("message", message)
245
- }
246
- Json.object()
247
- .with("uri", uri)
248
- .with("version", parameters.grab("textDocument").field("version"))
249
- .with("diagnostics", diagnostics)
250
- }
251
-
252
- handleClearDiagnostic(system: NodeSystem, parameters: Map[String, Json]): List[Json] {
253
- let js = system.js()
254
- let files = parameters.grab("files").grabArray()
255
- files.map {file =>
256
- let uri = file.getField("uri").else {file.grabField("oldUri")}
257
- Json.object()
258
- .with("uri", uri)
259
- .with("diagnostics", Json.array([]))
260
- }
261
- }
262
-
263
- handleDocumentSymbol(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
264
- let uri = parameters.grab("textDocument").field("uri").grabString()
265
- let path = system.pathFromUrl(uri)
266
- let symbols = self.getDocumentSymbolsWithCache(system, path)
267
- let lspSymbols = symbols.map {SymbolHandler.documentSymbolToLsp(_)}
268
- Result(Json.array(lspSymbols).write())
269
- }
270
-
271
- getDocumentSymbols(system: NodeSystem, path: Path): List[DocumentSymbol] {
272
- try {
273
- let lspHook = LspHook.new(at = None, definedAt = None, insertIdentifier = False, trackSymbols = True)
274
- let code = self.virtualFiles.get(path.absolute()).else {path.readText()}
275
- let tokens = Tokenizer.tokenize(path.absolute(), code, None, True)
276
- let parser = Parser.new(PackagePair("script", "script"), path.absolute(), tokens, False, lspHook)
277
- parser.parseModuleWithPackageInfo()
278
- SymbolHandler.readAllSymbols(lspHook.results())
279
- } tryCatch {| CompileError(at, message), error =>
280
- Log.trace("handleDocumentSymbol parse error: " + message)
281
- []
282
- } catch {| ReadSymbolsError e, _ =>
283
- Log.trace("handleDocumentSymbol ReadSymbolsError")
284
- []
285
- }
286
- }
287
-
288
- getDocumentSymbolsWithCache(system: NodeSystem, file: Path): List[DocumentSymbol] {
289
- self.fileSymbolsCache.get(file.absolute()).{
290
- | Some(symbols) =>
291
- //Log.trace("HIT symbols: " + file)
292
- symbols
293
- | None =>
294
- //Log.trace("MISS symbols: " + file)
295
- let symbols = self.getDocumentSymbols(system, file)
296
- self.fileSymbolsCache = self.fileSymbolsCache.add(file.absolute(), symbols)
297
- symbols
298
- }
299
- }
300
-
301
- handleWorkspaceSymbol(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
302
- self.rootPath.or {Error(-32803 /*Request Failed*/, "Open a folder to enable workspace symbols")}: rootPath =>
303
- let files = if(rootPath.isFile()) {
304
- [rootPath]
305
- } else {
306
- self.printTime(system.mainTask(), "findFireflyFiles") {Builder.findFireflyFiles(rootPath, None, Set.new())}
307
- }
308
- let root = if(rootPath.isFile()) {rootPath.parent().grab()} else {rootPath}
309
- let query = parameters.grab("query").grabString()
310
- let symbols = files.flatMap {file =>
311
- let documentSymbols = self.getDocumentSymbolsWithCache(system, file)
312
- let workspaceSymbols = documentSymbols.flatMap {SymbolHandler.documentToWorkspaceSymbols(_, None)}
313
- if(query == "") {
314
- workspaceSymbols.filter {_.kind == SType}
315
- } else {
316
- workspaceSymbols.filter {SymbolHandler.symbolFilter(_, query)}
317
- }
318
- }
319
- let limitedSymbols = symbols.sortBy {_.name.size()}.takeFirst(100)
320
- let jsSymbols = limitedSymbols.map {SymbolHandler.workspaceSymbolToLsp(root, _)}
321
- Result(Json.array(jsSymbols).write())
322
- }
323
-
324
- handleDidChangeWatched(system: NodeSystem, parameters: Map[String, Json]): List[Pair[String, Json]] {
325
- let creates = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 1}
326
- let changes = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 2}
327
- let deletes = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 3}
328
- creates.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
329
- changes.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
330
- deletes.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
331
- let diagnostics = self.handleClearDiagnostic(system, [Pair("files", Json.array(deletes))].toMap())
332
- diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
333
- }
334
-
335
- handleDidOpen(system: NodeSystem, parameters: Map[String, Json]): Unit {
336
- let uri = parameters.grab("textDocument").field("uri").grabString()
337
- let path = system.pathFromUrl(uri)
338
- if(self.rootPath.isEmpty()) {
339
- Log.trace("Using file as rootPath: " + path.absolute())
340
- self.rootPath = Some(path)
341
- }
342
- }
343
-
344
- handleDidChange(system: NodeSystem, parameters: Map[String, Json]): Unit {
345
- let uri = parameters.grab("textDocument").field("uri").grabString()
346
- let path = system.pathFromUrl(uri)
347
- self.responseCache = Map.new()
348
- self.fileSymbolsCache = self.fileSymbolsCache.remove(path.absolute())
349
- self.moduleCache.invalidate(path.absolute())
350
- let contentChanges = parameters.grab("contentChanges").grabArray()
351
- if(contentChanges.size() != 1) {throw(LanguageServerException("Expected a single element in contentChanges"))} else:
352
- let contentChange = contentChanges.grab(0)
353
- if(!contentChange.field("range").isNull()) {throw(LanguageServerException("Expected a complete contentChange"))} else:
354
- let content = contentChange.field("text").grabString()
355
- self.virtualFiles = self.virtualFiles.add(path.absolute(), content)
356
- }
357
-
358
- handleDidClose(system: NodeSystem, parameters: Map[String, Json]): Unit {
359
- let uri = parameters.grab("textDocument").field("uri").grabString()
360
- let path = system.pathFromUrl(uri)
361
- self.virtualFiles = self.virtualFiles.remove(path.absolute())
362
- }
363
-
364
- handleHover(system: NodeSystem, targetAt: Location, goToDefinition: Bool, version: Int): ResultOrError {
365
- let lspHook = LspHook.new(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
366
- let path = system.path(targetAt.file)
367
- let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
368
- errors.each {| CompileError(at, message) =>
369
- Log.trace("handleHover check error: " + message)
370
- }
371
- let o = if(goToDefinition) {
372
- HoverHandler.handleGoToDefinition(system, self, lspHook)
373
- } else {
374
- HoverHandler.handleHover(system, self, lspHook)
375
- }
376
- Result(o.write())
377
- }
378
-
379
- handleCompletion(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
380
- let location = self.findLocationFromParameters(system, parameters)
381
- let token = self.findToken(system, location, insertToken = True)
382
- let completionAt = Location(
383
- file = location.file
384
- line = token.startLine
385
- column = token.startColumn
386
- )
387
- let lspHook = LspHook.new(at = Some(completionAt), definedAt = None, insertIdentifier = True, trackSymbols = False)
388
- let path = system.path(completionAt.file)
389
- let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
390
- errors.each {| CompileError(at, message) =>
391
- Log.trace("handleCompletion check error: " + message)
392
- }
393
- let o = CompletionHandler.handleCompletion(lspHook, completionAt.column == 1, token.followedByLeftBracket)
394
- Result(o.write())
395
- }
396
-
397
- handleSignatureHelp(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
398
- let cursorAt = self.findLocationFromParameters(system, parameters)
399
- let lspHook = LspHook.new(at = Some(cursorAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
400
- try {
401
- let code = self.virtualFiles.get(cursorAt.file).else {system.path(cursorAt.file).readText()}
402
- let tokens = Tokenizer.tokenize(cursorAt.file, code, None, True)
403
- let parser = Parser.new(PackagePair("script", "script"), cursorAt.file, tokens, False, lspHook)
404
- parser.parseModuleWithPackageInfo()
405
- } catch {| CompileError(at, message), error =>
406
- Log.trace("handleSignatureHelp check error: " + message)
407
- }
408
- let o = lspHook.results().collectFirst {
409
- | ParseArgumentHook h {parameters.get("context") | Some(context)} {
410
- context.field("activeSignatureHelp") | help
411
- } {!help.isNull() && help.hasField("signatures")} {
412
- help.field("signatures").index(0) | activeSignature
413
- } {!activeSignature.isNull()} {
414
- activeSignature.field("parameters").grabArray().last() | Some(fakeParameter)
415
- } {
416
- fakeParameter.field("label").grabString() == "/* Call " + h.callAt.show() + " */"
417
- } =>
418
- Some(SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName))
419
- | ParseArgumentHook h =>
420
- let callLspHook = LspHook.new(at = Some(h.callAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
421
- let path = system.path(cursorAt.file)
422
- let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, callLspHook, True)
423
- errors.each {| CompileError(at, message) =>
424
- Log.trace("handleSignatureHelp check 2 error: " + message)
425
- }
426
- let help = SignatureHelpHandler.handleSignatureHelp(system, callLspHook)
427
- if(!help.isNull()) {
428
- SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName)
429
- }
430
- | _ =>
431
- None
432
- }
433
- Result((o.else {Json.null()}).write())
434
- }
435
-
436
- handleReferences(system: NodeSystem, targetAt: Location, includeDeclaration: Bool, local: Bool, version: Int): ResultOrError {
437
- let tokens = self.findReferences(system, targetAt, local, includeDeclaration, version)
438
- let o = tokens.{
439
- | None => Json.null()
440
- | Some(tokens) =>
441
- let lspTokens = tokens.map {self.tokenLocationToLspLocation(system, _)}
442
- Json.array(lspTokens)
443
- }
444
- Result(o.write())
445
- }
446
-
447
- handleRename(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
448
- let newName = parameters.grab("newName").grabString()
449
- let targetAt = self.findTokenFromParameters(system, parameters)
450
- let tokens = self.findReferences(system, targetAt, local = False, includeDeclaration = True, version = version)
451
- tokens.{
452
- | None => Error(-32602, "Token definition not found") // InvalidParams
453
- | Some([]) => Result(Json.null().write())
454
- | Some([first, ...] @ tokens) =>
455
- let oldName = first.raw
456
- Log.trace("Rename '" + oldName + "' to '" + newName + "'")
457
-
458
- // TODO findReferences returns some bad tokens
459
- let goodTokens = tokens.filter {_.raw == oldName}
460
- let badTokens = tokens.filter {_.raw != oldName}
461
- badTokens.each {t => Log.trace("Rename bad token: " + Show.show(t))}
462
-
463
- let byFile = goodTokens.map {t => Pair(t.file, t)}.group()
464
- let allChanges = byFile.toList().foldLeft(Json.object(), {| o, Pair(file, fileTokens) =>
465
- let uri = system.path(file).url()
466
- let fileChanges = fileTokens.map {tokenLocation =>
467
- Json.object()
468
- .with("range", self.tokenLocationToLspRange(tokenLocation))
469
- .with("newText", newName)
470
- }
471
- o.with(uri, fileChanges)
472
- })
473
- let o = Json.object().with("changes", allChanges)
474
- Result(o.write())
475
- }
476
- }
477
-
478
- findReferences(
479
- system: NodeSystem
480
- targetAt: Location
481
- local: Bool
482
- includeDeclaration: Bool
483
- version: Int
484
- ): Option[List[TokenLocation]] {
485
- let temporaryLspHook = LspHook.new(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
486
- let path = system.path(targetAt.file)
487
- let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, temporaryLspHook, True)
488
- errors.each {| CompileError(at, message) =>
489
- Log.trace("findReferences first check error: " + message + " in " + at.file + ":" + at.line + ":" + at.column)
490
- }
491
-
492
- //Log.trace("defined at hooks: " + Show.show(temporaryLspHook.results().map(LspHook.showHook)))
493
- let definedAtList = temporaryLspHook.results().collect {
494
- | ResolveSymbolHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, local = !h.topLevel))
495
- | ResolveVariantFieldHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
496
- | ResolveTypeHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
497
- | ResolveConstraintHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
498
- | ResolveSignatureHook h {!h.isInstanceMethod} => Some(Definition(h.signature.at, h.signature.name, local = !h.topLevel))
499
- | InferParameterHook h => Some(Definition(h.parameter.at, h.parameter.name, False))
500
- | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
501
- h.parameters.find {_.name == n}.map {Definition(_.at, n, False)}
502
- | InferLookupHook h => Some(Definition(h.symbol.value.definedAt, h.symbol.value.qualifiedName, False))
503
- | InferPatternHook h {h.pattern | PVariantAs p} => Some(Definition(p.variableAt, p.name, False))
504
- | InferPatternHook h {h.pattern | PVariable p} => p.name.map {Definition(p.at, _, False)}
505
- | InferPatternHook h {h.pattern | PAlias p} => Some(Definition(p.at, p.variable, False))
506
- | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
507
- let fieldNames = n.split('$').dropFirst(1)
508
- let fieldDefinedAt = fieldNames.zip(ts).collectFirst {| Pair(name, t) =>
509
- if(name == h.fieldName) {t.at}
510
- }
511
- fieldDefinedAt.map {Definition(_, h.fieldName, False)}
512
- | h => None
513
- }.filter {pair => !pair.at.file.endsWith(">")}
514
-
515
- function unqualify(qualifiedName: String): String {
516
- qualifiedName.split('.').grabLast().split('_').grabLast()
517
- }
518
-
519
- function extractModuleName(qualifiedName: String): String {
520
- qualifiedName.split('/').grabLast().takeWhile {c => c != '.' || c == '_'}
521
- }
522
-
523
- function extractPackagePair(qualifiedName: String): PackagePair {
524
- let group = qualifiedName.split(':').grabFirst()
525
- let name = qualifiedName.split(':').grabLast().takeWhile {_ != '/'}
526
- PackagePair(group, name)
527
- }
528
-
529
- definedAtList.first().{
530
- | Some(definition) =>
531
- let lspHook = LspHook.new(at = None, definedAt = Some(definition.at), insertIdentifier = False, trackSymbols = False)
532
- let localCheck = local || definition.local
533
- let path = if(localCheck) {system.path(targetAt.file)} else {self.rootPath.else {system.path(targetAt.file)}}
534
- let mustContain = Some(definition.name).filter {_ => !localCheck}.map(unqualify)
535
- Log.trace("findReferences definition: " + Show.show(definition))
536
- mustContain.each {Log.trace("mustContain: " + _)}
537
-
538
- let skipFiles = if(definition.name.contains(":")) {
539
- let list = self.moduleCache.filesNotImporting(extractPackagePair(definition.name), extractModuleName(definition.name))
540
- list.toSet()
541
- } else {
542
- self.moduleCache.parsedModules.get(definition.at.file).map {| Pair(module, _) =>
543
- let list = self.moduleCache.filesNotImporting(module.packagePair, module.file.dropLast(3))
544
- list.toSet()
545
- }.else {
546
- Log.trace("No skip list due to unqualified definition.name: " + definition.name)
547
- Set.new()
548
- }
549
- }
550
- let errors = self.check(system, self.fireflyPath, path, mustContain, skipFiles, self.virtualFiles, version, lspHook, True, False)
551
- errors.each {| CompileError(at, message) =>
552
- Log.trace("findReferences second check error: " + message + " in " + at.file + ":" + at.line + ":" + at.column)
553
- }
554
-
555
- let referencesResult = lspHook.results().collect {
556
- | ResolveSymbolHook h => Some(h.symbol.usageAt)
557
- | ResolveVariantFieldHook h => Some(h.symbol.usageAt)
558
- | ResolveTypeHook h => Some(h.symbol.usageAt)
559
- | ResolveConstraintHook h => Some(h.symbol.usageAt)
560
- | ResolveSignatureHook h => Some(h.signature.at)
561
- | InferParameterHook h => Some(h.parameter.at)
562
- | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
563
- h.parameters.find {_.name == n}.map {_ => a.at}
564
- | InferLookupHook h => Some(h.symbol.value.usageAt)
565
- | InferRecordFieldHook h => Some(h.usageAt)
566
- | _ => None
567
- }.filter {at =>
568
- !at.file.endsWith(">") &&
569
- (includeDeclaration || at != definition.at)
570
- }
571
-
572
- //referencesResult.each {Log.trace(Show.show(_))}
573
-
574
- let clientLocations = referencesResult.addAll(
575
- if(includeDeclaration) {[definition.at]} else {[]}
576
- ).distinct().filter {
577
- !local || _.file == targetAt.file
578
- }.map {at =>
579
- self.findToken(system, at)
580
- }
581
- let sorted = clientLocations.sortBy {c => Location(c.file, c.startLine, c.startColumn)}
582
- Some(sorted)
583
- | None =>
584
- None
585
- }
586
- }
587
-
588
- makeNotificationMessage(method: String, params: Json): Json {
589
- Json.object()
590
- .with("jsonrpc", "2.0")
591
- .with("method", method)
592
- .with("params", params)
593
- }
594
-
595
- findToken(system: NodeSystem, at: Location, insertToken: Bool = False): TokenLocation {
596
- let fallback = TokenLocation(
597
- file = at.file
598
- startLine = at.line
599
- startColumn = at.column
600
- endLine = at.line
601
- endColumn = at.column + 1
602
- raw = ""
603
- followedByLeftBracket = True
604
- )
605
- try {
606
- let code = self.virtualFiles.get(at.file).else {system.path(at.file).readText()}
607
- let tokens = Tokenizer.tokenize(at.file, code, Some(at).filter {_ => insertToken}, True)
608
- let token = tokens.toStream().find {token =>
609
- at.line >= token.startLine && at.line <= token.stopLine && (
610
- (at.line > token.startLine || at.column >= 1 + token.startOffset - token.startLineOffset) &&
611
- (at.line < token.stopLine || at.column < 1 + token.stopOffset - token.stopLineOffset || (
612
- (token.kind == LLower || token.kind == LUpper) &&
613
- at.column == 1 + token.stopOffset - token.stopLineOffset
614
- ))
615
- )
616
- }
617
- token.map {t =>
618
- function leftBracketAt(i: Int): Bool {
619
- i >= 0 && i < code.size() &&
620
- (code.grab(i) == '(' || code.grab(i) == '[' || code.grab(i) == '{')
621
- }
622
- let followedByLeftBracket =
623
- leftBracketAt(t.startOffset) ||
624
- leftBracketAt(t.stopOffset) ||
625
- leftBracketAt(t.stopOffset + 1)
626
- TokenLocation(
627
- file = t.file
628
- startLine = t.startLine
629
- startColumn = 1 + t.startOffset - t.startLineOffset
630
- endLine = t.stopLine
631
- endColumn = 1 + t.stopOffset - t.stopLineOffset
632
- raw = t.raw()
633
- followedByLeftBracket = followedByLeftBracket
634
- )
635
- }.else {fallback}
636
- } catchAny {_ =>
637
- fallback
638
- }
639
- }
640
-
641
- findTokenFromParameters(system: NodeSystem, parameters: Map[String, Json]): Location {
642
- let location = self.findLocationFromParameters(system, parameters)
643
- let token = self.findToken(system, location)
644
- Location(
645
- file = location.file
646
- line = token.startLine
647
- column = token.startColumn
648
- )
649
- }
650
-
651
- findLocationFromParameters(system: NodeSystem, parameters: Map[String, Json]): Location {
652
- let uri = parameters.grab("textDocument").field("uri").grabString()
653
- let path = system.pathFromUrl(uri)
654
- let line = parameters.grab("position").field("line").grabInt() + 1
655
- let column = parameters.grab("position").field("character").grabInt() + 1
656
- Location(path.absolute(), line, column)
657
- }
658
-
659
- tokenLocationToLspLocation(system: NodeSystem, tokenLocation: TokenLocation): Json {
660
- Json.object()
661
- .with("uri", system.path(tokenLocation.file).url())
662
- .with("range", self.tokenLocationToLspRange(tokenLocation))
663
- }
664
-
665
- tokenLocationToLspRange(tokenLocation: TokenLocation): Json {
666
- Json.object()
667
- .with("start", Json.object()
668
- .with("line", tokenLocation.startLine - 1)
669
- .with("character", tokenLocation.startColumn - 1)
670
- )
671
- .with("end", Json.object()
672
- .with("line", tokenLocation.endLine - 1)
673
- .with("character", tokenLocation.endColumn - 1)
674
- )
675
- }
676
-
677
- printTime[R](task: Task, label: String, body: () => R): R {
678
- let start = task.elapsed()
679
- let result = body()
680
- let stop = task.elapsed()
681
- let duration = ("" + (stop.seconds - start.seconds)).slice(0, 5)
682
- Log.trace(label + ": " + duration + "s")
683
- result
684
- }
685
-
686
- check(
687
- system: NodeSystem
688
- fireflyPath: Path
689
- path: Path
690
- mustContain: Option[String]
691
- skipFiles: Set[String]
692
- virtualFiles: Map[String, String]
693
- newModuleCacheVersion: Int
694
- lspHook: LspHook
695
- infer: Bool
696
- checkDependencies: Bool = True
697
- ): List[CompileError] {
698
- Builder.check(
699
- system
700
- fireflyPath
701
- path
702
- mustContain
703
- skipFiles
704
- virtualFiles
705
- self.moduleCache
706
- self.dependencyLock
707
- newModuleCacheVersion
708
- lspHook
709
- infer
710
- checkDependencies
711
- )
712
- }
713
-
714
- }
1
+ import JsEmitter from ff:compiler
2
+ import Builder from ff:compiler
3
+ import Syntax from ff:compiler
4
+ import Tokenizer from ff:compiler
5
+ import Parser from ff:compiler
6
+ import Token from ff:compiler
7
+ import Environment from ff:compiler
8
+ import Unification from ff:compiler
9
+ import LspHook from ff:compiler
10
+ import ModuleCache from ff:compiler
11
+ import DependencyLock from ff:compiler
12
+ import HoverHandler
13
+ import CompletionHandler
14
+ import SignatureHelpHandler
15
+ import SymbolHandler
16
+
17
+ capability Handler(
18
+ fireflyPath: Path
19
+ mutable rootPath: Option[Path]
20
+ mutable virtualFiles: Map[String, String]
21
+ mutable cancelledRequests: Set[MessageId]
22
+ mutable responseCache: Map[TokenRequestCacheKey, ResultOrError]
23
+ mutable fileSymbolsCache: Map[String, List[DocumentSymbol]]
24
+ moduleCache: ModuleCache
25
+ dependencyLock: DependencyLock
26
+ )
27
+
28
+ data TokenRequestCacheKey(
29
+ method: String
30
+ targetAt: Location
31
+ includeDeclaration: Bool
32
+ )
33
+
34
+ data MessageId {
35
+ MessageIdInt(id: Int)
36
+ MessageIdString(id: String)
37
+ }
38
+
39
+ data ResultOrError {
40
+ Result(result: String)
41
+ Error(code: Int, message: String)
42
+ }
43
+
44
+ data LanguageServerException(message: String)
45
+
46
+ data TokenLocation(
47
+ file: String
48
+ startLine: Int
49
+ startColumn: Int
50
+ endLine: Int
51
+ endColumn: Int
52
+ raw: String
53
+ followedByLeftBracket: Bool
54
+ )
55
+
56
+ data Definition(at: Location, name: String, local: Bool)
57
+
58
+
59
+ extend self: Handler {
60
+
61
+ handleNotification(system: NodeSystem, method: String, parameters: Map[String, Json], version: Int): List[Pair[String, Json]] {
62
+ method.{
63
+ | "initialized" =>
64
+ []
65
+ | "textDocument/didOpen" =>
66
+ self.handleDidOpen(system, parameters)
67
+ []
68
+ | "textDocument/didChange" =>
69
+ self.handleDidChange(system, parameters)
70
+ let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
71
+ [Pair("textDocument/publishDiagnostics", diagnostics)]
72
+ | "textDocument/didClose" =>
73
+ self.handleDidClose(system, parameters)
74
+ let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
75
+ [Pair("textDocument/publishDiagnostics", diagnostics)]
76
+ | "custom/focusDocument" =>
77
+ let diagnostics = self.handleFocusDiagnostic(system, parameters, version)
78
+ [Pair("textDocument/publishDiagnostics", diagnostics)]
79
+ | "workspace/didRenameFiles" =>
80
+ let diagnostics = self.handleClearDiagnostic(system, parameters)
81
+ diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
82
+ | "workspace/didDeleteFiles" =>
83
+ let diagnostics = self.handleClearDiagnostic(system, parameters)
84
+ diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
85
+ | "workspace/didChangeWatchedFiles" =>
86
+ self.handleDidChangeWatched(system, parameters)
87
+ | "exit" =>
88
+ system.exit(0)
89
+ | _ =>
90
+ []
91
+ }
92
+ }
93
+
94
+ handleRequest(system: NodeSystem, method: String, parameters: Map[String, Json], version: Int): ResultOrError {
95
+ method.{
96
+ | "initialize" => self.handleInitialize(system, parameters)
97
+ //| "textDocument/diagnostic" => self.handleDiagnostic(system, parameters)
98
+ | "textDocument/documentSymbol" => self.handleDocumentSymbol(system, parameters)
99
+ | "textDocument/completion" => self.handleCompletion(system, parameters, version)
100
+ | "textDocument/signatureHelp" => self.handleSignatureHelp(system, parameters, version)
101
+ | "textDocument/hover" =>
102
+ self.handleTokenRequestWithCache(system, method, parameters) {key =>
103
+ self.handleHover(system, key.targetAt, goToDefinition = False, version)
104
+ }
105
+ | "textDocument/definition" =>
106
+ self.handleTokenRequestWithCache(system, method, parameters) {key =>
107
+ self.handleHover(system, key.targetAt, goToDefinition = True, version)
108
+ }
109
+ | "textDocument/references" =>
110
+ self.handleTokenRequestWithCache(system, method, parameters) {key =>
111
+ self.handleReferences(system, key.targetAt, key.includeDeclaration, local = False, version)
112
+ }
113
+ | "textDocument/rename" => self.handleRename(system, parameters, version)
114
+ | "textDocument/documentHighlight" =>
115
+ self.handleTokenRequestWithCache(system, method, parameters) {key =>
116
+ self.handleReferences(system, key.targetAt, includeDeclaration = True, local = True, version)
117
+ }
118
+ | "workspace/symbol" =>
119
+ self.printTime(system.mainTask(), "handleWorkspaceSymbol") {
120
+ self.handleWorkspaceSymbol(system, parameters)
121
+ }
122
+ | "shutdown" => Result(Json.null().write())
123
+ | _ => self.handleUnsupported()
124
+ }
125
+ }
126
+
127
+ handleTokenRequestWithCache(
128
+ system: NodeSystem
129
+ method: String
130
+ parameters: Map[String, Json]
131
+ handle: TokenRequestCacheKey => ResultOrError
132
+ ): ResultOrError {
133
+ let targetAt = self.findTokenFromParameters(system, parameters)
134
+ let includeDeclaration = parameters.get("context").{
135
+ | Some(context) {context.field("includeDeclaration") | includeDeclaration} {includeDeclaration.getBool() | Some(b)} => b
136
+ | _ => True
137
+ }
138
+ let cacheKey = TokenRequestCacheKey(method, targetAt, includeDeclaration)
139
+ self.responseCache.get(cacheKey).{
140
+ | Some(hit) => hit
141
+ | None =>
142
+ let result = handle(cacheKey)
143
+ self.responseCache = self.responseCache.add(cacheKey, result)
144
+ result
145
+ }
146
+ }
147
+
148
+ handleUnsupported(): ResultOrError {
149
+ Error(1234, "Unsupported method")
150
+ }
151
+
152
+ handleInitialize(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
153
+ self.rootPath = parameters.grab("rootUri").getString().map {system.pathFromUrl(_)}
154
+
155
+ let anyFireflyFile = Json.object()
156
+ .with("filters", [
157
+ Json.object()
158
+ .with("pattern", Json.object()
159
+ .with("glob", "**/*.ff")
160
+ .with("matches", "file")
161
+ )
162
+ ])
163
+
164
+ let o = Json.object()
165
+ .with("capabilities", Json.object()
166
+ .with("textDocumentSync", Json.object()
167
+ .with("openClose", True)
168
+ .with("change", 1 /* TextDocumentSyncKind.Full */)
169
+ )
170
+ .with("hoverProvider", True)
171
+ .with("definitionProvider", True)
172
+ .with("documentHighlightProvider", True)
173
+ /*.with("diagnosticProvider", Json.object()
174
+ .with("interFileDependencies", False)
175
+ .with("workspaceDiagnostics", False)
176
+ )*/
177
+ .with("documentSymbolProvider", True)
178
+ .with("completionProvider", Json.object()
179
+ .with("triggerCharacters", [".", " ", "("])
180
+ .with("allCommitCharacters", [";"])
181
+ )
182
+ .with("signatureHelpProvider", Json.object()
183
+ .with("triggerCharacters", ["("])
184
+ .with("retriggerCharacters", [","])
185
+ )
186
+ .with("referencesProvider", True)
187
+ .with("workspaceSymbolProvider", True)
188
+ .with("workspace", Json.object()
189
+ .with("fileOperations", Json.object()
190
+ .with("didRename", anyFireflyFile)
191
+ .with("didDelete", anyFireflyFile)
192
+ )
193
+ )
194
+ /*
195
+ .with("workspaceFolders", js.object()
196
+ .with("supported", True)
197
+ .with("changeNotifications", True)
198
+ )
199
+ .with("fileOperations", js.object()
200
+ .with("didCreate", anyFireflyFile)
201
+ .with("didRename", anyFireflyFile)
202
+ .with("didDelete", anyFireflyFile)
203
+ )
204
+ )*/
205
+ .with("renameProvider", True)
206
+ )
207
+ .with("serverInfo", Json.object()
208
+ .with("name", "Firefly Language Server")
209
+ .with("version", "0.0.0")
210
+ )
211
+ Result(o.write())
212
+ }
213
+
214
+ /*handleDiagnostic(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
215
+ let js = system.js()
216
+ let uri = parameters.grab("textDocument").field("uri").grabString()
217
+ let path = system.pathFromUrl(uri)
218
+ let fireflyPath = system.path(".")
219
+ let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, LspHook.disabled(), True)
220
+ let diagnostics = errors.map {| CompileError(at, message) =>
221
+ let tokenLocation = self.findToken(system, at)
222
+ Json.object()
223
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
224
+ .with("severity", 1)
225
+ .with("message", message)
226
+ }
227
+ let o = Json.object()
228
+ .with("kind", "full")
229
+ .with("items", diagnostics)
230
+ Result(o.write())
231
+ }*/
232
+
233
+ handleFocusDiagnostic(system: NodeSystem, parameters: Map[String, Json], version: Int): Json {
234
+ let js = system.js()
235
+ let uri = parameters.grab("textDocument").field("uri").grabString()
236
+ let path = system.pathFromUrl(uri)
237
+ let fireflyPath = system.path(".")
238
+ let errors = self.check(system, fireflyPath, path, None, Set.new(), self.virtualFiles, version, LspHook.disabled(), True)
239
+ let diagnostics = errors.filter {_.at.file == path.absolute()}.map {| CompileError(at, message) =>
240
+ let tokenLocation = self.findToken(system, at)
241
+ Json.object()
242
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
243
+ .with("severity", 1)
244
+ .with("message", message)
245
+ }
246
+ Json.object()
247
+ .with("uri", uri)
248
+ .with("version", parameters.grab("textDocument").field("version"))
249
+ .with("diagnostics", diagnostics)
250
+ }
251
+
252
+ handleClearDiagnostic(system: NodeSystem, parameters: Map[String, Json]): List[Json] {
253
+ let js = system.js()
254
+ let files = parameters.grab("files").grabArray()
255
+ files.map {file =>
256
+ let uri = file.getField("uri").else {file.grabField("oldUri")}
257
+ Json.object()
258
+ .with("uri", uri)
259
+ .with("diagnostics", Json.array([]))
260
+ }
261
+ }
262
+
263
+ handleDocumentSymbol(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
264
+ let uri = parameters.grab("textDocument").field("uri").grabString()
265
+ let path = system.pathFromUrl(uri)
266
+ let symbols = self.getDocumentSymbolsWithCache(system, path)
267
+ let lspSymbols = symbols.map {SymbolHandler.documentSymbolToLsp(_)}
268
+ Result(Json.array(lspSymbols).write())
269
+ }
270
+
271
+ getDocumentSymbols(system: NodeSystem, path: Path): List[DocumentSymbol] {
272
+ try {
273
+ let lspHook = LspHook.new(at = None, definedAt = None, insertIdentifier = False, trackSymbols = True)
274
+ let code = self.virtualFiles.get(path.absolute()).else {path.readText()}
275
+ let tokens = Tokenizer.tokenize(path.absolute(), code, None, True)
276
+ let parser = Parser.new(PackagePair("script", "script"), path.absolute(), tokens, False, lspHook)
277
+ parser.parseModuleWithPackageInfo()
278
+ SymbolHandler.readAllSymbols(lspHook.results())
279
+ } tryCatch {| CompileError(at, message), error =>
280
+ Log.trace("handleDocumentSymbol parse error: " + message)
281
+ []
282
+ } catch {| ReadSymbolsError e, _ =>
283
+ Log.trace("handleDocumentSymbol ReadSymbolsError")
284
+ []
285
+ }
286
+ }
287
+
288
+ getDocumentSymbolsWithCache(system: NodeSystem, file: Path): List[DocumentSymbol] {
289
+ self.fileSymbolsCache.get(file.absolute()).{
290
+ | Some(symbols) =>
291
+ //Log.trace("HIT symbols: " + file)
292
+ symbols
293
+ | None =>
294
+ //Log.trace("MISS symbols: " + file)
295
+ let symbols = self.getDocumentSymbols(system, file)
296
+ self.fileSymbolsCache = self.fileSymbolsCache.add(file.absolute(), symbols)
297
+ symbols
298
+ }
299
+ }
300
+
301
+ handleWorkspaceSymbol(system: NodeSystem, parameters: Map[String, Json]): ResultOrError {
302
+ self.rootPath.or {Error(-32803 /*Request Failed*/, "Open a folder to enable workspace symbols")}: rootPath =>
303
+ let files = if(rootPath.isFile()) {
304
+ [rootPath]
305
+ } else {
306
+ self.printTime(system.mainTask(), "findFireflyFiles") {Builder.findFireflyFiles(rootPath, None, Set.new())}
307
+ }
308
+ let root = if(rootPath.isFile()) {rootPath.parent().grab()} else {rootPath}
309
+ let query = parameters.grab("query").grabString()
310
+ let symbols = files.flatMap {file =>
311
+ let documentSymbols = self.getDocumentSymbolsWithCache(system, file)
312
+ let workspaceSymbols = documentSymbols.flatMap {SymbolHandler.documentToWorkspaceSymbols(_, None)}
313
+ if(query == "") {
314
+ workspaceSymbols.filter {_.kind == SType}
315
+ } else {
316
+ workspaceSymbols.filter {SymbolHandler.symbolFilter(_, query)}
317
+ }
318
+ }
319
+ let limitedSymbols = symbols.sortBy {_.name.size()}.takeFirst(100)
320
+ let jsSymbols = limitedSymbols.map {SymbolHandler.workspaceSymbolToLsp(root, _)}
321
+ Result(Json.array(jsSymbols).write())
322
+ }
323
+
324
+ handleDidChangeWatched(system: NodeSystem, parameters: Map[String, Json]): List[Pair[String, Json]] {
325
+ let creates = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 1}
326
+ let changes = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 2}
327
+ let deletes = parameters.grab("changes").grabArray().filter {_.field("type").grabInt() == 3}
328
+ creates.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
329
+ changes.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
330
+ deletes.map {_.field("uri").grabString()}.each {self.moduleCache.invalidate(system.pathFromUrl(_).absolute())}
331
+ let diagnostics = self.handleClearDiagnostic(system, [Pair("files", Json.array(deletes))].toMap())
332
+ diagnostics.map {Pair("textDocument/publishDiagnostics", _)}
333
+ }
334
+
335
+ handleDidOpen(system: NodeSystem, parameters: Map[String, Json]): Unit {
336
+ let uri = parameters.grab("textDocument").field("uri").grabString()
337
+ let path = system.pathFromUrl(uri)
338
+ if(self.rootPath.isEmpty()) {
339
+ Log.trace("Using file as rootPath: " + path.absolute())
340
+ self.rootPath = Some(path)
341
+ }
342
+ }
343
+
344
+ handleDidChange(system: NodeSystem, parameters: Map[String, Json]): Unit {
345
+ let uri = parameters.grab("textDocument").field("uri").grabString()
346
+ let path = system.pathFromUrl(uri)
347
+ self.responseCache = Map.new()
348
+ self.fileSymbolsCache = self.fileSymbolsCache.remove(path.absolute())
349
+ self.moduleCache.invalidate(path.absolute())
350
+ let contentChanges = parameters.grab("contentChanges").grabArray()
351
+ if(contentChanges.size() != 1) {throw(LanguageServerException("Expected a single element in contentChanges"))} else:
352
+ let contentChange = contentChanges.grab(0)
353
+ if(!contentChange.field("range").isNull()) {throw(LanguageServerException("Expected a complete contentChange"))} else:
354
+ let content = contentChange.field("text").grabString()
355
+ self.virtualFiles = self.virtualFiles.add(path.absolute(), content)
356
+ }
357
+
358
+ handleDidClose(system: NodeSystem, parameters: Map[String, Json]): Unit {
359
+ let uri = parameters.grab("textDocument").field("uri").grabString()
360
+ let path = system.pathFromUrl(uri)
361
+ self.virtualFiles = self.virtualFiles.remove(path.absolute())
362
+ }
363
+
364
+ handleHover(system: NodeSystem, targetAt: Location, goToDefinition: Bool, version: Int): ResultOrError {
365
+ let lspHook = LspHook.new(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
366
+ let path = system.path(targetAt.file)
367
+ let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
368
+ errors.each {| CompileError(at, message) =>
369
+ Log.trace("handleHover check error: " + message)
370
+ }
371
+ let o = if(goToDefinition) {
372
+ HoverHandler.handleGoToDefinition(system, self, lspHook)
373
+ } else {
374
+ HoverHandler.handleHover(system, self, lspHook)
375
+ }
376
+ Result(o.write())
377
+ }
378
+
379
+ handleCompletion(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
380
+ let location = self.findLocationFromParameters(system, parameters)
381
+ let token = self.findToken(system, location, insertToken = True)
382
+ let completionAt = Location(
383
+ file = location.file
384
+ line = token.startLine
385
+ column = token.startColumn
386
+ )
387
+ let lspHook = LspHook.new(at = Some(completionAt), definedAt = None, insertIdentifier = True, trackSymbols = False)
388
+ let path = system.path(completionAt.file)
389
+ let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, lspHook, True)
390
+ errors.each {| CompileError(at, message) =>
391
+ Log.trace("handleCompletion check error: " + message)
392
+ }
393
+ let o = CompletionHandler.handleCompletion(lspHook, completionAt.column == 1, token.followedByLeftBracket)
394
+ Result(o.write())
395
+ }
396
+
397
+ handleSignatureHelp(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
398
+ let cursorAt = self.findLocationFromParameters(system, parameters)
399
+ let lspHook = LspHook.new(at = Some(cursorAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
400
+ try {
401
+ let code = self.virtualFiles.get(cursorAt.file).else {system.path(cursorAt.file).readText()}
402
+ let tokens = Tokenizer.tokenize(cursorAt.file, code, None, True)
403
+ let parser = Parser.new(PackagePair("script", "script"), cursorAt.file, tokens, False, lspHook)
404
+ parser.parseModuleWithPackageInfo()
405
+ } catch {| CompileError(at, message), error =>
406
+ Log.trace("handleSignatureHelp check error: " + message)
407
+ }
408
+ let o = lspHook.results().collectFirst {
409
+ | ParseArgumentHook h {parameters.get("context") | Some(context)} {
410
+ context.field("activeSignatureHelp") | help
411
+ } {!help.isNull() && help.hasField("signatures")} {
412
+ help.field("signatures").index(0) | activeSignature
413
+ } {!activeSignature.isNull()} {
414
+ activeSignature.field("parameters").grabArray().last() | Some(fakeParameter)
415
+ } {
416
+ fakeParameter.field("label").grabString() == "/* Call " + h.callAt.show() + " */"
417
+ } =>
418
+ Some(SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName))
419
+ | ParseArgumentHook h =>
420
+ let callLspHook = LspHook.new(at = Some(h.callAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
421
+ let path = system.path(cursorAt.file)
422
+ let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, callLspHook, True)
423
+ errors.each {| CompileError(at, message) =>
424
+ Log.trace("handleSignatureHelp check 2 error: " + message)
425
+ }
426
+ let help = SignatureHelpHandler.handleSignatureHelp(system, callLspHook)
427
+ if(!help.isNull()) {
428
+ SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName)
429
+ }
430
+ | _ =>
431
+ None
432
+ }
433
+ Result((o.else {Json.null()}).write())
434
+ }
435
+
436
+ handleReferences(system: NodeSystem, targetAt: Location, includeDeclaration: Bool, local: Bool, version: Int): ResultOrError {
437
+ let tokens = self.findReferences(system, targetAt, local, includeDeclaration, version)
438
+ let o = tokens.{
439
+ | None => Json.null()
440
+ | Some(tokens) =>
441
+ let lspTokens = tokens.map {self.tokenLocationToLspLocation(system, _)}
442
+ Json.array(lspTokens)
443
+ }
444
+ Result(o.write())
445
+ }
446
+
447
+ handleRename(system: NodeSystem, parameters: Map[String, Json], version: Int): ResultOrError {
448
+ let newName = parameters.grab("newName").grabString()
449
+ let targetAt = self.findTokenFromParameters(system, parameters)
450
+ let tokens = self.findReferences(system, targetAt, local = False, includeDeclaration = True, version = version)
451
+ tokens.{
452
+ | None => Error(-32602, "Token definition not found") // InvalidParams
453
+ | Some([]) => Result(Json.null().write())
454
+ | Some([first, ...] @ tokens) =>
455
+ let oldName = first.raw
456
+ Log.trace("Rename '" + oldName + "' to '" + newName + "'")
457
+
458
+ // TODO findReferences returns some bad tokens
459
+ let goodTokens = tokens.filter {_.raw == oldName}
460
+ let badTokens = tokens.filter {_.raw != oldName}
461
+ badTokens.each {t => Log.trace("Rename bad token: " + Show.show(t))}
462
+
463
+ let byFile = goodTokens.map {t => Pair(t.file, t)}.group()
464
+ let allChanges = byFile.toList().foldLeft(Json.object(), {| o, Pair(file, fileTokens) =>
465
+ let uri = system.path(file).url()
466
+ let fileChanges = fileTokens.map {tokenLocation =>
467
+ Json.object()
468
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
469
+ .with("newText", newName)
470
+ }
471
+ o.with(uri, fileChanges)
472
+ })
473
+ let o = Json.object().with("changes", allChanges)
474
+ Result(o.write())
475
+ }
476
+ }
477
+
478
+ findReferences(
479
+ system: NodeSystem
480
+ targetAt: Location
481
+ local: Bool
482
+ includeDeclaration: Bool
483
+ version: Int
484
+ ): Option[List[TokenLocation]] {
485
+ let temporaryLspHook = LspHook.new(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
486
+ let path = system.path(targetAt.file)
487
+ let errors = self.check(system, self.fireflyPath, path, None, Set.new(), self.virtualFiles, version, temporaryLspHook, True)
488
+ errors.each {| CompileError(at, message) =>
489
+ Log.trace("findReferences first check error: " + message + " in " + at.file + ":" + at.line + ":" + at.column)
490
+ }
491
+
492
+ //Log.trace("defined at hooks: " + Show.show(temporaryLspHook.results().map(LspHook.showHook)))
493
+ let definedAtList = temporaryLspHook.results().collect {
494
+ | ResolveSymbolHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, local = !h.topLevel))
495
+ | ResolveVariantFieldHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
496
+ | ResolveTypeHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
497
+ | ResolveConstraintHook h => Some(Definition(h.symbol.definedAt, h.symbol.qualifiedName, False))
498
+ | ResolveSignatureHook h {!h.isInstanceMethod} => Some(Definition(h.signature.at, h.signature.name, local = !h.topLevel))
499
+ | InferParameterHook h => Some(Definition(h.parameter.at, h.parameter.name, False))
500
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
501
+ h.parameters.find {_.name == n}.map {Definition(_.at, n, False)}
502
+ | InferLookupHook h => Some(Definition(h.symbol.value.definedAt, h.symbol.value.qualifiedName, False))
503
+ | InferPatternHook h {h.pattern | PVariantAs p} => Some(Definition(p.variableAt, p.name, False))
504
+ | InferPatternHook h {h.pattern | PVariable p} => p.name.map {Definition(p.at, _, False)}
505
+ | InferPatternHook h {h.pattern | PAlias p} => Some(Definition(p.at, p.variable, False))
506
+ | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
507
+ let fieldNames = n.split('$').dropFirst(1)
508
+ let fieldDefinedAt = fieldNames.zip(ts).collectFirst {| Pair(name, t) =>
509
+ if(name == h.fieldName) {t.at}
510
+ }
511
+ fieldDefinedAt.map {Definition(_, h.fieldName, False)}
512
+ | h => None
513
+ }.filter {pair => !pair.at.file.endsWith(">")}
514
+
515
+ function unqualify(qualifiedName: String): String {
516
+ qualifiedName.split('.').grabLast().split('_').grabLast()
517
+ }
518
+
519
+ function extractModuleName(qualifiedName: String): String {
520
+ qualifiedName.split('/').grabLast().takeWhile {c => c != '.' || c == '_'}
521
+ }
522
+
523
+ function extractPackagePair(qualifiedName: String): PackagePair {
524
+ let group = qualifiedName.split(':').grabFirst()
525
+ let name = qualifiedName.split(':').grabLast().takeWhile {_ != '/'}
526
+ PackagePair(group, name)
527
+ }
528
+
529
+ definedAtList.first().{
530
+ | Some(definition) =>
531
+ let lspHook = LspHook.new(at = None, definedAt = Some(definition.at), insertIdentifier = False, trackSymbols = False)
532
+ let localCheck = local || definition.local
533
+ let path = if(localCheck) {system.path(targetAt.file)} else {self.rootPath.else {system.path(targetAt.file)}}
534
+ let mustContain = Some(definition.name).filter {_ => !localCheck}.map(unqualify)
535
+ Log.trace("findReferences definition: " + Show.show(definition))
536
+ mustContain.each {Log.trace("mustContain: " + _)}
537
+
538
+ let skipFiles = if(definition.name.contains(":")) {
539
+ let list = self.moduleCache.filesNotImporting(extractPackagePair(definition.name), extractModuleName(definition.name))
540
+ list.toSet()
541
+ } else {
542
+ self.moduleCache.parsedModules.get(definition.at.file).map {| Pair(module, _) =>
543
+ let list = self.moduleCache.filesNotImporting(module.packagePair, module.file.dropLast(3))
544
+ list.toSet()
545
+ }.else {
546
+ Log.trace("No skip list due to unqualified definition.name: " + definition.name)
547
+ Set.new()
548
+ }
549
+ }
550
+ let errors = self.check(system, self.fireflyPath, path, mustContain, skipFiles, self.virtualFiles, version, lspHook, True, False)
551
+ errors.each {| CompileError(at, message) =>
552
+ Log.trace("findReferences second check error: " + message + " in " + at.file + ":" + at.line + ":" + at.column)
553
+ }
554
+
555
+ let referencesResult = lspHook.results().collect {
556
+ | ResolveSymbolHook h => Some(h.symbol.usageAt)
557
+ | ResolveVariantFieldHook h => Some(h.symbol.usageAt)
558
+ | ResolveTypeHook h => Some(h.symbol.usageAt)
559
+ | ResolveConstraintHook h => Some(h.symbol.usageAt)
560
+ | ResolveSignatureHook h => Some(h.signature.at)
561
+ | InferParameterHook h => Some(h.parameter.at)
562
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
563
+ h.parameters.find {_.name == n}.map {_ => a.at}
564
+ | InferLookupHook h => Some(h.symbol.value.usageAt)
565
+ | InferRecordFieldHook h => Some(h.usageAt)
566
+ | _ => None
567
+ }.filter {at =>
568
+ !at.file.endsWith(">") &&
569
+ (includeDeclaration || at != definition.at)
570
+ }
571
+
572
+ //referencesResult.each {Log.trace(Show.show(_))}
573
+
574
+ let clientLocations = referencesResult.addAll(
575
+ if(includeDeclaration) {[definition.at]} else {[]}
576
+ ).distinct().filter {
577
+ !local || _.file == targetAt.file
578
+ }.map {at =>
579
+ self.findToken(system, at)
580
+ }
581
+ let sorted = clientLocations.sortBy {c => Location(c.file, c.startLine, c.startColumn)}
582
+ Some(sorted)
583
+ | None =>
584
+ None
585
+ }
586
+ }
587
+
588
+ makeNotificationMessage(method: String, params: Json): Json {
589
+ Json.object()
590
+ .with("jsonrpc", "2.0")
591
+ .with("method", method)
592
+ .with("params", params)
593
+ }
594
+
595
+ findToken(system: NodeSystem, at: Location, insertToken: Bool = False): TokenLocation {
596
+ let fallback = TokenLocation(
597
+ file = at.file
598
+ startLine = at.line
599
+ startColumn = at.column
600
+ endLine = at.line
601
+ endColumn = at.column + 1
602
+ raw = ""
603
+ followedByLeftBracket = True
604
+ )
605
+ try {
606
+ let code = self.virtualFiles.get(at.file).else {system.path(at.file).readText()}
607
+ let tokens = Tokenizer.tokenize(at.file, code, Some(at).filter {_ => insertToken}, True)
608
+ let token = tokens.toStream().find {token =>
609
+ at.line >= token.startLine && at.line <= token.stopLine && (
610
+ (at.line > token.startLine || at.column >= 1 + token.startOffset - token.startLineOffset) &&
611
+ (at.line < token.stopLine || at.column < 1 + token.stopOffset - token.stopLineOffset || (
612
+ (token.kind == LLower || token.kind == LUpper) &&
613
+ at.column == 1 + token.stopOffset - token.stopLineOffset
614
+ ))
615
+ )
616
+ }
617
+ token.map {t =>
618
+ function leftBracketAt(i: Int): Bool {
619
+ i >= 0 && i < code.size() &&
620
+ (code.grab(i) == '(' || code.grab(i) == '[' || code.grab(i) == '{')
621
+ }
622
+ let followedByLeftBracket =
623
+ leftBracketAt(t.startOffset) ||
624
+ leftBracketAt(t.stopOffset) ||
625
+ leftBracketAt(t.stopOffset + 1)
626
+ TokenLocation(
627
+ file = t.file
628
+ startLine = t.startLine
629
+ startColumn = 1 + t.startOffset - t.startLineOffset
630
+ endLine = t.stopLine
631
+ endColumn = 1 + t.stopOffset - t.stopLineOffset
632
+ raw = t.raw()
633
+ followedByLeftBracket = followedByLeftBracket
634
+ )
635
+ }.else {fallback}
636
+ } catchAny {_ =>
637
+ fallback
638
+ }
639
+ }
640
+
641
+ findTokenFromParameters(system: NodeSystem, parameters: Map[String, Json]): Location {
642
+ let location = self.findLocationFromParameters(system, parameters)
643
+ let token = self.findToken(system, location)
644
+ Location(
645
+ file = location.file
646
+ line = token.startLine
647
+ column = token.startColumn
648
+ )
649
+ }
650
+
651
+ findLocationFromParameters(system: NodeSystem, parameters: Map[String, Json]): Location {
652
+ let uri = parameters.grab("textDocument").field("uri").grabString()
653
+ let path = system.pathFromUrl(uri)
654
+ let line = parameters.grab("position").field("line").grabInt() + 1
655
+ let column = parameters.grab("position").field("character").grabInt() + 1
656
+ Location(path.absolute(), line, column)
657
+ }
658
+
659
+ tokenLocationToLspLocation(system: NodeSystem, tokenLocation: TokenLocation): Json {
660
+ Json.object()
661
+ .with("uri", system.path(tokenLocation.file).url())
662
+ .with("range", self.tokenLocationToLspRange(tokenLocation))
663
+ }
664
+
665
+ tokenLocationToLspRange(tokenLocation: TokenLocation): Json {
666
+ Json.object()
667
+ .with("start", Json.object()
668
+ .with("line", tokenLocation.startLine - 1)
669
+ .with("character", tokenLocation.startColumn - 1)
670
+ )
671
+ .with("end", Json.object()
672
+ .with("line", tokenLocation.endLine - 1)
673
+ .with("character", tokenLocation.endColumn - 1)
674
+ )
675
+ }
676
+
677
+ printTime[R](task: Task, label: String, body: () => R): R {
678
+ let start = task.elapsed()
679
+ let result = body()
680
+ let stop = task.elapsed()
681
+ let duration = ("" + (stop.seconds - start.seconds)).slice(0, 5)
682
+ Log.trace(label + ": " + duration + "s")
683
+ result
684
+ }
685
+
686
+ check(
687
+ system: NodeSystem
688
+ fireflyPath: Path
689
+ path: Path
690
+ mustContain: Option[String]
691
+ skipFiles: Set[String]
692
+ virtualFiles: Map[String, String]
693
+ newModuleCacheVersion: Int
694
+ lspHook: LspHook
695
+ infer: Bool
696
+ checkDependencies: Bool = True
697
+ ): List[CompileError] {
698
+ Builder.check(
699
+ system
700
+ fireflyPath
701
+ path
702
+ mustContain
703
+ skipFiles
704
+ virtualFiles
705
+ self.moduleCache
706
+ self.dependencyLock
707
+ newModuleCacheVersion
708
+ lspHook
709
+ infer
710
+ checkDependencies
711
+ )
712
+ }
713
+
714
+ }