firefly-compiler 0.4.79 → 0.4.80

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 (158) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +153 -153
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +257 -257
  6. package/compiler/Compiler.ff +227 -227
  7. package/compiler/Dependencies.ff +187 -187
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/Inference.ff +2 -1
  10. package/compiler/JsEmitter.ff +940 -946
  11. package/compiler/LspHook.ff +202 -202
  12. package/compiler/Main.ff +3 -3
  13. package/compiler/ModuleCache.ff +178 -178
  14. package/compiler/Tokenizer.ff +1 -1
  15. package/compiler/Unification.ff +1 -1
  16. package/compiler/Workspace.ff +88 -88
  17. package/core/.firefly/include/package-lock.json +564 -564
  18. package/core/.firefly/include/package.json +5 -5
  19. package/core/.firefly/include/prepare.sh +1 -1
  20. package/core/.firefly/package.ff +2 -2
  21. package/core/Array.ff +265 -265
  22. package/core/Atomic.ff +64 -64
  23. package/core/Box.ff +7 -7
  24. package/core/BrowserSystem.ff +40 -40
  25. package/core/BuildSystem.ff +148 -148
  26. package/core/Crypto.ff +96 -96
  27. package/core/Equal.ff +36 -36
  28. package/core/Float.ff +25 -0
  29. package/core/HttpClient.ff +148 -148
  30. package/core/JsSystem.ff +69 -69
  31. package/core/Json.ff +434 -434
  32. package/core/List.ff +486 -486
  33. package/core/Lock.ff +144 -144
  34. package/core/NodeSystem.ff +216 -216
  35. package/core/Ordering.ff +161 -161
  36. package/core/Path.ff +401 -401
  37. package/core/Random.ff +134 -134
  38. package/core/RbMap.ff +216 -216
  39. package/core/Show.ff +43 -43
  40. package/core/SourceLocation.ff +68 -68
  41. package/core/Stream.ff +9 -9
  42. package/core/Task.ff +141 -141
  43. package/core/Try.ff +25 -4
  44. package/experimental/benchmarks/ListGrab.ff +23 -23
  45. package/experimental/benchmarks/ListGrab.java +55 -55
  46. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  47. package/experimental/benchmarks/Pyrotek45.java +64 -64
  48. package/experimental/bidirectional/Bidi.ff +88 -88
  49. package/experimental/random/Index.ff +53 -53
  50. package/experimental/random/Process.ff +120 -120
  51. package/experimental/random/Scrape.ff +51 -51
  52. package/experimental/random/Symbols.ff +73 -73
  53. package/experimental/random/Tensor.ff +52 -52
  54. package/experimental/random/Units.ff +36 -36
  55. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  56. package/experimental/s3/S3TestPut.ff +16 -16
  57. package/experimental/tests/TestJson.ff +26 -26
  58. package/firefly.sh +0 -0
  59. package/fireflysite/.firefly/package.ff +4 -4
  60. package/fireflysite/CommunityOverview.ff +20 -20
  61. package/fireflysite/CountingButtonDemo.ff +58 -58
  62. package/fireflysite/DocumentParser.ff +331 -217
  63. package/fireflysite/ExamplesOverview.ff +40 -40
  64. package/fireflysite/FrontPage.ff +344 -360
  65. package/fireflysite/{GuideIntroduction.ff → GettingStarted.ff} +45 -52
  66. package/fireflysite/Guide.ff +443 -411
  67. package/fireflysite/Main.ff +141 -137
  68. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  69. package/fireflysite/PackagesOverview.ff +49 -49
  70. package/fireflysite/PostgresqlDemo.ff +34 -34
  71. package/fireflysite/ReferenceAll.ff +19 -0
  72. package/fireflysite/ReferenceIntroduction.ff +11 -0
  73. package/fireflysite/Styles.ff +567 -495
  74. package/fireflysite/Test.ff +38 -0
  75. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -0
  76. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +208 -0
  77. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +168 -0
  78. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -0
  79. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -0
  80. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -0
  81. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -0
  82. package/fireflysite/assets/markdown/{ControlFlow.md → scratch/ControlFlow.md} +136 -136
  83. package/fireflysite/assets/markdown/scratch/Toc.md +41 -0
  84. package/lsp/.firefly/package.ff +1 -1
  85. package/lsp/CompletionHandler.ff +828 -828
  86. package/lsp/Handler.ff +714 -714
  87. package/lsp/HoverHandler.ff +79 -79
  88. package/lsp/LanguageServer.ff +272 -272
  89. package/lsp/SignatureHelpHandler.ff +55 -55
  90. package/lsp/SymbolHandler.ff +181 -181
  91. package/lsp/TestReferences.ff +17 -17
  92. package/lsp/TestReferencesCase.ff +7 -7
  93. package/lsp/stderr.txt +1 -1
  94. package/lsp/stdout.txt +34 -34
  95. package/lux/.firefly/package.ff +1 -1
  96. package/lux/Css.ff +648 -648
  97. package/lux/CssTest.ff +48 -48
  98. package/lux/Lux.ff +487 -487
  99. package/lux/LuxEvent.ff +116 -116
  100. package/lux/Main.ff +123 -123
  101. package/lux/Main2.ff +143 -143
  102. package/output/js/ff/compiler/Builder.mjs +47 -47
  103. package/output/js/ff/compiler/Dependencies.mjs +3 -3
  104. package/output/js/ff/compiler/Inference.mjs +2 -2
  105. package/output/js/ff/compiler/JsEmitter.mjs +18 -72
  106. package/output/js/ff/compiler/Main.mjs +4 -4
  107. package/output/js/ff/compiler/ModuleCache.mjs +4 -4
  108. package/output/js/ff/core/Array.mjs +59 -59
  109. package/output/js/ff/core/Atomic.mjs +36 -36
  110. package/output/js/ff/core/BrowserSystem.mjs +11 -11
  111. package/output/js/ff/core/BuildSystem.mjs +30 -30
  112. package/output/js/ff/core/Crypto.mjs +40 -40
  113. package/output/js/ff/core/Float.mjs +50 -0
  114. package/output/js/ff/core/HttpClient.mjs +56 -56
  115. package/output/js/ff/core/Json.mjs +147 -147
  116. package/output/js/ff/core/List.mjs +50 -50
  117. package/output/js/ff/core/Lock.mjs +97 -97
  118. package/output/js/ff/core/NodeSystem.mjs +87 -87
  119. package/output/js/ff/core/Ordering.mjs +8 -8
  120. package/output/js/ff/core/Path.mjs +231 -231
  121. package/output/js/ff/core/Random.mjs +56 -56
  122. package/output/js/ff/core/Task.mjs +39 -39
  123. package/output/js/ff/core/Try.mjs +98 -4
  124. package/package.json +1 -1
  125. package/postgresql/Pg.ff +1 -1
  126. package/rpc/.firefly/package.ff +1 -1
  127. package/rpc/Rpc.ff +70 -70
  128. package/s3/.firefly/package.ff +1 -1
  129. package/s3/S3.ff +94 -94
  130. package/unsafejs/UnsafeJs.ff +19 -19
  131. package/vscode/LICENSE.txt +21 -21
  132. package/vscode/Prepublish.ff +15 -15
  133. package/vscode/README.md +16 -16
  134. package/vscode/client/package.json +22 -22
  135. package/vscode/client/src/extension.ts +104 -104
  136. package/vscode/icons/firefly-icon.svg +10 -10
  137. package/vscode/language-configuration.json +61 -61
  138. package/vscode/package-lock.json +3623 -3623
  139. package/vscode/package.json +1 -1
  140. package/vscode/snippets.json +241 -241
  141. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  142. package/webserver/.firefly/include/package-lock.json +22 -22
  143. package/webserver/.firefly/include/package.json +5 -5
  144. package/webserver/.firefly/package.ff +2 -2
  145. package/webserver/WebServer.ff +685 -685
  146. package/websocket/.firefly/package.ff +1 -1
  147. package/websocket/WebSocket.ff +131 -131
  148. package/fireflysite/GuideAll.ff +0 -21
  149. package/fireflysite/GuideBaseTypes.ff +0 -168
  150. package/fireflysite/GuideControlFlow.ff +0 -212
  151. package/fireflysite/assets/markdown/Example.md +0 -78
  152. /package/fireflysite/assets/{NotoSansMono-Regular.ttf → font/NotoSansMono-Regular.ttf} +0 -0
  153. /package/fireflysite/assets/{NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf → font/NunitoSans-VariableFont_YTLC,opsz,wdth,wght.ttf} +0 -0
  154. /package/fireflysite/assets/{autocomplete-small.png → image/autocomplete-small.png} +0 -0
  155. /package/fireflysite/assets/{autocomplete.png → image/autocomplete.png} +0 -0
  156. /package/fireflysite/assets/{edit-time-error.png → image/edit-time-error.png} +0 -0
  157. /package/fireflysite/assets/{firefly-logo-notext.png → image/firefly-logo-notext.png} +0 -0
  158. /package/fireflysite/assets/{firefly-logo-yellow.png → image/firefly-logo-yellow.png} +0 -0
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
- } catch {| CompileError(at, message), error =>
280
- Log.trace("handleDocumentSymbol parse error: " + message)
281
- []
282
- } catch {| ReadSymbolsError e, _ =>
283
- Log.trace("handleDocumentSymbol ReadSymbolsError")
284
- []
285
- } grab()
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
- } grab()
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
- } grab()
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
+ }