diff --git a/index.html b/index.html
index 578d72460..b83186472 100644
--- a/index.html
+++ b/index.html
@@ -27,6 +27,9 @@
Examples
React Client Example
+ Python
+ Python Pyright Client Example
+
Verification
Webpack
Please start npm run start:verify:webpack
beforehand:
diff --git a/package-lock.json b/package-lock.json
index e1ee30da8..cbccd70d0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3008,7 +3008,6 @@
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
"hasInstallScript": true,
"optional": true,
"os": [
@@ -4729,6 +4728,21 @@
"node": ">=6"
}
},
+ "node_modules/pyright": {
+ "version": "1.1.322",
+ "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.322.tgz",
+ "integrity": "sha512-PyWajD2Fs3zQ9ngT/qKMqqw3KXF1kQGOk13QGO9IfPDiK5KQVUb35eDCc05sZkEQvufkbG934X4SbsapPYg4YA==",
+ "bin": {
+ "pyright": "index.js",
+ "pyright-langserver": "langserver.index.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
"node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -6442,6 +6456,7 @@
"monaco-editor-workers": "~0.41.0",
"monaco-languageclient": "~6.4.0",
"normalize-url": "~8.0.0",
+ "pyright": "~1.1.322",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"request-light": "~0.7.0",
diff --git a/packages/examples/main/package.json b/packages/examples/main/package.json
index 7697f34a9..0d5c8d8d1 100644
--- a/packages/examples/main/package.json
+++ b/packages/examples/main/package.json
@@ -25,6 +25,7 @@
"monaco-editor-workers": "~0.41.0",
"monaco-languageclient": "~6.4.0",
"normalize-url": "~8.0.0",
+ "pyright": "~1.1.322",
"react": "~18.2.0",
"react-dom": "~18.2.0",
"request-light": "~0.7.0",
@@ -57,6 +58,7 @@
"build:worker:langium": "esbuild ./src/langium/langiumServerWorker.js --bundle --tree-shaking=true --minify --format=iife --outfile=./dist/worker/langiumServerWorker.js",
"build": "npm run build:msg && npm run clean && npm run compile && npm run build:worker:statemachine && npm run build:worker:langium",
"start": "node --loader ts-node/esm src/server/main.ts",
- "start:ext": "node --loader ts-node/esm src/server/main.ts --external"
+ "start:ext": "node --loader ts-node/esm src/server/main.ts --external",
+ "start:python": "node --loader ts-node/esm src/python/server.ts"
}
}
\ No newline at end of file
diff --git a/packages/examples/main/python.html b/packages/examples/main/python.html
new file mode 100644
index 000000000..d2fed7307
--- /dev/null
+++ b/packages/examples/main/python.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Monaco Language Client Python Example
+
+
+
+
+
+ Monaco Language Client Python Example
+
+
+
+
+
diff --git a/packages/examples/main/src/python/client.ts b/packages/examples/main/src/python/client.ts
new file mode 100644
index 000000000..710994c92
--- /dev/null
+++ b/packages/examples/main/src/python/client.ts
@@ -0,0 +1,106 @@
+/* --------------------------------------------------------------------------------------------
+ * Copyright (c) 2018-2022 TypeFox GmbH (http://www.typefox.io). All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ * ------------------------------------------------------------------------------------------ */
+
+import 'monaco-editor/esm/vs/editor/editor.all.js';
+import 'monaco-editor/esm/vs/editor/standalone/browser/iPadShowKeyboard/iPadShowKeyboard.js';
+import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
+import * as vscode from 'vscode';
+import 'vscode/default-extensions/theme-defaults';
+import 'vscode/default-extensions/python';
+import { updateUserConfiguration } from 'vscode/service-override/configuration';
+import { createConfiguredEditor, createModelReference } from 'vscode/monaco';
+import { initServices, MonacoLanguageClient } from 'monaco-languageclient';
+import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient';
+import { WebSocketMessageReader, WebSocketMessageWriter, toSocket } from 'vscode-ws-jsonrpc';
+import { RegisteredFileSystemProvider, registerFileSystemOverlay, RegisteredMemoryFile } from 'vscode/service-override/files';
+
+import { buildWorkerDefinition } from 'monaco-editor-workers';
+buildWorkerDefinition('../../../node_modules/monaco-editor-workers/dist/workers/', new URL('', window.location.href).href, false);
+
+const createWebSocket = (url: string): WebSocket => {
+ const webSocket = new WebSocket(url);
+ webSocket.onopen = () => {
+ const socket = toSocket(webSocket);
+ const reader = new WebSocketMessageReader(socket);
+ const writer = new WebSocketMessageWriter(socket);
+ const languageClient = createLanguageClient({
+ reader,
+ writer
+ });
+ languageClient.start();
+ reader.onClose(() => languageClient.stop());
+ };
+ return webSocket;
+};
+
+const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => {
+ return new MonacoLanguageClient({
+ name: 'Pyright Language Client',
+ clientOptions: {
+ // use a language id as a document selector
+ documentSelector: ['json'],
+ // disable the default error handler
+ errorHandler: {
+ error: () => ({ action: ErrorAction.Continue }),
+ closed: () => ({ action: CloseAction.DoNotRestart })
+ },
+ synchronize: {
+ fileEvents: [vscode.workspace.createFileSystemWatcher('**')]
+ }
+ },
+ // create a language client connection from the JSON RPC connection on demand
+ connectionProvider: {
+ get: () => {
+ return Promise.resolve(transports);
+ }
+ }
+ });
+};
+
+const run = async () => {
+ // init vscode-api
+ await initServices({
+ enableModelService: true,
+ enableThemeService: true,
+ enableTextmateService: true,
+ enableKeybindingsService: true,
+ configureConfigurationServiceConfig: {
+ defaultWorkspaceUri: '/tmp'
+ },
+ debugLogging: true
+ });
+
+ updateUserConfiguration(`{
+ "editor.fontSize": 14,
+ "workbench.colorTheme": "Default Dark Modern"
+ }`);
+
+ const fileSystemProvider = new RegisteredFileSystemProvider(false);
+ fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/tmp/hello.py'), 'print("Hello, World!")'));
+ registerFileSystemOverlay(1, fileSystemProvider);
+
+ // create the web socket and configure to start the language client on open
+ createWebSocket('ws://localhost:30000/pyright');
+
+ // register the JSON language with Monaco
+ const languageId = 'python';
+ monaco.languages.register({
+ id: languageId,
+ extensions: ['.py'],
+ aliases: ['PYTHON', 'python']
+ });
+
+ // create the model inside the workspace
+ const modelRef = await createModelReference(monaco.Uri.file('/tmp/hello.py'));
+ modelRef.object.setLanguageId(languageId);
+
+ // create monaco editor
+ createConfiguredEditor(document.getElementById('container')!, {
+ model: modelRef.object.textEditorModel,
+ automaticLayout: true
+ });
+};
+
+run();
diff --git a/packages/examples/main/src/python/index.html b/packages/examples/main/src/python/index.html
new file mode 100644
index 000000000..07c7bfa58
--- /dev/null
+++ b/packages/examples/main/src/python/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+ Pyright Python Language Server
+
+
+
+ Hello Pyright
+
+
+
diff --git a/packages/examples/main/src/python/server.ts b/packages/examples/main/src/python/server.ts
new file mode 100644
index 000000000..692d653f1
--- /dev/null
+++ b/packages/examples/main/src/python/server.ts
@@ -0,0 +1,86 @@
+/* --------------------------------------------------------------------------------------------
+ * Copyright (c) 2018-2022 TypeFox GmbH (http://www.typefox.io). All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ * ------------------------------------------------------------------------------------------ */
+import { WebSocketServer } from 'ws';
+import { IncomingMessage } from 'http';
+import { URL } from 'url';
+import { Socket } from 'net';
+import express from 'express';
+import { IWebSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc';
+import { createConnection, createServerProcess, forward } from 'vscode-ws-jsonrpc/server';
+import { Message, InitializeRequest, InitializeParams } from 'vscode-languageserver';
+import { getLocalDirectory } from '../server/fs-utils.js';
+
+const launchLanguageServer = (socket: IWebSocket) => {
+ const reader = new WebSocketMessageReader(socket);
+ const writer = new WebSocketMessageWriter(socket);
+
+ // start the language server as an external process
+ const socketConnection = createConnection(reader, writer, () => socket.dispose());
+ const serverConnection = createServerProcess('PYRIGHT', 'npx', ['pyright', '--verbose', '--watch', '/tmp']);
+ if (serverConnection) {
+ forward(socketConnection, serverConnection, message => {
+ if (Message.isRequest(message)) {
+ console.log(message);
+ if (message.method === InitializeRequest.type.method) {
+ const initializeParams = message.params as InitializeParams;
+ initializeParams.processId = process.pid;
+ }
+ }
+ return message;
+ });
+ }
+};
+
+const run = () => {
+ process.on('uncaughtException', function(err: any) {
+ console.error('Uncaught Exception: ', err.toString());
+ if (err.stack) {
+ console.error(err.stack);
+ }
+ });
+
+ // create the express application
+ const app = express();
+ // server the static content, i.e. index.html
+ const dir = getLocalDirectory(import.meta.url);
+ app.use(express.static(dir));
+ // start the server
+ const server = app.listen(30000);
+ // create the web socket
+ const wss = new WebSocketServer({
+ noServer: true,
+ perMessageDeflate: false
+ });
+ server.on('upgrade', (request: IncomingMessage, socket: Socket, head: Buffer) => {
+ const baseURL = `http://${request.headers.host}/`;
+ const pathname = request.url ? new URL(request.url, baseURL).pathname : undefined;
+ if (pathname === '/pyright') {
+ wss.handleUpgrade(request, socket, head, webSocket => {
+ const socket: IWebSocket = {
+ send: content => webSocket.send(content, error => {
+ if (error) {
+ throw error;
+ }
+ }),
+ onMessage: cb => webSocket.on('message', (data) => {
+ console.log(data.toString());
+ cb(data);
+ }),
+ onError: cb => webSocket.on('error', cb),
+ onClose: cb => webSocket.on('close', cb),
+ dispose: () => webSocket.close()
+ };
+ // launch the server when the web socket is opened
+ if (webSocket.readyState === webSocket.OPEN) {
+ launchLanguageServer(socket);
+ } else {
+ webSocket.on('open', () => launchLanguageServer(socket));
+ }
+ });
+ }
+ });
+};
+
+run();
diff --git a/packages/examples/main/src/python/workspace/src/hello.py b/packages/examples/main/src/python/workspace/src/hello.py
new file mode 100644
index 000000000..d2c66e8ce
--- /dev/null
+++ b/packages/examples/main/src/python/workspace/src/hello.py
@@ -0,0 +1,2 @@
+
+print("Hello, World!")
diff --git a/packages/examples/main/src/server/fs-utils.ts b/packages/examples/main/src/server/fs-utils.ts
index 9fc01e3e9..4e4fdd7bd 100644
--- a/packages/examples/main/src/server/fs-utils.ts
+++ b/packages/examples/main/src/server/fs-utils.ts
@@ -8,7 +8,7 @@ import { fileURLToPath } from 'url';
/**
* Solves: __dirname is not defined in ES module scope
*/
-export function getLocalDirectory() {
- const __filename = fileURLToPath(import.meta.url);
+export const getLocalDirectory = (referenceUrl: string | URL) => {
+ const __filename = fileURLToPath(referenceUrl);
return dirname(__filename);
-}
+};
diff --git a/packages/examples/main/src/server/json-server-launcher.ts b/packages/examples/main/src/server/json-server-launcher.ts
index e4d08c21b..38cfe16a2 100644
--- a/packages/examples/main/src/server/json-server-launcher.ts
+++ b/packages/examples/main/src/server/json-server-launcher.ts
@@ -15,7 +15,7 @@ export function launch(socket: IWebSocket) {
const asExternalProccess = process.argv.findIndex(value => value === '--external') !== -1;
if (asExternalProccess) {
// start the language server as an external process
- const extJsonServerPath = resolve(getLocalDirectory(), '../../dist/server/ext-json-server.js');
+ const extJsonServerPath = resolve(getLocalDirectory(import.meta.url), '../../dist/server/ext-json-server.js');
const socketConnection = createConnection(reader, writer, () => socket.dispose());
const serverConnection = createServerProcess('JSON', 'node', [extJsonServerPath]);
if (serverConnection) {
diff --git a/packages/examples/main/src/server/main.ts b/packages/examples/main/src/server/main.ts
index 6d4597740..339488f55 100644
--- a/packages/examples/main/src/server/main.ts
+++ b/packages/examples/main/src/server/main.ts
@@ -21,7 +21,8 @@ process.on('uncaughtException', function(err: any) {
// create the express application
const app = express();
// server the static content, i.e. index.html
-app.use(express.static(getLocalDirectory()));
+const dir = getLocalDirectory(import.meta.url);
+app.use(express.static(dir));
// start the server
const server = app.listen(3000);
// create the web socket
diff --git a/vite.config.ts b/vite.config.ts
index e0936d467..4887891da 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -12,7 +12,8 @@ export default defineConfig(() => {
langiumClient: resolve(__dirname, 'packages/examples/main/langium_client.html'),
statemachineClient: resolve(__dirname, 'packages/examples/main/statemachine_client.html'),
browser: resolve(__dirname, 'packages/examples/main/browser.html'),
- react: resolve(__dirname, 'packages/examples/main/react.html')
+ react: resolve(__dirname, 'packages/examples/main/react.html'),
+ python: resolve(__dirname, 'packages/examples/main/python.html')
}
}
},