Skip to content

Cannot read property 'addCommand' of undefined #244

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

Closed
palkerecsenyi opened this issue Dec 30, 2020 · 3 comments
Closed

Cannot read property 'addCommand' of undefined #244

palkerecsenyi opened this issue Dec 30, 2020 · 3 comments

Comments

@palkerecsenyi
Copy link

I'm trying to set up a language server with gopls on the backend, and a React app on the frontend.

I get this error the moment the editor loads:

TypeError: Cannot read property 'addCommand' of undefined
    at MonacoCommands.registerCommand (monaco-commands.ts:12)
    at Object.registerCommand (vscode-api.ts:788)
    at ExecuteCommandFeature.register (client.js:1650)
    at ExecuteCommandFeature.initialize (client.js:1630)
    at MonacoLanguageClient.initializeFeatures (client.js:2462)
    at client.js:2147

Interestingly, it works perfectly fine with bash-language-server and pyls, so it's only Go that's causing this error.

I've established that it's probably not being caused by gopls itself, and is most likely an issue with monaco-languageclient: golang/go#43420

Here are all the relevant versions I'm using:

"monaco-editor-webpack-plugin": "^2.0.0",
"vscode-ws-jsonrpc": "^0.2.0",
"monaco-editor": "^0.21.2",
"monaco-languageclient": "^0.13.0",

Here's my code:

View frontend code
import {
    CloseAction,
    createConnection,
    ErrorAction,
    MonacoLanguageClient,
    MonacoServices,
} from 'monaco-languageclient';
import { listen, MessageConnection } from 'vscode-ws-jsonrpc';
import { editor } from 'monaco-editor';
import getEnvVariable from './getEnv';
import { TaskLanguage } from '../types';

function createLanguageClient(connection: MessageConnection) {
    return new MonacoLanguageClient({
        name: 'lsp.palcode.dev',
        clientOptions: {
            documentSelector: ['python', 'shell', 'go'],
            errorHandler: {
                error: () => ErrorAction.Continue,
                closed: () => CloseAction.DoNotRestart,
            }
        },
        connectionProvider: {
            get(errorHandler, closeHandler) {
                return Promise.resolve(createConnection(
                    connection,
                    errorHandler,
                    closeHandler,
                ));
            }
        }
    })
}

type DisposeFunction = () => void;
export default function connectToLanguageServer(
    language: TaskLanguage,
): undefined | DisposeFunction {
    const lspURL = getEnvVariable('LSP');
    if (!lspURL) {
        return;
    }

    try {
        MonacoServices.get();
    } catch (e) {
        MonacoServices.install(editor);
    }

    const webSocket = new WebSocket(lspURL + '/' + language);
    webSocket.onerror = () => {};
    let pingInterval: number;
    listen({
        webSocket,
        onConnection: (connection) => {
            const client = createLanguageClient(connection);
            const disposable = client.start();
            connection.onClose(() => disposable.dispose());
            connection.onError(() => {});

            pingInterval = window.setInterval(() => {
                webSocket.send('ping');
            }, 20000);
        },
    });

    return () => {
        try {
            webSocket.close();
            clearInterval(pingInterval);
        } catch (e) {}
    };
}
View backend code
const http = require("http");
const rpc = require('vscode-ws-jsonrpc');
const server = require('vscode-ws-jsonrpc/lib/server');
const lsp = require('vscode-languageserver');
const ws = require("ws");

const languages = [
    {
        command: 'pyls',
        args: [],
        port: 8080,
    },
    {
        command: 'bash-language-server',
        args: ['start'],
        port: 8081,
    },
    {
        command: 'gopls',
        args: ['serve'],
        port: 8082,
    }
];

function launch(socket, command, args) {
    const reader = new rpc.WebSocketMessageReader(socket);
    const writer = new rpc.WebSocketMessageWriter(socket);
    const socketConnection = server.createConnection(reader, writer, () => socket.dispose());
    const serverConnection = server.createServerProcess('JSON', command, args);
    server.forward(socketConnection, serverConnection, message => {
        if (rpc.isRequestMessage(message)) {
            if (message.method === lsp.InitializeRequest.type.method) {
                const initializeParams = message.params;
                initializeParams.processId = process.pid;
            }
        }
        return message;
    });
}

for (const language of languages) {
    const server = http.createServer().listen(language.port);
    const socket = new ws.Server({
        server,
        perMessageDeflate: false,
    });

    socket.on('connection', (client) => {
        const iWebSocket = {
            send: content => client.send(content),
            onMessage: cb => client.onmessage = event => {
                if (event.data !== 'ping') {
                    cb(event.data);
                }
            },
            onError: cb => client.onerror = event => {
                if ('message' in event) {
                    cb(event.message)
                }
            },
            onClose: cb => client.onclose = event => cb(event.code, event.reason),
            dispose: () => client.close(),
        };

        launch(iWebSocket, language.command, language.args);
    });

}

Thanks in advance!

@CGNonofr
Copy link
Collaborator

The last version of monaco-languageclient is not compatible with the last version of monaco-editor. You need to use the 'next' version (and the usage changed, see the changelog)

@palkerecsenyi
Copy link
Author

Great, that works!

FYI for anyone else struggling with this, I upgraded to "monaco-languageclient": "^0.13.1-next.4" and I also had to manually install monaco-editor-core, and (since I'm using TypeScript) I had to add a @ts-ignore:

// @ts-ignore
MonacoServices.install(require('monaco-editor-core/esm/vs/platform/commands/common/commands').CommandsRegistry);

Thanks for the quick reply @CGNonofr

@pawansingh00
Copy link

@palkerecsenyi Hi, Will you be able to share the code of this -- import { editor } from 'monaco-editor';
To be specific I just wantto understand what to configure here -- monaco.Uri.parse('inmemory://model.json') -- in the below code -

monaco.editor.create(document.getElementById("container")!, {
    model: monaco.editor.createModel(value, 'bash', **monaco.Uri.parse('inmemory://model.json')**),
    glyphMargin: true,
    lightbulb: {
        enabled: true
    }
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants