Skip to content

Commit

Permalink
Support python language server
Browse files Browse the repository at this point in the history
  • Loading branch information
ManuelSilva-Movai committed Feb 3, 2022
1 parent 43227c3 commit d3f3037
Show file tree
Hide file tree
Showing 9 changed files with 12,571 additions and 11,326 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
# Storybook static
storybook-static

# Logs
logs
*.log
Expand Down
39 changes: 28 additions & 11 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
// const path = require("path");
const path = require('path');
const { merge } = require('webpack-merge');
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');

module.exports = {
stories: ["../src/stories/**/*.stories.[tj]s"],
stories: ['../src/stories/**/*.stories.[tj]s'],
// addons: ["@storybook/addon-knobs/register"],
// webpackFinal: async (config, { configType }) => {
// config.module.rules.push({
// test: /\.css$/,
// use: ["style-loader", "css-loader", "sass-loader"],
// include: path.resolve(__dirname, "../src"),
// });

// return config;
// },
webpackFinal: async (config) => {
const finalConfig = merge(config, {
resolve: {
alias: {
vscode: require.resolve(
'@codingame/monaco-languageclient/lib/vscode-compatibility'
),
},
extensions: ['.js', '.json', '.ttf'],
},
});

finalConfig.entry = {
main: finalConfig.entry,
'editor.worker': 'monaco-editor-core/esm/vs/editor/editor.worker.js',
};

finalConfig.output.filename = '[name].bundle.js';

return finalConfig;
},
core: {
builder: 'webpack5',
},
};
23,470 changes: 12,307 additions & 11,163 deletions package-lock.json

Large diffs are not rendered by default.

19 changes: 15 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,18 @@
},
"homepage": "https://github.com/MOV-AI/frontend-npm-lib-code-editor#readme",
"dependencies": {
"monaco-editor": "^0.28.1",
"monaco-editor-webpack-plugin": "^4.2.0",
"@codingame/monaco-jsonrpc": "^0.3.1",
"@codingame/monaco-languageclient": "^0.17.3",
"monaco-editor": "^0.31.1",
"monaco-editor-core": "^0.31.1",
"normalize-url": "^7.0.3",
"prop-types": "^15.7.2",
"react": "^16.14.0",
"react-dom": "^16.14.0"
"react-dom": "^16.14.0",
"reconnecting-websocket": "^4.4.0",
"vscode-json-languageservice": "^4.2.0",
"vscode-languageserver": "^7.0.0",
"vscode-uri": "^3.0.3"
},
"devDependencies": {
"@babel/core": "^7.15.5",
Expand All @@ -51,22 +58,26 @@
"@storybook/addon-knobs": "^6.3.1",
"@storybook/addons": "^6.3.8",
"@storybook/api": "^6.3.8",
"@storybook/builder-webpack5": "^6.4.18",
"@storybook/components": "^6.3.8",
"@storybook/core-events": "^6.3.8",
"@storybook/manager-webpack5": "^6.4.18",
"@storybook/react": "^6.3.8",
"@storybook/theming": "^6.3.8",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^4.0.0",
"css-loader": "^6.3.0",
"monaco-css": "^3.8.1",
"node-sass": "^6.0.1",
"monaco-editor-webpack-plugin": "^7.0.1",
"node-sass": "^7.0.1",
"path": "^0.12.7",
"sass-loader": "^12.1.0",
"style-loader": "^3.3.0",
"typescript": "^4.4.3",
"webpack": "^5.54.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.3.0",
"webpack-merge": "^5.8.0",
"webpack-node-externals": "^3.0.0"
}
}
65 changes: 22 additions & 43 deletions src/components/MonacoCodeEditor/MonacoCodeEditor.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
import React, { createRef, useRef } from "react";
import PropTypes from "prop-types";
import "./MonacoCodeEditor.css";
import * as monaco from "monaco-editor";
import { MenuRegistry } from "monaco-editor/esm/vs/platform/actions/common/actions";

