ebkalderon / tower-lsp

Language Server Protocol implementation written in Rust
Apache License 2.0
1.01k stars 55 forks source link

SyntaxError: Invalid or unexpected token #247

Closed tecosaur closed 2 years ago

tecosaur commented 3 years ago

Trying the example in the readme and https://github.com/ebkalderon/tower-lsp/blob/master/examples/stdio.rs I get the following in VSCode with what I think is a minimal extension:

/home/tec/Desktop/TEC/Projects/available/test-lsp/target/debug/lspserver:1
ELF
^

SyntaxError: Invalid or unexpected token
    at Module._compile (internal/modules/cjs/loader.js:942:18)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1051:10)
    at Module.load (internal/modules/cjs/loader.js:862:32)
    at Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.Module._load (electron/js2c/asar.js:769:28)
    at Function.Module.runMain (internal/modules/cjs/loader.js:1103:10)
    at internal/main/run_main_module.js:17:11
[Error - 1:22:17] Connection to server got closed. Server will not be restarted.

This is an excerpt from my my Cargo.toml

[dependencies]
env_logger = "0.8.2"
serde_json = "1.0.60"
tokio = { version = "0.2", features = ["full"] }
tower-lsp = "^0.13"

[[bin]]
name = "lspserver"
path = "src/server/main.rs"
ebkalderon commented 3 years ago

Thanks for reporting this issue, @tecosaur! Unfortunately, I can't really replicate the problem on my side, since I don't know the VSCode language client you are using. The example stdio server seems to work perfectly fine under Neovim with coc.nvim as the language client. Could you possibly provide more details?

Also, that text output reminds me a bit of an ELF executable header, e.g. 7f 45 4c 46 02 01 01, or ELF in ASCII, almost as if the executable file is being piped in as raw bytes rather than being executed. Are you sure the client is set up correctly on your end?

tecosaur commented 3 years ago

The VSCode extension side looked a bit like this (I've since deleted my test of the example, this is for the minimal LSP server I was trying to make):

import * as path from 'path';
import { workspace, ExtensionContext } from 'vscode';

import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from "vscode-languageclient/lib/node/main";

let client: LanguageClient;

export function activate(context: ExtensionContext) {
  let serverModule = context.asAbsolutePath(path.join('server', 'orgserver'));
  // The debug options for the server
  let debugOptions = { execArgv: [] };

  // If the extension is launched in debug mode then the debug server options are used
  // Otherwise the run options are used
  let serverOptions: ServerOptions = {
    run: { module: serverModule, transport: TransportKind.stdio },
    debug: {
      module: serverModule,
      transport: TransportKind.stdio,
      options: debugOptions
    }
  };

  // Options to control the language client
  let clientOptions: LanguageClientOptions = {
    // Register the server for plain text documents
    documentSelector: [{ scheme: 'file', language: 'org' }],
    synchronize: {
      // Notify the server about file changes to '.clientrc files contained in the workspace
      fileEvents: workspace.createFileSystemWatcher('*.org')
    }
  };

  // Create the language client and start the client.
  client = new LanguageClient(
    'orgLanguageServer',
    'Org Language Server',
    serverOptions,
    clientOptions
  );

  // Start the client. This will also launch the server
  client.start();
}

export function deactivate(): Thenable<void> | undefined {
  if (!client) {
    return undefined;
  }
  return client.stop();
}
ebkalderon commented 3 years ago

Hmm, it seems you were using vscode-languageclient incorrectly. I can get the stdio example to start fine in VSCode by making a few small changes to the code snippet above.

First, the initialization of the server is incorrect. It is being initialized as a NodeModule instead of a native Executable (see microsoft/vscode-languageserver-node source). The correct initialization should look like this instead:

let serverCommand = context.asAbsolutePath(path.join('server', 'stdio'));
let debugOptions = { env: { RUST_LOG: "trace" } };

// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
let serverOptions: ServerOptions = {
  run: { command: serverCommand },
  debug: {
    command: serverCommand,
    args: [],
    options: debugOptions
  }
};

This explains the �ELF��� seen in the error message you originally posted. As I suspected, the Node runtime was indeed attempting to parse the native Rust executable as though it was a piece of JavaScript, failing with a SyntaxError due to the invalid characters.

By initializing the server as an external command instead, the example stdio server starts up normally. While I was at it, I also chose to define RUST_LOG=trace in the debugOptions in order to get full verbose tower-lsp logs in the VSCode output console, which helped me debug this next issue I ran into.

Second, I wasn't able to successfully trigger the server for org files. I think this is because I needed to register the file association for the org language identifier (docs) with the extension, but since I was feeling lazy, I changed the client initialization to trigger on plaintext instead:

// Options to control the language client
let clientOptions: LanguageClientOptions = {
  // Register the server for plain text documents
  documentSelector: [{ scheme: 'file', language: 'plaintext' }],
  synchronize: {
    // Notify the server about file changes to '.clientrc files contained in the workspace
    fileEvents: workspace.createFileSystemWatcher('*.clientrc')
  }
};

After making these changes, I created a dummy VSCode language client project by following this official guide and pasting the corrected code into lsp-sample/client/src/extension.ts. I opened the lsp-sample directory in VSCode, hit Ctrl+Shift+B to build the project, and started the "Launch Client" debug profile.

After opening up a new file and starting to type, I confirmed that the tower-lsp example server works as expected, as per the screenshot below.

Screenshot from 2021-05-19 13-54-19

Fully fixed src/extension.ts ```typescript import * as path from 'path'; import { workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from "vscode-languageclient/lib/node/main"; let client: LanguageClient; export function activate(context: ExtensionContext) { let serverCommand = context.asAbsolutePath(path.join('server', 'stdio')); let debugOptions = { env: { RUST_LOG: "trace" } }; // If the extension is launched in debug mode then the debug server options are used // Otherwise the run options are used let serverOptions: ServerOptions = { run: { command: serverCommand }, debug: { command: serverCommand, args: [], options: debugOptions } }; // Options to control the language client let clientOptions: LanguageClientOptions = { // Register the server for plain text documents documentSelector: [{ scheme: 'file', language: 'plaintext' }], synchronize: { // Notify the server about file changes to '.clientrc files contained in the workspace fileEvents: workspace.createFileSystemWatcher('*.clientrc') } }; // Create the language client and start the client. client = new LanguageClient( 'orgLanguageServer', 'Org Language Server', serverOptions, clientOptions ); // Start the client. This will also launch the server client.start(); } export function deactivate(): Thenable | undefined { if (!client) { return undefined; } return client.stop(); } ```
ebkalderon commented 3 years ago

Ultimately, it seems like the root cause of the problem was with the language client, not with tower-lsp. Let me know if you can reproduce this, @tecosaur! If there are no other outstanding issues, then we can safely close this ticket.

tecosaur commented 3 years ago

Ah! Now I feel a little silly :sweat_smile:. Thank you very much for your help on this! I'm a bit frantic at the moment but I'll give this a test as soon as I get a chance (may be a bit) and let you know if everything works :)

