firefly-compiler 0.4.4

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 (221) hide show
  1. package/.firefly-workspace +1 -0
  2. package/.vscode/settings.json +5 -0
  3. package/LICENSE.txt +21 -0
  4. package/README.md +96 -0
  5. package/bin/firefly.mjs +2 -0
  6. package/compiler/.firefly/package.ff +1 -0
  7. package/compiler/Builder.ff +218 -0
  8. package/compiler/Compiler.ff +241 -0
  9. package/compiler/Dependencies.ff +179 -0
  10. package/compiler/Deriver.ff +647 -0
  11. package/compiler/Dictionaries.ff +205 -0
  12. package/compiler/Environment.ff +166 -0
  13. package/compiler/Inference.ff +1117 -0
  14. package/compiler/JsEmitter.ff +861 -0
  15. package/compiler/JsImporter.ff +56 -0
  16. package/compiler/LspHook.ff +188 -0
  17. package/compiler/Main.ff +237 -0
  18. package/compiler/Parser.ff +1383 -0
  19. package/compiler/Patterns.ff +111 -0
  20. package/compiler/Resolver.ff +620 -0
  21. package/compiler/Substitution.ff +178 -0
  22. package/compiler/Syntax.ff +299 -0
  23. package/compiler/Token.ff +180 -0
  24. package/compiler/Tokenizer.ff +278 -0
  25. package/compiler/Unification.ff +220 -0
  26. package/compiler/Wildcards.ff +50 -0
  27. package/compiler/Workspace.ff +88 -0
  28. package/core/.firefly/package.ff +2 -0
  29. package/core/Any.ff +30 -0
  30. package/core/Array.ff +249 -0
  31. package/core/AssetSystem.ff +61 -0
  32. package/core/Atomic.ff +64 -0
  33. package/core/Bool.ff +13 -0
  34. package/core/BrowserSystem.ff +14 -0
  35. package/core/Buffer.ff +211 -0
  36. package/core/BuildSystem.ff +144 -0
  37. package/core/Channel.ff +131 -0
  38. package/core/Char.ff +18 -0
  39. package/core/Core.ff +58 -0
  40. package/core/Duration.ff +15 -0
  41. package/core/Equal.ff +52 -0
  42. package/core/Error.ff +20 -0
  43. package/core/FileHandle.ff +41 -0
  44. package/core/Float.ff +41 -0
  45. package/core/HttpClient.ff +84 -0
  46. package/core/Instant.ff +9 -0
  47. package/core/Int.ff +61 -0
  48. package/core/IntMap.ff +85 -0
  49. package/core/JsSystem.ff +66 -0
  50. package/core/JsValue.ff +240 -0
  51. package/core/List.ff +440 -0
  52. package/core/Lock.ff +144 -0
  53. package/core/Log.ff +24 -0
  54. package/core/Map.ff +126 -0
  55. package/core/NodeSystem.ff +88 -0
  56. package/core/Nothing.ff +1 -0
  57. package/core/Option.ff +133 -0
  58. package/core/Ordering.ff +157 -0
  59. package/core/Pair.ff +55 -0
  60. package/core/Path.ff +393 -0
  61. package/core/RbMap.ff +216 -0
  62. package/core/Serializable.ff +173 -0
  63. package/core/Set.ff +38 -0
  64. package/core/Show.ff +43 -0
  65. package/core/Stack.ff +263 -0
  66. package/core/Stream.ff +406 -0
  67. package/core/String.ff +175 -0
  68. package/core/StringMap.ff +85 -0
  69. package/core/Task.ff +138 -0
  70. package/core/Try.ff +81 -0
  71. package/core/Unit.ff +3 -0
  72. package/experimental/random/AltGeneric.ff +44 -0
  73. package/experimental/random/Async.ff +68 -0
  74. package/experimental/random/Buffer2.ff +77 -0
  75. package/experimental/random/Cat.ff +12 -0
  76. package/experimental/random/Dictionary.ff +52 -0
  77. package/experimental/random/Example.ff +46 -0
  78. package/experimental/random/Generic.ff +102 -0
  79. package/experimental/random/HappyEyeballs.ff +40 -0
  80. package/experimental/random/HashMap.ff +72 -0
  81. package/experimental/random/IfElseUnit.ff +9 -0
  82. package/experimental/random/InputOutput.ff +23 -0
  83. package/experimental/random/ListVsArray.ff +45 -0
  84. package/experimental/random/Main.ff +44 -0
  85. package/experimental/random/MapTest.ff +67 -0
  86. package/experimental/random/OldTaskSystem.ff +210 -0
  87. package/experimental/random/PatternTest.ff +39 -0
  88. package/experimental/random/Patterns.ff +226 -0
  89. package/experimental/random/ReadBytesTest.ff +10 -0
  90. package/experimental/random/RunLength.ff +65 -0
  91. package/experimental/random/Scrape.ff +51 -0
  92. package/experimental/random/Serialization.ff +217 -0
  93. package/experimental/random/SerializationTest.ff +46 -0
  94. package/experimental/random/Serializer.ff +36 -0
  95. package/experimental/random/StdInOutErr.ff +4 -0
  96. package/experimental/random/Symbols.ff +74 -0
  97. package/experimental/random/Tag.ff +49 -0
  98. package/experimental/random/Tensor.ff +52 -0
  99. package/experimental/random/Try.ff +56 -0
  100. package/experimental/random/Tsv.ff +9 -0
  101. package/experimental/random/TypesAreModules.ff +87 -0
  102. package/experimental/random/blueprints/Blueprint.ff +52 -0
  103. package/experimental/random/blueprints/Main.ff +11 -0
  104. package/experimental/random/blueprints/Pretty.ff +58 -0
  105. package/experimental/random/blueprints/User.ff +64 -0
  106. package/experimental/random/blueprintsystem/BlueprintSystem.ff +48 -0
  107. package/experimental/random/blueprintsystem/Deserialize.ff +53 -0
  108. package/experimental/random/blueprintsystem/ReadJs.ff +13 -0
  109. package/experimental/random/blueprintsystem/User.ff +2 -0
  110. package/experimental/random/kahrs/Kahrs.ff +112 -0
  111. package/experimental/random/kahrs/TestKahrs.ff +22 -0
  112. package/experimental/random/kahrs/TestMap.ff +18 -0
  113. package/experimental/random/streaming/Gzip.ff +3 -0
  114. package/experimental/random/streaming/Main.ff +34 -0
  115. package/experimental/random/streaming/S3Bucket.ff +11 -0
  116. package/experimental/random/streaming/Tar.ff +5 -0
  117. package/experimental/rhymeapp/Main.ff +81 -0
  118. package/experimental/rhymeapp/index.html +14 -0
  119. package/firefly.sh +5 -0
  120. package/fireflysite/Main.ff +13 -0
  121. package/httpserver/.firefly/package.ff +1 -0
  122. package/httpserver/HttpServer.ff +184 -0
  123. package/lsp/.firefly/package.ff +1 -0
  124. package/lsp/CompletionHandler.ff +814 -0
  125. package/lsp/Handler.ff +551 -0
  126. package/lsp/HoverHandler.ff +82 -0
  127. package/lsp/LanguageServer.ff +229 -0
  128. package/lsp/SignatureHelpHandler.ff +55 -0
  129. package/lsp/SymbolHandler.ff +167 -0
  130. package/output/js/ff/compiler/Builder.mjs +483 -0
  131. package/output/js/ff/compiler/Compiler.mjs +410 -0
  132. package/output/js/ff/compiler/Dependencies.mjs +388 -0
  133. package/output/js/ff/compiler/Deriver.mjs +1166 -0
  134. package/output/js/ff/compiler/Dictionaries.mjs +1305 -0
  135. package/output/js/ff/compiler/Environment.mjs +1005 -0
  136. package/output/js/ff/compiler/Inference.mjs +4264 -0
  137. package/output/js/ff/compiler/JsEmitter.mjs +5353 -0
  138. package/output/js/ff/compiler/JsImporter.mjs +262 -0
  139. package/output/js/ff/compiler/LspHook.mjs +789 -0
  140. package/output/js/ff/compiler/Main.mjs +1695 -0
  141. package/output/js/ff/compiler/Parser.mjs +4004 -0
  142. package/output/js/ff/compiler/Patterns.mjs +923 -0
  143. package/output/js/ff/compiler/Resolver.mjs +2303 -0
  144. package/output/js/ff/compiler/Substitution.mjs +1146 -0
  145. package/output/js/ff/compiler/Syntax.mjs +12430 -0
  146. package/output/js/ff/compiler/Token.mjs +3092 -0
  147. package/output/js/ff/compiler/Tokenizer.mjs +589 -0
  148. package/output/js/ff/compiler/Unification.mjs +1748 -0
  149. package/output/js/ff/compiler/Wildcards.mjs +604 -0
  150. package/output/js/ff/compiler/Workspace.mjs +683 -0
  151. package/output/js/ff/core/Any.mjs +139 -0
  152. package/output/js/ff/core/Array.mjs +594 -0
  153. package/output/js/ff/core/AssetSystem.mjs +270 -0
  154. package/output/js/ff/core/Atomic.mjs +186 -0
  155. package/output/js/ff/core/Bool.mjs +141 -0
  156. package/output/js/ff/core/BrowserSystem.mjs +122 -0
  157. package/output/js/ff/core/Buffer.mjs +467 -0
  158. package/output/js/ff/core/BuildSystem.mjs +320 -0
  159. package/output/js/ff/core/Channel.mjs +268 -0
  160. package/output/js/ff/core/Char.mjs +145 -0
  161. package/output/js/ff/core/Core.mjs +300 -0
  162. package/output/js/ff/core/Duration.mjs +112 -0
  163. package/output/js/ff/core/Equal.mjs +175 -0
  164. package/output/js/ff/core/Error.mjs +138 -0
  165. package/output/js/ff/core/FileHandle.mjs +164 -0
  166. package/output/js/ff/core/Float.mjs +214 -0
  167. package/output/js/ff/core/HttpClient.mjs +210 -0
  168. package/output/js/ff/core/Instant.mjs +105 -0
  169. package/output/js/ff/core/Int.mjs +254 -0
  170. package/output/js/ff/core/IntMap.mjs +282 -0
  171. package/output/js/ff/core/JsSystem.mjs +234 -0
  172. package/output/js/ff/core/JsValue.mjs +678 -0
  173. package/output/js/ff/core/List.mjs +2335 -0
  174. package/output/js/ff/core/Lock.mjs +322 -0
  175. package/output/js/ff/core/Log.mjs +159 -0
  176. package/output/js/ff/core/Map.mjs +358 -0
  177. package/output/js/ff/core/NodeSystem.mjs +288 -0
  178. package/output/js/ff/core/Nothing.mjs +100 -0
  179. package/output/js/ff/core/Option.mjs +1002 -0
  180. package/output/js/ff/core/Ordering.mjs +734 -0
  181. package/output/js/ff/core/Pair.mjs +318 -0
  182. package/output/js/ff/core/Path.mjs +768 -0
  183. package/output/js/ff/core/RbMap.mjs +1936 -0
  184. package/output/js/ff/core/Serializable.mjs +434 -0
  185. package/output/js/ff/core/Set.mjs +250 -0
  186. package/output/js/ff/core/Show.mjs +201 -0
  187. package/output/js/ff/core/Stack.mjs +595 -0
  188. package/output/js/ff/core/Stream.mjs +1300 -0
  189. package/output/js/ff/core/String.mjs +433 -0
  190. package/output/js/ff/core/StringMap.mjs +282 -0
  191. package/output/js/ff/core/Task.mjs +345 -0
  192. package/output/js/ff/core/Try.mjs +503 -0
  193. package/output/js/ff/core/Unit.mjs +103 -0
  194. package/package.json +29 -0
  195. package/postgresql/.firefly/include/package-lock.json +250 -0
  196. package/postgresql/.firefly/include/package.json +5 -0
  197. package/postgresql/.firefly/include/prepare.sh +2 -0
  198. package/postgresql/.firefly/package.ff +3 -0
  199. package/postgresql/Pg.ff +530 -0
  200. package/unsafejs/.firefly/package.ff +1 -0
  201. package/unsafejs/UnsafeJs.ff +19 -0
  202. package/vscode/.vscode/launch.json +18 -0
  203. package/vscode/.vscode/tasks.json +33 -0
  204. package/vscode/LICENSE.txt +21 -0
  205. package/vscode/Prepublish.ff +15 -0
  206. package/vscode/README.md +17 -0
  207. package/vscode/client/package-lock.json +544 -0
  208. package/vscode/client/package.json +22 -0
  209. package/vscode/client/src/extension.ts +64 -0
  210. package/vscode/client/tsconfig.json +12 -0
  211. package/vscode/icons/firefly-icon.png +0 -0
  212. package/vscode/icons/firefly-icon.svg +10 -0
  213. package/vscode/icons/firefly-logo-notext.png +0 -0
  214. package/vscode/icons/firefly-logo.png +0 -0
  215. package/vscode/language-configuration.json +39 -0
  216. package/vscode/package-lock.json +3623 -0
  217. package/vscode/package.json +144 -0
  218. package/vscode/snippets-none.json +1 -0
  219. package/vscode/snippets.json +241 -0
  220. package/vscode/syntaxes/firefly.tmLanguage.json +294 -0
  221. package/vscode/tsconfig.json +20 -0