/**
* Create editor
* @param {{element: HTMLElement, value: string, language: string, theme: string, options: object, disableMinimap: boolean}} props
* Props to be used to compose editor
* @returns Monaco Editor object
*/
const createEditor = (props) => {
const { element, value, language, theme, options, disableMinimap } = props;
return monaco.editor.create(element, {
value: value,
language: language,
theme: theme,
"semanticHighlighting.enabled": true,
selectOnLineNumbers: true,
autoIndent: "full",
lineNumbers: disableMinimap ? "off" : "on",
overviewRulerBorder: !disableMinimap,
overviewRulerLanes: disableMinimap ? 0 : 3,
minimap: {
enabled: !disableMinimap,
},
...options,
});
};
import React, { createRef, useRef } from 'react';
import PropTypes from 'prop-types';
import './MonacoCodeEditor.css';
//import * as monaco from 'monaco-editor';
import { MenuRegistry } from 'monaco-editor/esm/vs/platform/actions/common/actions';
import * as monaco from 'monaco-editor-core';
import useMonacoEditor from './hooks/useMonacoEditorCore';

const MonacoCodeEditor = React.forwardRef((props, ref) => {
// Refs
Expand All @@ -45,6 +22,9 @@ const MonacoCodeEditor = React.forwardRef((props, ref) => {
const onChange = props.onChange;
const disableMinimap = props.disableMinimap;

// Hooks
const { createEditor } = useMonacoEditor();

//========================================================================================
/* *
* React callbacks *
Expand Down Expand Up @@ -138,27 +118,27 @@ const MonacoCodeEditor = React.forwardRef((props, ref) => {
* On update save function
*/
React.useEffect(() => {
const saveAction = editor.current.getAction("save");
const saveAction = editor.current.getAction('save');
// Remove previous save action (if existing)
if (saveAction) {
const menuItems = MenuRegistry._menuItems;
const contextMenuEntry = [...menuItems].find(
(entry) => entry[0]._debugName == "EditorContext"
(entry) => entry[0]._debugName == 'EditorContext'
);
const contextMenuLinks = contextMenuEntry[1];
removeAction(contextMenuLinks, saveAction.id);
}
// Add new save action
if (onSave)
editor.current.addAction({
id: "save",
label: "Save",
id: 'save',
label: 'Save',
keybindings: [
monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S),
],
precondition: null,
keybindingContext: null,
contextMenuGroupId: "navigation",
contextMenuGroupId: 'navigation',
contextMenuOrder: 1,
run: onSave,
});
Expand All @@ -172,10 +152,9 @@ const MonacoCodeEditor = React.forwardRef((props, ref) => {

return (
<div
style={{ ...style, maxHeight: "100%", maxWidth: "100%" }}
className="mov-ai-monaco-code-editor"
ref={editorRef}
></div>
style={{ ...style, maxHeight: '100%', maxWidth: '100%' }}
className='mov-ai-monaco-code-editor'
ref={editorRef}></div>
);
});

Expand All @@ -191,15 +170,15 @@ MonacoCodeEditor.propTypes = {
};

MonacoCodeEditor.defaultProps = {
value: "",
theme: "vs-dark",
language: "python",
value: '',
theme: 'vs-dark',
language: 'python',
options: {},
actions: [],
onChange: () => {},
onLoad: () => {},
disableMinimap: false,
style: { display: "flex", flexDirection: "column", flexGrow: 1 },
style: { display: 'flex', flexDirection: 'column', flexGrow: 1 },
};

export default MonacoCodeEditor;
34 changes: 34 additions & 0 deletions src/components/MonacoCodeEditor/hooks/useMonacoEditor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react';
import * as monaco from 'monaco-editor';

const useMonacoEditor = () => {
/**
* Create editor
* @param {{element: HTMLElement, value: string, language: string, theme: string, options: object, disableMinimap: boolean}} props
* Props to be used to compose editor
* @returns Monaco Editor object
*/
const createEditor = (props) => {
const { element, value, language, theme, options, disableMinimap } = props;

return monaco.editor.create(element, {
value: value,
language: language,
theme: theme,
'semanticHighlighting.enabled': true,
selectOnLineNumbers: true,
autoIndent: 'full',
lineNumbers: disableMinimap ? 'off' : 'on',
overviewRulerBorder: !disableMinimap,
overviewRulerLanes: disableMinimap ? 0 : 3,
minimap: {
enabled: !disableMinimap,
},
...options,
});
};

return { createEditor };
};

export default useMonacoEditor;
121 changes: 121 additions & 0 deletions src/components/MonacoCodeEditor/hooks/useMonacoEditorCore.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import React, { useCallback, useEffect } from 'react';
import { listen } from '@codingame/monaco-jsonrpc';
import * as monaco from 'monaco-editor';
import {
MonacoLanguageClient,
CloseAction,
ErrorAction,
MonacoServices,
createConnection,
} from '@codingame/monaco-languageclient';
import normalizeUrl from 'normalize-url';
import ReconnectingWebSocket from 'reconnecting-websocket';

const SERVER_URL = '/sampleServer';

self.MonacoEnvironment = {
getWorkerUrl: function (moduleId, label) {
if (label === 'json') {
return './json.worker.bundle.js';
}
if (label === 'css' || label === 'scss' || label === 'less') {
return './css.worker.bundle.js';
}
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return './html.worker.bundle.js';
}
if (label === 'typescript' || label === 'javascript') {
return './ts.worker.bundle.js';
}
return './editor.worker.bundle.js';
},
};

const createUrl = (path) => {
const protocol = location.protocol === 'https:' ? 'wss' : 'ws';
return normalizeUrl(`ws://localhost:3000/sampleServer`);
// return normalizeUrl(
// `${protocol}://${location.host}${location.pathname}${path}`
// );
};

const createWebSocket = (url) => {
const socketOptions = {
maxReconnectionDelay: 10000,
minReconnectionDelay: 1000,
reconnectionDelayGrowFactor: 1.3,
connectionTimeout: 10000,
maxRetries: Infinity,
debug: false,
};
return new ReconnectingWebSocket(url, [], socketOptions);
};

const useMonacoEditor = () => {
const createLanguageClient = useCallback((connection) => {
return new MonacoLanguageClient({
name: 'Sample Language Client',
clientOptions: {
// use a language id as a document selector
documentSelector: ['python'],
// disable the default error handler
errorHandler: {
error: () => ErrorAction.Continue,
closed: () => CloseAction.DoNotRestart,
},
},
// create a language client connection from the JSON RPC connection on demand
connectionProvider: {
get: (errorHandler, closeHandler) => {
return Promise.resolve(
createConnection(connection, errorHandler, closeHandler)
);
},
},
});
}, []);

/**
* Create editor
* @param {{element: HTMLElement, value: string, language: string, theme: string, options: object, disableMinimap: boolean}} props
* Props to be used to compose editor
* @returns Monaco Editor object
*/
const createEditor = useCallback((props) => {
const { element, value, language, theme, options, disableMinimap } = props;

MonacoServices.install(monaco);
const webSocket = createWebSocket(createUrl(SERVER_URL));

// listen when the web socket is opened
listen({
webSocket,
onConnection: (connection) => {
// create and start the language client
const languageClient = createLanguageClient(connection);
const disposable = languageClient.start();
connection.onClose(() => disposable.dispose());
},
});

return monaco.editor.create(element, {
value: value,
language: language,
theme: theme,
'semanticHighlighting.enabled': true,
selectOnLineNumbers: true,
autoIndent: 'full',
lineNumbers: disableMinimap ? 'off' : 'on',
overviewRulerBorder: !disableMinimap,
overviewRulerLanes: disableMinimap ? 0 : 3,
minimap: {
enabled: !disableMinimap,
},
...options,
});
}, []);

return { createEditor };
};

export default useMonacoEditor;
Loading

0 comments on commit d3f3037

Please sign in to comment.