ebkalderon commented 3 years ago

No worries! Whenever you happen to have a spare moment would be great. Feel free to ping me if there are any issues reproducing on your end or if something broke on the tower-lsp side in the process. :blush:

ebkalderon commented 2 years ago

This thread hasn't seen any activity in nearly a year, and I'm pretty sure the issue is not caused by tower-lsp itself. I'm closing this ticket for now, but please feel free to leave a reply and we can reopen it if something's amiss. Hope you managed to resolve the issue on your end! :heart:

tecosaur commented 2 years ago

Thanks for the well wishes. I actually haven't got around to trying this again yet (I spread myself faaar too thin), but I'll let you know if anything goes wrong 🙂

chen-bao-x commented 4 weeks ago

Hmm, it seems you were using vscode-languageclient incorrectly. I can get the stdio example to start fine in VSCode by making a few small changes to the code snippet above.

First, the initialization of the server is incorrect. It is being initialized as a NodeModule instead of a native Executable (see microsoft/vscode-languageserver-node source). The correct initialization should look like this instead:

let serverCommand = context.asAbsolutePath(path.join('server', 'stdio'));
let debugOptions = { env: { RUST_LOG: "trace" } };

// If the extension is launched in debug mode then the debug server options are used
// Otherwise the run options are used
let serverOptions: ServerOptions = {
  run: { command: serverCommand },
  debug: {
    command: serverCommand,
    args: [],
    options: debugOptions
  }
};

This explains the �ELF��� seen in the error message you originally posted. As I suspected, the Node runtime was indeed attempting to parse the native Rust executable as though it was a piece of JavaScript, failing with a SyntaxError due to the invalid characters.

By initializing the server as an external command instead, the example stdio server starts up normally. While I was at it, I also chose to define RUST_LOG=trace in the debugOptions in order to get full verbose tower-lsp logs in the VSCode output console, which helped me debug this next issue I ran into.

Second, I wasn't able to successfully trigger the server for org files. I think this is because I needed to register the file association for the org language identifier (docs) with the extension, but since I was feeling lazy, I changed the client initialization to trigger on plaintext instead:

// Options to control the language client
let clientOptions: LanguageClientOptions = {
  // Register the server for plain text documents
  documentSelector: [{ scheme: 'file', language: 'plaintext' }],
  synchronize: {
    // Notify the server about file changes to '.clientrc files contained in the workspace
    fileEvents: workspace.createFileSystemWatcher('*.clientrc')
  }
};

After making these changes, I created a dummy VSCode language client project by following this official guide and pasting the corrected code into lsp-sample/client/src/extension.ts. I opened the lsp-sample directory in VSCode, hit Ctrl+Shift+B to build the project, and started the "Launch Client" debug profile.

After opening up a new file and starting to type, I confirmed that the tower-lsp example server works as expected, as per the screenshot below.

Screenshot from 2021-05-19 13-54-19

Fully fixed src/extension.ts

Thanks a lot. this saved ma so much times.