firefly-compiler 0.5.39 → 0.5.40

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