firefly-compiler 0.4.5 → 0.4.7
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.
- package/.hintrc +4 -4
- package/README.md +1 -1
- package/core/HttpClient.ff +1 -1
- package/lsp/LanguageServer.ff +32 -7
- package/lsp/stderr.txt +1 -0
- package/lsp/stdin.txt +11 -0
- package/lsp/stdout.txt +41 -0
- package/lux/Main.ff +56 -2
- package/meetup/AutoCompletion.ff +6 -0
- package/output/js/ff/core/HttpClient.mjs +1 -1
- package/package.json +29 -29
- package/vscode/package-lock.json +5 -5
- package/vscode/package.json +1 -1
package/.hintrc
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": [
|
|
3
|
-
"development"
|
|
4
|
-
]
|
|
1
|
+
{
|
|
2
|
+
"extends": [
|
|
3
|
+
"development"
|
|
4
|
+
]
|
|
5
5
|
}
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ A straightforward language for full-stack development.
|
|
|
13
13
|
|
|
14
14
|
## Writing your first webapp
|
|
15
15
|
|
|
16
|
-
Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang)
|
|
16
|
+
Install the [VSCode extension](https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang).
|
|
17
17
|
|
|
18
18
|
Create a `WebApp.ff` file.
|
|
19
19
|
|
package/core/HttpClient.ff
CHANGED
|
@@ -27,7 +27,7 @@ extend self: HttpClient {
|
|
|
27
27
|
try {
|
|
28
28
|
const options = {headers: {}, signal: $task.controller.signal}
|
|
29
29
|
options.method = method_
|
|
30
|
-
ff_core_List.List_each(headers_, pair => {options.headers[pair.
|
|
30
|
+
ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
|
|
31
31
|
if(body_.value_) options.body = body_.value_
|
|
32
32
|
if(redirect_.RedirectError) options.redirect = "error"
|
|
33
33
|
else if(redirect_.RedirectManual) options.redirect = "manual"
|
package/lsp/LanguageServer.ff
CHANGED
|
@@ -17,17 +17,26 @@ capability RequestMessage(
|
|
|
17
17
|
parameters: Map[String, JsValue]
|
|
18
18
|
)
|
|
19
19
|
|
|
20
|
+
logMessages = False
|
|
20
21
|
|
|
21
22
|
main(system: NodeSystem) {
|
|
23
|
+
let logDirectory = if(logMessages) {
|
|
24
|
+
let directory = system.path(".").slash("messages")
|
|
25
|
+
if(directory.isDirectory()) {directory.delete()}
|
|
26
|
+
directory.createDirectory()
|
|
27
|
+
directory
|
|
28
|
+
}
|
|
29
|
+
|
|
22
30
|
let fireflyPath = system.path(".")
|
|
23
31
|
let handler = Handler(fireflyPath, None, Map.empty(), [].toSet(), Map.empty(), Map.empty())
|
|
24
32
|
try {
|
|
25
33
|
mutable input = system.readStream()
|
|
26
|
-
let responseChannel: Channel[JsValue] = system.mainTask().channel()
|
|
34
|
+
let responseChannel: Channel[Pair[JsValue, String]] = system.mainTask().channel()
|
|
27
35
|
system.mainTask().spawn {_ =>
|
|
28
36
|
while {True} {
|
|
29
37
|
let body = responseChannel.read()
|
|
30
|
-
let json = body.toJson(Some(" ")).toBuffer()
|
|
38
|
+
let json = body.first.toJson(Some(" ")).toBuffer()
|
|
39
|
+
logDirectory.each {logMessage(_, "response", body.first, body.second)}
|
|
31
40
|
system.writeText("Content-Length: " + json.size() + "\r\n\r\n")
|
|
32
41
|
system.writeBuffer(json)
|
|
33
42
|
}
|
|
@@ -38,13 +47,14 @@ main(system: NodeSystem) {
|
|
|
38
47
|
input = requestPair.second
|
|
39
48
|
let request = requestPair.first
|
|
40
49
|
let message = parseRequestMessage(request.object)
|
|
50
|
+
logDirectory.each {logMessage(_, "request", request.object, message.method)}
|
|
41
51
|
spawn(message.id) {_ =>
|
|
42
52
|
if(message.method == "$/cancelRequest") {
|
|
43
53
|
abort(Some(parseMessageId(message.parameters.grab("id"))))
|
|
44
54
|
}
|
|
45
55
|
try {
|
|
46
56
|
handleRequestMessage(system, handler, message).each {body =>
|
|
47
|
-
responseChannel.write(body)
|
|
57
|
+
responseChannel.write(Pair(body, message.method))
|
|
48
58
|
}
|
|
49
59
|
} catchAny {error =>
|
|
50
60
|
let problem = if(error.name() == "AbortError") {
|
|
@@ -55,7 +65,8 @@ main(system: NodeSystem) {
|
|
|
55
65
|
Error(-32603, "Internal error")
|
|
56
66
|
}
|
|
57
67
|
message.id.each {id =>
|
|
58
|
-
|
|
68
|
+
let body = makeResponseMessage(system.js(), id, problem)
|
|
69
|
+
responseChannel.write(Pair(body, message.method))
|
|
59
70
|
}
|
|
60
71
|
} grab()
|
|
61
72
|
}
|
|
@@ -74,17 +85,17 @@ taskGroup[K: Order, R](
|
|
|
74
85
|
mutable tasks = [].toMap()
|
|
75
86
|
function spawn(key: K, body: Task => Unit): Task {
|
|
76
87
|
let body2 = {task =>
|
|
77
|
-
lock.do {tasks = tasks.add(key, task)}
|
|
88
|
+
lock.do(reentrant = True) {tasks = tasks.add(key, task)}
|
|
78
89
|
try {
|
|
79
90
|
body(task)
|
|
80
91
|
} finally {
|
|
81
|
-
lock.do {tasks = tasks.remove(key)}
|
|
92
|
+
lock.do(reentrant = True) {tasks = tasks.remove(key)}
|
|
82
93
|
} grab()
|
|
83
94
|
}
|
|
84
95
|
parentTask.spawn(body2)
|
|
85
96
|
}
|
|
86
97
|
function abort(key: K): Unit {
|
|
87
|
-
lock.do {tasks.get(key).each {_.abort()}}
|
|
98
|
+
lock.do(reentrant = True) {tasks.get(key).each {_.abort()}}
|
|
88
99
|
}
|
|
89
100
|
groupBody(spawn, abort)
|
|
90
101
|
}
|
|
@@ -227,3 +238,17 @@ makeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue
|
|
|
227
238
|
}
|
|
228
239
|
o
|
|
229
240
|
}
|
|
241
|
+
|
|
242
|
+
logMessage(directory: Path, messageType: String, body: JsValue, method: String) {
|
|
243
|
+
let id = if(method == "$/cancelRequest") {
|
|
244
|
+
Some(body.get("params").get("id").grabInt())
|
|
245
|
+
} else {
|
|
246
|
+
body.getOwn("id").map {_.grabInt()}
|
|
247
|
+
}
|
|
248
|
+
let number = directory.entries().toList().size()
|
|
249
|
+
let idName = id.map {_ + ""}.else {"_"}
|
|
250
|
+
let methodName = method.replace("$", "notification").replace("/", "_")
|
|
251
|
+
let fileName = number + "-id-" + idName + "-" + messageType + "-" + methodName + ".json"
|
|
252
|
+
let path = directory.slash(fileName)
|
|
253
|
+
path.writeText(body.toJson(Some(" ")))
|
|
254
|
+
}
|
package/lsp/stderr.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
End of input while parsing request headers
|
package/lsp/stdin.txt
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Content-Length: 5836
|
|
2
|
+
|
|
3
|
+
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":43727,"clientInfo":{"name":"Visual Studio Code","version":"1.76.2"},"locale":"en","rootPath":"/Users/ahnfelt/Downloads/firefly-boot","rootUri":"file:///Users/ahnfelt/Downloads/firefly-boot","capabilities":{"workspace":{"applyEdit":true,"workspaceEdit":{"documentChanges":true,"resourceOperations":["create","rename","delete"],"failureHandling":"textOnlyTransactional","normalizesLineEndings":true,"changeAnnotationSupport":{"groupsOnLabel":true}},"configuration":true,"didChangeWatchedFiles":{"dynamicRegistration":true,"relativePatternSupport":true},"symbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"tagSupport":{"valueSet":[1]},"resolveSupport":{"properties":["location.range"]}},"codeLens":{"refreshSupport":true},"executeCommand":{"dynamicRegistration":true},"didChangeConfiguration":{"dynamicRegistration":true},"workspaceFolders":true,"semanticTokens":{"refreshSupport":true},"fileOperations":{"dynamicRegistration":true,"didCreate":true,"didRename":true,"didDelete":true,"willCreate":true,"willRename":true,"willDelete":true},"inlineValue":{"refreshSupport":true},"inlayHint":{"refreshSupport":true},"diagnostics":{"refreshSupport":true}},"textDocument":{"publishDiagnostics":{"relatedInformation":true,"versionSupport":false,"tagSupport":{"valueSet":[1,2]},"codeDescriptionSupport":true,"dataSupport":true},"synchronization":{"dynamicRegistration":true,"willSave":true,"willSaveWaitUntil":true,"didSave":true},"completion":{"dynamicRegistration":true,"contextSupport":true,"completionItem":{"snippetSupport":true,"commitCharactersSupport":true,"documentationFormat":["markdown","plaintext"],"deprecatedSupport":true,"preselectSupport":true,"tagSupport":{"valueSet":[1]},"insertReplaceSupport":true,"resolveSupport":{"properties":["documentation","detail","additionalTextEdits"]},"insertTextModeSupport":{"valueSet":[1,2]},"labelDetailsSupport":true},"insertTextMode":2,"completionItemKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25]},"completionList":{"itemDefaults":["commitCharacters","editRange","insertTextFormat","insertTextMode"]}},"hover":{"dynamicRegistration":true,"contentFormat":["markdown","plaintext"]},"signatureHelp":{"dynamicRegistration":true,"signatureInformation":{"documentationFormat":["markdown","plaintext"],"parameterInformation":{"labelOffsetSupport":true},"activeParameterSupport":true},"contextSupport":true},"definition":{"dynamicRegistration":true,"linkSupport":true},"references":{"dynamicRegistration":true},"documentHighlight":{"dynamicRegistration":true},"documentSymbol":{"dynamicRegistration":true,"symbolKind":{"valueSet":[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26]},"hierarchicalDocumentSymbolSupport":true,"tagSupport":{"valueSet":[1]},"labelSupport":true},"codeAction":{"dynamicRegistration":true,"isPreferredSupport":true,"disabledSupport":true,"dataSupport":true,"resolveSupport":{"properties":["edit"]},"codeActionLiteralSupport":{"codeActionKind":{"valueSet":["","quickfix","refactor","refactor.extract","refactor.inline","refactor.rewrite","source","source.organizeImports"]}},"honorsChangeAnnotations":false},"codeLens":{"dynamicRegistration":true},"formatting":{"dynamicRegistration":true},"rangeFormatting":{"dynamicRegistration":true},"onTypeFormatting":{"dynamicRegistration":true},"rename":{"dynamicRegistration":true,"prepareSupport":true,"prepareSupportDefaultBehavior":1,"honorsChangeAnnotations":true},"documentLink":{"dynamicRegistration":true,"tooltipSupport":true},"typeDefinition":{"dynamicRegistration":true,"linkSupport":true},"implementation":{"dynamicRegistration":true,"linkSupport":true},"colorProvider":{"dynamicRegistration":true},"foldingRange":{"dynamicRegistration":true,"rangeLimit":5000,"lineFoldingOnly":true,"foldingRangeKind":{"valueSet":["comment","imports","region"]},"foldingRange":{"collapsedText":false}},"declaration":{"dynamicRegistration":true,"linkSupport":true},"selectionRange":{"dynamicRegistration":true},"callHierarchy":{"dynamicRegistration":true},"semanticTokens":{"dynamicRegistration":true,"tokenTypes":["namespace","type","class","enum","interface","struct","typeParameter","parameter","variable","property","enumMember","event","function","method","macro","keyword","modifier","comment","string","number","regexp","operator","decorator"],"tokenModifiers":["declaration","definition","readonly","static","deprecated","abstract","async","modification","documentation","defaultLibrary"],"formats":["relative"],"requests":{"range":true,"full":{"delta":true}},"multilineTokenSupport":false,"overlappingTokenSupport":false,"serverCancelSupport":true,"augmentsSyntaxTokens":true},"linkedEditingRange":{"dynamicRegistration":true},"typeHierarchy":{"dynamicRegistration":true},"inlineValue":{"dynamicRegistration":true},"inlayHint":{"dynamicRegistration":true,"resolveSupport":{"properties":["tooltip","textEdits","label.tooltip","label.location","label.command"]}},"diagnostic":{"dynamicRegistration":true,"relatedDocumentSupport":false}},"window":{"showMessage":{"messageActionItem":{"additionalPropertiesSupport":true}},"showDocument":{"support":true},"workDoneProgress":true},"general":{"staleRequestSupport":{"cancel":true,"retryOnContentModified":["textDocument/semanticTokens/full","textDocument/semanticTokens/range","textDocument/semanticTokens/full/delta"]},"regularExpressions":{"engine":"ECMAScript","version":"ES2020"},"markdown":{"parser":"marked","version":"1.1.0"},"positionEncodings":["utf-16"]},"notebookDocument":{"synchronization":{"dynamicRegistration":true,"executionSummarySupport":true}}},"trace":"off","workspaceFolders":[{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot","name":"firefly-boot"}]}}Content-Length: 52
|
|
4
|
+
|
|
5
|
+
{"jsonrpc":"2.0","method":"initialized","params":{}}Content-Length: 9421
|
|
6
|
+
|
|
7
|
+
{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff","languageId":"firefly","version":1,"text":"class LanguageServer(\n mutable openFiles: Map[String, String]\n)\n\ndata BadRequestException(reason: String)\n\ncapability Request(\n headers: Map[String, String]\n object: JsValue\n)\n\ncapability RequestMessage(\n id: Option[MessageId] // None when the request is a Notification Message\n method: String\n parameters: Map[String, JsValue]\n)\n\ndata MessageId {\n MessageIdInt(id: Int)\n MessageIdString(id: String)\n}\n\ncapability ResultOrError {\n Result(result: JsValue)\n Error(code: Int, message: String)\n}\n\n\nmain(system: NodeSystem) {\n try {\n mutable input = system.readStream()\n while {True} {\n let requestPair = parseRequest(system, input)\n input = requestPair.second\n let request = requestPair.first\n let message = parseRequestMessage(request.object)\n handleRequestMessage(system.js(), message).each {body =>\n system.writeText(makeResponse(body))\n }\n //system.writeText(makeResponse(makeDiagnoseNotification(system.js())))\n }\n } catch {| BadRequestException(reason), error =>\n system.writeErrorText(reason + \"\\n\")\n } grab()\n}\n \nparseRequest(system: NodeSystem, input: Stream[Buffer]): Pair[Request, Stream[Buffer]] {\n let headersPair = parseRequestHeaders(input)\n let headers = headersPair.first\n let contentLength = headers\n .get(\"content-length\").else {throw(BadRequestException(\"'content-length' header is missing\"))}\n .getInt().else {throw(BadRequestException(\"Value for 'content-length' is not an integer\"))}\n let bodyPair = parseRequestBody(system.js(), contentLength, headersPair.second)\n bodyPair.mapFirst {body => Request(headers, body)}\n}\n\nparseRequestHeaders(input: Stream[Buffer]): Pair[Map[String, String], Stream[Buffer]] {\n let buffers = Stack.make()\n mutable buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n mutable offset = 0\n mutable droppingInitialNewlines = True\n mutable lastWasNewline = False\n mutable done = False\n while {!done} {\n if(offset == buffer.size()) {\n offset = 0\n buffers.push(buffer)\n buffer = input.next().else {\n throw(BadRequestException(\"End of input while parsing request headers\"))\n }\n }\n let byte = buffer.grabUint8(offset)\n if(droppingInitialNewlines && (byte == '\\n'.codeUnit || byte == '\\r'.codeUnit)) {\n // Skip\n } elseIf {byte == '\\n'.codeUnit} {\n if(lastWasNewline) {\n done = True\n } else {\n lastWasNewline = True\n }\n } elseIf {byte != '\\r'.codeUnit && lastWasNewline} {\n lastWasNewline = False\n } else {\n droppingInitialNewlines = False\n }\n offset += 1\n }\n buffers.push(buffer.view(0, offset))\n let headers = Buffer.fromBufferArray(buffers.drain()).toString()\n let map = headers.lines().pairs().filter {_.second.size() != 0}.map {| Pair(i, line) =>\n line.splitFirst(':').else {\n throw(BadRequestException(\"Invalid header at line \" + i + \" '\" + line + \"'\"))\n }.mapFirst {_.lower()} //.mapSecond {_.trim()}\n }.toMap()\n Pair(map, [buffer.view(offset, buffer.size())].toStream().addAll(input))\n}\n\nparseRequestBody(js: JsSystem, contentLength: Int, input: Stream[Buffer]): Pair[JsValue, Stream[Buffer]] {\n let bodyPair = try {\n input.readBytes(contentLength) // Should Stream.readBytes return an option?\n } catchAny {error =>\n throw(BadRequestException(\"End of input while parsing request body\"))\n } grab()\n let body = Buffer.fromBufferArray(bodyPair.first).toString()\n let json = try {\n js.parseJson(body)\n } catchAny {error =>\n throw(BadRequestException(\"Invalid JSON in request body: \" + body))\n } grab()\n Pair(json, bodyPair.second)\n}\n\nparseRequestMessage(object: JsValue): RequestMessage {\n let id = object.getOwn(\"id\").map {id =>\n if(id.isInt()) {\n MessageIdInt(id.grabInt())\n } elseIf {id.isString()} {\n MessageIdString(id.grabString())\n } else {\n throw(BadRequestException(\"Bad JSON-RPC id, int or string expected\"))\n }\n }\n\n let method = object.getOwn(\"method\").{\n | None => throw(BadRequestException(\"Bad JSON-RPC, missing method\"))\n | Some(m) {!m.isString()} => throw(BadRequestException(\"Bad JSON-RPC method, string expected\"))\n | Some(m) => m.grabString()\n }\n\n let parameters = object.getOwn(\"params\").{\n | None => [].toMap()\n | Some(o) {!o.isObject()} => throw(BadRequestException(\"Bad JSON-RPC params, object expected\"))\n | Some(o) => o.grabMap()\n }\n\n RequestMessage(id, method, parameters)\n}\n\nhandleRequestMessage(js: JsSystem, message: RequestMessage): Option[JsValue] {\n message.id.{\n // Notification Message\n | None =>\n message.method.{\n | \"initialized\" =>\n | _ =>\n }\n None\n // Request Message\n | Some(id) =>\n let result = message.method.{\n | \"initialize\" => handleInitialize(js, message.parameters)\n | \"textDocument/diagnostic\" => handleDiagnostic(js, message.parameters)\n | \"hover\" => handleHover(js, message.parameters)\n | _ => handleUnsupported(js)\n }\n Some(makeResponseMessage(js, id, result))\n }\n}\n\nmakeResponse(body: JsValue): String {\n let json = body.toJson(Some(\" \"))\n let length = json.size()\n \"Content-Length: \" + length + \"\\r\\n\" +\n \"\\r\\n\" +\n json\n}\n\nmakeResponseMessage(js: JsSystem, id: MessageId, result: ResultOrError): JsValue {\n let o = js.object()\n o.set(\"jsonrpc\", \"2.0\")\n id.{\n | MessageIdInt(id) => o.set(\"id\", id)\n | MessageIdString(id) => o.set(\"id\", id)\n }\n result.{\n | Result(result) =>\n o.set(\"result\", result)\n | Error(code, message) =>\n let e = js.object()\n e.set(\"code\", code)\n e.set(\"message\", message)\n o.set(\"error\", e)\n }\n o\n}\n\nhandleUnsupported(js: JsSystem): ResultOrError {\n Error(1234, \"Unsupported method\")\n}\n\nhandleInitialize(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let anyFireflyFile = js.object()\n .with(\"filters\", [\n js.object()\n .with(\"pattern\", js.object()\n .with(\"glob\", \"**/*.ff\")\n .with(\"matches\", \"file\")\n )\n ].toArray())\n\n let o = js.object()\n .with(\"capabilities\", js.object()\n .with(\"textDocumentSync\", js.object()\n .with(\"openClose\", True)\n .with(\"change\", 1 /* TextDocumentSyncKind.Full */)\n )\n .with(\"hoverProvider\", False)\n //.with(\"definitionProvider\", True)\n //.with(\"typeDefinitionProvider\", True)\n .with(\"diagnosticProvider\", js.object()\n .with(\"interFileDependencies\", True)\n .with(\"workspaceDiagnostics\", False)\n )\n /*.with(\"workspace\", js.object()\n .with(\"workspaceFolders\", js.object()\n .with(\"supported\", True)\n .with(\"changeNotifications\", True)\n )\n .with(\"fileOperations\", js.object()\n .with(\"didCreate\", anyFireflyFile)\n .with(\"didRename\", anyFireflyFile)\n .with(\"didDelete\", anyFireflyFile)\n )\n )*/\n )\n .with(\"serverInfo\", js.object()\n .with(\"name\", \"Firefly Language Server\")\n .with(\"version\", \"0.0.0\")\n )\n Result(o)\n}\n\nhandleDiagnostic(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let diagnostic = js.object()\n .with(\"range\", js.object()\n .with(\"start\", js.object()\n .with(\"line\", 0).with(\"character\", 6)\n )\n .with(\"end\", js.object()\n .with(\"line\", 0).with(\"character\", 20)\n )\n )\n .with(\"severity\", 1 /* Error */)\n .with(\"message\", \"Hurray!\")\n\n let o = js.object()\n .with(\"kind\", \"full\")\n .with(\"items\", [/*diagnostic*/].toArray())\n\n Result(o)\n}\n\nhandleHover(js: JsSystem, parameters: Map[String, JsValue]): ResultOrError {\n let o = js.object().with(\"contents\", \"TODO\")\n Result(o)\n}\n\nmakeNotificationMessage(js: JsSystem, method: String, params: JsValue): JsValue {\n js.object()\n .with(\"jsonrpc\", \"2.0\")\n .with(\"method\", method)\n .with(\"params\", params)\n}\n"}}}Content-Length: 162
|
|
8
|
+
|
|
9
|
+
{"jsonrpc":"2.0","id":1,"method":"textDocument/diagnostic","params":{"textDocument":{"uri":"file:///Users/ahnfelt/Downloads/firefly-boot/lsp/LanguageServer.ff"}}}Content-Length: 44
|
|
10
|
+
|
|
11
|
+
{"jsonrpc":"2.0","id":2,"method":"shutdown"}
|
package/lsp/stdout.txt
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Content-Length: 500
|
|
2
|
+
|
|
3
|
+
{
|
|
4
|
+
"jsonrpc": "2.0",
|
|
5
|
+
"id": 0,
|
|
6
|
+
"result": {
|
|
7
|
+
"capabilities": {
|
|
8
|
+
"textDocumentSync": {
|
|
9
|
+
"openClose": true,
|
|
10
|
+
"change": 1
|
|
11
|
+
},
|
|
12
|
+
"hoverProvider": false,
|
|
13
|
+
"diagnosticProvider": {
|
|
14
|
+
"interFileDependencies": true,
|
|
15
|
+
"workspaceDiagnostics": false
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"serverInfo": {
|
|
19
|
+
"name": "Firefly Language Server",
|
|
20
|
+
"version": "0.0.0"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}Content-Length: 104
|
|
24
|
+
|
|
25
|
+
{
|
|
26
|
+
"jsonrpc": "2.0",
|
|
27
|
+
"id": 1,
|
|
28
|
+
"result": {
|
|
29
|
+
"kind": "full",
|
|
30
|
+
"items": []
|
|
31
|
+
}
|
|
32
|
+
}Content-Length: 121
|
|
33
|
+
|
|
34
|
+
{
|
|
35
|
+
"jsonrpc": "2.0",
|
|
36
|
+
"id": 2,
|
|
37
|
+
"error": {
|
|
38
|
+
"code": 1234,
|
|
39
|
+
"message": "Unsupported method"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/lux/Main.ff
CHANGED
|
@@ -3,6 +3,10 @@ import HttpServer from ff:httpserver
|
|
|
3
3
|
import Lux
|
|
4
4
|
import LuxEvent
|
|
5
5
|
|
|
6
|
+
data ChatEntry(
|
|
7
|
+
question: String
|
|
8
|
+
answer: Option[String]
|
|
9
|
+
)
|
|
6
10
|
|
|
7
11
|
mainComponent(lux: Lux, http: HttpClient) {
|
|
8
12
|
lux.useState([]): chat, setChat =>
|
|
@@ -46,6 +50,7 @@ browserMain(system: BrowserSystem) {
|
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
nodeMain(system: NodeSystem) {
|
|
53
|
+
let openAiKey = system.arguments().first()
|
|
49
54
|
HttpServer.listen(system, "localhost", 8080) {request, response =>
|
|
50
55
|
if(request.path() == "/") {
|
|
51
56
|
response.setHeader("Content-Type", ["text/html; charset=UTF-8"])
|
|
@@ -56,15 +61,64 @@ nodeMain(system: NodeSystem) {
|
|
|
56
61
|
response.setHeader("Content-Type", ["text/javascript; charset=UTF-8"])
|
|
57
62
|
response.writeText(system.assets().readText(request.path()))
|
|
58
63
|
} elseIf {request.path() == "/chat"} {
|
|
59
|
-
|
|
64
|
+
let question = request.readText()
|
|
60
65
|
response.setHeader("Content-Type", ["text/plain; charset=UTF-8"])
|
|
61
|
-
|
|
66
|
+
openAiKey.{
|
|
67
|
+
| None =>
|
|
68
|
+
system.mainTask().sleep(Duration(question.size().toFloat()))
|
|
69
|
+
response.writeText("Hi! You need to give an OpenAI secret key at the command line.")
|
|
70
|
+
| Some(key) =>
|
|
71
|
+
let chatJson = encodeChat(system.js(), question)
|
|
72
|
+
let answer = fetchAnswer(system.httpClient(), system.js(), key, chatJson)
|
|
73
|
+
response.writeText(answer)
|
|
74
|
+
}
|
|
62
75
|
} else {
|
|
63
76
|
response.writeStatus(404, Some("Not found"))
|
|
64
77
|
}
|
|
65
78
|
}
|
|
66
79
|
}
|
|
67
80
|
|
|
81
|
+
fetchAnswer(httpClient: HttpClient, js: JsSystem, key: String, question: JsValue): String {
|
|
82
|
+
let json = httpClient.fetch(
|
|
83
|
+
url = "https://api.openai.com/v1/chat/completions"
|
|
84
|
+
method = "POST"
|
|
85
|
+
headers = [
|
|
86
|
+
Pair("Authorization", "Bearer " + key)
|
|
87
|
+
Pair("Content-Type", "application/json")
|
|
88
|
+
]
|
|
89
|
+
body = Some(HttpClient.bodyText(question.toJson()))
|
|
90
|
+
).readText()
|
|
91
|
+
js.parseJson(json).get("choices").get(0).get("message").get("content").grabString()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
encodeChat(js: JsSystem, question: String): JsValue {
|
|
95
|
+
let systemJson = js.object()
|
|
96
|
+
.with("role", "system")
|
|
97
|
+
.with("content", "You are a helpful assistant.")
|
|
98
|
+
let questionJson = js.object()
|
|
99
|
+
.with("role", "user")
|
|
100
|
+
.with("content", question)
|
|
101
|
+
let messagesJson = js.array([systemJson, questionJson])
|
|
102
|
+
js.object()
|
|
103
|
+
.with("model", "gpt-3.5-turbo")
|
|
104
|
+
.with("messages", messagesJson)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/*
|
|
109
|
+
let
|
|
110
|
+
encodeMessage role content =
|
|
111
|
+
E.object [ ("role", E.string role), ("content", E.string content) ]
|
|
112
|
+
encodeEntry question maybeAnswer = case maybeAnswer of
|
|
113
|
+
Nothing -> [ encodeMessage "user" question ]
|
|
114
|
+
Just answer -> [ encodeMessage "user" question, encodeMessage "assistant" answer ]
|
|
115
|
+
encodedMessages = encodeMessage "system" "You are a helpful assistant."
|
|
116
|
+
:: List.concatMap (\entry -> encodeEntry entry.question entry.answer) chat
|
|
117
|
+
in E.object
|
|
118
|
+
[ ("model", E.string "gpt-3.5-turbo")
|
|
119
|
+
, ("messages", E.list (\x -> x) encodedMessages)
|
|
120
|
+
]
|
|
121
|
+
*/
|
|
68
122
|
buildMain(system: BuildSystem) {
|
|
69
123
|
let browser = system.compileForBrowser("Main.ff")
|
|
70
124
|
let assets = AssetSystem.create().addAssets("/js", browser.assets())
|
|
@@ -138,7 +138,7 @@ export async function HttpClient_fetch$(self_, url_, method_ = "GET", headers_ =
|
|
|
138
138
|
try {
|
|
139
139
|
const options = {headers: {}, signal: $task.controller.signal}
|
|
140
140
|
options.method = method_
|
|
141
|
-
ff_core_List.List_each(headers_, pair => {options.headers[pair.
|
|
141
|
+
ff_core_List.List_each(headers_, pair => {options.headers[pair.first_] = pair.second_})
|
|
142
142
|
if(body_.value_) options.body = body_.value_
|
|
143
143
|
if(redirect_.RedirectError) options.redirect = "error"
|
|
144
144
|
else if(redirect_.RedirectManual) options.redirect = "manual"
|
package/package.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "firefly-compiler",
|
|
3
|
-
"displayName": "Firefly Compiler",
|
|
4
|
-
"description": "Firefly compiler",
|
|
5
|
-
"author": "Firefly team",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"version": "0.4.
|
|
8
|
-
"repository": {
|
|
9
|
-
"type": "git",
|
|
10
|
-
"url": "https://github.com/Ahnfelt/firefly-boot"
|
|
11
|
-
},
|
|
12
|
-
"publisher": "firefly-team",
|
|
13
|
-
"categories": [
|
|
14
|
-
"Programming Languages"
|
|
15
|
-
],
|
|
16
|
-
"engines": {
|
|
17
|
-
"node": "^18.0.0"
|
|
18
|
-
},
|
|
19
|
-
"main": "./vscode/client/out/extension",
|
|
20
|
-
"icon": "./vscode/icons/firefly-logo.png",
|
|
21
|
-
"dependencies": {
|
|
22
|
-
"esbuild": "^0.14.54",
|
|
23
|
-
"pkg": "^5.8.0",
|
|
24
|
-
"tar": "^6.1.11"
|
|
25
|
-
},
|
|
26
|
-
"bin": {
|
|
27
|
-
"firefly": "./bin/firefly.mjs"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "firefly-compiler",
|
|
3
|
+
"displayName": "Firefly Compiler",
|
|
4
|
+
"description": "Firefly compiler",
|
|
5
|
+
"author": "Firefly team",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"version": "0.4.7",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/Ahnfelt/firefly-boot"
|
|
11
|
+
},
|
|
12
|
+
"publisher": "firefly-team",
|
|
13
|
+
"categories": [
|
|
14
|
+
"Programming Languages"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": "^18.0.0"
|
|
18
|
+
},
|
|
19
|
+
"main": "./vscode/client/out/extension",
|
|
20
|
+
"icon": "./vscode/icons/firefly-logo.png",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"esbuild": "^0.14.54",
|
|
23
|
+
"pkg": "^5.8.0",
|
|
24
|
+
"tar": "^6.1.11"
|
|
25
|
+
},
|
|
26
|
+
"bin": {
|
|
27
|
+
"firefly": "./bin/firefly.mjs"
|
|
28
|
+
}
|
|
29
|
+
}
|
package/vscode/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "firefly",
|
|
3
|
-
"version": "0.4.
|
|
2
|
+
"name": "firefly-lang",
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
|
-
"name": "firefly",
|
|
9
|
-
"version": "0.4.
|
|
8
|
+
"name": "firefly-lang",
|
|
9
|
+
"version": "0.4.4",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"devDependencies": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"typescript": "^4.9.5"
|
|
20
20
|
},
|
|
21
21
|
"engines": {
|
|
22
|
-
"vscode": "^1.
|
|
22
|
+
"vscode": "^1.82.0"
|
|
23
23
|
}
|
|
24
24
|
},
|
|
25
25
|
"node_modules/@eslint-community/eslint-utils": {
|