firefly-compiler 0.4.51 → 0.4.53

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 (109) hide show
  1. package/.hintrc +4 -4
  2. package/.vscode/settings.json +4 -4
  3. package/bin/Release.ff +153 -153
  4. package/bin/firefly.mjs +1 -1
  5. package/compiler/Builder.ff +257 -257
  6. package/compiler/Compiler.ff +227 -227
  7. package/compiler/Dependencies.ff +186 -186
  8. package/compiler/DependencyLock.ff +17 -17
  9. package/compiler/JsEmitter.ff +946 -946
  10. package/compiler/LspHook.ff +202 -202
  11. package/compiler/ModuleCache.ff +178 -178
  12. package/compiler/Workspace.ff +88 -88
  13. package/core/.firefly/include/package-lock.json +394 -394
  14. package/core/.firefly/include/package.json +5 -5
  15. package/core/.firefly/include/prepare.sh +1 -1
  16. package/core/.firefly/package.ff +2 -2
  17. package/core/Array.ff +265 -265
  18. package/core/Atomic.ff +64 -64
  19. package/core/Box.ff +7 -7
  20. package/core/BrowserSystem.ff +40 -40
  21. package/core/BuildSystem.ff +148 -148
  22. package/core/Crypto.ff +96 -96
  23. package/core/Equal.ff +36 -36
  24. package/core/HttpClient.ff +87 -87
  25. package/core/JsSystem.ff +69 -69
  26. package/core/Json.ff +434 -434
  27. package/core/List.ff +486 -486
  28. package/core/Lock.ff +144 -144
  29. package/core/NodeSystem.ff +190 -189
  30. package/core/Ordering.ff +161 -161
  31. package/core/Path.ff +401 -401
  32. package/core/Random.ff +134 -134
  33. package/core/RbMap.ff +216 -216
  34. package/core/Show.ff +43 -43
  35. package/core/SourceLocation.ff +68 -68
  36. package/core/Task.ff +141 -141
  37. package/experimental/benchmarks/ListGrab.ff +23 -23
  38. package/experimental/benchmarks/ListGrab.java +55 -55
  39. package/experimental/benchmarks/Pyrotek45.ff +30 -30
  40. package/experimental/benchmarks/Pyrotek45.java +64 -64
  41. package/experimental/bidirectional/Bidi.ff +88 -88
  42. package/experimental/random/Index.ff +53 -53
  43. package/experimental/random/Process.ff +120 -120
  44. package/experimental/random/Scrape.ff +51 -51
  45. package/experimental/random/Symbols.ff +73 -73
  46. package/experimental/random/Tensor.ff +52 -52
  47. package/experimental/random/Units.ff +36 -36
  48. package/experimental/s3/S3TestAuthorizationHeader.ff +38 -38
  49. package/experimental/s3/S3TestPut.ff +15 -15
  50. package/experimental/tests/TestJson.ff +26 -26
  51. package/firefly.sh +0 -0
  52. package/fireflysite/Main.ff +13 -13
  53. package/lsp/.firefly/package.ff +1 -1
  54. package/lsp/CompletionHandler.ff +808 -808
  55. package/lsp/Handler.ff +714 -714
  56. package/lsp/HoverHandler.ff +79 -79
  57. package/lsp/LanguageServer.ff +272 -272
  58. package/lsp/SignatureHelpHandler.ff +55 -55
  59. package/lsp/SymbolHandler.ff +181 -181
  60. package/lsp/TestReferences.ff +16 -16
  61. package/lsp/TestReferencesCase.ff +7 -7
  62. package/lsp/stderr.txt +1 -1
  63. package/lsp/stdin.txt +10 -10
  64. package/lsp/stdout.txt +40 -40
  65. package/lux/.firefly/package.ff +1 -1
  66. package/lux/Css.ff +648 -648
  67. package/lux/CssTest.ff +48 -48
  68. package/lux/Lux.ff +487 -487
  69. package/lux/LuxEvent.ff +116 -116
  70. package/lux/Main.ff +128 -128
  71. package/lux/Main2.ff +144 -144
  72. package/output/js/ff/compiler/Builder.mjs +43 -43
  73. package/output/js/ff/compiler/Dependencies.mjs +3 -3
  74. package/output/js/ff/core/Array.mjs +59 -59
  75. package/output/js/ff/core/Atomic.mjs +36 -36
  76. package/output/js/ff/core/BrowserSystem.mjs +11 -11
  77. package/output/js/ff/core/BuildSystem.mjs +30 -30
  78. package/output/js/ff/core/Crypto.mjs +40 -40
  79. package/output/js/ff/core/HttpClient.mjs +24 -24
  80. package/output/js/ff/core/Json.mjs +147 -147
  81. package/output/js/ff/core/List.mjs +50 -50
  82. package/output/js/ff/core/Lock.mjs +97 -97
  83. package/output/js/ff/core/NodeSystem.mjs +78 -77
  84. package/output/js/ff/core/Ordering.mjs +8 -8
  85. package/output/js/ff/core/Path.mjs +231 -231
  86. package/output/js/ff/core/Random.mjs +56 -56
  87. package/output/js/ff/core/Task.mjs +31 -31
  88. package/package.json +1 -1
  89. package/rpc/.firefly/package.ff +1 -1
  90. package/rpc/Rpc.ff +69 -69
  91. package/s3/.firefly/package.ff +1 -1
  92. package/s3/S3.ff +90 -90
  93. package/unsafejs/UnsafeJs.ff +19 -19
  94. package/vscode/LICENSE.txt +21 -21
  95. package/vscode/Prepublish.ff +15 -15
  96. package/vscode/README.md +16 -16
  97. package/vscode/client/package.json +22 -22
  98. package/vscode/client/src/extension.ts +104 -104
  99. package/vscode/icons/firefly-icon.svg +10 -10
  100. package/vscode/language-configuration.json +61 -61
  101. package/vscode/package-lock.json +3623 -3623
  102. package/vscode/package.json +1 -1
  103. package/vscode/snippets.json +241 -241
  104. package/webserver/.firefly/include/package-lock.json +16 -16
  105. package/webserver/.firefly/include/package.json +5 -5
  106. package/webserver/.firefly/package.ff +2 -2
  107. package/webserver/WebServer.ff +685 -685
  108. package/websocket/.firefly/package.ff +1 -1
  109. package/websocket/WebSocket.ff +131 -131
