Skip to content
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

julia language server with monaco editor client side #617

Open
vladtimss opened this issue Mar 21, 2024 · 7 comments
Open

julia language server with monaco editor client side #617

vladtimss opened this issue Mar 21, 2024 · 7 comments

Comments

@vladtimss
Copy link

Hi!

I need help to understand how connect my monaco-editor react component to julia language server which my team did on our server. I cant see sending message via json-rpc in my monaco. please help.

I use this example example trying to do the same. But i faced misunderstanding of all logic. Please help, help!

There are steps of my attempt repeat their solution.

1

i seems that i need first create languageClient

const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => { // here now with latest versions transport is MessageTransports type instead of MessageConnection and in connectionProvider get method expects this one
    return new MonacoLanguageClient({
        name: 'Julia Language Client',
        clientOptions: {
            documentSelector: ['julia'],
            errorHandler: {
                error: () => ({ action: ErrorAction.Continue }),
                closed: () => ({ action: CloseAction.DoNotRestart }),
            },
        },
        connectionProvider: {
            get: () => Promise.resolve(transports), // here now expects MessageTransports instead MessageConnection
        },
    });
};

2

Then seems i should create WS and start client. and i try to do this one

const createWebSocketAndStartClient = () => {
    const url = normalizeUrl('wss://custom/julia-language-server');
    const webSocket = new WebSocket(url);

    listen({
        webSocket: webSocket,
        onConnection: (connection) => {
            // because of connectionProvider in get expects not connection now but transports i try to do this one
            const socket = toSocket(webSocket);

            const messageReader = new WebSocketMessageReader(socket);
            const messageWriter = new WebSocketMessageWriter(socket);

            const client = createLanguageClient({ reader: messageReader, writer: messageWriter });

            client
                .start()
                .then(() => console.log('client started'))
                .catch((e) => console.log('client starting failed', e));

            connection.onClose(() => connection.dispose());
            connection.onError((e) => console.log('Error', e));
        },
    });
};

3

i think i need to active monaco services in my monaco wrapper component and then after activation i should run createWebSocketAndStartClient i do it like this and i see also on this example

import { initialize as initializeMonacoServices } from 'vscode/services';

initializeMonacoServices({})
            .then(() => {
                createWebSocketAndStartClient();
            })
            .catch((e) => console.log('CATCH ERROR', e));

const LspMonaco = () => {
    const [value, setValue] = useState('');

    const handleMount = (editor: editor.IStandaloneCodeEditor) => {
        editor.onDidChangeModelContent(debounce((e) => {
            if (editor) {
                console.log(e);
                const myval = editor.getValue();
                setValue(myval);
            }
        }, 100));
    };

    return (
        <EditorContainer>
            <MonacoEditorWrap value={value} onMount={handleMount} language={'julia'}/>
        </EditorContainer>
    );
};

Thanks for any help, advices! Please help me.

@vladtimss vladtimss changed the title julia language server with monaco editor julia language server with monaco editor client side Mar 21, 2024
@kaisalmen
Copy link
Collaborator

Hi @vladtimss we have a complete solution for that now here:
https://github.com/TypeFox/monaco-languageclient/tree/main/packages/wrapper-react

See how it can be used with the python language server:
https://github.com/TypeFox/monaco-languageclient/tree/main/packages/examples/src/python/client

@vladtimss
Copy link
Author

Hi @vladtimss we have a complete solution for that now here: https://github.com/TypeFox/monaco-languageclient/tree/main/packages/wrapper-react

See how it can be used with the python language server: https://github.com/TypeFox/monaco-languageclient/tree/main/packages/examples/src/python/client

thanks for your answer

i tried many time to install and launch monaco-languageclient to play with examples, but in was not successfully because of errors after click on any example on html

also i tried detailed learn your attached links and i faced with problems to launch simple new projects with @typefox/monaco-editor-react,

Снимок экрана 2024-03-22 в 11 51 37
Снимок экрана 2024-03-22 в 11 51 45

i also attached my realization and i dont understand how start send message from monaco editor to soccket and get response via json-rpc

please could you give some advices? or see my components to fix it?

my repo here - https://github.com/vladtimss/monaco-julia-language-server/tree/main/my-app

@kaisalmen
Copy link
Collaborator

kaisalmen commented Mar 22, 2024

