-
Notifications
You must be signed in to change notification settings - Fork 62
Support experimental language server #1183
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
aspeddro
wants to merge
8
commits into
rescript-lang:master
Choose a base branch
from
aspeddro:experimental-lsp
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+126
−32
Draft
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a8ca9d8
Support experimental language server
aspeddro 8232a6d
format
aspeddro 57d9c7a
Update extension.ts
aspeddro 8f7860b
remove option
aspeddro 95b6153
add middleware workspace.configuration
aspeddro c209e41
update
aspeddro 81a1869
update
aspeddro bc2d350
format files
aspeddro File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 |
|---|---|---|
| @@ -1,4 +1,5 @@ | ||
| import * as path from "path"; | ||
| import * as fs from "fs"; | ||
| import { | ||
| workspace, | ||
| ExtensionContext, | ||
|
|
@@ -23,6 +24,7 @@ import { | |
| ServerOptions, | ||
| State, | ||
| TransportKind, | ||
| DidChangeConfigurationNotification, | ||
| } from "vscode-languageclient/node"; | ||
|
|
||
| import * as customCommands from "./commands"; | ||
|
|
@@ -84,12 +86,84 @@ let client: LanguageClient; | |
| // } | ||
| // }); | ||
|
|
||
| function getRescriptExecutablePath(): string | undefined { | ||
| const workspaceFolders = workspace.workspaceFolders; | ||
|
|
||
| if (!workspaceFolders || workspaceFolders.length === 0) { | ||
| return undefined; | ||
| } | ||
|
|
||
| const command = path.join("node_modules", ".bin", "rescript"); | ||
|
|
||
| for (const ws of workspaceFolders) { | ||
| const commandPath = path.resolve(ws.uri.fsPath, command); | ||
| if (fs.existsSync(commandPath)) { | ||
| return commandPath; | ||
| } | ||
| } | ||
| return undefined; | ||
| } | ||
|
|
||
| export function activate(context: ExtensionContext) { | ||
| let outputChannel = window.createOutputChannel( | ||
| "ReScript Language Server", | ||
| "rescript", | ||
| ); | ||
|
|
||
| const useExperimentalServer = workspace | ||
| .getConfiguration("rescript") | ||
| .get<boolean>("useExperimentalServer", false); | ||
|
|
||
| function createExperimentalLanguageClient() { | ||
| const binaryPath = getRescriptExecutablePath(); | ||
|
|
||
| if (!binaryPath) { | ||
| const message = "Could not find the ReScript binary in the workspace."; | ||
| window.showErrorMessage(message); | ||
| throw new Error(message); | ||
| } | ||
|
|
||
| let serverOptions: ServerOptions = { | ||
| run: { | ||
| command: binaryPath, | ||
| args: ["lsp"], | ||
| transport: TransportKind.stdio, | ||
| }, | ||
| debug: { | ||
| command: binaryPath, | ||
| args: ["lsp"], | ||
| transport: TransportKind.stdio, | ||
| }, | ||
| }; | ||
|
|
||
| // Options to control the language client | ||
| let clientOptions: LanguageClientOptions = { | ||
| documentSelector: [{ scheme: "file", language: "rescript" }], | ||
| outputChannel, | ||
| markdown: { | ||
| isTrusted: true, | ||
| }, | ||
| middleware: { | ||
| workspace: { | ||
| configuration: async (_params, _token, _next) => { | ||
| // For the experimental server, we don't want to send the full configuration | ||
| // We send only setting inside rescript.settings, i.e, server settings | ||
| return [workspace.getConfiguration("rescript.settings")]; | ||
| }, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| const client = new LanguageClient( | ||
| "ReScriptLSP", | ||
| "Experimental ReScript Language Server", | ||
| serverOptions, | ||
| clientOptions, | ||
| ); | ||
|
|
||
| return client; | ||
| } | ||
|
|
||
| function createLanguageClient() { | ||
| // The server is implemented in node | ||
| let serverModule = context.asAbsolutePath( | ||
|
|
@@ -143,36 +217,42 @@ export function activate(context: ExtensionContext) { | |
| clientOptions, | ||
| ); | ||
|
|
||
| // This sets up a listener that, if we're in code analysis mode, triggers | ||
| // code analysis as the LS server reports that ReScript compilation has | ||
| // finished. This is needed because code analysis must wait until | ||
| // compilation has finished, and the most reliable source for that is the LS | ||
| // server, that already keeps track of when the compiler finishes in order to | ||
| // other provide fresh diagnostics. | ||
| context.subscriptions.push( | ||
| client.onDidChangeState(({ newState }) => { | ||
| if (newState === State.Running) { | ||
| context.subscriptions.push( | ||
| client.onNotification("rescript/compilationFinished", () => { | ||
| if (inCodeAnalysisState.active === true) { | ||
| customCommands.codeAnalysisWithReanalyze( | ||
| diagnosticsCollection, | ||
| diagnosticsResultCodeActions, | ||
| outputChannel, | ||
| codeAnalysisRunningStatusBarItem, | ||
| ); | ||
| } | ||
| }), | ||
| ); | ||
| } | ||
| }), | ||
| ); | ||
|
|
||
| return client; | ||
| } | ||
|
|
||
| function createClient() { | ||
| return useExperimentalServer != null | ||
| ? createExperimentalLanguageClient() | ||
| : createLanguageClient(); | ||
| } | ||
|
|
||
| // Create the language client and start the client. | ||
| client = createLanguageClient(); | ||
| client = createClient(); | ||
|
|
||
| // This sets up a listener that, if we're in code analysis mode, triggers | ||
| // code analysis as the LS server reports that ReScript compilation has | ||
| // finished. This is needed because code analysis must wait until | ||
| // compilation has finished, and the most reliable source for that is the LS | ||
| // server, that already keeps track of when the compiler finishes in order to | ||
| // other provide fresh diagnostics. | ||
| context.subscriptions.push( | ||
| client.onDidChangeState(({ newState }) => { | ||
| if (newState === State.Running) { | ||
| context.subscriptions.push( | ||
| client.onNotification("rescript/compilationFinished", () => { | ||
| if (inCodeAnalysisState.active === true) { | ||
| customCommands.codeAnalysisWithReanalyze( | ||
| diagnosticsCollection, | ||
| diagnosticsResultCodeActions, | ||
| outputChannel, | ||
| codeAnalysisRunningStatusBarItem, | ||
| ); | ||
| } | ||
| }), | ||
| ); | ||
| } | ||
| }), | ||
| ); | ||
|
|
||
| // Create a custom diagnostics collection, for cases where we want to report | ||
| // diagnostics programatically from inside of the extension. The reason this | ||
|
|
@@ -263,7 +343,7 @@ export function activate(context: ExtensionContext) { | |
| // Compact success display: project label plus a green check emoji | ||
| compilationStatusBarItem.text = `$(check) ReScript: Ok`; | ||
| compilationStatusBarItem.backgroundColor = undefined; | ||
| compilationStatusBarItem.color = null; | ||
| compilationStatusBarItem.color = undefined; | ||
| compilationStatusBarItem.command = undefined; | ||
| const projects = successes.map((e) => e.project).join(", "); | ||
| compilationStatusBarItem.tooltip = projects | ||
|
|
@@ -333,7 +413,7 @@ export function activate(context: ExtensionContext) { | |
| const removeAllCodeAction = new CodeAction("Remove all unused in file"); | ||
| const edit = new WorkspaceEdit(); | ||
| allRemoveActionEdits.forEach((subEdit) => { | ||
| subEdit.codeAction.edit.entries().forEach(([uri, [textEdit]]) => { | ||
| subEdit.codeAction.edit?.entries().forEach(([uri, [textEdit]]) => { | ||
| edit.replace(uri, textEdit.range, textEdit.newText); | ||
| }); | ||
| }); | ||
|
|
@@ -361,7 +441,7 @@ export function activate(context: ExtensionContext) { | |
|
|
||
| const document = editor.document; | ||
| const diagnostics = diagnosticsCollection.get(document.uri); | ||
| const newDiagnostics = diagnostics.filter((d) => d !== diagnostic); | ||
| const newDiagnostics = diagnostics?.filter((d) => d !== diagnostic); | ||
| diagnosticsCollection.set(document.uri, newDiagnostics); | ||
| }, | ||
| ); | ||
|
|
@@ -513,7 +593,7 @@ export function activate(context: ExtensionContext) { | |
|
|
||
| commands.registerCommand("rescript-vscode.restart_language_server", () => { | ||
| client.stop().then(() => { | ||
| client = createLanguageClient(); | ||
| client = createClient(); | ||
| client.start(); | ||
| }); | ||
| }); | ||
|
|
@@ -549,7 +629,9 @@ export function activate(context: ExtensionContext) { | |
| // Send a general message that configuration has updated. Clients | ||
| // interested can then pull the new configuration as they see fit. | ||
| client | ||
| .sendNotification("workspace/didChangeConfiguration") | ||
| .sendNotification(DidChangeConfigurationNotification.type, { | ||
| settings: null, | ||
| }) | ||
|
Comment on lines
+632
to
+634
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The notification This was breaking the experimental server. |
||
| .catch((err) => { | ||
| window.showErrorMessage(String(err)); | ||
| }); | ||
|
|
||
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't use middleware, the server will retrieve all fields within
rescript, but we only wantrescript.settings. Otherwise, the server fails to parse the received JSON.