firefly-compiler 0.5.35 → 0.5.37

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 (225) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +157 -154
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +275 -277
  6. package/compiler/Compiler.ff +234 -233
  7. package/compiler/Dependencies.ff +186 -187
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/Deriver.ff +23 -31
  10. package/compiler/Dictionaries.ff +1 -1
  11. package/compiler/Inference.ff +43 -20
  12. package/compiler/JsEmitter.ff +1437 -1282
  13. package/compiler/LspHook.ff +202 -202
  14. package/compiler/Main.ff +25 -24
  15. package/compiler/ModuleCache.ff +178 -178
  16. package/compiler/Parser.ff +36 -109
  17. package/compiler/Resolver.ff +5 -8
  18. package/compiler/Substitution.ff +1 -1
  19. package/compiler/Syntax.ff +1 -16
  20. package/compiler/Token.ff +9 -0
  21. package/compiler/Tokenizer.ff +4 -0
  22. package/compiler/Workspace.ff +88 -88
  23. package/core/.firefly/include/package.json +5 -5
  24. package/core/.firefly/package.ff +2 -2
  25. package/core/Any.ff +26 -30
  26. package/core/Array.ff +298 -265
  27. package/core/Atomic.ff +63 -64
  28. package/core/Box.ff +7 -7
  29. package/core/BrowserSystem.ff +40 -40
  30. package/core/Buffer.ff +185 -152
  31. package/core/BuildSystem.ff +156 -148
  32. package/core/Channel.ff +95 -92
  33. package/core/Char.ff +3 -2
  34. package/core/Core.ff +16 -23
  35. package/core/Crypto.ff +94 -96
  36. package/core/Equal.ff +41 -36
  37. package/core/Error.ff +15 -10
  38. package/core/FileHandle.ff +45 -37
  39. package/core/Float.ff +176 -200
  40. package/core/HttpClient.ff +142 -148
  41. package/core/Instant.ff +6 -8
  42. package/core/Int.ff +40 -24
  43. package/core/IntMap.ff +61 -39
  44. package/core/Js.ff +305 -0
  45. package/core/JsSystem.ff +135 -114
  46. package/core/JsValue.ff +303 -159
  47. package/core/Json.ff +423 -443
  48. package/core/List.ff +482 -486
  49. package/core/Lock.ff +108 -144
  50. package/core/Log.ff +25 -14
  51. package/core/NodeSystem.ff +198 -191
  52. package/core/Ordering.ff +160 -161
  53. package/core/Path.ff +377 -409
  54. package/core/Queue.ff +90 -0
  55. package/core/Random.ff +140 -134
  56. package/core/RbMap.ff +216 -216
  57. package/core/Serializable.ff +16 -13
  58. package/core/Show.ff +44 -43
  59. package/core/SourceLocation.ff +68 -68
  60. package/core/Stream.ff +1 -1
  61. package/core/String.ff +224 -202
  62. package/core/StringMap.ff +58 -36
  63. package/core/Task.ff +165 -149
  64. package/experimental/benchmarks/ListGrab.ff +23 -23
  65. package/experimental/benchmarks/ListGrab.java +55 -55
  66. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  67. package/experimental/benchmarks/Pyrotek45.java +64 -64
  68. package/experimental/bidirectional/Bidi.ff +88 -88
  69. package/experimental/lines/Main.ff +40 -0
  70. package/experimental/random/Index.ff +53 -53
  71. package/experimental/random/Process.ff +120 -120
  72. package/experimental/random/RunLength.ff +65 -65
  73. package/experimental/random/Scrape.ff +51 -51
  74. package/experimental/random/Symbols.ff +73 -73
  75. package/experimental/random/Tensor.ff +52 -52
  76. package/experimental/random/Units.ff +36 -36
  77. package/experimental/s3/S3TestAuthorizationHeader.ff +39 -39
  78. package/experimental/s3/S3TestPut.ff +16 -16
  79. package/experimental/tests/TestJson.ff +26 -26
  80. package/firefly.sh +0 -0
  81. package/fireflysite/.firefly/package.ff +4 -4
  82. package/fireflysite/CommunityOverview.ff +20 -20
  83. package/fireflysite/CountingButtonDemo.ff +58 -58
  84. package/fireflysite/DocumentParser.ff +325 -331
  85. package/fireflysite/ExamplesOverview.ff +40 -40
  86. package/fireflysite/FrontPage.ff +344 -344
  87. package/fireflysite/GettingStarted.ff +45 -45
  88. package/fireflysite/Guide.ff +456 -456
  89. package/fireflysite/Main.ff +163 -152
  90. package/fireflysite/MatchingPasswordsDemo.ff +82 -82
  91. package/fireflysite/PackagesOverview.ff +49 -49
  92. package/fireflysite/PostgresqlDemo.ff +34 -34
  93. package/fireflysite/ReferenceAll.ff +18 -18
  94. package/fireflysite/ReferenceIntroduction.ff +11 -11
  95. package/fireflysite/Styles.ff +567 -567
  96. package/fireflysite/Test.ff +121 -62
  97. package/fireflysite/assets/markdown/reference/BaseTypes.md +209 -209
  98. package/fireflysite/assets/markdown/reference/EmittedJavascript.md +65 -65
  99. package/fireflysite/assets/markdown/reference/Exceptions.md +101 -101
  100. package/fireflysite/assets/markdown/reference/FunctionsAndMethods.md +364 -364
  101. package/fireflysite/assets/markdown/reference/JavascriptInterop.md +235 -172
  102. package/fireflysite/assets/markdown/reference/ModulesAndPackages.md +162 -162
  103. package/fireflysite/assets/markdown/reference/OldStructuredConcurrency.md +48 -48
  104. package/fireflysite/assets/markdown/reference/PatternMatching.md +224 -224
  105. package/fireflysite/assets/markdown/reference/StatementsAndExpressions.md +86 -86
  106. package/fireflysite/assets/markdown/reference/StructuredConcurrency.md +99 -99
  107. package/fireflysite/assets/markdown/reference/TraitsAndInstances.md +100 -100
  108. package/fireflysite/assets/markdown/reference/UserDefinedTypes.md +184 -184
  109. package/fireflysite/assets/markdown/scratch/ControlFlow.md +136 -136
  110. package/fireflysite/assets/markdown/scratch/Toc.md +40 -40
  111. package/lsp/.firefly/package.ff +1 -1
  112. package/lsp/CompletionHandler.ff +827 -827
  113. package/lsp/Handler.ff +714 -714
  114. package/lsp/HoverHandler.ff +79 -79
  115. package/lsp/LanguageServer.ff +272 -272
  116. package/lsp/SignatureHelpHandler.ff +55 -55
  117. package/lsp/SymbolHandler.ff +181 -181
  118. package/lsp/TestReferences.ff +17 -17
  119. package/lsp/TestReferencesCase.ff +7 -7
  120. package/lsp/stderr.txt +1 -1
  121. package/lsp/stdout.txt +34 -34
  122. package/lux/.firefly/package.ff +1 -1
  123. package/lux/Css.ff +648 -648
  124. package/lux/CssTest.ff +48 -48
  125. package/lux/Lux.ff +608 -617
  126. package/lux/LuxEvent.ff +79 -116
  127. package/lux/Main.ff +123 -123
  128. package/lux/Main2.ff +143 -143
  129. package/lux/TestDry.ff +28 -28
  130. package/output/js/ff/compiler/Builder.mjs +72 -71
  131. package/output/js/ff/compiler/Compiler.mjs +19 -13
  132. package/output/js/ff/compiler/Dependencies.mjs +8 -7
  133. package/output/js/ff/compiler/DependencyLock.mjs +6 -4
  134. package/output/js/ff/compiler/Deriver.mjs +26 -24
  135. package/output/js/ff/compiler/Dictionaries.mjs +14 -18
  136. package/output/js/ff/compiler/Environment.mjs +6 -4
  137. package/output/js/ff/compiler/Inference.mjs +238 -164
  138. package/output/js/ff/compiler/JsEmitter.mjs +1160 -350
  139. package/output/js/ff/compiler/JsImporter.mjs +20 -18
  140. package/output/js/ff/compiler/LspHook.mjs +12 -10
  141. package/output/js/ff/compiler/Main.mjs +61 -41
  142. package/output/js/ff/compiler/ModuleCache.mjs +10 -8
  143. package/output/js/ff/compiler/Parser.mjs +153 -669
  144. package/output/js/ff/compiler/Patterns.mjs +12 -10
  145. package/output/js/ff/compiler/Resolver.mjs +52 -78
  146. package/output/js/ff/compiler/Substitution.mjs +12 -16
  147. package/output/js/ff/compiler/Syntax.mjs +50 -341
  148. package/output/js/ff/compiler/Token.mjs +126 -4
  149. package/output/js/ff/compiler/Tokenizer.mjs +62 -52
  150. package/output/js/ff/compiler/Unification.mjs +74 -90
  151. package/output/js/ff/compiler/Wildcards.mjs +4 -2
  152. package/output/js/ff/compiler/Workspace.mjs +26 -20
  153. package/output/js/ff/core/Any.mjs +20 -20
  154. package/output/js/ff/core/Array.mjs +268 -175
  155. package/output/js/ff/core/AssetSystem.mjs +8 -6
  156. package/output/js/ff/core/Atomic.mjs +84 -52
  157. package/output/js/ff/core/Bool.mjs +6 -4
  158. package/output/js/ff/core/BrowserSystem.mjs +38 -29
  159. package/output/js/ff/core/Buffer.mjs +285 -133
  160. package/output/js/ff/core/BuildSystem.mjs +36 -56
  161. package/output/js/ff/core/Channel.mjs +250 -97
  162. package/output/js/ff/core/Char.mjs +5 -3
  163. package/output/js/ff/core/Core.mjs +28 -34
  164. package/output/js/ff/core/Crypto.mjs +30 -52
  165. package/output/js/ff/core/Duration.mjs +4 -2
  166. package/output/js/ff/core/Equal.mjs +14 -12
  167. package/output/js/ff/core/Error.mjs +17 -11
  168. package/output/js/ff/core/FileHandle.mjs +76 -38
  169. package/output/js/ff/core/Float.mjs +92 -160
  170. package/output/js/ff/core/HttpClient.mjs +208 -76
  171. package/output/js/ff/core/Instant.mjs +8 -10
  172. package/output/js/ff/core/Int.mjs +36 -26
  173. package/output/js/ff/core/IntMap.mjs +79 -33
  174. package/output/js/ff/core/Js.mjs +751 -0
  175. package/output/js/ff/core/JsSystem.mjs +54 -60
  176. package/output/js/ff/core/JsValue.mjs +294 -143
  177. package/output/js/ff/core/Json.mjs +443 -253
  178. package/output/js/ff/core/List.mjs +262 -214
  179. package/output/js/ff/core/Lock.mjs +156 -125
  180. package/output/js/ff/core/Log.mjs +20 -10
  181. package/output/js/ff/core/Map.mjs +10 -8
  182. package/output/js/ff/core/NodeSystem.mjs +189 -123
  183. package/output/js/ff/core/Nothing.mjs +4 -2
  184. package/output/js/ff/core/Option.mjs +40 -38
  185. package/output/js/ff/core/Ordering.mjs +26 -20
  186. package/output/js/ff/core/Pair.mjs +4 -2
  187. package/output/js/ff/core/Path.mjs +517 -315
  188. package/output/js/ff/core/Queue.mjs +306 -0
  189. package/output/js/ff/core/Random.mjs +141 -77
  190. package/output/js/ff/core/RbMap.mjs +36 -34
  191. package/output/js/ff/core/Serializable.mjs +44 -28
  192. package/output/js/ff/core/Set.mjs +6 -4
  193. package/output/js/ff/core/Show.mjs +8 -6
  194. package/output/js/ff/core/SourceLocation.mjs +4 -2
  195. package/output/js/ff/core/Stream.mjs +30 -50
  196. package/output/js/ff/core/String.mjs +263 -172
  197. package/output/js/ff/core/StringMap.mjs +77 -31
  198. package/output/js/ff/core/Task.mjs +91 -76
  199. package/output/js/ff/core/Try.mjs +20 -18
  200. package/output/js/ff/core/Unit.mjs +4 -2
  201. package/package.json +1 -1
  202. package/postgresql/Pg.ff +53 -59
  203. package/rpc/.firefly/package.ff +1 -1
  204. package/rpc/Rpc.ff +70 -70
  205. package/s3/.firefly/package.ff +1 -1
  206. package/s3/S3.ff +92 -94
  207. package/vscode/LICENSE.txt +21 -21
  208. package/vscode/Prepublish.ff +15 -15
  209. package/vscode/README.md +16 -16
  210. package/vscode/client/package-lock.json +544 -544
  211. package/vscode/client/package.json +22 -22
  212. package/vscode/client/src/extension.ts +104 -104
  213. package/vscode/icons/firefly-icon.svg +10 -10
  214. package/vscode/language-configuration.json +61 -61
  215. package/vscode/package-lock.json +3623 -3623
  216. package/vscode/package.json +1 -1
  217. package/vscode/snippets.json +241 -241
  218. package/vscode/syntaxes/firefly-markdown-injection.json +45 -45
  219. package/webserver/.firefly/include/package.json +5 -5
  220. package/webserver/.firefly/package.ff +2 -2
  221. package/webserver/WebServer.ff +647 -685
  222. package/websocket/.firefly/package.ff +1 -1
  223. package/websocket/WebSocket.ff +100 -131
  224. package/core/UnsafeJs.ff +0 -42
  225. package/output/js/ff/core/UnsafeJs.mjs +0 -191
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
+ }