@vladtimss you have to change the configuration. You just copy-pasted the python config. This can't work. You need changes it according you julia language server needs (e.g. languageId: 'julia'). This defines how the wrapper connects to the languageclient:

                $type: 'WebSocket',
                host: 'localhost',
                port: 30001,
                path: 'pyright',

The whole configuration is typed.

@vladtimss
Copy link
Author

@vladtimss you have to change the configuration. You just copy-pasted the python config. This can't work. You need changes it according you julia language server needs (e.g. languageId: 'julia'). This defines how the wrapper connects to the languageclient:

                $type: 'WebSocket',
                host: 'localhost',
                port: 30001,
                path: 'pyright',

The whole configuration is typed.

thank you @kaisalmen. Now i am trying to solve my troubles. I hope soon i may come back with news. Could you tell me. Does i understand right, that this component https://www.npmjs.com/package/@typefox/monaco-editor-react create monaco-editor from node_module and does not use cdn?

@vladtimss
Copy link
Author

Dear @kaisalmen thank for your help and your work!

I've had some success. I was able to connect to the socket and even see the component communicating with the server over the socket via the json-rpc. Also this component can show LSP suggestions. It is great. But i faced with some errors, and has new problems. Please, could you help and give some advices.

  1. First i have the error: editorAppBase.ts:134 Uncaught (in promise) Error: You cannot update the editor model, because the regular editor is not configured.
Снимок экрана 2024-04-05 в 13 44 16
  1. Second problem relates with worker - Not allowed to load local resource: file:///Users/.../node_modules/monaco-editor-wrapper/dist/workers/editorWorker-es.js
Снимок экрана 2024-04-05 в 14 17 44
  1. I don't understand how activate default highlight for julia language.

This my code

import React, { useEffect, useRef } from 'react';
import { EditorWrapper } from '@/modules/lsp-editor/lsp-editor.styles';

import { MonacoEditorProps, MonacoEditorReactComp } from '@typefox/monaco-editor-react';
import { UserConfig } from 'monaco-editor-wrapper';
import * as vscode from 'vscode';
import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory';

const config: UserConfig = {
    languageClientConfig: {
        options: {
            name: 'Julia Language Server Example',
            $type: 'WebSocketUrl',
            url: '.........ijulia/lsp',
        },
        clientOptions: {
            documentSelector: ['julia'],
            workspaceFolder: {
                index: 0,
                name: 'workspace',
                uri: vscode.Uri.parse('/workspace/'),
            },
        },
    },
    wrapperConfig: {
        serviceConfig: {
            userServices: {},
            debugLogging: true,
        },
        editorAppConfig: {
            $type: 'extended',
            languageId: 'julia',
            codeUri: '/workspace/julia.jl',
            extensions: [
                {
                    config: {
                        name: 'julia-client',
                        publisher: 'monaco-languageclient-project',
                        version: '1.0.0',
                        engines: {
                            vscode: '^1.85.0',
                        },
                        contributes: {
                            languages: [
                                {
                                    id: 'julia',
                                    extensions: ['.jl'],
                                    aliases: ['julia'],
                                    mimetypes: ['application/julia'],
                                },
                            ],
                        },
                    },
                },
            ],
            editorOptions: {},
            useDiffEditor: false,
        },
    },
};

const LspEditor = () => {
    const editorRef = useRef<MonacoEditorReactComp<MonacoEditorProps>>(null);

    useWorkerFactory({ basePath: '../../../../node_modules' }); // Perhaps this is the place of troubles with worker?

    const onTextChanged = (text: string, isDirty: boolean) => {
        console.log(`Dirty? ${isDirty} Content: ${text}`);
    };

    useEffect(() => {
        if (editorRef.current) {
            console.log(editorRef.current);
        }
    }, []);

    return (
        <EditorWrapper>
            <MonacoEditorReactComp
                ref={editorRef}
                onTextChanged={onTextChanged}
                userConfig={config}
                style={{ height: '90vh', width: '100%' }}
                onLoad={() => console.log('Loaded')}
                onError={(e) => console.error(e)}
            />
        </EditorWrapper>
    );
};

export { LspEditor };

@kaisalmen
Copy link
Collaborator

Hi @vladtimss The first issue seems to be that you use the editor (via updateModel) before it is initialized.

