forked from goplus/builder
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tools/spxls): implement initial LSP features
- Add WASM adapter for spxls. - Implement spxlc (JavaScript client for spxls). - Add `MapFS` implementation for file system operations. - Implement `textDocument/formatting` LSP capability. - Fix multiple `var` blocks handling in spx source code. (Fixes goplus#752) - Fix declaration order requirements (vars -> funcs -> others). (Fixes goplus#591) - Set up basic project layout. Updates goplus#1059 Signed-off-by: Aofei Sheng <aofei@aofeisheng.com>
- Loading branch information
Showing
15 changed files
with
1,749 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
import { Files, NewSpxls, NotificationMessage, RequestMessage, ResponseMessage, Spxls } from '.' | ||
|
||
/** | ||
* Client wrapper for the spxls. | ||
*/ | ||
export class Spxlc { | ||
private ls: Spxls | ||
private nextRequestId: number = 1 | ||
private pendingRequests = new Map<number, { | ||
resolve: (response: any) => void | ||
reject: (error: any) => void | ||
}>() | ||
private notificationHandlers = new Map<string, (params: any) => void>() | ||
|
||
/** | ||
* Creates a new client instance. | ||
* @param filesProvider Function that provides access to workspace files. | ||
*/ | ||
constructor(filesProvider: () => Files) { | ||
this.ls = NewSpxls(filesProvider, this.handleMessage.bind(this)) | ||
} | ||
|
||
/** | ||
* Handles messages from the language server. | ||
* @param message Message from the server. | ||
* @throws Error if the message type is unknown. | ||
*/ | ||
private handleMessage(message: ResponseMessage | NotificationMessage): void { | ||
if ('id' in message) return this.handleResponseMessage(message) | ||
if ('method' in message) return this.handleNotificationMessage(message) | ||
throw new Error('unknown message type') | ||
} | ||
|
||
/** | ||
* Handles response messages from the language server. | ||
* @param message Response message from the server. | ||
* @throws Error if no pending request is found for the message ID. | ||
*/ | ||
private handleResponseMessage(message: ResponseMessage): void { | ||
const pending = this.pendingRequests.get(message.id) | ||
if (pending == null) throw new Error(`no pending request found for id: ${message.id}`) | ||
this.pendingRequests.delete(message.id) | ||
|
||
if ('error' in message) pending.reject(message.error) | ||
else pending.resolve(message.result) | ||
} | ||
|
||
/** | ||
* Handles notification messages from the language server. | ||
* @param message Notification message from the server. | ||
* @throws Error if no handler is found for the notification method. | ||
*/ | ||
private handleNotificationMessage(message: NotificationMessage): void { | ||
const handler = this.notificationHandlers.get(message.method) | ||
if (handler == null) throw new Error(`no notification handler found for method: ${message.method}`) | ||
|
||
handler(message.params) | ||
} | ||
|
||
/** | ||
* Sends a request to the language server and waits for response. | ||
* @param method LSP method name. | ||
* @param params Method parameters. | ||
* @returns Promise that resolves with the response. | ||
*/ | ||
request<T>(method: string, params?: any): Promise<T> { | ||
return new Promise((resolve, reject) => { | ||
const id = this.nextRequestId++ | ||
this.pendingRequests.set(id, { resolve, reject }) | ||
|
||
const message: RequestMessage = { | ||
jsonrpc: '2.0', | ||
id, | ||
method, | ||
params | ||
} | ||
this.ls.handleMessage(message) | ||
}) | ||
} | ||
|
||
/** | ||
* Sends a notification to the language server (no response expected). | ||
* @param method LSP method name. | ||
* @param params Method parameters. | ||
*/ | ||
notify(method: string, params?: any): void { | ||
const message: NotificationMessage = { | ||
jsonrpc: '2.0', | ||
method, | ||
params | ||
} | ||
this.ls.handleMessage(message) | ||
} | ||
|
||
/** | ||
* Registers a handler for server notifications. | ||
* @param method LSP method name. | ||
* @param handler Function to handle the notification. | ||
*/ | ||
onNotification(method: string, handler: (params: any) => void): void { | ||
this.notificationHandlers.set(method, handler) | ||
} | ||
|
||
/** | ||
* Cleans up client resources. | ||
*/ | ||
dispose(): void { | ||
this.pendingRequests.clear() | ||
this.notificationHandlers.clear() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module github.com/goplus/builder/tools/spxls | ||
|
||
go 1.21.0 | ||
|
||
require ( | ||
github.com/goplus/gop v1.2.6 | ||
github.com/stretchr/testify v1.9.0 | ||
) | ||
|
||
require ( | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/goplus/gogen v1.15.2 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
github.com/qiniu/x v1.13.10 // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||
github.com/goplus/gogen v1.15.2 h1:Q6XaSx/Zi5tWnjfAziYsQI6Jv6MgODRpFtOYqNkiiqM= | ||
github.com/goplus/gogen v1.15.2/go.mod h1:92qEzVgv7y8JEFICWG9GvYI5IzfEkxYdsA1DbmnTkqk= | ||
github.com/goplus/gop v1.2.6 h1:kog3c5Js+8EopqmI4+CwueXsqibnBwYVt5q5N7juRVY= | ||
github.com/goplus/gop v1.2.6/go.mod h1:uREWbR1MrFaviZ4Mbx4ZCcAYDoqzO0iv1Qo6Np0Xx4E= | ||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||
github.com/qiniu/x v1.13.10 h1:J4Z3XugYzAq85SlyAfqlKVrbf05glMbAOh+QncsDQpE= | ||
github.com/qiniu/x v1.13.10/go.mod h1:INZ2TSWSJVWO/RuELQROERcslBwVgFG7MkTfEdaQz9E= | ||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/** | ||
* A lightweight Go+ language server for spx that runs in the browser using WebAssembly. | ||
*/ | ||
export interface Spxls { | ||
/** | ||
* Handles incoming LSP messages from the client. | ||
* | ||
* @param message - The message to process. Any required response will be sent via the messageReplier callback. | ||
*/ | ||
handleMessage(message: RequestMessage | NotificationMessage): void | ||
} | ||
|
||
/** | ||
* Creates a new instance of the spx language server. | ||
* | ||
* @param filesProvider - Function that provides access to the workspace files. This will be called whenever the | ||
* language server needs to access the file system. The returned map should have relative file | ||
* paths as keys and file contents values. | ||
* | ||
* @param messageReplier - Function called when the language server needs to reply to the client. The client should | ||
* handle these messages according to the LSP specification. | ||
*/ | ||
export function NewSpxls(filesProvider: () => Files, messageReplier: (message: ResponseMessage | NotificationMessage) => void): Spxls | ||
|
||
/** | ||
* A general message as defined by JSON-RPC. The language server protocol always uses “2.0” as the `jsonrpc` version. | ||
* | ||
* See https://microsoft.github.io/language-server-protocol/specifications/base/0.9/specification/#abstractMessage. | ||
*/ | ||
export interface Message { | ||
jsonrpc: string | ||
} | ||
|
||
/** | ||
* A request message to describe a request between the client and the server. Every processed request must send a | ||
* response back to the sender of the request. | ||
* | ||
* See https://microsoft.github.io/language-server-protocol/specifications/base/0.9/specification/#requestMessage. | ||
*/ | ||
export interface RequestMessage extends Message { | ||
/** | ||
* The request id. | ||
*/ | ||
id: number | ||
|
||
/** | ||
* The method to be invoked. | ||
*/ | ||
method: string | ||
|
||
/** | ||
* The method's params. | ||
*/ | ||
params?: any[] | object | ||
} | ||
|
||
/** | ||
* A Response Message sent as a result of a request. If a request doesn’t provide a result value the receiver of a | ||
* request still needs to return a response message to conform to the JSON-RPC specification. The result property of the | ||
* ResponseMessage should be set to `null` in this case to signal a successful request. | ||
* | ||
* See https://microsoft.github.io/language-server-protocol/specifications/base/0.9/specification/#responseMessage. | ||
*/ | ||
export interface ResponseMessage extends Message { | ||
/** | ||
* The request id. | ||
*/ | ||
id: number | ||
|
||
/** | ||
* The result of a request. This member is REQUIRED on success. | ||
* This member MUST NOT exist if there was an error invoking the method. | ||
*/ | ||
result?: string | number | boolean | any[] | object | null | ||
|
||
/** | ||
* The error object in case a request fails. | ||
*/ | ||
error?: ResponseError | ||
} | ||
|
||
export interface ResponseError { | ||
/** | ||
* A number indicating the error type that occurred. | ||
*/ | ||
code: number | ||
|
||
/** | ||
* A string providing a short description of the error. | ||
*/ | ||
message: string | ||
|
||
/** | ||
* A primitive or structured value that contains additional | ||
* information about the error. Can be omitted. | ||
*/ | ||
data?: string | number | boolean | any[] | object | null | ||
} | ||
|
||
/** | ||
* A notification message. A processed notification message must not send a response back. They work like events. | ||
* | ||
* See https://microsoft.github.io/language-server-protocol/specifications/base/0.9/specification/#notificationMessage. | ||
*/ | ||
export interface NotificationMessage extends Message { | ||
/** | ||
* The method to be invoked. | ||
*/ | ||
method: string | ||
|
||
/** | ||
* The notification's params. | ||
*/ | ||
params?: any[] | object | ||
} | ||
|
||
/** | ||
* Map from relative path to file content. | ||
*/ | ||
export type Files = { | ||
[path: string]: Uint8Array | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Copyright 2018 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Package jsonrpc2 is a minimal implementation of the JSON RPC 2 spec. | ||
// https://www.jsonrpc.org/specification | ||
// It is intended to be compatible with other implementations at the wire level. | ||
package jsonrpc2 |
Oops, something went wrong.