summaryrefslogtreecommitdiff
path: root/extension/src/activation/languageClient.ts
blob: 5b0bd612c510066d3354d664186d23f3149a8ccf (plain)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
 * If we wanted to run or use another language server from our extension, this is how we would do it.
 */

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

import {
  DefinitionParams,
  LanguageClient,
  LanguageClientOptions,
  ServerOptions,
  StateChangeEvent,
  TransportKind,
  State,
} from "vscode-languageclient/node";
import { getExtensionUri } from "../util/vscode";

let client: LanguageClient;

export async function startLanguageClient(context: ExtensionContext) {
  let pythonLS = startPythonLanguageServer(context);
  pythonLS.start();
}

export async function makeRequest(method: string, param: any): Promise<any> {
  if (!client) {
    return;
  } else if (client.state === State.Starting) {
    return new Promise((resolve, reject) => {
      let stateListener = client.onDidChangeState((e: StateChangeEvent) => {
        if (e.newState === State.Running) {
          stateListener.dispose();
          resolve(client.sendRequest(method, param));
        } else if (e.newState === State.Stopped) {
          stateListener.dispose();
          reject(new Error("Language server stopped unexpectedly"));
        }
      });
    });
  } else {
    return client.sendRequest(method, param);
  }
}

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

function startPythonLanguageServer(context: ExtensionContext): LanguageClient {
  let extensionPath = getExtensionUri().fsPath;
  const command = `cd ${path.join(
    extensionPath,
    "scripts"
  )} && source env/bin/activate.fish && python -m pyls`;
  const serverOptions: ServerOptions = {
    command: command,
    args: ["-vv"],
  };
  const clientOptions: LanguageClientOptions = {
    documentSelector: ["python"],
    synchronize: {
      configurationSection: "pyls",
    },
  };
  return new LanguageClient(command, serverOptions, clientOptions);
}

async function startPylance(context: ExtensionContext) {
  let pylance = extensions.getExtension("ms-python.vscode-pylance");
  await pylance?.activate();
  if (!pylance) {
    return;
  }
  let { path: lsPath } = await pylance.exports.languageServerFolder();

  // The server is implemented in node
  let serverModule = context.asAbsolutePath(lsPath);
  // The debug options for the server
  // --inspect=6009: runs the server in Node's Inspector mode so VS Code can attach to the server for debugging
  let debugOptions = { execArgv: ["--nolazy", "--inspect=6009"] };

  // 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.ipc },
    debug: {
      module: serverModule,
      transport: TransportKind.ipc,
      options: debugOptions,
    },
  };

  // Options to control the language client
  let clientOptions: LanguageClientOptions = {
    // Register the server for plain text documents
    documentSelector: [{ scheme: "file", language: "python" }],
    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(
    "languageServerExample",
    "Language Server Example",
    serverOptions,
    clientOptions
  );
  return client;
}