You can freely overwrite the worker loading see here. In you case you only need the editor worker, so it will be very similar to what is done in the linked example at line 18.

For syntac hghligthing you can use the npm package @codingame/monaco-vscode-python-default-extension and just import like this: import '@codingame/monaco-vscode-python-default-extension';

@vladtimss
Copy link
Author

here

oh, @kaisalmen I'm desperate(((

I'm doing my best but

1)i cant solve the problem with updateModel. my first problem here - #617 (comment)
2) i installed and did import '@codingame/monaco-vscode-julia-default-extension' to activate syntax highlight but seems it doesn't load script. it doesn't work.

i tried to do the same as this example but i have this problems yet.

By the way, many thanks im succeeded load worker. but not sure that right.

please if you can give any advice, help. i will be glad you!

this my component with config and react. and i set also questions

import React, { useEffect, useRef } from 'react';
import * as vscode from 'vscode';
import '@codingame/monaco-vscode-julia-default-extension'; // HERE I SET EXTENSION BUT IT DOES NOT WORK
import { EditorWrapper } from '@/modules/script-editor/components/lsp-editor/lsp-editor.styles';
import { MonacoEditorProps, MonacoEditorReactComp } from '@typefox/monaco-editor-react';
import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory';
import { UserConfig } from 'monaco-editor-wrapper';

export const configureMonacoWorkers = () => {
    // override the worker factory with your own direct definition
    // eslint-disable-next-line react-hooks/rules-of-hooks
    useWorkerFactory({
        ignoreMapping: true,
        workerLoaders: {
            editorWorkerService: () =>
                new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), { // DOES IT RIGHT WAY? DOES NEED ADD HERE MORE MODULES, FOR EXAMPLE FOR JSON? HOW?
                    type: 'module',
                }),
        },
    });
};

export const createUserConfig = (code: string): UserConfig => {
    return {
        languageClientConfig: {
            options: {
                name: 'Julia Language Server Example',
                $type: 'WebSocketUrl',
                url: 'wss:///ijulia/lsp',
            },
            clientOptions: {
                documentSelector: ['julia'],
                workspaceFolder: {
                    index: 0,
                    name: 'workspace',
                    uri: vscode.Uri.parse('/workspace/'), // I HAVE DOUBTS ABOUT THIS PART. COULD YOU EXPLAIN WHY THIS NEED?
                },
            },
        },
        wrapperConfig: {
            editorAppConfig: {
                $type: 'extended',
                languageId: 'julia',
                codeUri: '/workspace/julia.jl', //  I HAVE DOUBTS ABOUT THIS PART. COULD YOU EXPLAIN WHY THIS NEED?
                extensions: [
                    {
                        config: {
                            name: 'julia-client',
                            publisher: 'monaco-languageclient-project',
                            version: '1.0.0',
                            engines: {
                                vscode: '^1.85.0',
                            },
                            contributes: {
                                languages: [
                                    {
                                        id: 'julia',
                                        extensions: ['.jl'],
                                        aliases: ['julia'],
                                        mimetypes: ['application/julia'],
                                    },
                                ],
                            },
                        },
                    },
                ],
                code: code,
                editorOptions: {},
                useDiffEditor: false,
            },
        },
    };
};

configureMonacoWorkers(); // THIS I RUN WORKER

const LspEditor = () => { // THIS COMPONENT I USE IN MY PROJECT AS COMMON REACT COMPONENT. WHATS WRONG?
    const editorRef = useRef<MonacoEditorReactComp<MonacoEditorProps>>(null);

    const onTextChanged = (text: string, isDirty: boolean) => {
        console.log(`Dirty? ${isDirty} Content: ${text}`);
    };

    useEffect(() => {
        if (editorRef.current) {
            console.log(editorRef.current);
        }
    }, []);

    return (
        <EditorWrapper>
            <MonacoEditorReactComp
                ref={editorRef}
                onTextChanged={onTextChanged}
                userConfig={createUserConfig('using')}
                style={{ height: '90vh', width: '100%' }}
                onLoad={() => console.log('Loaded')}
                onError={(e) => console.error(e)}
            />
        </EditorWrapper>
    );
};

export { LspEditor };

thats i have in devtools
Снимок экрана 2024-04-18 в 11 04 13

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

2 participants