@@ -1,808 +1,808 @@
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
- data CompletionInfo(
8
- label: String
9
- extra: String
10
- snippet: String
11
- member: Bool
12
- type: Type
13
- documentation: String
14
- expectedType: Option[Type]
15
- secondarySort: Int = 5
16
- )
17
-
18
- handleCompletion(lspHook: LspHook, toplevel: Bool, followedByOpenBracket: Bool): Json {
19
- let topLevelCompletions = if(!toplevel) {[]} else {toplevelCompletion(lspHook)}
20
- let patternCompletions = lspHook.results().collectFirst {
21
- | InferPatternHook h =>
22
- let expected = h.unification.substitute(h.expected)
23
- Some(patternCompletion(h.unification, h.environment, expected))
24
- | _ =>
25
- None
26
- }
27
- mutable sawLookupHook = False // Only consider the first lookup
28
- mutable sawParameterOrVariantFieldHook = False // Rewrite the type completions to parameter completions
29
- mutable typeCompletions = []
30
- let completions = patternCompletions.else {lspHook.results().flatMap {
31
- | ResolveTypeHook h =>
32
- typeCompletions = [...typeCompletions, ...typeCompletion(h.types, h.typeGenerics)]
33
- []
34
- | ResolveVariantFieldHook _ =>
35
- sawParameterOrVariantFieldHook = True
36
- []
37
- | InferLambdaStartHook h =>
38
- h.unification.substitute(h.lambdaType).{
39
- | TConstructor(_, "Function$1", [_, TConstructor(_, prefix, _), _]) =>
40
- exhaustiveMatchCompletion(h.environment, prefix + "_", True)
41
- | _ =>
42
- []
43
- }
44
- | InferLookupHook h {!sawLookupHook && !h.symbol.value.qualifiedName.startsWith("_")} =>
45
- sawLookupHook = True
46
- let typePrefix = if(h.symbol.value.qualifiedName.contains("_")) {
47
- h.symbol.value.qualifiedName.reverse().dropWhile {_ != '_'}.reverse()
48
- } else {
49
- ""
50
- }
51
- let expected = h.unification.substitute(h.expected)
52
- let qualifiedByAlias = h.symbol.value.qualifiedName.{n =>
53
- if(n.contains(".") && !n.contains("/") && !n.contains(":")) {n.takeWhile {_ != '.'}}
54
- }
55
- h.selfVariable.{
56
- | Some(selfName) {typePrefix == "" && qualifiedByAlias == None} {
57
- h.environment.symbols.get(selfName) | Some(scheme)
58
- } {
59
- scheme.signature.returnType | TConstructor(_, selfTypeName, _)
60
- } =>
61
- let otherCompletions = completion(h.unification, h.environment, "", None, expected)
62
- let selfCompletions = h.environment.symbols.get(selfName).toList().flatMap {scheme =>
63
- let prefix = selfTypeName + "_"
64
- completion(h.unification, h.environment, prefix, None, expected).filter {
65
- _.label.first().any {_.isAsciiLower()}
66
- }.map {c =>
67
- c.CompletionInfo(label = selfName + "." + c.label, snippet = selfName + "." + c.snippet)
68
- }
69
- }
70
- [...otherCompletions, ...selfCompletions]
71
- | _ =>
72
- completion(h.unification, h.environment, typePrefix, qualifiedByAlias, expected)
73
- }
74
- | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
75
- let fieldNames = n.split('$').dropFirst(1)
76
- let fieldCompletions = fieldNames.zip(ts).map {| Pair(name, t) =>
77
- let t2 = h.unification.substitute(t)
78
- CompletionInfo(name, "", name, True, t2, "(...)." + name + ": " + t2.show([]), Some(h.expected))
79
- }
80
- [...fieldCompletions, ...completion(h.unification, h.environment, n, None, h.expected)]
81
- | InferArgumentHook h {
82
- ![ // Avoids named argument suggestions for operators that become trait method calls, such as ==
83
- "ff:core/Equal.equals", "ff:core/Equal.notEquals"
84
- "ff:core/Ordering.before", "ff:core/Ordering.notBefore"
85
- "ff:core/Ordering.after", "ff:core/Ordering.notAfter"
86
- ].any {_ == h.callName}
87
- } =>
88
- let usedNames = h.arguments.pairs().filter {| Pair(i, a) => i != h.argumentIndex}.collect {_.second.name}
89
- let usedPlaces = h.arguments.takeFirst(h.argumentIndex).filter {_.name.isEmpty()}.size()
90
- let remainingParameters = h.parameters.filter {p => !usedNames.any {_ == p.name}}.dropFirst(usedPlaces)
91
- let preselect = h.isCopy || usedNames.size() != 0
92
- remainingParameters.pairs().map {| Pair(i, p) =>
93
- namedParameterCompletion(p, i, preselect)
94
- }
95
- | InferTermHook h {h.term | ELet _} =>
96
- h.missing.toList().filter {_.first.all {_.isAsciiLetterOrDigit()}}.flatMap {
97
- | Pair(x, Pair(instantiated, None)) =>
98
- missingCompletion(False, False, h.unification, x, instantiated, None)
99
- | _ =>
100
- []
101
- }
102
- | InferSequentialStartHook h {h.term | ESequential(_, before, _)} =>
103
- function lastIsVariable(term: Term): Bool {
104
- | ESequential(_, _, e) => lastIsVariable(e)
105
- | EVariable _ => True
106
- | _ => False
107
- }
108
- if(!lastIsVariable(before)) {[]} else:
109
- h.missing.toList().filter {| Pair(x, _) =>
110
- x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
111
- }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
112
- missingCompletion(True, False, h.unification, x, instantiated, arguments)
113
- }
114
- | InferParameterHook h =>
115
- sawParameterOrVariantFieldHook = True
116
- h.missing.toList().filter {| Pair(x, _) =>
117
- x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
118
- }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
119
- missingCompletion(False, True, h.unification, x, instantiated, arguments)
120
- }
121
- | InferFunctionDefinitionHook h {!h.definition.signature.member} =>
122
- h.missing.toList().filter {| Pair(x, _) =>
123
- x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
124
- }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
125
- arguments.toList().flatMap {as =>
126
- missingCompletion(False, False, h.unification, x, instantiated, Some(as))
127
- }
128
- }
129
- | InferFunctionDefinitionHook h {h.definition.signature.member} =>
130
- h.missing.toList().filter {_.first.contains("_")}.map {
131
- _.mapFirst {_.reverse().takeWhile {_ != '_'}.reverse()}
132
- }.filter {| Pair(x, _) =>
133
- x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
134
- }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
135
- arguments.toList().flatMap {as =>
136
- let instantiated2 = instantiated.Instantiated(scheme = instantiated.scheme.Scheme(
137
- signature = instantiated.scheme.signature.Signature(
138
- parameters = instantiated.scheme.signature.parameters.dropFirst()
139
- )
140
- ))
141
- missingCompletion(False, False, h.unification, x, instantiated2, Some(as.dropFirst()))
142
- }
143
- }
144
- | _ =>
145
- []
146
- }}
147
- let fixedTypeCompletions = if(!sawParameterOrVariantFieldHook) {typeCompletions} else {
148
- typeCompletions.map {c => c.CompletionInfo(snippet =
149
- (c.snippet.slice(0, 1).lower() + c.snippet.dropFirst()).takeWhile {_ != '['} + ": " + c.snippet
150
- )}
151
- }
152
- let fixedCompletions = if(!followedByOpenBracket) {completions} else {
153
- completions.map {c =>
154
- if(c.snippet.dropFirst(1).any {c => c != '(' && c != '[' && c != '{'}) {
155
- c.CompletionInfo(snippet = c.snippet.takeWhile {c => c != ' ' && c != '(' && c != '[' && c != '{'})
156
- } else {c}
157
- }
158
- }
159
- completionsToJson([...fixedCompletions, ...fixedTypeCompletions, ...topLevelCompletions])
160
- }
161
-
162
- completionsToJson(completions: List[CompletionInfo]): Json {
163
- Json.object()
164
- .with("isIncomplete", False)
165
- .with("items", completions.distinct().map {| CompletionInfo i =>
166
- let shownType = i.type.show([])
167
- let isAlpha = i.label.first().any {_.isAsciiLetter()}
168
- let isLower = i.label.first().any {_.isAsciiLower()}
169
- let isUnqualified = !i.label.contains(".")
170
- let isMember = isLower && !isUnqualified
171
- let isVariable = isLower && !i.extra.contains("(") && !i.extra.contains("{")
172
- let isDefinition = ["let ", "mutable ", "function "].any {i.snippet.startsWith(_)}
173
- let isParameter = i.snippet.endsWith(" = ")
174
- let sortText = if(isParameter || isDefinition) {0} else {9 - (
175
- isVariable.toInt() + isUnqualified.toInt() +
176
- isAlpha.toInt() + isLower.toInt() + isMember.toInt()
177
- )} + if(isMember) {"1"} else {"0"} + i.secondarySort + i.label.lower()
178
- let preselect = !isDefinition && Pair(i.type, i.expectedType).{
179
- | Pair(TConstructor(_, n1, _), Some(TConstructor(_, n2, _))) => n1 == n2
180
- | _ => False
181
- }
182
- Json.object()
183
- // Namespace or Property or Constructor or EnumMember or Constructor or Method/Function or Field/Variable
184
- .with("kind"
185
- if(shownType == "") {3} else:
186
- if(isParameter) {10} else:
187
- if(i.type.{| TConstructor(_, "type", _) => True | _ => False}) {9} else:
188
- if(i.label.first().any {c => c == '{' || c == '|'}) {22} else:
189
- if(i.label.first().any {_.isAsciiUpper()}) {4} else:
190
- if(i.extra.any {c => c == '(' || c == '{'}) {2} else {5} + if(i.member) {0} else {1}
191
- )
192
- .with("sortText", sortText)
193
- .with("preselect", preselect)
194
- .with("label", i.label)
195
- .with("labelDetails", Json.object()
196
- .with("detail", i.extra + if(shownType == "") {""} else {": " + shownType})
197
- )
198
- .with("insertText", i.snippet)
199
- .with("insertTextFormat", 2 /* Snippet */)
200
- .with("documentation", Json.object()
201
- .with("kind", "markdown")
202
- .with("value", "```\n" + i.documentation + "\n```")
203
- )
204
- })
205
- }
206
-
207
- typeCompletion(types: Map[String, String], typeGenerics: Map[String, List[String]]): List[CompletionInfo] {
208
- let completions = types.toList().filter {| Pair(n, full) =>
209
- n.all {_.isAsciiLetterOrDigit()}
210
- }.map {| Pair(typeName, full) =>
211
- let generics = typeGenerics.find {k, _ => k == typeName}.toList().flatMap {_.second}
212
- let realGenerics = generics.filter {_ != "Q$"}
213
- let label = typeName
214
- let extra = if(realGenerics.isEmpty()) {""} else {"[" + realGenerics.join(", ") + "]"}
215
- let snippet = typeName + if(realGenerics.isEmpty()) {""} else {"[$0]"}
216
- CompletionInfo(label, extra, snippet, False, TConstructor(Location("", 0, 0), "type", []), full, None)
217
- }
218
- completions
219
- }
220
-
221
- completion(
222
- unification: Unification
223
- environment: Environment
224
- prefix: String
225
- qualifiedByAlias: Option[String]
226
- expected: Type
227
- ): List[CompletionInfo] {
228
-
229
- let member = prefix.contains("_")
230
-
231
- let members = Array.new()
232
-
233
- let symbols = if(prefix == "") {
234
- environment.symbols.toList().collect {
235
- | Pair(name, _)@pair {name.all {_.isAsciiLetterOrDigit()}} =>
236
- Some(pair)
237
- | Pair(name, _)@pair {name.startsWith("ff:core/Core.")} =>
238
- Some(pair.Pair(first = name.dropFirst("ff:core/Core.".size())))
239
- | Pair(name, _)@pair {name.startsWith(environment.modulePrefix)} {
240
- name.dropFirst(environment.modulePrefix.size()) | n
241
- } {n.all {_.isAsciiLetterOrDigit()}} =>
242
- Some(pair.Pair(first = n))
243
- | Pair(name, scheme) {name.dropWhile {_ != '/'}.dropFirst() | short} {
244
- short.reverse().takeWhile {_ != '.'}.reverse() | shorter
245
- } {shorter.all {_.isAsciiLetterOrDigit()}} =>
246
- if(shorter.first().any {_.isAsciiUpper()}) {
247
- Some(Pair(shorter, scheme))
248
- } else {
249
- let module = name.dropLast(shorter.size() + 1)
250
- let alias = environment.imports.find {_, i =>
251
- module == i.package.groupName() + "/" + i.directory.map {_ + "/"}.join() + i.file
252
- }
253
- alias.map {| Pair(alias, i) =>
254
- Pair(alias + short.dropFirst(i.file.size()), scheme)
255
- }
256
- }
257
- | _ =>
258
- None
259
- }.toMap()
260
- } else {
261
- members.pushArray(exhaustiveMatchCompletion(environment, prefix, False).toArray())
262
- let shorterPrefix = prefix.dropLast()
263
- let recordFields = shorterPrefix.split('$').dropFirst().toSet()
264
- environment.symbols.each {
265
- | shortName, scheme {shortName.dropWhile {_ != '/'}.dropFirst() | short} {
266
- short.reverse().takeWhile {_ != '.'}.reverse() | shorter
267
- } {shorter.all {_.isAsciiLetterOrDigit()} && shorter.first().any {_.isAsciiUpper()}} =>
268
- scheme.signature.returnType.{
269
- | _ {prefix.startsWith("Record$")} =>
270
- if(scheme.signature.parameters.any {recordFields.contains(_.name)}) {
271
- members.push(makeCompletion(unification, expected, "", shorter, scheme, True, member))
272
- }
273
- | TConstructor(_, name, _) {name.startsWith(shorterPrefix)} =>
274
- if(!scheme.signature.parameters.isEmpty()) {
275
- members.push(makeCompletion(unification, expected, "", shorter, scheme, True, member))
276
- }
277
- | _ =>
278
- }
279
- | _, _ =>
280
- }
281
- environment.symbols
282
- }
283
-
284
- let filteredSymbols = qualifiedByAlias.{
285
- | None => symbols
286
- | Some(alias) =>
287
- let aliasPrefix = alias + "."
288
- symbols.toList().filter {
289
- _.first.startsWith(aliasPrefix)
290
- }.map {
291
- _.mapFirst {_.dropFirst(aliasPrefix.size())}
292
- }.toMap()
293
- }
294
-
295
- filteredSymbols.each {memberName, memberScheme =>
296
- if(memberName.startsWith(prefix)) {
297
- members.push(makeCompletion(unification, expected, prefix, memberName, memberScheme, False, member))
298
- }
299
- }
300
- members.toList()
301
- }
302
-
303
- makeCompletion(
304
- unification: Unification
305
- expected: Type
306
- prefix: String
307
- memberName: String
308
- memberScheme: Scheme
309
- copy: Bool
310
- member: Bool
311
- ): CompletionInfo {
312
- let shortName = memberName.dropFirst(prefix.size())
313
- let unqualifiedName = shortName.reverse().takeWhile {_ != '.'}.reverse()
314
- let upper = unqualifiedName.first().any {_.isAsciiUpper()}
315
- let variantWithoutParameters = upper && memberScheme.signature.parameters.isEmpty()
316
- let realParameters = memberScheme.signature.parameters.dropFirst(if(member && !copy) {1} else {0})
317
- let pair = if(!memberScheme.isVariable && !variantWithoutParameters) {
318
- let trailing = realParameters.pairs().reverse().map {| Pair(index, p) =>
319
- p.valueType.{
320
- | TConstructor(_, name, _) {name.startsWith("Function$")} =>
321
- Some(Pair(
322
- " {...}"
323
- if(index == 0) {" {$0}"} else {" {}"}
324
- ))
325
- | _ =>
326
- None
327
- }
328
- }.toStream().takeWhile {_ != None}.collect {_}.toList().reverse()
329
- let allRequired = realParameters.filter {_.default.isEmpty()}
330
- let required = allRequired.dropLast(trailing.size()).map {_.name}
331
- let optional = if(allRequired.size() != realParameters.size()) {"..."}
332
- Pair(
333
- if(trailing.isEmpty() || !required.isEmpty()) {
334
- "(" + [...required, ...optional.toList()].join(", ") + ")"
335
- } else {
336
- ""
337
- } + trailing.map {_.first}.join()
338
- if(copy) {
339
- "(${1|" + realParameters.map {_.name}.map {f => f + " = "}.join(",") + "|}$0)"
340
- } else {
341
- if(trailing.isEmpty() || !required.isEmpty()) {
342
- if(required.isEmpty()) {"()"} else {"($0)"}
343
- } else {
344
- ""
345
- } + if(trailing.isEmpty()) {""} else {trailing.map {_.second}.join()}
346
- }
347
- )
348
- } else {Pair("", "")}
349
- let returnType = unification.substitute(memberScheme.signature.returnType)
350
- let documentation = if(memberScheme.isVariable || variantWithoutParameters) {
351
- let methodGenerics = memberScheme.signature.generics
352
- let generics = if(member || methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
353
- let beforeAfter = memberScheme.signature.parameters.first().map {
354
- Pair(unification.substitute(_.valueType).show([]) + ".", "")
355
- }.else {Pair("", "")}
356
- beforeAfter.first +
357
- if(memberScheme.isMutable) {"mutable "} else {""} +
358
- unqualifiedName + generics +
359
- ": " + returnType.show([]) +
360
- beforeAfter.second
361
- } else {
362
- let selfType = memberScheme.signature.parameters.first().filter {_ => member && !copy}.map {_.valueType}
363
- let generics = selfType.map {
364
- | TConstructor(_, _, gs) =>
365
- // TODO: Needs constraints as well
366
- // TODO: This drops the wrong number of type parameters - more environment needed
367
- let methodGenerics = memberScheme.signature.generics.dropFirst(gs.size() + 1)
368
- if(methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
369
- | _ =>
370
- ""
371
- }.else {
372
- let methodGenerics = memberScheme.signature.generics.filter {_ != "Q$"}
373
- if(methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
374
- }
375
- let parameters = if(realParameters.isEmpty()) {""} else {
376
- "\n" + realParameters.map {p =>
377
- showCompletionParameter(" ", p)
378
- }.join("\n") + "\n"
379
- }
380
- selfType.map {_.show([]) + "."}.else {""} + unqualifiedName +
381
- generics + "(" + parameters + "): " +
382
- returnType.show([])
383
- }
384
- CompletionInfo(
385
- label = shortName
386
- extra = pair.first
387
- snippet = shortName + pair.second
388
- member = member && !copy
389
- type = returnType
390
- documentation = documentation
391
- expectedType = Some(expected)
392
- )
393
- }
394
-
395
- exhaustiveMatchCompletion(environment: Environment, prefix: String, inside: Bool): List[CompletionInfo] {
396
- if(prefix == "ff:core/List.List_") {
397
- let curly = if(inside) {Pair("", "")} else {Pair("{", "}")}
398
- [CompletionInfo(
399
- label = curly.first + "| "
400
- extra = "[] => ... | [first, ...rest] => ..." + curly.second
401
- snippet = curly.first + "\n | [] => $0\n | [first, ...rest] =>\n" + curly.second
402
- member = True
403
- type = TConstructor(Location("", 0, 0), "exhaustive match", [])
404
- documentation = "// Exhaustive list match"
405
- expectedType = None
406
- )]
407
- } else:
408
- let shorterPrefix = prefix.dropLast()
409
- let variants = environment.symbols.toList().filter {s =>
410
- !s.first.contains("_") &&
411
- s.first.reverse().takeWhile {_ != '.'}.reverse().first().any {_.isAsciiUpper()} &&
412
- s.second.signature.returnType.{
413
- | TConstructor(_, n, _) => n == shorterPrefix
414
- | _ => False
415
- }
416
- }
417
- if(variants.isEmpty()) {[]} else:
418
- let label = if(inside) {""} else {"{"} +
419
- variants.map {v =>
420
- "| " + v.first.reverse().takeWhile {_ != '.'}.reverse() +
421
- if(v.second.signature.parameters.isEmpty()) {""} else {"(...)"} + " => ..."
422
- }.join(" ") +
423
- if(inside) {""} else {"}"}
424
- let snippetParts = variants.pairs().map {| Pair(index, Pair(name, scheme)) =>
425
- "| " + name.reverse().takeWhile {_ != '.'}.reverse() +
426
- if(scheme.signature.parameters.isEmpty()) {""} else {
427
- "(" + scheme.signature.parameters.map {_.name}.join(", ") + ")"
428
- } +
429
- " => " + if(index == 0) {"$0"} else {""}
430
- }
431
- let snippet =
432
- if(inside) {""} else {"{"} +
433
- if(snippetParts.size() != 1) {
434
- "\n" + snippetParts.map {" " + _}.join("\n") + "\n"
435
- } else {
436
- snippetParts.join(" ")
437
- } +
438
- if(inside) {""} else {"}"}
439
- [CompletionInfo(
440
- label = label.slice(0, 2)
441
- extra = label.dropFirst(2)
442
- snippet = snippet
443
- member = True
444
- type = TConstructor(Location("", 0, 0), "exhaustive match", [])
445
- documentation = "// Exhaustive match:\n" + snippetParts.join("\n").replace("$0", "")
446
- expectedType = None
447
- )]
448
- }
449
-
450
- patternCompletion(unification: Unification, environment: Environment, expected: Type): List[CompletionInfo] {
451
- let typeName = unification.substitute(expected).{
452
- | TConstructor(_, name, _) => name
453
- | _ => ""
454
- }
455
- if(typeName == "") {[]} else:
456
- if(typeName == "ff:core/List.List") {
457
- [CompletionInfo("[...]", "", "[$0]", False, expected, "// List pattern", Some(expected))]
458
- } else:
459
- if(typeName == "ff:core/String.String") {
460
- [CompletionInfo("\"...\"", "", "\"$0\"", False, expected, "// String pattern", Some(expected))]
461
- } else:
462
- if(typeName == "ff:core/Char.Char") {
463
- [CompletionInfo("'...'", "", "'$0'", False, expected, "// Char pattern", Some(expected))]
464
- } else:
465
- if(typeName == "ff:core/Int.Int") {
466
- [CompletionInfo("0", "", "0", False, expected, "// Int pattern", Some(expected))]
467
- } else:
468
- let variants = do {
469
- environment.symbols.toList().filter {s =>
470
- !s.first.contains("_") &&
471
- s.first.reverse().takeWhile {_ != '.'}.reverse().first().any {_.isAsciiUpper()} &&
472
- s.second.signature.returnType.{
473
- | TConstructor(_, n, _) => n == typeName
474
- | _ => False
475
- }
476
- }
477
- }
478
- let completions = variants.map {| Pair(name, scheme) =>
479
- let shortName = name.reverse().takeWhile {_ != '.'}.reverse()
480
- let extra = if(scheme.signature.parameters.isEmpty()) {""} else {
481
- "(" + scheme.signature.parameters.map {_.name}.join(", ") + ")"
482
- }
483
- let generics = scheme.signature.generics.filter {_ != "Q$"}
484
- let documentation =
485
- shortName +
486
- if(generics.isEmpty()) {""} else {"[" + generics.join(", ") + "]"} +
487
- if(scheme.signature.parameters.isEmpty()) {""} else {
488
- "(\n" + scheme.signature.parameters.map {
489
- showCompletionParameter(" ", _)
490
- }.join("\n") + "\n)"
491
- } + ": " + scheme.signature.returnType.show([])
492
- CompletionInfo(
493
- label = shortName
494
- extra = extra
495
- snippet = shortName + if(scheme.signature.parameters.isEmpty()) {""} else {"($0)"}
496
- member = False
497
- type = expected
498
- documentation = documentation
499
- expectedType = Some(expected)
500
- )
501
- }
502
- completions
503
- }
504
-
505
- missingCompletion(
506
- keyword: Bool
507
- isParameter: Bool
508
- unification: Unification
509
- name: String
510
- instantiated: Instantiated
511
- arguments: Option[List[Argument]]
512
- ): List[CompletionInfo] {
513
- function findTermName(term: Term): Option[String] {
514
- | ECall e {e.target | DynamicCall c} {
515
- e.arguments.last() | Some(Argument(_, _, e2))
516
- } {e2 | ELambda(_, Lambda(_, _, cases))} =>
517
- cases.collectFirst {findTermName(_.body)}
518
- | ECall e {e.target | DynamicCall c} => findTermName(c.function)
519
- | ECopy e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
520
- | EField e => Some(e.field)
521
- | ELambda e {e.lambda.cases | [MatchCase c]} => findTermName(c.body)
522
- | EList e {e.items | []} => Some("list")
523
- | EList e {e.items | [e1, ...]} => findTermName(e1.first)
524
- | EPipe e => findTermName(e.function)
525
- | ERecord _ => Some("record")
526
- | EVariable e {e.name != "" && e.name.all {_.isAsciiLetterOrDigit()}} => Some(e.name)
527
- | EVariant e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
528
- | EVariantIs e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
529
- | _ => None
530
- }
531
- function findTypeName(type: Type): String {
532
- | TConstructor(_, name, _) {name.startsWith("Function$")} =>
533
- "lambda"
534
- | TConstructor(_, name, _) =>
535
- let n = name.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
536
- if(!n.first().all {_.isAsciiLetter()}) {"p"} else {n.slice(0, 1).lower() + n.dropFirst()}
537
- | TVariable(_, index) => "p"
538
- }
539
- let completions = arguments.{
540
- | None {isParameter} =>
541
- let t = unification.substitute(instantiated.scheme.signature.returnType)
542
- let snippet = t.{
543
- | TConstructor _ => name + ": " + t.show([])
544
- | TVariable _ => name + ": "
545
- }
546
- let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
547
- [CompletionInfo(name, "", snippet, False, t, documentation, Some(t))]
548
- | Some(as) {isParameter} =>
549
- let noEffect = TConstructor(instantiated.scheme.signature.at, "ff:core/Nothing.Nothing", [])
550
- let t = unification.substitute(TConstructor(
551
- instantiated.scheme.signature.at
552
- "Function$" + instantiated.scheme.signature.parameters.size()
553
- [
554
- noEffect
555
- ...instantiated.scheme.signature.parameters.map {_.valueType}
556
- instantiated.scheme.signature.returnType
557
- ]
558
- ))
559
- let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
560
- [CompletionInfo(name, "", name + ": " + t.show([]), False, t, documentation, Some(t))]
561
- | None =>
562
- let t = unification.substitute(instantiated.scheme.signature.returnType)
563
- let snippet = name + " = "
564
- let documentation = "// Define missing variable\n" + name + ": " + t.show([])
565
- [CompletionInfo(snippet, "...", snippet, False, t, documentation, Some(t))]
566
- | Some(as) =>
567
- mutable remainingParameters = instantiated.scheme.signature.parameters
568
- mutable usedNames = [].toSet()
569
- function uniqueName(name: String): String {
570
- let alternativeName = name + (usedNames.size() + 1)
571
- if(!usedNames.contains(name)) {
572
- usedNames = usedNames.add(name)
573
- name
574
- } elseIf {!usedNames.contains(alternativeName)} {
575
- usedNames = usedNames.add(alternativeName)
576
- alternativeName
577
- } else {
578
- usedNames = usedNames.add(alternativeName + "_" + (usedNames.size() + 1))
579
- uniqueName(name)
580
- }
581
- }
582
- let parameters = as.collect {a =>
583
- let found = a.name.{
584
- | Some(n) => remainingParameters.find {_.name == n}
585
- | None => remainingParameters.first()
586
- }
587
- found.map {p =>
588
- remainingParameters = remainingParameters.filter {_.name != p.name}
589
- let t = unification.substitute(p.valueType)
590
- let x = a.name.orElse {findTermName(a.value)}.else {findTypeName(t)}
591
- Pair(uniqueName(x), t)
592
- }
593
- }
594
- let returnType = unification.substitute(instantiated.scheme.signature.returnType)
595
- let types = [...parameters.map {_.second}, returnType]
596
- let returnTypeCode = returnType.{| TVariable _ => "" | _ => ": " + returnType.show(types)}
597
- let parameterSnippets = parameters.map {| Pair(x, t) =>
598
- x + ": " + t.show(types)
599
- }
600
- let extra = "(" + parameterSnippets.join(", ") + ") {...}"
601
- let shortSnippet = name + "(" + parameterSnippets.join(", ") + ")" + returnTypeCode
602
- let longSnippet = name + "(\n " + parameterSnippets.join("\n ") + "\n)" + returnTypeCode
603
- let snippet = if(shortSnippet.size() <= 100) {shortSnippet} else {longSnippet}
604
- let documentation = "// Define missing function\n" +
605
- if(shortSnippet.size() < 30) {shortSnippet} else {longSnippet}
606
- [CompletionInfo(name, extra, snippet + " {\n $0\n}", False, returnType, documentation, None)]
607
- }
608
- if(keyword && arguments.isEmpty()) {
609
- ["let ", "mutable "].flatMap {k => completions.map {c =>
610
- c.CompletionInfo(label = k + c.label, snippet = k + c.snippet)
611
- }}
612
- } elseIf {keyword} {
613
- ["function "].flatMap {k => completions.map {c =>
614
- c.CompletionInfo(label = k + c.label, snippet = k + c.snippet)
615
- }}
616
- } else {
617
- completions
618
- }
619
- }
620
-
621
- namedParameterCompletion(parameter: Parameter, index: Int, preselect: Bool): CompletionInfo {
622
- CompletionInfo(
623
- label = parameter.name + " = "
624
- extra = "..."
625
- snippet = parameter.name + " = "
626
- member = False
627
- type = parameter.valueType
628
- documentation = showCompletionParameter("", parameter)
629
- expectedType = if(preselect) {parameter.valueType}
630
- secondarySort = (1000 + index).clamp(1000, 9999)
631
- )
632
- }
633
-
634
- toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
635
- [
636
- CompletionInfo(
637
- label = "package",
638
- extra = " some:package:0.0.0",
639
- snippet = "package ",
640
- member = False,
641
- type = TConstructor(lspHook.at, "", []),
642
- documentation = "// Declares the group, name and version of this package",
643
- expectedType = None
644
- )
645
- CompletionInfo(
646
- label = "dependency",
647
- extra = " some:package:0.0.0",
648
- snippet = "dependency ",
649
- member = False,
650
- type = TConstructor(lspHook.at, "", []),
651
- documentation = "// Declares a dependency on the specified version of the package",
652
- expectedType = None
653
- )
654
- CompletionInfo(
655
- label = "import",
656
- extra = " Module from some:package",
657
- snippet = "import ",
658
- member = False,
659
- type = TConstructor(lspHook.at, "", []),
660
- documentation = "// Imports a module from a package",
661
- expectedType = None
662
- )
663
- CompletionInfo(
664
- label = "extend",
665
- extra = " self[T]: SomeType[T] {...}",
666
- snippet = "extend ",
667
- member = False,
668
- type = TConstructor(lspHook.at, "", []),
669
- documentation = "// Add methods to a type defined in this module",
670
- expectedType = None
671
- )
672
- CompletionInfo(
673
- label = "data",
674
- extra = " SomeType[T](...) {...}",
675
- snippet = "data ",
676
- member = False,
677
- type = TConstructor(lspHook.at, "", []),
678
- documentation = "// Defines an immutable type",
679
- expectedType = None
680
- )
681
- CompletionInfo(
682
- label = "class",
683
- extra = " SomeType[T](...) {...}",
684
- snippet = "class ",
685
- member = False,
686
- type = TConstructor(lspHook.at, "", []),
687
- documentation = "// Defines a mutable type",
688
- expectedType = None
689
- )
690
- CompletionInfo(
691
- label = "capability",
692
- extra = " SomeType[T](...) {...}",
693
- snippet = "capability ",
694
- member = False,
695
- type = TConstructor(lspHook.at, "", []),
696
- documentation = "// Defines a type with capabilities and functions",
697
- expectedType = None
698
- )
699
- CompletionInfo(
700
- label = "trait",
701
- extra = " T: SomeTrait {...}",
702
- snippet = "trait ",
703
- member = False,
704
- type = TConstructor(lspHook.at, "", []),
705
- documentation = "// Defines a trait",
706
- expectedType = None
707
- )
708
- CompletionInfo(
709
- label = "instance",
710
- extra = " SomeType: SomeTrait {...}",
711
- snippet = "instance ",
712
- member = False,
713
- type = TConstructor(lspHook.at, "", []),
714
- documentation = "// Defines an instance of a trait",
715
- expectedType = None
716
- )
717
- CompletionInfo(
718
- label = "nodeMain",
719
- extra = "(system: NodeSystem) {...}",
720
- snippet = "nodeMain(system: NodeSystem) {\n $0\n}",
721
- member = False,
722
- type = TConstructor(lspHook.at, "", []),
723
- documentation = "// Main function for targetting Node.js",
724
- expectedType = None
725
- )
726
- CompletionInfo(
727
- label = "browserMain",
728
- extra = "(system: BrowserSystem) {...}",
729
- snippet = "browserMain(system: BrowserSystem) {\n $0\n}",
730
- member = False,
731
- type = TConstructor(lspHook.at, "", []),
732
- documentation = "// Main function for targetting the browser",
733
- expectedType = None
734
- )
735
- CompletionInfo(
736
- label = "buildMain",
737
- extra = "(system: BuildSystem) {...}",
738
- snippet = "buildMain(system: NodeSystem) {\n $0\n}",
739
- member = False,
740
- type = TConstructor(lspHook.at, "", []),
741
- documentation = "// Main function for building assets",
742
- expectedType = None
743
- )
744
- CompletionInfo(
745
- label = "webapp",
746
- extra = " with frontend and backend",
747
- snippet = [
748
- "dependency ff:webserver:0.0.0"
749
- "import WebServer from ff:webserver"
750
- ""
751
- "browserMain(system: BrowserSystem): Unit {"
752
- " let response = system.httpClient().fetch(\"http://localhost:8080/hello\")"
753
- " let window = system.js().global().get(\"window\")"
754
- " window.call1(\"alert\", response.readText())"
755
- "}"
756
- ""
757
- "nodeMain(system: NodeSystem): Unit {"
758
- " WebServer.make(system, \"localhost\", 8080).listen {request =>"
759
- " if(request.readPath() == \"/\") {"
760
- " request.writeHeader(\"Content-Type\", \"text/html; charset=UTF-8\")"
761
- " request.writeText(\"<!doctype html>\")"
762
- " request.writeText(\"<script type='module' src='/js/script/script/$TM_FILENAME_BASE.mjs'></script>\")"
763
- " } elseIf {request.readPath() == \"/hello\"} {"
764
- " request.writeHeader(\"Content-Type\", \"text/plain; charset=UTF-8\")"
765
- " request.writeText(\"Hello from server!\")"
766
- " } elseIf {request.readPath().startsWith(\"/js/\") && !request.readPath().contains(\"..\")} {"
767
- " request.writeHeader(\"Content-Type\", \"text/javascript; charset=UTF-8\")"
768
- " request.writeText(system.assets().readText(request.readPath()))"
769
- " } else {"
770
- " request.writeStatus(\"404 Not found\")"
771
- " }"
772
- " }"
773
- "}"
774
- ""
775
- "buildMain(system: BuildSystem) {"
776
- " let browser = system.compileForBrowser(\"$TM_FILENAME_BASE.ff\")"
777
- " let assets = AssetSystem.create().addAssets(\"/js\", browser.assets())"
778
- " system.setAssets(assets)"
779
- "}"
780
- ].join("\n"),
781
- member = False,
782
- type = TConstructor(lspHook.at, "", []),
783
- documentation = "// Example webapp with frontend and backend",
784
- expectedType = None
785
- )
786
- ]
787
- }
788
-
789
- showCompletionParameter(indentation: String, parameter: Parameter): String {
790
- if(parameter.mutable) {indentation + "mutable "} else {indentation} +
791
- parameter.name + ": " + parameter.valueType.show([]) +
792
- parameter.default.map {
793
- | EVariant(_, n, _, None) =>
794
- n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
795
- | EVariant(_, n, _, Some([])) =>
796
- n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
797
- | EVariant(_, n, _, _) =>
798
- n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse() + "(...)"
799
- | EChar(_, v) => v
800
- | EInt(_, v) => v
801
- | EFloat(_, v) => v
802
- | EString(_, v) => v.replace("```", "'''")
803
- | ELambda(_, _) => "{...}"
804
- | EList(_, _, []) => "[]"
805
- | EList(_, _, _) => "[...]"
806
- | _ => "..."
807
- }.map {" = " + _}.else {""}
808
- }
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
+ data CompletionInfo(
8
+ label: String
9
+ extra: String
10
+ snippet: String
11
+ member: Bool
12
+ type: Type
13
+ documentation: String
14
+ expectedType: Option[Type]
15
+ secondarySort: Int = 5
16
+ )
17
+
18
+ handleCompletion(lspHook: LspHook, toplevel: Bool, followedByOpenBracket: Bool): Json {
19
+ let topLevelCompletions = if(!toplevel) {[]} else {toplevelCompletion(lspHook)}
20
+ let patternCompletions = lspHook.results().collectFirst {
21
+ | InferPatternHook h =>
22
+ let expected = h.unification.substitute(h.expected)
23
+ Some(patternCompletion(h.unification, h.environment, expected))
24
+ | _ =>
25
+ None
26
+ }
27
+ mutable sawLookupHook = False // Only consider the first lookup
28
+ mutable sawParameterOrVariantFieldHook = False // Rewrite the type completions to parameter completions
29
+ mutable typeCompletions = []
30
+ let completions = patternCompletions.else {lspHook.results().flatMap {
31
+ | ResolveTypeHook h =>
32
+ typeCompletions = [...typeCompletions, ...typeCompletion(h.types, h.typeGenerics)]
33
+ []
34
+ | ResolveVariantFieldHook _ =>
35
+ sawParameterOrVariantFieldHook = True
36
+ []
37
+ | InferLambdaStartHook h =>
38
+ h.unification.substitute(h.lambdaType).{
39
+ | TConstructor(_, "Function$1", [_, TConstructor(_, prefix, _), _]) =>
40
+ exhaustiveMatchCompletion(h.environment, prefix + "_", True)
41
+ | _ =>
42
+ []
43
+ }
44
+ | InferLookupHook h {!sawLookupHook && !h.symbol.value.qualifiedName.startsWith("_")} =>
45
+ sawLookupHook = True
46
+ let typePrefix = if(h.symbol.value.qualifiedName.contains("_")) {
47
+ h.symbol.value.qualifiedName.reverse().dropWhile {_ != '_'}.reverse()
48
+ } else {
49
+ ""
50
+ }
51
+ let expected = h.unification.substitute(h.expected)
52
+ let qualifiedByAlias = h.symbol.value.qualifiedName.{n =>
53
+ if(n.contains(".") && !n.contains("/") && !n.contains(":")) {n.takeWhile {_ != '.'}}
54
+ }
55
+ h.selfVariable.{
56
+ | Some(selfName) {typePrefix == "" && qualifiedByAlias == None} {
57
+ h.environment.symbols.get(selfName) | Some(scheme)
58
+ } {
59
+ scheme.signature.returnType | TConstructor(_, selfTypeName, _)
60
+ } =>
61
+ let otherCompletions = completion(h.unification, h.environment, "", None, expected)
62
+ let selfCompletions = h.environment.symbols.get(selfName).toList().flatMap {scheme =>
63
+ let prefix = selfTypeName + "_"
64
+ completion(h.unification, h.environment, prefix, None, expected).filter {
65
+ _.label.first().any {_.isAsciiLower()}
66
+ }.map {c =>
67
+ c.CompletionInfo(label = selfName + "." + c.label, snippet = selfName + "." + c.snippet)
68
+ }
69
+ }
70
+ [...otherCompletions, ...selfCompletions]
71
+ | _ =>
72
+ completion(h.unification, h.environment, typePrefix, qualifiedByAlias, expected)
73
+ }
74
+ | InferRecordFieldHook h {h.unification.substitute(h.recordType) | TConstructor(_, n, ts)} =>
75
+ let fieldNames = n.split('$').dropFirst(1)
76
+ let fieldCompletions = fieldNames.zip(ts).map {| Pair(name, t) =>
77
+ let t2 = h.unification.substitute(t)
78
+ CompletionInfo(name, "", name, True, t2, "(...)." + name + ": " + t2.show([]), Some(h.expected))
79
+ }
80
+ [...fieldCompletions, ...completion(h.unification, h.environment, n, None, h.expected)]
81
+ | InferArgumentHook h {
82
+ ![ // Avoids named argument suggestions for operators that become trait method calls, such as ==
83
+ "ff:core/Equal.equals", "ff:core/Equal.notEquals"
84
+ "ff:core/Ordering.before", "ff:core/Ordering.notBefore"
85
+ "ff:core/Ordering.after", "ff:core/Ordering.notAfter"
86
+ ].any {_ == h.callName}
87
+ } =>
88
+ let usedNames = h.arguments.pairs().filter {| Pair(i, a) => i != h.argumentIndex}.collect {_.second.name}
89
+ let usedPlaces = h.arguments.takeFirst(h.argumentIndex).filter {_.name.isEmpty()}.size()
90
+ let remainingParameters = h.parameters.filter {p => !usedNames.any {_ == p.name}}.dropFirst(usedPlaces)
91
+ let preselect = h.isCopy || usedNames.size() != 0
92
+ remainingParameters.pairs().map {| Pair(i, p) =>
93
+ namedParameterCompletion(p, i, preselect)
94
+ }
95
+ | InferTermHook h {h.term | ELet _} =>
96
+ h.missing.toList().filter {_.first.all {_.isAsciiLetterOrDigit()}}.flatMap {
97
+ | Pair(x, Pair(instantiated, None)) =>
98
+ missingCompletion(False, False, h.unification, x, instantiated, None)
99
+ | _ =>
100
+ []
101
+ }
102
+ | InferSequentialStartHook h {h.term | ESequential(_, before, _)} =>
103
+ function lastIsVariable(term: Term): Bool {
104
+ | ESequential(_, _, e) => lastIsVariable(e)
105
+ | EVariable _ => True
106
+ | _ => False
107
+ }
108
+ if(!lastIsVariable(before)) {[]} else:
109
+ h.missing.toList().filter {| Pair(x, _) =>
110
+ x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
111
+ }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
112
+ missingCompletion(True, False, h.unification, x, instantiated, arguments)
113
+ }
114
+ | InferParameterHook h =>
115
+ sawParameterOrVariantFieldHook = True
116
+ h.missing.toList().filter {| Pair(x, _) =>
117
+ x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
118
+ }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
119
+ missingCompletion(False, True, h.unification, x, instantiated, arguments)
120
+ }
121
+ | InferFunctionDefinitionHook h {!h.definition.signature.member} =>
122
+ h.missing.toList().filter {| Pair(x, _) =>
123
+ x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
124
+ }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
125
+ arguments.toList().flatMap {as =>
126
+ missingCompletion(False, False, h.unification, x, instantiated, Some(as))
127
+ }
128
+ }
129
+ | InferFunctionDefinitionHook h {h.definition.signature.member} =>
130
+ h.missing.toList().filter {_.first.contains("_")}.map {
131
+ _.mapFirst {_.reverse().takeWhile {_ != '_'}.reverse()}
132
+ }.filter {| Pair(x, _) =>
133
+ x.size() != 0 && x.all {_.isAsciiLetterOrDigit()}
134
+ }.flatMap {| Pair(x, Pair(instantiated, arguments)) =>
135
+ arguments.toList().flatMap {as =>
136
+ let instantiated2 = instantiated.Instantiated(scheme = instantiated.scheme.Scheme(
137
+ signature = instantiated.scheme.signature.Signature(
138
+ parameters = instantiated.scheme.signature.parameters.dropFirst()
139
+ )
140
+ ))
141
+ missingCompletion(False, False, h.unification, x, instantiated2, Some(as.dropFirst()))
142
+ }
143
+ }
144
+ | _ =>
145
+ []
146
+ }}
147
+ let fixedTypeCompletions = if(!sawParameterOrVariantFieldHook) {typeCompletions} else {
148
+ typeCompletions.map {c => c.CompletionInfo(snippet =
149
+ (c.snippet.slice(0, 1).lower() + c.snippet.dropFirst()).takeWhile {_ != '['} + ": " + c.snippet
150
+ )}
151
+ }
152
+ let fixedCompletions = if(!followedByOpenBracket) {completions} else {
153
+ completions.map {c =>
154
+ if(c.snippet.dropFirst(1).any {c => c != '(' && c != '[' && c != '{'}) {
155
+ c.CompletionInfo(snippet = c.snippet.takeWhile {c => c != ' ' && c != '(' && c != '[' && c != '{'})
156
+ } else {c}
157
+ }
158
+ }
159
+ completionsToJson([...fixedCompletions, ...fixedTypeCompletions, ...topLevelCompletions])
160
+ }
161
+
162
+ completionsToJson(completions: List[CompletionInfo]): Json {
163
+ Json.object()
164
+ .with("isIncomplete", False)
165
+ .with("items", completions.distinct().map {| CompletionInfo i =>
166
+ let shownType = i.type.show([])
167
+ let isAlpha = i.label.first().any {_.isAsciiLetter()}
168
+ let isLower = i.label.first().any {_.isAsciiLower()}
169
+ let isUnqualified = !i.label.contains(".")
170
+ let isMember = isLower && !isUnqualified
171
+ let isVariable = isLower && !i.extra.contains("(") && !i.extra.contains("{")
172
+ let isDefinition = ["let ", "mutable ", "function "].any {i.snippet.startsWith(_)}
173
+ let isParameter = i.snippet.endsWith(" = ")
174
+ let sortText = if(isParameter || isDefinition) {0} else {9 - (
175
+ isVariable.toInt() + isUnqualified.toInt() +
176
+ isAlpha.toInt() + isLower.toInt() + isMember.toInt()
177
+ )} + if(isMember) {"1"} else {"0"} + i.secondarySort + i.label.lower()
178
+ let preselect = !isDefinition && Pair(i.type, i.expectedType).{
179
+ | Pair(TConstructor(_, n1, _), Some(TConstructor(_, n2, _))) => n1 == n2
180
+ | _ => False
181
+ }
182
+ Json.object()
183
+ // Namespace or Property or Constructor or EnumMember or Constructor or Method/Function or Field/Variable
184
+ .with("kind"
185
+ if(shownType == "") {3} else:
186
+ if(isParameter) {10} else:
187
+ if(i.type.{| TConstructor(_, "type", _) => True | _ => False}) {9} else:
188
+ if(i.label.first().any {c => c == '{' || c == '|'}) {22} else:
189
+ if(i.label.first().any {_.isAsciiUpper()}) {4} else:
190
+ if(i.extra.any {c => c == '(' || c == '{'}) {2} else {5} + if(i.member) {0} else {1}
191
+ )
192
+ .with("sortText", sortText)
193
+ .with("preselect", preselect)
194
+ .with("label", i.label)
195
+ .with("labelDetails", Json.object()
196
+ .with("detail", i.extra + if(shownType == "") {""} else {": " + shownType})
197
+ )
198
+ .with("insertText", i.snippet)
199
+ .with("insertTextFormat", 2 /* Snippet */)
200
+ .with("documentation", Json.object()
201
+ .with("kind", "markdown")
202
+ .with("value", "```\n" + i.documentation + "\n```")
203
+ )
204
+ })
205
+ }
206
+
207
+ typeCompletion(types: Map[String, String], typeGenerics: Map[String, List[String]]): List[CompletionInfo] {
208
+ let completions = types.toList().filter {| Pair(n, full) =>
209
+ n.all {_.isAsciiLetterOrDigit()}
210
+ }.map {| Pair(typeName, full) =>
211
+ let generics = typeGenerics.find {k, _ => k == typeName}.toList().flatMap {_.second}
212
+ let realGenerics = generics.filter {_ != "Q$"}
213
+ let label = typeName
214
+ let extra = if(realGenerics.isEmpty()) {""} else {"[" + realGenerics.join(", ") + "]"}
215
+ let snippet = typeName + if(realGenerics.isEmpty()) {""} else {"[$0]"}
216
+ CompletionInfo(label, extra, snippet, False, TConstructor(Location("", 0, 0), "type", []), full, None)
217
+ }
218
+ completions
219
+ }
220
+
221
+ completion(
222
+ unification: Unification
223
+ environment: Environment
224
+ prefix: String
225
+ qualifiedByAlias: Option[String]
226
+ expected: Type
227
+ ): List[CompletionInfo] {
228
+
229
+ let member = prefix.contains("_")
230
+
231
+ let members = Array.new()
232
+
233
+ let symbols = if(prefix == "") {
234
+ environment.symbols.toList().collect {
235
+ | Pair(name, _)@pair {name.all {_.isAsciiLetterOrDigit()}} =>
236
+ Some(pair)
237
+ | Pair(name, _)@pair {name.startsWith("ff:core/Core.")} =>
238
+ Some(pair.Pair(first = name.dropFirst("ff:core/Core.".size())))
239
+ | Pair(name, _)@pair {name.startsWith(environment.modulePrefix)} {
240
+ name.dropFirst(environment.modulePrefix.size()) | n
241
+ } {n.all {_.isAsciiLetterOrDigit()}} =>
242
+ Some(pair.Pair(first = n))
243
+ | Pair(name, scheme) {name.dropWhile {_ != '/'}.dropFirst() | short} {
244
+ short.reverse().takeWhile {_ != '.'}.reverse() | shorter
245
+ } {shorter.all {_.isAsciiLetterOrDigit()}} =>
246
+ if(shorter.first().any {_.isAsciiUpper()}) {
247
+ Some(Pair(shorter, scheme))
248
+ } else {
249
+ let module = name.dropLast(shorter.size() + 1)
250
+ let alias = environment.imports.find {_, i =>
251
+ module == i.package.groupName() + "/" + i.directory.map {_ + "/"}.join() + i.file
252
+ }
253
+ alias.map {| Pair(alias, i) =>
254
+ Pair(alias + short.dropFirst(i.file.size()), scheme)
255
+ }
256
+ }
257
+ | _ =>
258
+ None
259
+ }.toMap()
260
+ } else {
261
+ members.pushArray(exhaustiveMatchCompletion(environment, prefix, False).toArray())
262
+ let shorterPrefix = prefix.dropLast()
263
+ let recordFields = shorterPrefix.split('$').dropFirst().toSet()
264
+ environment.symbols.each {
265
+ | shortName, scheme {shortName.dropWhile {_ != '/'}.dropFirst() | short} {
266
+ short.reverse().takeWhile {_ != '.'}.reverse() | shorter
267
+ } {shorter.all {_.isAsciiLetterOrDigit()} && shorter.first().any {_.isAsciiUpper()}} =>
268
+ scheme.signature.returnType.{
269
+ | _ {prefix.startsWith("Record$")} =>
270
+ if(scheme.signature.parameters.any {recordFields.contains(_.name)}) {
271
+ members.push(makeCompletion(unification, expected, "", shorter, scheme, True, member))
272
+ }
273
+ | TConstructor(_, name, _) {name.startsWith(shorterPrefix)} =>
274
+ if(!scheme.signature.parameters.isEmpty()) {
275
+ members.push(makeCompletion(unification, expected, "", shorter, scheme, True, member))
276
+ }
277
+ | _ =>
278
+ }
279
+ | _, _ =>
280
+ }
281
+ environment.symbols
282
+ }
283
+
284
+ let filteredSymbols = qualifiedByAlias.{
285
+ | None => symbols
286
+ | Some(alias) =>
287
+ let aliasPrefix = alias + "."
288
+ symbols.toList().filter {
289
+ _.first.startsWith(aliasPrefix)
290
+ }.map {
291
+ _.mapFirst {_.dropFirst(aliasPrefix.size())}
292
+ }.toMap()
293
+ }
294
+
295
+ filteredSymbols.each {memberName, memberScheme =>
296
+ if(memberName.startsWith(prefix)) {
297
+ members.push(makeCompletion(unification, expected, prefix, memberName, memberScheme, False, member))
298
+ }
299
+ }
300
+ members.toList()
301
+ }
302
+
303
+ makeCompletion(
304
+ unification: Unification
305
+ expected: Type
306
+ prefix: String
307
+ memberName: String
308
+ memberScheme: Scheme
309
+ copy: Bool
310
+ member: Bool
311
+ ): CompletionInfo {
312
+ let shortName = memberName.dropFirst(prefix.size())
313
+ let unqualifiedName = shortName.reverse().takeWhile {_ != '.'}.reverse()
314
+ let upper = unqualifiedName.first().any {_.isAsciiUpper()}
315
+ let variantWithoutParameters = upper && memberScheme.signature.parameters.isEmpty()
316
+ let realParameters = memberScheme.signature.parameters.dropFirst(if(member && !copy) {1} else {0})
317
+ let pair = if(!memberScheme.isVariable && !variantWithoutParameters) {
318
+ let trailing = realParameters.pairs().reverse().map {| Pair(index, p) =>
319
+ p.valueType.{
320
+ | TConstructor(_, name, _) {name.startsWith("Function$")} =>
321
+ Some(Pair(
322
+ " {...}"
323
+ if(index == 0) {" {$0}"} else {" {}"}
324
+ ))
325
+ | _ =>
326
+ None
327
+ }
328
+ }.toStream().takeWhile {_ != None}.collect {_}.toList().reverse()
329
+ let allRequired = realParameters.filter {_.default.isEmpty()}
330
+ let required = allRequired.dropLast(trailing.size()).map {_.name}
331
+ let optional = if(allRequired.size() != realParameters.size()) {"..."}
332
+ Pair(
333
+ if(trailing.isEmpty() || !required.isEmpty()) {
334
+ "(" + [...required, ...optional.toList()].join(", ") + ")"
335
+ } else {
336
+ ""
337
+ } + trailing.map {_.first}.join()
338
+ if(copy) {
339
+ "(${1|" + realParameters.map {_.name}.map {f => f + " = "}.join(",") + "|}$0)"
340
+ } else {
341
+ if(trailing.isEmpty() || !required.isEmpty()) {
342
+ if(required.isEmpty()) {"()"} else {"($0)"}
343
+ } else {
344
+ ""
345
+ } + if(trailing.isEmpty()) {""} else {trailing.map {_.second}.join()}
346
+ }
347
+ )
348
+ } else {Pair("", "")}
349
+ let returnType = unification.substitute(memberScheme.signature.returnType)
350
+ let documentation = if(memberScheme.isVariable || variantWithoutParameters) {
351
+ let methodGenerics = memberScheme.signature.generics
352
+ let generics = if(member || methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
353
+ let beforeAfter = memberScheme.signature.parameters.first().map {
354
+ Pair(unification.substitute(_.valueType).show([]) + ".", "")
355
+ }.else {Pair("", "")}
356
+ beforeAfter.first +
357
+ if(memberScheme.isMutable) {"mutable "} else {""} +
358
+ unqualifiedName + generics +
359
+ ": " + returnType.show([]) +
360
+ beforeAfter.second
361
+ } else {
362
+ let selfType = memberScheme.signature.parameters.first().filter {_ => member && !copy}.map {_.valueType}
363
+ let generics = selfType.map {
364
+ | TConstructor(_, _, gs) =>
365
+ // TODO: Needs constraints as well
366
+ // TODO: This drops the wrong number of type parameters - more environment needed
367
+ let methodGenerics = memberScheme.signature.generics.dropFirst(gs.size() + 1)
368
+ if(methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
369
+ | _ =>
370
+ ""
371
+ }.else {
372
+ let methodGenerics = memberScheme.signature.generics.filter {_ != "Q$"}
373
+ if(methodGenerics.isEmpty()) {""} else {"[" + methodGenerics.join(", ") + "]"}
374
+ }
375
+ let parameters = if(realParameters.isEmpty()) {""} else {
376
+ "\n" + realParameters.map {p =>
377
+ showCompletionParameter(" ", p)
378
+ }.join("\n") + "\n"
379
+ }
380
+ selfType.map {_.show([]) + "."}.else {""} + unqualifiedName +
381
+ generics + "(" + parameters + "): " +
382
+ returnType.show([])
383
+ }
384
+ CompletionInfo(
385
+ label = shortName
386
+ extra = pair.first
387
+ snippet = shortName + pair.second
388
+ member = member && !copy
389
+ type = returnType
390
+ documentation = documentation
391
+ expectedType = Some(expected)
392
+ )
393
+ }
394
+
395
+ exhaustiveMatchCompletion(environment: Environment, prefix: String, inside: Bool): List[CompletionInfo] {
396
+ if(prefix == "ff:core/List.List_") {
397
+ let curly = if(inside) {Pair("", "")} else {Pair("{", "}")}
398
+ [CompletionInfo(
399
+ label = curly.first + "| "
400
+ extra = "[] => ... | [first, ...rest] => ..." + curly.second
401
+ snippet = curly.first + "\n | [] => $0\n | [first, ...rest] =>\n" + curly.second
402
+ member = True
403
+ type = TConstructor(Location("", 0, 0), "exhaustive match", [])
404
+ documentation = "// Exhaustive list match"
405
+ expectedType = None
406
+ )]
407
+ } else:
408
+ let shorterPrefix = prefix.dropLast()
409
+ let variants = environment.symbols.toList().filter {s =>
410
+ !s.first.contains("_") &&
411
+ s.first.reverse().takeWhile {_ != '.'}.reverse().first().any {_.isAsciiUpper()} &&
412
+ s.second.signature.returnType.{
413
+ | TConstructor(_, n, _) => n == shorterPrefix
414
+ | _ => False
415
+ }
416
+ }
417
+ if(variants.isEmpty()) {[]} else:
418
+ let label = if(inside) {""} else {"{"} +
419
+ variants.map {v =>
420
+ "| " + v.first.reverse().takeWhile {_ != '.'}.reverse() +
421
+ if(v.second.signature.parameters.isEmpty()) {""} else {"(...)"} + " => ..."
422
+ }.join(" ") +
423
+ if(inside) {""} else {"}"}
424
+ let snippetParts = variants.pairs().map {| Pair(index, Pair(name, scheme)) =>
425
+ "| " + name.reverse().takeWhile {_ != '.'}.reverse() +
426
+ if(scheme.signature.parameters.isEmpty()) {""} else {
427
+ "(" + scheme.signature.parameters.map {_.name}.join(", ") + ")"
428
+ } +
429
+ " => " + if(index == 0) {"$0"} else {""}
430
+ }
431
+ let snippet =
432
+ if(inside) {""} else {"{"} +
433
+ if(snippetParts.size() != 1) {
434
+ "\n" + snippetParts.map {" " + _}.join("\n") + "\n"
435
+ } else {
436
+ snippetParts.join(" ")
437
+ } +
438
+ if(inside) {""} else {"}"}
439
+ [CompletionInfo(
440
+ label = label.slice(0, 2)
441
+ extra = label.dropFirst(2)
442
+ snippet = snippet
443
+ member = True
444
+ type = TConstructor(Location("", 0, 0), "exhaustive match", [])
445
+ documentation = "// Exhaustive match:\n" + snippetParts.join("\n").replace("$0", "")
446
+ expectedType = None
447
+ )]
448
+ }
449
+
450
+ patternCompletion(unification: Unification, environment: Environment, expected: Type): List[CompletionInfo] {
451
+ let typeName = unification.substitute(expected).{
452
+ | TConstructor(_, name, _) => name
453
+ | _ => ""
454
+ }
455
+ if(typeName == "") {[]} else:
456
+ if(typeName == "ff:core/List.List") {
457
+ [CompletionInfo("[...]", "", "[$0]", False, expected, "// List pattern", Some(expected))]
458
+ } else:
459
+ if(typeName == "ff:core/String.String") {
460
+ [CompletionInfo("\"...\"", "", "\"$0\"", False, expected, "// String pattern", Some(expected))]
461
+ } else:
462
+ if(typeName == "ff:core/Char.Char") {
463
+ [CompletionInfo("'...'", "", "'$0'", False, expected, "// Char pattern", Some(expected))]
464
+ } else:
465
+ if(typeName == "ff:core/Int.Int") {
466
+ [CompletionInfo("0", "", "0", False, expected, "// Int pattern", Some(expected))]
467
+ } else:
468
+ let variants = do {
469
+ environment.symbols.toList().filter {s =>
470
+ !s.first.contains("_") &&
471
+ s.first.reverse().takeWhile {_ != '.'}.reverse().first().any {_.isAsciiUpper()} &&
472
+ s.second.signature.returnType.{
473
+ | TConstructor(_, n, _) => n == typeName
474
+ | _ => False
475
+ }
476
+ }
477
+ }
478
+ let completions = variants.map {| Pair(name, scheme) =>
479
+ let shortName = name.reverse().takeWhile {_ != '.'}.reverse()
480
+ let extra = if(scheme.signature.parameters.isEmpty()) {""} else {
481
+ "(" + scheme.signature.parameters.map {_.name}.join(", ") + ")"
482
+ }
483
+ let generics = scheme.signature.generics.filter {_ != "Q$"}
484
+ let documentation =
485
+ shortName +
486
+ if(generics.isEmpty()) {""} else {"[" + generics.join(", ") + "]"} +
487
+ if(scheme.signature.parameters.isEmpty()) {""} else {
488
+ "(\n" + scheme.signature.parameters.map {
489
+ showCompletionParameter(" ", _)
490
+ }.join("\n") + "\n)"
491
+ } + ": " + scheme.signature.returnType.show([])
492
+ CompletionInfo(
493
+ label = shortName
494
+ extra = extra
495
+ snippet = shortName + if(scheme.signature.parameters.isEmpty()) {""} else {"($0)"}
496
+ member = False
497
+ type = expected
498
+ documentation = documentation
499
+ expectedType = Some(expected)
500
+ )
501
+ }
502
+ completions
503
+ }
504
+
505
+ missingCompletion(
506
+ keyword: Bool
507
+ isParameter: Bool
508
+ unification: Unification
509
+ name: String
510
+ instantiated: Instantiated
511
+ arguments: Option[List[Argument]]
512
+ ): List[CompletionInfo] {
513
+ function findTermName(term: Term): Option[String] {
514
+ | ECall e {e.target | DynamicCall c} {
515
+ e.arguments.last() | Some(Argument(_, _, e2))
516
+ } {e2 | ELambda(_, Lambda(_, _, cases))} =>
517
+ cases.collectFirst {findTermName(_.body)}
518
+ | ECall e {e.target | DynamicCall c} => findTermName(c.function)
519
+ | ECopy e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
520
+ | EField e => Some(e.field)
521
+ | ELambda e {e.lambda.cases | [MatchCase c]} => findTermName(c.body)
522
+ | EList e {e.items | []} => Some("list")
523
+ | EList e {e.items | [e1, ...]} => findTermName(e1.first)
524
+ | EPipe e => findTermName(e.function)
525
+ | ERecord _ => Some("record")
526
+ | EVariable e {e.name != "" && e.name.all {_.isAsciiLetterOrDigit()}} => Some(e.name)
527
+ | EVariant e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
528
+ | EVariantIs e => Some(e.name.slice(0, 1).lower() + e.name.dropFirst())
529
+ | _ => None
530
+ }
531
+ function findTypeName(type: Type): String {
532
+ | TConstructor(_, name, _) {name.startsWith("Function$")} =>
533
+ "lambda"
534
+ | TConstructor(_, name, _) =>
535
+ let n = name.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
536
+ if(!n.first().all {_.isAsciiLetter()}) {"p"} else {n.slice(0, 1).lower() + n.dropFirst()}
537
+ | TVariable(_, index) => "p"
538
+ }
539
+ let completions = arguments.{
540
+ | None {isParameter} =>
541
+ let t = unification.substitute(instantiated.scheme.signature.returnType)
542
+ let snippet = t.{
543
+ | TConstructor _ => name + ": " + t.show([])
544
+ | TVariable _ => name + ": "
545
+ }
546
+ let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
547
+ [CompletionInfo(name, "", snippet, False, t, documentation, Some(t))]
548
+ | Some(as) {isParameter} =>
549
+ let noEffect = TConstructor(instantiated.scheme.signature.at, "ff:core/Nothing.Nothing", [])
550
+ let t = unification.substitute(TConstructor(
551
+ instantiated.scheme.signature.at
552
+ "Function$" + instantiated.scheme.signature.parameters.size()
553
+ [
554
+ noEffect
555
+ ...instantiated.scheme.signature.parameters.map {_.valueType}
556
+ instantiated.scheme.signature.returnType
557
+ ]
558
+ ))
559
+ let documentation = "// Add missing parameter\n" + name + ": " + t.show([])
560
+ [CompletionInfo(name, "", name + ": " + t.show([]), False, t, documentation, Some(t))]
561
+ | None =>
562
+ let t = unification.substitute(instantiated.scheme.signature.returnType)
563
+ let snippet = name + " = "
564
+ let documentation = "// Define missing variable\n" + name + ": " + t.show([])
565
+ [CompletionInfo(snippet, "...", snippet, False, t, documentation, Some(t))]
566
+ | Some(as) =>
567
+ mutable remainingParameters = instantiated.scheme.signature.parameters
568
+ mutable usedNames = [].toSet()
569
+ function uniqueName(name: String): String {
570
+ let alternativeName = name + (usedNames.size() + 1)
571
+ if(!usedNames.contains(name)) {
572
+ usedNames = usedNames.add(name)
573
+ name
574
+ } elseIf {!usedNames.contains(alternativeName)} {
575
+ usedNames = usedNames.add(alternativeName)
576
+ alternativeName
577
+ } else {
578
+ usedNames = usedNames.add(alternativeName + "_" + (usedNames.size() + 1))
579
+ uniqueName(name)
580
+ }
581
+ }
582
+ let parameters = as.collect {a =>
583
+ let found = a.name.{
584
+ | Some(n) => remainingParameters.find {_.name == n}
585
+ | None => remainingParameters.first()
586
+ }
587
+ found.map {p =>
588
+ remainingParameters = remainingParameters.filter {_.name != p.name}
589
+ let t = unification.substitute(p.valueType)
590
+ let x = a.name.orElse {findTermName(a.value)}.else {findTypeName(t)}
591
+ Pair(uniqueName(x), t)
592
+ }
593
+ }
594
+ let returnType = unification.substitute(instantiated.scheme.signature.returnType)
595
+ let types = [...parameters.map {_.second}, returnType]
596
+ let returnTypeCode = returnType.{| TVariable _ => "" | _ => ": " + returnType.show(types)}
597
+ let parameterSnippets = parameters.map {| Pair(x, t) =>
598
+ x + ": " + t.show(types)
599
+ }
600
+ let extra = "(" + parameterSnippets.join(", ") + ") {...}"
601
+ let shortSnippet = name + "(" + parameterSnippets.join(", ") + ")" + returnTypeCode
602
+ let longSnippet = name + "(\n " + parameterSnippets.join("\n ") + "\n)" + returnTypeCode
603
+ let snippet = if(shortSnippet.size() <= 100) {shortSnippet} else {longSnippet}
604
+ let documentation = "// Define missing function\n" +
605
+ if(shortSnippet.size() < 30) {shortSnippet} else {longSnippet}
606
+ [CompletionInfo(name, extra, snippet + " {\n $0\n}", False, returnType, documentation, None)]
607
+ }
608
+ if(keyword && arguments.isEmpty()) {
609
+ ["let ", "mutable "].flatMap {k => completions.map {c =>
610
+ c.CompletionInfo(label = k + c.label, snippet = k + c.snippet)
611
+ }}
612
+ } elseIf {keyword} {
613
+ ["function "].flatMap {k => completions.map {c =>
614
+ c.CompletionInfo(label = k + c.label, snippet = k + c.snippet)
615
+ }}
616
+ } else {
617
+ completions
618
+ }
619
+ }
620
+
621
+ namedParameterCompletion(parameter: Parameter, index: Int, preselect: Bool): CompletionInfo {
622
+ CompletionInfo(
623
+ label = parameter.name + " = "
624
+ extra = "..."
625
+ snippet = parameter.name + " = "
626
+ member = False
627
+ type = parameter.valueType
628
+ documentation = showCompletionParameter("", parameter)
629
+ expectedType = if(preselect) {parameter.valueType}
630
+ secondarySort = (1000 + index).clamp(1000, 9999)
631
+ )
632
+ }
633
+
634
+ toplevelCompletion(lspHook: LspHook): List[CompletionInfo] {
635
+ [
636
+ CompletionInfo(
637
+ label = "package",
638
+ extra = " some:package:0.0.0",
639
+ snippet = "package ",
640
+ member = False,
641
+ type = TConstructor(lspHook.at, "", []),
642
+ documentation = "// Declares the group, name and version of this package",
643
+ expectedType = None
644
+ )
645
+ CompletionInfo(
646
+ label = "dependency",
647
+ extra = " some:package:0.0.0",
648
+ snippet = "dependency ",
649
+ member = False,
650
+ type = TConstructor(lspHook.at, "", []),
651
+ documentation = "// Declares a dependency on the specified version of the package",
652
+ expectedType = None
653
+ )
654
+ CompletionInfo(
655
+ label = "import",
656
+ extra = " Module from some:package",
657
+ snippet = "import ",
658
+ member = False,
659
+ type = TConstructor(lspHook.at, "", []),
660
+ documentation = "// Imports a module from a package",
661
+ expectedType = None
662
+ )
663
+ CompletionInfo(
664
+ label = "extend",
665
+ extra = " self[T]: SomeType[T] {...}",
666
+ snippet = "extend ",
667
+ member = False,
668
+ type = TConstructor(lspHook.at, "", []),
669
+ documentation = "// Add methods to a type defined in this module",
670
+ expectedType = None
671
+ )
672
+ CompletionInfo(
673
+ label = "data",
674
+ extra = " SomeType[T](...) {...}",
675
+ snippet = "data ",
676
+ member = False,
677
+ type = TConstructor(lspHook.at, "", []),
678
+ documentation = "// Defines an immutable type",
679
+ expectedType = None
680
+ )
681
+ CompletionInfo(
682
+ label = "class",
683
+ extra = " SomeType[T](...) {...}",
684
+ snippet = "class ",
685
+ member = False,
686
+ type = TConstructor(lspHook.at, "", []),
687
+ documentation = "// Defines a mutable type",
688
+ expectedType = None
689
+ )
690
+ CompletionInfo(
691
+ label = "capability",
692
+ extra = " SomeType[T](...) {...}",
693
+ snippet = "capability ",
694
+ member = False,
695
+ type = TConstructor(lspHook.at, "", []),
696
+ documentation = "// Defines a type with capabilities and functions",
697
+ expectedType = None
698
+ )
699
+ CompletionInfo(
700
+ label = "trait",
701
+ extra = " T: SomeTrait {...}",
702
+ snippet = "trait ",
703
+ member = False,
704
+ type = TConstructor(lspHook.at, "", []),
705
+ documentation = "// Defines a trait",
706
+ expectedType = None
707
+ )
708
+ CompletionInfo(
709
+ label = "instance",
710
+ extra = " SomeType: SomeTrait {...}",
711
+ snippet = "instance ",
712
+ member = False,
713
+ type = TConstructor(lspHook.at, "", []),
714
+ documentation = "// Defines an instance of a trait",
715
+ expectedType = None
716
+ )
717
+ CompletionInfo(
718
+ label = "nodeMain",
719
+ extra = "(system: NodeSystem) {...}",
720
+ snippet = "nodeMain(system: NodeSystem) {\n $0\n}",
721
+ member = False,
722
+ type = TConstructor(lspHook.at, "", []),
723
+ documentation = "// Main function for targetting Node.js",
724
+ expectedType = None
725
+ )
726
+ CompletionInfo(
727
+ label = "browserMain",
728
+ extra = "(system: BrowserSystem) {...}",
729
+ snippet = "browserMain(system: BrowserSystem) {\n $0\n}",
730
+ member = False,
731
+ type = TConstructor(lspHook.at, "", []),
732
+ documentation = "// Main function for targetting the browser",
733
+ expectedType = None
734
+ )
735
+ CompletionInfo(
736
+ label = "buildMain",
737
+ extra = "(system: BuildSystem) {...}",
738
+ snippet = "buildMain(system: NodeSystem) {\n $0\n}",
739
+ member = False,
740
+ type = TConstructor(lspHook.at, "", []),
741
+ documentation = "// Main function for building assets",
742
+ expectedType = None
743
+ )
744
+ CompletionInfo(
745
+ label = "webapp",
746
+ extra = " with frontend and backend",
747
+ snippet = [
748
+ "dependency ff:webserver:0.0.0"
749
+ "import WebServer from ff:webserver"
750
+ ""
751
+ "browserMain(system: BrowserSystem): Unit {"
752
+ " let response = system.httpClient().fetch(\"http://localhost:8080/hello\")"
753
+ " let window = system.js().global().get(\"window\")"
754
+ " window.call1(\"alert\", response.readText())"
755
+ "}"
756
+ ""
757
+ "nodeMain(system: NodeSystem): Unit {"
758
+ " WebServer.make(system, \"localhost\", 8080).listen {request =>"
759
+ " if(request.readPath() == \"/\") {"
760
+ " request.writeHeader(\"Content-Type\", \"text/html; charset=UTF-8\")"
761
+ " request.writeText(\"<!doctype html>\")"
762
+ " request.writeText(\"<script type='module' src='/js/script/script/$TM_FILENAME_BASE.mjs'></script>\")"
763
+ " } elseIf {request.readPath() == \"/hello\"} {"
764
+ " request.writeHeader(\"Content-Type\", \"text/plain; charset=UTF-8\")"
765
+ " request.writeText(\"Hello from server!\")"
766
+ " } elseIf {request.readPath().startsWith(\"/js/\") && !request.readPath().contains(\"..\")} {"
767
+ " request.writeHeader(\"Content-Type\", \"text/javascript; charset=UTF-8\")"
768
+ " request.writeText(system.assets().readText(request.readPath()))"
769
+ " } else {"
770
+ " request.writeStatus(\"404 Not found\")"
771
+ " }"
772
+ " }"
773
+ "}"
774
+ ""
775
+ "buildMain(system: BuildSystem) {"
776
+ " let browser = system.compileForBrowser(\"$TM_FILENAME_BASE.ff\")"
777
+ " let assets = AssetSystem.create().addAssets(\"/js\", browser.assets())"
778
+ " system.setAssets(assets)"
779
+ "}"
780
+ ].join("\n"),
781
+ member = False,
782
+ type = TConstructor(lspHook.at, "", []),
783
+ documentation = "// Example webapp with frontend and backend",
784
+ expectedType = None
785
+ )
786
+ ]
787
+ }
788
+
789
+ showCompletionParameter(indentation: String, parameter: Parameter): String {
790
+ if(parameter.mutable) {indentation + "mutable "} else {indentation} +
791
+ parameter.name + ": " + parameter.valueType.show([]) +
792
+ parameter.default.map {
793
+ | EVariant(_, n, _, None) =>
794
+ n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
795
+ | EVariant(_, n, _, Some([])) =>
796
+ n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse()
797
+ | EVariant(_, n, _, _) =>
798
+ n.reverse().takeWhile {_.isAsciiLetterOrDigit()}.reverse() + "(...)"
799
+ | EChar(_, v) => v
800
+ | EInt(_, v) => v
801
+ | EFloat(_, v) => v
802
+ | EString(_, v) => v.replace("```", "'''")
803
+ | ELambda(_, _) => "{...}"
804
+ | EList(_, _, []) => "[]"
805
+ | EList(_, _, _) => "[...]"
806
+ | _ => "..."
807
+ }.map {" = " + _}.else {""}
808
+ }