package/lsp/Handler.ff ADDED
@@ -0,0 +1,551 @@
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 HoverHandler
11
+ import CompletionHandler
12
+ import SignatureHelpHandler
13
+ import SymbolHandler
14
+
15
+ capability Handler(
16
+ fireflyPath: Path
17
+ mutable rootPath: Option[Path]
18
+ mutable virtualFiles: Map[String, String]
19
+ mutable cancelledRequests: Set[MessageId]
20
+ mutable responseCache: Map[Pair[String, Location], ResultOrError]
21
+ mutable fileSymbolsCache: Map[String, List[DocumentSymbol]]
22
+ )
23
+
24
+ data MessageId {
25
+ MessageIdInt(id: Int)
26
+ MessageIdString(id: String)
27
+ }
28
+
29
+ data ResultOrError {
30
+ Result(result: String)
31
+ Error(code: Int, message: String)
32
+ }
33
+
34
+ data LanguageServerException(message: String)
35
+
36
+ data TokenLocation(
37
+ file: String
38
+ startLine: Int
39
+ startColumn: Int
40
+ endLine: Int
41
+ endColumn: Int
42
+ raw: String
43
+ followedByLeftBracket: Bool
44
+ )
45
+
46
+
47
+ extend self: Handler {
48
+
49
+ handleNotification(system: NodeSystem, method: String, parameters: Map[String, JsValue]): Unit {
50
+ method.{
51
+ | "initialized" =>
52
+ | "textDocument/didChange" =>
53
+ self.handleDidChange(system, parameters)
54
+ | "textDocument/didClose" => self.handleDidClose(system, parameters)
55
+ | "exit" => system.exit(0)
56
+ | _ =>
57
+ }
58
+ }
59
+
60
+ handleRequest(system: NodeSystem, method: String, parameters: Map[String, JsValue]): ResultOrError {
61
+ method.{
62
+ | "initialize" => self.handleInitialize(system, parameters)
63
+ | "textDocument/diagnostic" => self.handleDiagnostic(system, parameters)
64
+ | "textDocument/documentSymbol" => self.handleDocumentSymbol(system, parameters)
65
+ | "textDocument/completion" => self.handleCompletion(system, parameters)
66
+ | "textDocument/signatureHelp" => self.handleSignatureHelp(system, parameters)
67
+ | "textDocument/hover" =>
68
+ self.handleTokenRequestWithCache(system, method, parameters) {targetAt =>
69
+ self.handleHover(system, targetAt, goToDefinition = False)
70
+ }
71
+ | "textDocument/definition" =>
72
+ self.handleTokenRequestWithCache(system, method, parameters) {targetAt =>
73
+ self.handleHover(system, targetAt, goToDefinition = True)
74
+ }
75
+ | "textDocument/references" =>
76
+ self.handleTokenRequestWithCache(system, method, parameters) {targetAt =>
77
+ self.handleReferences(system, targetAt, local = False)
78
+ }
79
+ | "textDocument/rename" => self.handleRename(system, parameters)
80
+ | "textDocument/documentHighlight" =>
81
+ self.handleTokenRequestWithCache(system, method, parameters) {targetAt =>
82
+ self.handleReferences(system, targetAt, local = True)
83
+ }
84
+ | "workspace/symbol" => self.printTime(system.mainTask(), "handleWorkspaceSymbol") {self.handleWorkspaceSymbol(system, parameters)}
85
+ | "shutdown" => Result(system.js().null().toJson())
86
+ | _ => self.handleUnsupported(system.js())
87
+ }
88
+ }
89
+
90
+ handleTokenRequestWithCache(
91
+ system: NodeSystem
92
+ method: String
93
+ parameters: Map[String, JsValue]
94
+ handle: Location => ResultOrError
95
+ ): ResultOrError {
96
+ let targetAt = self.findTokenFromParameters(system, parameters)
97
+ let cacheKey = Pair(method, targetAt)
98
+ self.responseCache.get(cacheKey).{
99
+ | Some(hit) => hit
100
+ | None =>
101
+ let result = handle(targetAt)
102
+ self.responseCache = self.responseCache.add(cacheKey, result)
103
+ result
104
+ }
105
+ }
106
+
107
+ handleUnsupported(js: JsSystem): ResultOrError {
108
+ Error(1234, "Unsupported method")
109
+ }
110
+
111
+ handleInitialize(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
112
+ let js = system.js()
113
+ self.rootPath = Some(system.pathFromUrl(parameters.grab("rootUri").grabString()))
114
+
115
+ let anyFireflyFile = js.object()
116
+ .with("filters", [
117
+ js.object()
118
+ .with("pattern", js.object()
119
+ .with("glob", "**/*.ff")
120
+ .with("matches", "file")
121
+ )
122
+ ].toArray())
123
+
124
+ let o = js.object()
125
+ .with("capabilities", js.object()
126
+ .with("textDocumentSync", js.object()
127
+ .with("openClose", True)
128
+ .with("change", 1 /* TextDocumentSyncKind.Full */)
129
+ )
130
+ .with("hoverProvider", True)
131
+ .with("definitionProvider", True)
132
+ .with("documentHighlightProvider", True)
133
+ .with("diagnosticProvider", js.object()
134
+ .with("interFileDependencies", False)
135
+ .with("workspaceDiagnostics", False)
136
+ )
137
+ .with("documentSymbolProvider", True)
138
+ .with("completionProvider", js.object()
139
+ .with("triggerCharacters", js.array([".", " ", "("].map {js.value(_)}))
140
+ .with("allCommitCharacters", js.array([";"].map {js.value(_)}))
141
+ )
142
+ .with("signatureHelpProvider", js.object()
143
+ .with("triggerCharacters", js.array(["("].map {js.value(_)}))
144
+ .with("retriggerCharacters", js.array([","].map {js.value(_)}))
145
+ )
146
+ .with("referencesProvider", True)
147
+ .with("workspaceSymbolProvider", True)
148
+ /*.with("workspace", js.object()
149
+ .with("workspaceFolders", js.object()
150
+ .with("supported", True)
151
+ .with("changeNotifications", True)
152
+ )
153
+ .with("fileOperations", js.object()
154
+ .with("didCreate", anyFireflyFile)
155
+ .with("didRename", anyFireflyFile)
156
+ .with("didDelete", anyFireflyFile)
157
+ )
158
+ )*/
159
+ .with("renameProvider", True)
160
+ )
161
+ .with("serverInfo", js.object()
162
+ .with("name", "Firefly Language Server")
163
+ .with("version", "0.0.0")
164
+ )
165
+ Result(o.toJson())
166
+ }
167
+
168
+ handleDiagnostic(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
169
+ let js = system.js()
170
+ let uri = parameters.grab("textDocument").get("uri").grabString()
171
+ let path = system.pathFromUrl(uri)
172
+ let fireflyPath = system.path(".")
173
+ let diagnostics = try {
174
+ Builder.check(system, fireflyPath, path, self.virtualFiles, LspHook.disabled(), True)
175
+ []
176
+ } catch {| CompileError(at, message), error =>
177
+ let tokenLocation = self.findToken(system, at)
178
+ let diagnostic = js.object()
179
+ .with("range", self.tokenLocationToLspRange(js, tokenLocation))
180
+ .with("severity", 1 /* Error */)
181
+ .with("message", message)
182
+ [diagnostic]
183
+ } grab()
184
+
185
+ let o = js.object()
186
+ .with("kind", "full")
187
+ .with("items", diagnostics.toArray())
188
+ Result(o.toJson())
189
+ }
190
+
191
+ handleDocumentSymbol(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
192
+ let js = system.js()
193
+ let uri = parameters.grab("textDocument").get("uri").grabString()
194
+ let path = system.pathFromUrl(uri)
195
+ let symbols = self.getDocumentSymbolsWithCache(system, path)
196
+ let lspSymbols = symbols.map {SymbolHandler.documentSymbolToLsp(js, _)}
197
+ Result(js.array(lspSymbols).toJson())
198
+ }
199
+
200
+ getDocumentSymbols(system: NodeSystem, path: Path): List[DocumentSymbol] {
201
+ try {
202
+ let lspHook = LspHook.make(at = None, definedAt = None, insertIdentifier = False, trackSymbols = True)
203
+ let code = self.virtualFiles.get(path.absolute()).else {path.readText()}
204
+ let tokens = Tokenizer.tokenize(path.absolute(), code, None, True)
205
+ let parser = Parser.make(PackagePair("script", "script"), path.absolute(), tokens, False, lspHook)
206
+ parser.parseModuleWithPackageInfo()
207
+ SymbolHandler.readAllSymbols(lspHook.results())
208
+ } catch {| CompileError(at, message), error =>
209
+ Log.trace("handleDocumentSymbol parse error: " + message)
210
+ []
211
+ } catch {| ReadSymbolsError e, _ =>
212
+ Log.trace("handleDocumentSymbol ReadSymbolsError")
213
+ []
214
+ } grab()
215
+ }
216
+
217
+ getDocumentSymbolsWithCache(system: NodeSystem, file: Path): List[DocumentSymbol] {
218
+ self.fileSymbolsCache.get(file.absolute()).{
219
+ | Some(symbols) =>
220
+ //Log.trace("HIT symbols: " + file)
221
+ symbols
222
+ | None =>
223
+ //Log.trace("MISS symbols: " + file)
224
+ let symbols = self.getDocumentSymbols(system, file)
225
+ self.fileSymbolsCache = self.fileSymbolsCache.add(file.absolute(), symbols)
226
+ symbols
227
+ }
228
+ }
229
+
230
+ handleWorkspaceSymbol(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
231
+ let js = system.js()
232
+ let query = parameters.grab("query").grabString()
233
+ let files = self.printTime(system.mainTask(), "findFireflyFiles") {Builder.findFireflyFiles(self.rootPath.grab())}
234
+ let symbols = files.flatMap {file =>
235
+ let documentSymbols = self.getDocumentSymbolsWithCache(system, file)
236
+ let workspaceSymbols = documentSymbols.flatMap {SymbolHandler.documentToWorkspaceSymbols(_, None)}
237
+ if(query == "") {
238
+ workspaceSymbols.filter {_.kind == 5} // Class
239
+ } else {
240
+ workspaceSymbols.filter {SymbolHandler.symbolFilter(_, query)}
241
+ }
242
+ }
243
+ let limitedSymbols = symbols.sortBy {_.name.size()}.takeFirst(100)
244
+ let jsSymbols = limitedSymbols.map {SymbolHandler.workspaceSymbolToLsp(js, self.rootPath.grab(), _)}
245
+ Result(js.array(jsSymbols).toJson())
246
+ }
247
+
248
+ handleDidChange(system: NodeSystem, parameters: Map[String, JsValue]): Unit {
249
+ let js = system.js()
250
+ let uri = parameters.grab("textDocument").get("uri").grabString()
251
+ let path = system.pathFromUrl(uri)
252
+ self.responseCache = Map.empty()
253
+ self.fileSymbolsCache = self.fileSymbolsCache.remove(path.absolute())
254
+ let contentChanges = parameters.grab("contentChanges").grabArray()
255
+ if(contentChanges.size() != 1) {throw(LanguageServerException("Expected a single element in contentChanges"))} else:
256
+ let contentChange = contentChanges.grab(0)
257
+ if(!contentChange.get("range").isNullOrUndefined()) {throw(LanguageServerException("Expected a complete contentChange"))} else:
258
+ let content = contentChange.get("text").grabString()
259
+ self.virtualFiles = self.virtualFiles.add(path.absolute(), content)
260
+ }
261
+
262
+ handleDidClose(system: NodeSystem, parameters: Map[String, JsValue]): Unit {
263
+ let js = system.js()
264
+ let uri = parameters.grab("textDocument").get("uri").grabString()
265
+ let path = system.pathFromUrl(uri)
266
+ self.virtualFiles = self.virtualFiles.remove(path.absolute())
267
+ }
268
+
269
+ handleHover(system: NodeSystem, targetAt: Location, goToDefinition: Bool): ResultOrError {
270
+ let lspHook = LspHook.make(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
271
+ try {
272
+ Builder.check(system, self.fireflyPath, system.path(targetAt.file), self.virtualFiles, lspHook, True)
273
+ } catch {| CompileError(at, message), error =>
274
+ Log.trace("handleHover check error: " + message)
275
+ } grab()
276
+ let o = if(goToDefinition) {
277
+ HoverHandler.handleGoToDefinition(system, self, lspHook)
278
+ } else {
279
+ HoverHandler.handleHover(system, self, lspHook)
280
+ }
281
+ Result(o.toJson())
282
+ }
283
+
284
+ handleCompletion(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
285
+ let location = self.findLocationFromParameters(system, parameters)
286
+ let token = self.findToken(system, location, insertToken = True)
287
+ let completionAt = Location(
288
+ file = location.file
289
+ line = token.startLine
290
+ column = token.startColumn
291
+ )
292
+ Log.show(token.followedByLeftBracket)
293
+ let lspHook = LspHook.make(at = Some(completionAt), definedAt = None, insertIdentifier = True, trackSymbols = False)
294
+ try {
295
+ Builder.check(system, self.fireflyPath, system.path(completionAt.file), self.virtualFiles, lspHook, True)
296
+ } catch {| CompileError(at, message), error =>
297
+ Log.trace("handleCompletion check error: " + message)
298
+ } grab()
299
+ let o = CompletionHandler.handleCompletion(system, lspHook, completionAt.column == 1, token.followedByLeftBracket)
300
+ Result(o.toJson())
301
+ }
302
+
303
+ handleSignatureHelp(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
304
+ let cursorAt = self.findLocationFromParameters(system, parameters)
305
+ let lspHook = LspHook.make(at = Some(cursorAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
306
+ try {
307
+ let code = self.virtualFiles.get(cursorAt.file).else {system.path(cursorAt.file).readText()}
308
+ let tokens = Tokenizer.tokenize(cursorAt.file, code, None, True)
309
+ let parser = Parser.make(PackagePair("script", "script"), cursorAt.file, tokens, False, lspHook)
310
+ parser.parseModuleWithPackageInfo()
311
+ Unit // Think of a way to avoid writing this
312
+ } catch {| CompileError(at, message), error =>
313
+ Log.trace("handleSignatureHelp check error: " + message)
314
+ } grab()
315
+ let o = lspHook.results().collectFirst {
316
+ | ParseArgumentHook h {parameters.get("context") | Some(context)} {
317
+ context.get("activeSignatureHelp") | help
318
+ } {!help.isNullOrUndefined() && help.hasOwn("signatures")} {
319
+ help.get("signatures").get(0) | activeSignature
320
+ } {!activeSignature.isNullOrUndefined()} {
321
+ activeSignature.get("parameters").grabArray().last() | Some(fakeParameter)
322
+ } {
323
+ fakeParameter.get("label").grabString() == "/* Call " + h.callAt.show() + " */"
324
+ } =>
325
+ Some(SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName))
326
+ | ParseArgumentHook h =>
327
+ let callLspHook = LspHook.make(at = Some(h.callAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
328
+ try {
329
+ Builder.check(system, self.fireflyPath, system.path(cursorAt.file), self.virtualFiles, callLspHook, True)
330
+ } catch {| CompileError(at, message), error =>
331
+ Log.trace("handleSignatureHelp check 2 error: " + message)
332
+ } grab()
333
+ let help = SignatureHelpHandler.handleSignatureHelp(system, callLspHook)
334
+ Some(SignatureHelpHandler.pickActiveParameter(help, h.argumentIndex, h.parameterName))
335
+ | _ =>
336
+ None
337
+ }
338
+ Result((o.else {system.js().null()}).toJson())
339
+ }
340
+
341
+ handleReferences(system: NodeSystem, targetAt: Location, local: Bool): ResultOrError {
342
+ let tokens = self.findReferences(system, targetAt, local)
343
+ let js = system.js()
344
+ let o = tokens.{
345
+ | None => js.null()
346
+ | Some(tokens) =>
347
+ let lspTokens = tokens.map {self.tokenLocationToLspLocation(system, _)}
348
+ js.array(lspTokens)
349
+ }
350
+ Result(o.toJson())
351
+ }
352
+
353
+ handleRename(system: NodeSystem, parameters: Map[String, JsValue]): ResultOrError {
354
+ let newName = parameters.grab("newName").grabString()
355
+ let targetAt = self.findTokenFromParameters(system, parameters)
356
+ let tokens = self.findReferences(system, targetAt, False)
357
+ let js = system.js()
358
+ tokens.{
359
+ | None => Error(-32602, "Token definition not found") // InvalidParams
360
+ | Some([]) => Result(js.null().toJson())
361
+ | Some([first, ..._] @ tokens) =>
362
+ let oldName = first.raw
363
+ Log.trace("Rename '" + oldName + "' to '" + newName + "'")
364
+
365
+ // TODO findReferences returns some bad tokens
366
+ let goodTokens = tokens.filter {_.raw == oldName}
367
+ let badTokens = tokens.filter {_.raw != oldName}
368
+ badTokens.each {t => Log.trace("Rename bad token: " + Show.show(t))}
369
+
370
+ let byFile = goodTokens.map {t => Pair(t.file, t)}.group()
371
+ let allChanges = byFile.toList().foldLeft(js.object(), {| o, Pair(file, fileTokens) =>
372
+ let uri = system.path(file).url()
373
+ let fileChanges = fileTokens.map {tokenLocation =>
374
+ js.object()
375
+ .with("range", self.tokenLocationToLspRange(js, tokenLocation))
376
+ .with("newText", newName)
377
+ }
378
+ o.with(uri, js.array(fileChanges))
379
+ })
380
+ let o = js.object().with("changes", allChanges)
381
+ Result(o.toJson())
382
+ }
383
+ }
384
+
385
+ findReferences(system: NodeSystem, targetAt: Location, local: Bool): Option[List[TokenLocation]] {
386
+ let temporaryLspHook = LspHook.make(at = Some(targetAt), definedAt = None, insertIdentifier = False, trackSymbols = False)
387
+ try {
388
+ Builder.check(system, self.fireflyPath, system.path(targetAt.file), self.virtualFiles, temporaryLspHook, True)
389
+ } catch {| CompileError(at, message), error =>
390
+ Log.trace("findReferences first check error: " + message)
391
+ } grab()
392
+
393
+ //Log.trace("defined at hooks: " + Show.show(temporaryLspHook.results().map(LspHook.showHook)))
394
+ let definedAtList = temporaryLspHook.results().collect {
395
+ | ResolveSymbolHook h => Some(h.symbol.definedAt)
396
+ | ResolveVariantFieldHook h => Some(h.symbol.definedAt)
397
+ | ResolveTypeHook h => Some(h.symbol.definedAt)
398
+ | ResolveConstraintHook h => Some(h.symbol.definedAt)
399
+ | ResolveSignatureHook h {!h.isInstanceMethod} => Some(h.signature.at)
400
+ | InferParameterHook h => Some(h.parameter.at)
401
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
402
+ h.parameters.find {_.name == n}.map {_.at}
403
+ | InferLookupHook h => Some(h.symbol.value.definedAt)
404
+ | InferPatternHook h {h.pattern | PVariantAs p} => Some(p.variableAt)
405
+ | InferPatternHook h {h.pattern | PVariable p} => Some(p.at)
406
+ | InferPatternHook h {h.pattern | PAlias p} => Some(p.at)
407
+ | _ => None
408
+ }.filter {at => !at.file.endsWith(">")}
409
+
410
+ //Log.trace("definedAtList: " + Show.show(definedAtList))
411
+
412
+ let js = system.js()
413
+ definedAtList.first().{
414
+ | Some(definedAt) =>
415
+ //Log.trace("handleReferences definedAt: " + Show.show(definedAt))
416
+ let lspHook = LspHook.make(at = None, definedAt = Some(definedAt), insertIdentifier = False, trackSymbols = False)
417
+ try {
418
+ let path = if(local) {system.path(targetAt.file)} else {self.rootPath.grab()}
419
+ Builder.check(system, self.fireflyPath, path, self.virtualFiles, lspHook, True)
420
+ } catch {| CompileError(at, message), error =>
421
+ // TODO: This skips checking all the other files
422
+ Log.trace("findReferences second check error: " + message)
423
+ } grab()
424
+
425
+ let referencesResult = lspHook.results().collect {
426
+ | ResolveSymbolHook h => Some(h.symbol.usageAt)
427
+ | ResolveVariantFieldHook h => Some(h.symbol.usageAt)
428
+ | ResolveTypeHook h => Some(h.symbol.usageAt)
429
+ | ResolveConstraintHook h => Some(h.symbol.usageAt)
430
+ | ResolveSignatureHook h => Some(h.signature.at)
431
+ | InferParameterHook h => Some(h.parameter.at)
432
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} {a.name | Some(n)} =>
433
+ h.parameters.find {_.name == n}.map {_ => a.at}
434
+ | InferLookupHook h => Some(h.symbol.value.usageAt)
435
+ | _ => None
436
+ }.filter {at => !at.file.endsWith(">")}
437
+
438
+ //Log.trace("referencesResult: " + Show.show(referencesResult))
439
+
440
+ let clientLocations = referencesResult.addAll([definedAt]).distinct().filter {
441
+ !local || _.file == targetAt.file
442
+ }.map {at =>
443
+ self.findToken(system, at)
444
+ }
445
+ let sorted = clientLocations.sortBy {c => Location(c.file, c.startLine, c.startColumn)}
446
+ Some(sorted)
447
+ | None =>
448
+ None
449
+ }
450
+ }
451
+
452
+ makeNotificationMessage(js: JsSystem, method: String, params: JsValue): JsValue {
453
+ js.object()
454
+ .with("jsonrpc", "2.0")
455
+ .with("method", method)
456
+ .with("params", params)
457
+ }
458
+
459
+ findToken(system: NodeSystem, at: Location, insertToken: Bool = False): TokenLocation {
460
+ let fallback = TokenLocation(
461
+ file = at.file
462
+ startLine = at.line
463
+ startColumn = at.column
464
+ endLine = at.line
465
+ endColumn = at.column + 1
466
+ raw = ""
467
+ followedByLeftBracket = True
468
+ )
469
+ try {
470
+ let code = self.virtualFiles.get(at.file).else {system.path(at.file).readText()}
471
+ let tokens = Tokenizer.tokenize(at.file, code, Some(at).filter {_ => insertToken}, True)
472
+ let token = tokens.toStream().find {token =>
473
+ at.line >= token.startLine && at.line <= token.stopLine && (
474
+ (at.line > token.startLine || at.column >= 1 + token.startOffset - token.startLineOffset) &&
475
+ (at.line < token.stopLine || at.column < 1 + token.stopOffset - token.stopLineOffset || (
476
+ (token.kind == LLower || token.kind == LUpper) &&
477
+ at.column == 1 + token.stopOffset - token.stopLineOffset
478
+ ))
479
+ )
480
+ }
481
+ token.map {t =>
482
+ function leftBracketAt(i: Int): Bool {
483
+ i >= 0 && i < code.size() &&
484
+ (code.grab(i) == '(' || code.grab(i) == '[' || code.grab(i) == '{')
485
+ }
486
+ let followedByLeftBracket =
487
+ leftBracketAt(t.startOffset) ||
488
+ leftBracketAt(t.stopOffset) ||
489
+ leftBracketAt(t.stopOffset + 1)
490
+ TokenLocation(
491
+ file = t.file
492
+ startLine = t.startLine
493
+ startColumn = 1 + t.startOffset - t.startLineOffset
494
+ endLine = t.stopLine
495
+ endColumn = 1 + t.stopOffset - t.stopLineOffset
496
+ raw = t.raw()
497
+ followedByLeftBracket = followedByLeftBracket
498
+ )
499
+ }.else {fallback}
500
+ } catchAny {_ =>
501
+ fallback
502
+ } grab()
503
+ }
504
+
505
+ findTokenFromParameters(system: NodeSystem, parameters: Map[String, JsValue]): Location {
506
+ let location = self.findLocationFromParameters(system, parameters)
507
+ let token = self.findToken(system, location)
508
+ Location(
509
+ file = location.file
510
+ line = token.startLine
511
+ column = token.startColumn
512
+ )
513
+ }
514
+
515
+ findLocationFromParameters(system: NodeSystem, parameters: Map[String, JsValue]): Location {
516
+ let uri = parameters.grab("textDocument").get("uri").grabString()
517
+ let path = system.pathFromUrl(uri)
518
+ let line = parameters.grab("position").get("line").grabInt() + 1
519
+ let column = parameters.grab("position").get("character").grabInt() + 1
520
+ Location(path.absolute(), line, column)
521
+ }
522
+
523
+ tokenLocationToLspLocation(system: NodeSystem, tokenLocation: TokenLocation): JsValue {
524
+ let js = system.js()
525
+ js.object()
526
+ .with("uri", system.path(tokenLocation.file).url())
527
+ .with("range", self.tokenLocationToLspRange(js, tokenLocation))
528
+ }
529
+
530
+ tokenLocationToLspRange(js: JsSystem, tokenLocation: TokenLocation): JsValue {
531
+ js.object()
532
+ .with("start", js.object()
533
+ .with("line", tokenLocation.startLine - 1)
534
+ .with("character", tokenLocation.startColumn - 1)
535
+ )
536
+ .with("end", js.object()
537
+ .with("line", tokenLocation.endLine - 1)
538
+ .with("character", tokenLocation.endColumn - 1)
539
+ )
540
+ }
541
+
542
+ printTime[R](task: Task, label: String, body: () => R): R {
543
+ let start = task.elapsed()
544
+ let result = body()
545
+ let stop = task.elapsed()
546
+ let duration = ("" + (stop.seconds - start.seconds)).slice(0, 5)
547
+ Log.trace(label + ": " + duration + "s")
548
+ result
549
+ }
550
+
551
+ }
@@ -0,0 +1,82 @@
1
+ import LspHook from ff:compiler
2
+ import Unification from ff:compiler
3
+ import Environment from ff:compiler
4
+ import Syntax from ff:compiler
5
+ import Handler
6
+
7
+ handleGoToDefinition(system: NodeSystem, handler: Handler, lspHook: LspHook): JsValue {
8
+ let js = system.js()
9
+ let definedAt = lspHook.results().collect {
10
+ | ResolveSymbolHook h => Some(h.symbol.definedAt)
11
+ | ResolveTypeHook h => Some(h.symbol.definedAt)
12
+ | ResolveConstraintHook h => Some(h.symbol.definedAt)
13
+ | InferLookupHook h => Some(h.symbol.value.definedAt)
14
+ | InferParameterHook h => Some(h.parameter.at)
15
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} =>
16
+ a.name.flatMap {name =>
17
+ h.parameters.find {_.name == name}.map {p =>
18
+ p.at
19
+ }
20
+ }
21
+ | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
22
+ let fieldNames = n.split('$').toList().dropFirst(1)
23
+ fieldNames.zip(ts).collectFirst {| Pair(name, t) =>
24
+ if(h.fieldName == name) {
25
+ h.unification.substitute(t).at // TODO: This points to the field type rather than the field name
26
+ }
27
+ }
28
+ | _ => None
29
+ }
30
+ Log.trace("handleGoToDefinition definedAt: " + Show.show(definedAt))
31
+ definedAt.first().map {at =>
32
+ let token = handler.findToken(system, at)
33
+ handler.tokenLocationToLspLocation(system, token)
34
+ }.else {js.null()}
35
+ }
36
+
37
+ handleHover(system: NodeSystem, handler: Handler, lspHook: LspHook): JsValue {
38
+ let js = system.js()
39
+ let typeAndEffect = lspHook.results().collectFirst {
40
+ | InferLookupHook h {h.instantiated.value | Some(instantiated)} =>
41
+ let t = h.unification.substitute(instantiated.scheme.signature.returnType)
42
+ let q = h.unification.substitute(instantiated.scheme.signature.effect)
43
+ Some(Pair(t, q))
44
+ | InferPatternHook h =>
45
+ let t = h.unification.substitute(h.expected)
46
+ let noEffect = TConstructor(h.pattern.at, "ff:core/Nothing.Nothing", [])
47
+ Some(Pair(t, noEffect))
48
+ | InferTermHook h {h.term | ELet e} =>
49
+ let t = h.unification.substitute(e.valueType)
50
+ let noEffect = TConstructor(e.at, "ff:core/Nothing.Nothing", [])
51
+ Some(Pair(t, noEffect))
52
+ | InferArgumentHook h {h.arguments.dropFirst(h.argumentIndex).first() | Some(a)} =>
53
+ let noEffect = TConstructor(h.callAt, "ff:core/Nothing.Nothing", [])
54
+ a.name.flatMap {name =>
55
+ h.parameters.find {_.name == name}.map {p =>
56
+ Pair(h.unification.substitute(p.valueType), noEffect)
57
+ }
58
+ }
59
+ | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
60
+ let fieldNames = n.split('$').toList().dropFirst(1)
61
+ fieldNames.zip(ts).collectFirst {| Pair(name, t) =>
62
+ if(h.fieldName == name) {
63
+ let noEffect = TConstructor(t.at, "ff:core/Nothing.Nothing", [])
64
+ let t2 = h.unification.substitute(t)
65
+ Pair(h.unification.substitute(t), noEffect)
66
+ }
67
+ }
68
+ | _ =>
69
+ None
70
+ }
71
+ typeAndEffect.map {| Pair(t, q) =>
72
+ let extra = q.{
73
+ | TConstructor(_, "Q$", _) => "*This call may be asynchronous.*"
74
+ | _ => ""
75
+ }
76
+ js.object().with("contents",
77
+ js.object()
78
+ .with("kind", "markdown")
79
+ .with("value", "```\n" + t.show([]) + "\n```\n" + extra)
80
+ )
81
+ }.else {js.null()}
82
+ }