From 14cbf34b1a085cf3a536b68dc88cab5035392667 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Thu, 5 Sep 2024 11:03:04 +0200 Subject: [PATCH 1/7] Update to next.0 major versions, update dependencies --- docs/versions-and-history.md | 1 + package-lock.json | 177 ++++++++++++------------ package.json | 14 +- packages/client/CHANGELOG.md | 4 + packages/client/package.json | 6 +- packages/examples/CHANGELOG.md | 4 + packages/examples/package.json | 16 +-- packages/vscode-ws-jsonrpc/package.json | 4 +- packages/wrapper-react/CHANGELOG.md | 4 + packages/wrapper-react/package.json | 14 +- packages/wrapper/CHANGELOG.md | 4 + packages/wrapper/package.json | 8 +- verify/angular/package.json | 4 +- verify/pnpm/package.json | 4 +- verify/vite/package.json | 4 +- verify/webpack/package.json | 4 +- verify/yarn/package.json | 8 +- 17 files changed, 151 insertions(+), 129 deletions(-) diff --git a/docs/versions-and-history.md b/docs/versions-and-history.md index 4a9bb74fc..187b3678d 100644 --- a/docs/versions-and-history.md +++ b/docs/versions-and-history.md @@ -6,6 +6,7 @@ The following table describes which version of **monaco-languageclient** and **@ | monaco-languageclient | monaco-editor-wrapper | monaco-editor-react | monaco-vscode-api / editor-api | vscode | monaco-editor | release date | comment | | :---- | :---- | :--- | :--- | :--- | :--- | :--- | :--- | +| 9.0.0-next.0 | 6.0.0-next.0 | 6.0.0-next.0 | 8.0.4 | 1.92.2 | 0.51.0 | 2024-09-xy | | | 8.8.3 | 5.5.3 | 4.5.3 | 8.0.4 | 1.92.2 | 0.51.0 | 2024-08-26 | | | 8.8.2 | 5.5.2 | 4.5.2 | 8.0.2 | 1.92.2 | 0.50.0 | 2024-08-21 | | | 8.8.1 | 5.5.1 | 4.5.1 | 8.0.1 | 1.92.1 | 0.50.0 | 2024-08-12 | | diff --git a/package-lock.json b/package-lock.json index 391158240..a85637691 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,9 +15,9 @@ "@codingame/esbuild-import-meta-url-plugin": "~1.0.2", "@codingame/monaco-vscode-rollup-vsix-plugin": "~8.0.4", "@rollup/pluginutils": "~5.1.0", - "@testing-library/react": "~16.0.0", - "@types/node": "~20.14.15", - "@types/react": "~18.3.4", + "@testing-library/react": "~16.0.1", + "@types/node": "~20.16.5", + "@types/react": "~18.3.5", "@types/react-dom": "~18.3.0", "@types/vscode": "~1.92.0", "@typescript-eslint/eslint-plugin": "~7.18.0", @@ -33,10 +33,10 @@ "http-server": "~14.1.1", "minimatch": "~10.0.1", "typescript": "~5.5.4", - "vite": "~5.4.2", + "vite": "~5.4.3", "vite-node": "~2.0.5", "vitest": "~2.0.5", - "webdriverio": "~9.0.6" + "webdriverio": "~9.0.7" } }, "node_modules/@ampproject/remapping": { @@ -1953,9 +1953,9 @@ "dev": true }, "node_modules/@promptbook/utils": { - "version": "0.66.0", - "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.66.0.tgz", - "integrity": "sha512-L8CDhFE2p6lEGFW4sWSICljfXCQTeQ10hQrhVjULf8e2UDpqJ2C3nSTe3a890nGLwI5Rg8SStpIIndkQo/zpIw==", + "version": "0.70.0-1", + "resolved": "https://registry.npmjs.org/@promptbook/utils/-/utils-0.70.0-1.tgz", + "integrity": "sha512-qd2lLRRN+sE6UuNMi2tEeUUeb4zmXnxY5EMdfHVXNE+bqBDpUC7/aEfXgA3jnUXEr+xFjQ8PTFQgWvBMaKvw0g==", "dev": true, "funding": [ { @@ -1973,9 +1973,9 @@ } }, "node_modules/@puppeteer/browsers": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.3.1.tgz", - "integrity": "sha512-uK7o3hHkK+naEobMSJ+2ySYyXtQkBxIH8Gn4MK9ciePjNV+Pf+PgY/W7iPzn2MTjl3stcYB5AlcTmPYw7AXDwA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-2.4.0.tgz", + "integrity": "sha512-x8J1csfIygOwf6D6qUAZ0ASk3z63zPb7wkNeHRerCMh82qWKUrOgkuP005AJC8lDL6/evtXETGEJVcwykKT4/g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -2262,10 +2262,11 @@ } }, "node_modules/@testing-library/react": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.0.tgz", - "integrity": "sha512-guuxUKRWQ+FgNX0h0NS0FIq3Q3uLtWVpBzcLOggmfMoUpgBnzBzvLLd4fbm6yS8ydJd94cIfY4yP9qUQjM2KwQ==", + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.0.1.tgz", + "integrity": "sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5" }, @@ -2468,13 +2469,13 @@ } }, "node_modules/@types/node": { - "version": "20.14.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz", - "integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==", + "version": "20.16.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz", + "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.19.2" } }, "node_modules/@types/prop-types": { @@ -2496,9 +2497,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "18.3.4", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.4.tgz", - "integrity": "sha512-J7W30FTdfCxDDjmfRM+/JqLHBIyl7xUIp9kwK637FGmY7+mkSFSe6L4jpZzhj5QMfLssSDP4/i75AKkrdC7/Jw==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.5.tgz", + "integrity": "sha512-WeqMfGJLGuLCqHGYRGHxnKrXcTitc6L/nBUWfWPcTarG3t9PsquqUMuVeXZeca+mglY4Vo5GZjCi0A3Or2lnxA==", "dev": true, "license": "MIT", "dependencies": { @@ -3148,9 +3149,9 @@ } }, "node_modules/@zip.js/zip.js": { - "version": "2.7.48", - "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.48.tgz", - "integrity": "sha512-J7cliimZ2snAbr0IhLx2U8BwfA1pKucahKzTpFtYq4hEgKxwvFJcIjCIVNPwQpfVab7iVP+AKmoH1gidBlyhiQ==", + "version": "2.7.52", + "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.52.tgz", + "integrity": "sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3603,9 +3604,9 @@ "optional": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.3.tgz", + "integrity": "sha512-7RYKL+vZVCyAsMLi5SPu7QGauGGT8avnP/HO571ndEuV4MYdGXvLhtW67FuLPeEI8EiIY7zbbRR9x7x7HU0kgw==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -3616,9 +3617,9 @@ } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.2.tgz", + "integrity": "sha512-HZoJwzC+rZ9lqEemTMiO0luOePoGYNBgsLLgegKR/cljiJvcDNhDZQkzC+NC5Oh0aHbdBNSOHpghwMuB5tqhjg==", "dev": true, "license": "Apache-2.0", "optional": true @@ -3635,13 +3636,14 @@ } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.1.tgz", + "integrity": "sha512-YTB47kHwBW9zSG8LD77MIBAAQXjU2WjAkMHeeb7hUplVs6+IoM5I7uEVQNPMB7lj9r8I76UMdoMkGnCodHOLqg==", "dev": true, "license": "Apache-2.0", "optional": true, "dependencies": { + "b4a": "^1.6.6", "streamx": "^2.18.0" } }, @@ -5701,9 +5703,9 @@ "dev": true }, "node_modules/fast-xml-parser": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", - "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", + "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", "dev": true, "funding": [ { @@ -6001,9 +6003,9 @@ } }, "node_modules/geckodriver": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.4.3.tgz", - "integrity": "sha512-79rvaq8pvKVUtuM9XBjQApb04kOVkl3TFRX+zTt1wlmL+wqpt85ocWCdqiENU/3zIzg2Me21eClUcnE7F1kL2w==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/geckodriver/-/geckodriver-4.4.4.tgz", + "integrity": "sha512-0zaw19tcmWeluqx7+Y559JGBtidu1D0Lb8ElYKiNEQu8r3sCfrLUf5V10xypl8u29ZLbgRV7WflxCJVTCkCMFA==", "dev": true, "hasInstallScript": true, "license": "MPL-2.0", @@ -7299,9 +7301,9 @@ } }, "node_modules/locate-app": { - "version": "2.4.28", - "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.4.28.tgz", - "integrity": "sha512-+fcrSieGg19C7YS7MBrngAcaWmdeTpljyWneKof7X9NGe3F2xPodl3y9kx1MpyaXtRmzFnV1/frARI3O73agQg==", + "version": "2.4.38", + "resolved": "https://registry.npmjs.org/locate-app/-/locate-app-2.4.38.tgz", + "integrity": "sha512-fJNTsDQZSiy+bn98RicvVX8e7HwH3YqZnRRisircGDGPpf0eZ2x57Ev7LGs0pCBO7hzjINVtVr5QFfK8KH7hjg==", "dev": true, "funding": [ { @@ -7315,7 +7317,7 @@ ], "license": "SEE LICENSE IN LICENSE", "dependencies": { - "@promptbook/utils": "0.66.0", + "@promptbook/utils": "0.70.0-1", "type-fest": "2.13.0", "userhome": "1.0.0" } @@ -8281,9 +8283,9 @@ } }, "node_modules/postcss": { - "version": "8.4.41", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", - "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "version": "8.4.45", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.45.tgz", + "integrity": "sha512-7KTLTdzdZZYscUc65XmjFiB73vBhBfbPztCYdUNvlaso9PrzjzcmjqBPR0lNGkcVlcO4BjiO5rK/qNz+XAen1Q==", "dev": true, "funding": [ { @@ -8446,9 +8448,9 @@ } }, "node_modules/pyright": { - "version": "1.1.377", - "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.377.tgz", - "integrity": "sha512-y6ENYuyZXTczPnPWZnqx78pE+ZgyIotEas2M/LFRTq3EfbgVk84EcvuSKLIy2DJeDKjKDxVP/LVmDNHabljD3g==", + "version": "1.1.379", + "resolved": "https://registry.npmjs.org/pyright/-/pyright-1.1.379.tgz", + "integrity": "sha512-n0X+IMqot6zL5b54vfU9GattS8jM9IOh8TRFho1k/6VoyjrpzQ7TnU6PtZzwEZNJaZi5izoLIDeMnGmbin8n8Q==", "license": "MIT", "bin": { "pyright": "index.js", @@ -9877,10 +9879,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" }, "node_modules/union": { "version": "0.5.0", @@ -10012,14 +10015,14 @@ } }, "node_modules/vite": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", - "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.3.tgz", + "integrity": "sha512-IH+nl64eq9lJjFqU+/yrRnrHPVTlgy42/+IzbOdaFDVlyLgI/wDlf+FCobXLX1cT0X5+7LMyH1mIy2xJdLfo8Q==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.41", + "postcss": "^8.4.43", "rollup": "^4.20.0" }, "bin": { @@ -10584,13 +10587,14 @@ } }, "node_modules/vscode-json-languageservice": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.0.tgz", - "integrity": "sha512-NCkkCr63OHVkE4lcb0xlUAaix6vE5gHQW4NrswbLEh3ArXj81lrGuFTsGEYEUXlNHdnc53vWPcjeSy/nMTrfXg==", + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz", + "integrity": "sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "jsonc-parser": "^3.3.0", - "vscode-languageserver-textdocument": "^1.0.11", + "jsonc-parser": "^3.3.1", + "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", "vscode-uri": "^3.0.8" } @@ -10656,9 +10660,10 @@ } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", @@ -10723,9 +10728,9 @@ } }, "node_modules/webdriver": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.0.6.tgz", - "integrity": "sha512-S9f1qkEuIN1RgDFF0KmfWUc2NiBUb9d1UpGI/Y9IkSy9wTnAJk+xTvCezSUGO2D9rtZYHLal7jU+yOf9ntBg5Q==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/webdriver/-/webdriver-9.0.7.tgz", + "integrity": "sha512-0PN4omqCGlgi3RG0LyrQXr0RUmlDCenNtpN+dfzikfYoV+CHiCw2GMnZp2XCuYUnU01MaCKgRQxLuGobyZov+A==", "dev": true, "license": "MIT", "dependencies": { @@ -10744,9 +10749,9 @@ } }, "node_modules/webdriverio": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.0.6.tgz", - "integrity": "sha512-STRjhpC18tUPFox1DSRPMhKfMXwsgr7ZqyWWbLXCPUeKpejVXlVigcXM4bXVWXpuAbLVo8PHGtX/zy28Jkwmow==", + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/webdriverio/-/webdriverio-9.0.7.tgz", + "integrity": "sha512-/6CvJkKpUWYbX/59PNJCHXGLPwulQ/bXZwlIUrsF6MWKdf8Eb6yTaXkMJBaBy5x496b50PQcXkbe+qpfsnqXsg==", "dev": true, "license": "MIT", "dependencies": { @@ -10776,7 +10781,7 @@ "rgb2hex": "0.2.5", "serialize-error": "^11.0.3", "urlpattern-polyfill": "^10.0.0", - "webdriver": "9.0.6" + "webdriver": "9.0.7" }, "engines": { "node": ">=18" @@ -11079,7 +11084,7 @@ }, "packages/client": { "name": "monaco-languageclient", - "version": "8.8.3", + "version": "9.0.0-next.0", "license": "MIT", "dependencies": { "@codingame/monaco-vscode-extensions-service-override": "~8.0.4", @@ -11109,7 +11114,7 @@ }, "packages/examples": { "name": "monaco-languageclient-examples", - "version": "2024.8.4", + "version": "2024.9.1", "license": "MIT", "dependencies": { "@codingame/monaco-vscode-configuration-service-override": "~8.0.4", @@ -11130,18 +11135,18 @@ "@codingame/monaco-vscode-theme-service-override": "~8.0.4", "@codingame/monaco-vscode-typescript-basics-default-extension": "~8.0.4", "@codingame/monaco-vscode-typescript-language-features-default-extension": "~8.0.4", - "@typefox/monaco-editor-react": "~4.5.3", + "@typefox/monaco-editor-react": "~6.0.0-next.0", "express": "~4.19.2", "langium": "~3.1.3", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", - "pyright": "~1.1.377", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", + "pyright": "~1.1.379", "react": "~18.3.1", "react-dom": "~18.3.1", "request-light": "~0.8.0", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4", - "vscode-json-languageservice": "~5.4.0", + "vscode-json-languageservice": "~5.4.1", "vscode-languageclient": "~9.0.1", "vscode-languageserver": "~9.0.1", "vscode-uri": "~3.0.8", @@ -11169,7 +11174,7 @@ }, "packages/wrapper": { "name": "monaco-editor-wrapper", - "version": "5.5.3", + "version": "6.0.0-next.0", "license": "MIT", "dependencies": { "@codingame/monaco-vscode-configuration-service-override": "~8.0.4", @@ -11207,7 +11212,7 @@ }, "peerDependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-languageclient": "~8.8.3", + "monaco-languageclient": "~9.0.0-next.0", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, "peerDependenciesMeta": { @@ -11224,19 +11229,19 @@ }, "packages/wrapper-react": { "name": "@typefox/monaco-editor-react", - "version": "4.5.3", + "version": "6.0.0-next.0", "license": "MIT", "dependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", "react": "~18.3.1", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, "peerDependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", "react": "~18.3.1", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, diff --git a/package.json b/package.json index 13c353eca..d3ca70917 100644 --- a/package.json +++ b/package.json @@ -5,9 +5,9 @@ "@codingame/esbuild-import-meta-url-plugin": "~1.0.2", "@codingame/monaco-vscode-rollup-vsix-plugin": "~8.0.4", "@rollup/pluginutils": "~5.1.0", - "@testing-library/react": "~16.0.0", - "@types/node": "~20.14.15", - "@types/react": "~18.3.4", + "@testing-library/react": "~16.0.1", + "@types/node": "~20.16.5", + "@types/react": "~18.3.5", "@types/react-dom": "~18.3.0", "@types/vscode": "~1.92.0", "@typescript-eslint/eslint-plugin": "~7.18.0", @@ -23,14 +23,14 @@ "http-server": "~14.1.1", "minimatch": "~10.0.1", "typescript": "~5.5.4", - "vite": "~5.4.2", + "vite": "~5.4.3", "vite-node": "~2.0.5", "vitest": "~2.0.5", - "webdriverio": "~9.0.6" + "webdriverio": "~9.0.7" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "scripts": { "clean": "npm run clean --workspaces", diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index 523b90d4f..559d25601 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this npm module are documented in this file. +## [9.0.0-next.0] - 2024-08-26 + +- Pass MessageTransports directly + ## [8.8.3] - 2024-08-26 - Update to monaco-vscode-api 8.0.4 (monaco-editor 0.51.0) diff --git a/packages/client/package.json b/packages/client/package.json index 0ac9e208c..dd8e84871 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "monaco-languageclient", - "version": "8.8.3", + "version": "9.0.0-next.0", "description": "Monaco Language client implementation", "author": { "name": "TypeFox GmbH", @@ -51,8 +51,8 @@ "npm": ">=9.0.0" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "files": [ "lib", diff --git a/packages/examples/CHANGELOG.md b/packages/examples/CHANGELOG.md index 995d2f26a..b0b521bca 100644 --- a/packages/examples/CHANGELOG.md +++ b/packages/examples/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to this npm module are documented in this file. +## [2024.9.1] - 2024-09-xy + +- Updated to `monaco-languageclient@9.0.0-next.0`, `monaco-editor-wrapper@6.0.0-next.0` and `@typefox/monaco-editor-react@6.0.0-next.0`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. + ## [2024.8.4] - 2024-08-26 - Updated to `monaco-languageclient@8.8.3`, `monaco-editor-wrapper@5.5.3` and `@typefox/monaco-editor-react@4.5.3`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. diff --git a/packages/examples/package.json b/packages/examples/package.json index 60473c769..2ce10f289 100644 --- a/packages/examples/package.json +++ b/packages/examples/package.json @@ -1,6 +1,6 @@ { "name": "monaco-languageclient-examples", - "version": "2024.8.4", + "version": "2024.9.1", "description": "Monaco Language client examples", "author": { "name": "TypeFox GmbH", @@ -72,18 +72,18 @@ "@codingame/monaco-vscode-theme-service-override": "~8.0.4", "@codingame/monaco-vscode-typescript-basics-default-extension": "~8.0.4", "@codingame/monaco-vscode-typescript-language-features-default-extension": "~8.0.4", - "@typefox/monaco-editor-react": "~4.5.3", + "@typefox/monaco-editor-react": "~6.0.0-next.0", "express": "~4.19.2", "langium": "~3.1.3", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", - "pyright": "~1.1.377", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", + "pyright": "~1.1.379", "react": "~18.3.1", "react-dom": "~18.3.1", "request-light": "~0.8.0", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4", - "vscode-json-languageservice": "~5.4.0", + "vscode-json-languageservice": "~5.4.1", "vscode-languageclient": "~9.0.1", "vscode-languageserver": "~9.0.1", "vscode-uri": "~3.0.8", @@ -98,8 +98,8 @@ "vscode-languageserver-types": "~3.17.5" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "files": [ "dist", diff --git a/packages/vscode-ws-jsonrpc/package.json b/packages/vscode-ws-jsonrpc/package.json index 068865a3f..9e5f6a51c 100644 --- a/packages/vscode-ws-jsonrpc/package.json +++ b/packages/vscode-ws-jsonrpc/package.json @@ -51,8 +51,8 @@ "npm": ">=8.0.0" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "files": [ "lib", diff --git a/packages/wrapper-react/CHANGELOG.md b/packages/wrapper-react/CHANGELOG.md index 715d34385..19c250728 100644 --- a/packages/wrapper-react/CHANGELOG.md +++ b/packages/wrapper-react/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to npm module [@typefox/monaco-editor-react](https://www.npmjs.com/package/@typefox/monaco-editor-react) are documented in this file. +## [6.0.0-next.0] - 2024-09-xy + +- Updated to `monaco-editor-wrapper@9.0.0-next.0` and `monaco-languageclient@6.0.0-next.0`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. + ## [4.5.3] - 2024-08-26 - Updated to `monaco-editor-wrapper@5.5.3` and `monaco-languageclient@8.8.3`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. diff --git a/packages/wrapper-react/package.json b/packages/wrapper-react/package.json index 302868429..e7fee5f18 100644 --- a/packages/wrapper-react/package.json +++ b/packages/wrapper-react/package.json @@ -1,6 +1,6 @@ { "name": "@typefox/monaco-editor-react", - "version": "4.5.3", + "version": "6.0.0-next.0", "license": "MIT", "description": "React component for Monaco-Editor and Monaco Languageclient", "keywords": [ @@ -38,20 +38,20 @@ "build": "npm run clean && npm run compile" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "dependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", "react": "~18.3.1", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, "peerDependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3", - "monaco-languageclient": "~8.8.3", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient": "~9.0.0-next.0", "react": "~18.3.1", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, diff --git a/packages/wrapper/CHANGELOG.md b/packages/wrapper/CHANGELOG.md index 127c0db1f..7d5701b77 100644 --- a/packages/wrapper/CHANGELOG.md +++ b/packages/wrapper/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to npm module [monaco-editor-wrapper](https://www.npmjs.com/package/monaco-editor-wrapper) are documented in this file. +## [6.0.0-next.0] - 2024-08-xy + +- Updated to `monaco-languageclient@9.0.0-next.0`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. + ## [5.5.3] - 2024-08-26 - Updated to `monaco-languageclient@8.8.3`. Updated all `@codingame/monaco-vscode` packages to `8.0.4`. diff --git a/packages/wrapper/package.json b/packages/wrapper/package.json index 441498f80..5cc3da210 100644 --- a/packages/wrapper/package.json +++ b/packages/wrapper/package.json @@ -1,6 +1,6 @@ { "name": "monaco-editor-wrapper", - "version": "5.5.3", + "version": "6.0.0-next.0", "license": "MIT", "description": "Wrapper for monaco-vscode-editor-api and monaco-languageclient", "keywords": [ @@ -81,8 +81,8 @@ "build": "npm run clean && npm run compile && npm run build:workers:esbuild && npm run build:workers:vite" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "dependencies": { "@codingame/monaco-vscode-configuration-service-override": "~8.0.4", @@ -120,7 +120,7 @@ }, "peerDependencies": { "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-languageclient": "~8.8.3", + "monaco-languageclient": "~9.0.0-next.0", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4" }, "peerDependenciesMeta": { diff --git a/verify/angular/package.json b/verify/angular/package.json index f1758a006..3a5e760fb 100644 --- a/verify/angular/package.json +++ b/verify/angular/package.json @@ -39,7 +39,7 @@ "style-loader": "~4.0.0" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" } } diff --git a/verify/pnpm/package.json b/verify/pnpm/package.json index 49b452485..1c272e5f6 100644 --- a/verify/pnpm/package.json +++ b/verify/pnpm/package.json @@ -14,8 +14,8 @@ "vite": "~5.3.3" }, "volta": { - "node": "20.16.0", - "pnpm": "9.7.0" + "node": "20.17.0", + "pnpm": "9.9.0" }, "scripts": { "verify": "pnpm install && pnpm run build && pnpm run start", diff --git a/verify/vite/package.json b/verify/vite/package.json index f0166e7fa..cea2b9cdb 100644 --- a/verify/vite/package.json +++ b/verify/vite/package.json @@ -14,8 +14,8 @@ "vite": "~5.3.3" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "scripts": { "verify": "npm install && npm run build && npm run start", diff --git a/verify/webpack/package.json b/verify/webpack/package.json index 0dbd3d03f..b7178f549 100644 --- a/verify/webpack/package.json +++ b/verify/webpack/package.json @@ -19,8 +19,8 @@ "webpack-cli": "~5.1.4" }, "volta": { - "node": "20.16.0", - "npm": "10.8.1" + "node": "20.17.0", + "npm": "10.8.3" }, "scripts": { "verify": "npm install && npm run build && npm run start", diff --git a/verify/yarn/package.json b/verify/yarn/package.json index f846e338c..474b9e998 100644 --- a/verify/yarn/package.json +++ b/verify/yarn/package.json @@ -4,10 +4,10 @@ "private": true, "type": "module", "dependencies": { - "@typefox/monaco-editor-react": "~4.5.3", + "@typefox/monaco-editor-react": "~6.0.0-next.0", "monaco-editor": "npm:@codingame/monaco-vscode-editor-api@~8.0.4", - "monaco-editor-wrapper": "~5.5.3s", - "monaco-languageclient-examples": "~2024.8.4", + "monaco-editor-wrapper": "~6.0.0-next.0", + "monaco-languageclient-examples": "~2024.9.1", "vscode": "npm:@codingame/monaco-vscode-api@~8.0.4", "vscode-ws-jsonrpc": "~3.3.2" }, @@ -16,7 +16,7 @@ "vite": "~5.3.3" }, "volta": { - "node": "20.16.0", + "node": "20.17.0", "yarn": "1.22.22" }, "scripts": { From d6f4fd386405e0a2295a225d1a22769cf6d0a5ad Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Wed, 4 Sep 2024 15:38:16 +0200 Subject: [PATCH 2/7] LanguageClientWrapper improvements: Allow to directly pass a web socket --- packages/client/src/client.ts | 30 +++ .../{wrapper => client}/src/commonTypes.ts | 38 ++- packages/client/src/index.ts | 29 +-- packages/client/tsconfig.src.json | 7 +- packages/examples/src/bare/client.ts | 6 +- .../src/common/node/server-commons.ts | 25 +- .../src/eclipse.jdt.ls/client/main.ts | 2 +- .../langium-dsl/config/extendedConfig.ts | 2 +- packages/examples/src/python/client/config.ts | 30 ++- packages/examples/src/python/server/main.ts | 3 +- packages/examples/src/ts/wrapperAdvanced.ts | 2 +- packages/examples/tsconfig.build.json | 6 +- packages/vscode-ws-jsonrpc/tsconfig.src.json | 4 +- packages/wrapper-react/test/index.test.tsx | 2 +- packages/wrapper-react/tsconfig.src.json | 7 +- packages/wrapper-react/tsconfig.test.json | 6 +- packages/wrapper/src/index.ts | 15 -- packages/wrapper/src/languageClientWrapper.ts | 234 ++++++++++-------- packages/wrapper/src/utils.ts | 10 +- packages/wrapper/test/utils.test.ts | 17 +- packages/wrapper/tsconfig.build.json | 6 +- packages/wrapper/tsconfig.src.json | 5 +- packages/wrapper/tsconfig.test.json | 6 +- tsconfig.json | 4 +- 24 files changed, 284 insertions(+), 212 deletions(-) create mode 100644 packages/client/src/client.ts rename packages/{wrapper => client}/src/commonTypes.ts (59%) diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts new file mode 100644 index 000000000..1f84cfe66 --- /dev/null +++ b/packages/client/src/client.ts @@ -0,0 +1,30 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) 2024 TypeFox and others. + * Licensed under the MIT License. See LICENSE in the package root for license information. + * ------------------------------------------------------------------------------------------ */ + +import { BaseLanguageClient, MessageTransports, LanguageClientOptions } from 'vscode-languageclient/browser.js'; + +export interface IConnectionProvider { + get(encoding: string): Promise; +} + +export type MonacoLanguageClientOptions = { + name: string; + id?: string; + clientOptions: LanguageClientOptions; + connectionProvider: IConnectionProvider; +} + +export class MonacoLanguageClient extends BaseLanguageClient { + protected readonly connectionProvider: IConnectionProvider; + + constructor({ id, name, clientOptions, connectionProvider }: MonacoLanguageClientOptions) { + super(id ?? name.toLowerCase(), name, clientOptions); + this.connectionProvider = connectionProvider; + } + + protected override createMessageTransports(encoding: string): Promise { + return this.connectionProvider.get(encoding); + } +} diff --git a/packages/wrapper/src/commonTypes.ts b/packages/client/src/commonTypes.ts similarity index 59% rename from packages/wrapper/src/commonTypes.ts rename to packages/client/src/commonTypes.ts index 263476b10..f37e15d9a 100644 --- a/packages/wrapper/src/commonTypes.ts +++ b/packages/client/src/commonTypes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ -import { MonacoLanguageClient } from 'monaco-languageclient'; +import { MonacoLanguageClient } from './client.js'; export type WebSocketCallOptions = { /** Adds handle on languageClient */ @@ -12,29 +12,45 @@ export type WebSocketCallOptions = { reportStatus?: boolean; } -export type LanguageClientConfigType = 'WebSocket' | 'WebSocketUrl' | 'WorkerConfig' | 'Worker'; +export type LanguageClientRestartOptions = { + retries: number; + timeout: number; + keepWorker?: boolean; +} -export type WebSocketUrl = { - secured: boolean; - host: string; - port?: number; - path?: string; +export type LanguageClientConfigType = 'WebSocket' | 'WebSocketUrl' | 'WebSocketDirect' | 'WorkerConfig' | 'Worker'; + +export type LanguageClientConfigOptions = (WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl | WorkerConfigOptions | WorkerConfigDirect) & { + restartOptions?: LanguageClientRestartOptions; } -export type WebSocketConfigOptions = { - $type: 'WebSocket' +export type WebSocketUrlParams = { secured: boolean; host: string; port?: number; path?: string; extraParams?: Record>; +} + +export type WebSocketUrlString = { + url: string; +} + +export type WebSocketConfigOptionsDirect = { + $type: 'WebSocketDirect' + webSocket: WebSocket + startOptions?: WebSocketCallOptions; + stopOptions?: WebSocketCallOptions; +} + +export type WebSocketConfigOptionsParams = WebSocketUrlParams & { + $type: 'WebSocketParams' startOptions?: WebSocketCallOptions; stopOptions?: WebSocketCallOptions; } -export type WebSocketConfigOptionsUrl = { +export type WebSocketConfigOptionsUrl = WebSocketUrlString & { $type: 'WebSocketUrl' - url: string; startOptions?: WebSocketCallOptions; stopOptions?: WebSocketCallOptions; } diff --git a/packages/client/src/index.ts b/packages/client/src/index.ts index bf4ef8e5f..de65a7bd3 100644 --- a/packages/client/src/index.ts +++ b/packages/client/src/index.ts @@ -3,28 +3,7 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ -import { BaseLanguageClient, MessageTransports, LanguageClientOptions } from 'vscode-languageclient/lib/common/client.js'; - -export interface IConnectionProvider { - get(encoding: string): Promise; -} - -export type MonacoLanguageClientOptions = { - name: string; - id?: string; - clientOptions: LanguageClientOptions; - connectionProvider: IConnectionProvider; -} - -export class MonacoLanguageClient extends BaseLanguageClient { - protected readonly connectionProvider: IConnectionProvider; - - constructor({ id, name, clientOptions, connectionProvider }: MonacoLanguageClientOptions) { - super(id ?? name.toLowerCase(), name, clientOptions); - this.connectionProvider = connectionProvider; - } - - protected override createMessageTransports(encoding: string): Promise { - return this.connectionProvider.get(encoding); - } -} +export type * from './client.js'; +export * from './client.js'; +export type * from './commonTypes.js'; +export * from './commonTypes.js'; diff --git a/packages/client/tsconfig.src.json b/packages/client/tsconfig.src.json index fe6194cd2..33c4b69bf 100644 --- a/packages/client/tsconfig.src.json +++ b/packages/client/tsconfig.src.json @@ -4,9 +4,10 @@ "rootDir": "src", "outDir": "lib", "declarationDir": "lib", - "types": [ - "vscode" - ] + // because vscode-jsonrpc requires DedicatedWorkerGlobalScope + // we are required to include both DOM and WebWorker libs + // the only way out currently is to disable lib checking + "skipLibCheck": true }, "include": [ "src/**/*.ts", diff --git a/packages/examples/src/bare/client.ts b/packages/examples/src/bare/client.ts index 1a020b45c..faf6f0fe1 100644 --- a/packages/examples/src/bare/client.ts +++ b/packages/examples/src/bare/client.ts @@ -13,7 +13,7 @@ import '@codingame/monaco-vscode-theme-defaults-default-extension'; import '@codingame/monaco-vscode-json-default-extension'; import { MonacoLanguageClient } from 'monaco-languageclient'; import { WebSocketMessageReader, WebSocketMessageWriter, toSocket } from 'vscode-ws-jsonrpc'; -import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient'; +import { CloseAction, ErrorAction, MessageTransports } from 'vscode-languageclient/browser.js'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; export const configureMonacoWorkers = () => { @@ -87,9 +87,7 @@ export const createLanguageClient = (transports: MessageTransports): MonacoLangu }, // create a language client connection from the JSON RPC connection on demand connectionProvider: { - get: () => { - return Promise.resolve(transports); - } + get: async () => (transports) } }); }; diff --git a/packages/examples/src/common/node/server-commons.ts b/packages/examples/src/common/node/server-commons.ts index 55af67eef..bf95c60ef 100644 --- a/packages/examples/src/common/node/server-commons.ts +++ b/packages/examples/src/common/node/server-commons.ts @@ -10,7 +10,7 @@ import { dirname } from 'node:path'; import { fileURLToPath } from 'node:url'; 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 { Message, InitializeRequest, InitializeParams, RequestMessage, ResponseMessage } from 'vscode-languageserver-protocol'; import * as cp from 'child_process'; export enum LanguageName { @@ -28,6 +28,9 @@ export interface LanguageServerRunConfig { runCommandArgs: string[]; wsServerOptions: ServerOptions, spawnOptions?: cp.SpawnOptions; + logMessages?: boolean; + requestMessageHandler?: (message: RequestMessage) => RequestMessage; + responseMessageHandler?: (message: ResponseMessage) => ResponseMessage; } /** @@ -43,16 +46,27 @@ export const launchLanguageServer = (runconfig: LanguageServerRunConfig, socket: if (serverConnection) { forward(socketConnection, serverConnection, message => { if (Message.isRequest(message)) { - console.log(`${serverName} Server received:`); - console.log(message); if (message.method === InitializeRequest.type.method) { const initializeParams = message.params as InitializeParams; initializeParams.processId = process.pid; } + + if (runconfig.logMessages ?? false) { + console.log(`${serverName} Server received: ${message.method}`); + console.log(message); + } + if (runconfig.requestMessageHandler !== undefined) { + return runconfig.requestMessageHandler(message); + } } if (Message.isResponse(message)) { - console.log(`${serverName} Server sent:`); - console.log(message); + if (runconfig.logMessages ?? false) { + console.log(`${serverName} Server sent:`); + console.log(message); + } + if (runconfig.responseMessageHandler !== undefined) { + return runconfig.responseMessageHandler(message); + } } return message; }); @@ -76,7 +90,6 @@ export const upgradeWsServer = (runconfig: LanguageServerRunConfig, } }), onMessage: cb => webSocket.on('message', (data) => { - console.log(data.toString()); cb(data); }), onError: cb => webSocket.on('error', cb), diff --git a/packages/examples/src/eclipse.jdt.ls/client/main.ts b/packages/examples/src/eclipse.jdt.ls/client/main.ts index 895a915b9..48fe77d4a 100644 --- a/packages/examples/src/eclipse.jdt.ls/client/main.ts +++ b/packages/examples/src/eclipse.jdt.ls/client/main.ts @@ -10,7 +10,7 @@ import '@codingame/monaco-vscode-java-default-extension'; import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; import { RegisteredFileSystemProvider, RegisteredMemoryFile, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override'; -import { eclipseJdtLsConfig } from '../config'; +import { eclipseJdtLsConfig } from '../config.js'; import helloJavaCode from '../../../resources/eclipse.jdt.ls/workspace/hello.java?raw'; export const configureMonacoWorkers = () => { diff --git a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts index 425ffc76d..a1fe8bb6a 100644 --- a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts @@ -83,7 +83,7 @@ export const setupLangiumClientExtended = async (): Promise => { worker: langiumWorker }, connectionProvider: { - get: async () => ({ reader, writer }), + get: async () => ({ reader, writer }) } } }; diff --git a/packages/examples/src/python/client/config.ts b/packages/examples/src/python/client/config.ts index cdb670e5a..961186c61 100644 --- a/packages/examples/src/python/client/config.ts +++ b/packages/examples/src/python/client/config.ts @@ -7,24 +7,33 @@ import * as vscode from 'vscode'; import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; import '@codingame/monaco-vscode-python-default-extension'; -import { UserConfig } from 'monaco-editor-wrapper'; +import { createUrl, UserConfig } from 'monaco-editor-wrapper'; import { useOpenEditorStub } from 'monaco-editor-wrapper/vscode/services'; import { MonacoLanguageClient } from 'monaco-languageclient'; +import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; export const createUserConfig = (workspaceRoot: string, code: string, codeUri: string): UserConfig => { + const url = createUrl({ + secured: false, + host: 'localhost', + port: 30001, + path: 'pyright', + extraParams: { + authorization: 'UserAuth' + } + }); + const webSocket = new WebSocket(url); + const iWebSocket = toSocket(webSocket); + const reader = new WebSocketMessageReader(iWebSocket); + const writer = new WebSocketMessageWriter(iWebSocket); + return { languageClientConfig: { languageId: 'python', name: 'Python Language Server Example', options: { - $type: 'WebSocket', - host: 'localhost', - port: 30001, - path: 'pyright', - extraParams: { - authorization: 'UserAuth' - }, - secured: false, + $type: 'WebSocketDirect', + webSocket: webSocket, startOptions: { onCall: (languageClient?: MonacoLanguageClient) => { setTimeout(() => { @@ -46,6 +55,9 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s uri: vscode.Uri.parse(workspaceRoot) }, }, + connectionProvider: { + get: async () => ({ reader, writer }) + } }, wrapperConfig: { serviceConfig: { diff --git a/packages/examples/src/python/server/main.ts b/packages/examples/src/python/server/main.ts index 684f9b6e9..1e736b5df 100644 --- a/packages/examples/src/python/server/main.ts +++ b/packages/examples/src/python/server/main.ts @@ -35,6 +35,7 @@ export const runPythonServer = (baseDir: string, relativeDir: string) => { callback(false); } } - } + }, + logMessages: true }); }; diff --git a/packages/examples/src/ts/wrapperAdvanced.ts b/packages/examples/src/ts/wrapperAdvanced.ts index a8e80dab8..dafa8931c 100644 --- a/packages/examples/src/ts/wrapperAdvanced.ts +++ b/packages/examples/src/ts/wrapperAdvanced.ts @@ -57,7 +57,7 @@ Same again.`, languageId: 'json', name: 'wrapper42 language client', options: { - $type: 'WebSocket', + $type: 'WebSocketParams', host: 'localhost', port: 30000, path: 'sampleServer', diff --git a/packages/examples/tsconfig.build.json b/packages/examples/tsconfig.build.json index e41c77a38..0cb7e416d 100644 --- a/packages/examples/tsconfig.build.json +++ b/packages/examples/tsconfig.build.json @@ -2,11 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./build", - "noEmit": true, - // all other types are not needed here - "types": [ - "node" - ] + "noEmit": true }, "include": [ "build/**/*.ts", diff --git a/packages/vscode-ws-jsonrpc/tsconfig.src.json b/packages/vscode-ws-jsonrpc/tsconfig.src.json index 618618919..b7c5ac54f 100644 --- a/packages/vscode-ws-jsonrpc/tsconfig.src.json +++ b/packages/vscode-ws-jsonrpc/tsconfig.src.json @@ -3,9 +3,7 @@ "compilerOptions": { "rootDir": "src", "outDir": "lib", - "declarationDir": "lib", - // no types definitions are required to undertand dependent code - "types": [] + "declarationDir": "lib" }, "include": [ "src/**/*.ts", diff --git a/packages/wrapper-react/test/index.test.tsx b/packages/wrapper-react/test/index.test.tsx index 3a4a8e399..5885b12f6 100644 --- a/packages/wrapper-react/test/index.test.tsx +++ b/packages/wrapper-react/test/index.test.tsx @@ -8,7 +8,7 @@ import { render, RenderResult } from '@testing-library/react'; import React from 'react'; import { MonacoEditorReactComp, TextChanges } from '@typefox/monaco-editor-react'; import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; -import { updateExtendedAppPrototyp } from './helper'; +import { updateExtendedAppPrototyp } from './helper.js'; describe('Test MonacoEditorReactComp', () => { test('rerender', async () => { diff --git a/packages/wrapper-react/tsconfig.src.json b/packages/wrapper-react/tsconfig.src.json index fe71f2b09..36d5005ae 100644 --- a/packages/wrapper-react/tsconfig.src.json +++ b/packages/wrapper-react/tsconfig.src.json @@ -4,9 +4,10 @@ "rootDir": "src", "outDir": "dist", "declarationDir": "dist", - "types": [ - "vscode" - ] + // because vscode-jsonrpc requires DedicatedWorkerGlobalScope + // we are required to include both DOM and WebWorker libs + // the only way out currently is to disable lib checking + "skipLibCheck": true }, "references": [{ "path": "../wrapper/tsconfig.src.json" diff --git a/packages/wrapper-react/tsconfig.test.json b/packages/wrapper-react/tsconfig.test.json index b3ee8ba7a..dfccd5b7a 100644 --- a/packages/wrapper-react/tsconfig.test.json +++ b/packages/wrapper-react/tsconfig.test.json @@ -2,7 +2,11 @@ "extends": "./tsconfig.src.json", "compilerOptions": { "noEmit": true, - "rootDir": "test" + "rootDir": "test", + // because vscode-jsonrpc requires DedicatedWorkerGlobalScope + // we are required to include both DOM and WebWorker libs + // the only way out currently is to disable lib checking + "skipLibCheck": true }, "references": [{ "path": "./tsconfig.src.json" diff --git a/packages/wrapper/src/index.ts b/packages/wrapper/src/index.ts index 46e0035c4..fcde29f30 100644 --- a/packages/wrapper/src/index.ts +++ b/packages/wrapper/src/index.ts @@ -39,15 +39,6 @@ import { EditorAppExtended } from './editorAppExtended.js'; -import type { - WebSocketCallOptions, - LanguageClientConfigType, - WebSocketConfigOptions, - WebSocketConfigOptionsUrl, - WorkerConfigOptions, - WorkerConfigDirect -} from './commonTypes.js'; - import type { LanguageClientConfig, LanguageClientError @@ -76,12 +67,6 @@ export type { RegisterExtensionResult, RegisterLocalProcessExtensionResult, UserConfiguration, - WebSocketCallOptions, - LanguageClientConfigType, - WebSocketConfigOptions, - WebSocketConfigOptionsUrl, - WorkerConfigOptions, - WorkerConfigDirect, LanguageClientConfig, LanguageClientError, UserConfig, diff --git a/packages/wrapper/src/languageClientWrapper.ts b/packages/wrapper/src/languageClientWrapper.ts index a964e1770..b5acdcab2 100644 --- a/packages/wrapper/src/languageClientWrapper.ts +++ b/packages/wrapper/src/languageClientWrapper.ts @@ -3,17 +3,16 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ -import { MonacoLanguageClient, IConnectionProvider } from 'monaco-languageclient'; -import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; -import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver-protocol/browser.js'; -import { CloseAction, ErrorAction, LanguageClientOptions, MessageTransports, State } from 'vscode-languageclient/lib/common/client.js'; +import { MonacoLanguageClient, IConnectionProvider, WorkerConfigOptions, WorkerConfigDirect, LanguageClientConfigOptions, WebSocketConfigOptionsParams, WebSocketConfigOptionsDirect, WebSocketConfigOptionsUrl, LanguageClientRestartOptions } from 'monaco-languageclient'; import { Logger } from 'monaco-languageclient/tools'; +import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver-protocol/browser.js'; +import { CloseAction, ErrorAction, LanguageClientOptions, MessageTransports, State } from 'vscode-languageclient/browser.js'; import { createUrl } from './utils.js'; -import { WebSocketConfigOptions, WebSocketConfigOptionsUrl, WorkerConfigDirect, WorkerConfigOptions } from './commonTypes.js'; +import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; export type LanguageClientConfig = { languageId: string; - options: WebSocketConfigOptions | WebSocketConfigOptionsUrl | WorkerConfigOptions | WorkerConfigDirect; + options: LanguageClientConfigOptions; name?: string; clientOptions?: LanguageClientOptions; connectionProvider?: IConnectionProvider; @@ -28,8 +27,6 @@ export class LanguageClientWrapper { private languageClient?: MonacoLanguageClient; private languageClientConfig?: LanguageClientConfig; - private messageTransports?: MessageTransports; - private connectionProvider?: IConnectionProvider; private languageId: string; private worker?: Worker; private port?: MessagePort; @@ -66,14 +63,6 @@ export class LanguageClientWrapper { return this.languageClient !== undefined && this.languageClient.isRunning(); } - getMessageTransports(): MessageTransports | undefined { - return this.messageTransports; - } - - getConnectionProvider(): IConnectionProvider | undefined { - return this.connectionProvider; - } - async start() { if (this.languageClientConfig) { return this.startLanguageClientConnection(); @@ -92,12 +81,9 @@ export class LanguageClientWrapper { * @param updatedWorker Set a new worker here that should be used. keepWorker has no effect then, as we want to dispose of the prior workers * @param keepWorker Set to true if worker should not be disposed */ - async restartLanguageClient(updatedWorker?: Worker, keepWorker?: boolean): Promise { - if (updatedWorker) { - await this.disposeLanguageClient(false); - } else { - await this.disposeLanguageClient(keepWorker); - } + async restartLanguageClient(updatedWorker?: Worker, keepWorker: boolean = false): Promise { + await this.disposeLanguageClient(keepWorker); + this.worker = updatedWorker; if (this.languageClientConfig) { this.logger?.info('Re-Starting monaco-languageclient'); @@ -111,84 +97,101 @@ export class LanguageClientWrapper { } } - private startLanguageClientConnection(): Promise { - if (this.languageClient && this.languageClient.isRunning()) { - this.logger?.info('monaco-languageclient already running!'); + protected async startLanguageClientConnection(): Promise { + if (this.languageClient?.isRunning() ?? false) { + this.logger?.info('startLanguageClientConnection: monaco-languageclient already running!'); return Promise.resolve(); } + const lccOptions = this.languageClientConfig?.options; - const lcConfig = this.languageClientConfig?.options; - // allow to fully override the clonnecetionProvider - // if it is undefined it will be created from the message transports in handleLanguageClientStart - this.connectionProvider = this.languageClientConfig?.connectionProvider; - + // eslint-disable-next-line no-async-promise-executor return new Promise((resolve, reject) => { - if (lcConfig?.$type === 'WebSocket' || lcConfig?.$type === 'WebSocketUrl') { - const url = createUrl(lcConfig); - const webSocket = new WebSocket(url); - - webSocket.onopen = async () => { - const socket = toSocket(webSocket); - this.messageTransports = await this.connectionProvider?.get('') ?? { - reader: new WebSocketMessageReader(socket), - writer: new WebSocketMessageWriter(socket) - }; - this.handleLanguageClientStart(resolve, reject); - }; - webSocket.onerror = (ev: Event) => { - const languageClientError: LanguageClientError = { - message: `languageClientWrapper (${this.name}): Websocket connection failed.`, - error: (ev as ErrorEvent).error ?? 'No error was provided.' - }; - reject(languageClientError); - }; + if (lccOptions === undefined) { + reject('Unable to start languageclient, because configuration options are not provided.'); } else { - if (!this.worker) { - if (lcConfig?.$type === 'WorkerConfig') { - const workerConfig = lcConfig as WorkerConfigOptions; - this.worker = new Worker(new URL(workerConfig.url, import.meta.url).href, { - type: workerConfig.type, - name: workerConfig.workerName - }); - - this.worker.onerror = (ev) => { - const languageClientError: LanguageClientError = { - message: `languageClientWrapper (${this.name}): Illegal worker configuration detected. Potentially the url is wrong.`, - error: ev.error ?? 'No error was provided.' - }; - reject(languageClientError); - }; - } else { - const workerDirectConfig = lcConfig as WorkerConfigDirect; - this.worker = workerDirectConfig.worker; - } - if (lcConfig?.messagePort) { - this.port = lcConfig.messagePort; - } + if (lccOptions.$type === 'WebSocketDirect' || lccOptions.$type === 'WebSocketParams' || lccOptions.$type === 'WebSocketUrl') { + this.initMessageTransportWebSocket(lccOptions, resolve, reject); + } else { + // init of worker and start of languageclient can be handled directly, because worker available already + this.initMessageTransportWorker(lccOptions, resolve, reject); } - - const startWorkerLS = async (port: MessagePort | Worker) => { - this.messageTransports = await this.connectionProvider?.get('') ?? { - reader: new BrowserMessageReader(port), - writer: new BrowserMessageWriter(port) - }; - this.handleLanguageClientStart(resolve, reject); - }; - startWorkerLS(this.port ? this.port : this.worker); } }); } - private async handleLanguageClientStart(resolve: () => void, reject: (reason?: unknown) => void) { - if (!this.connectionProvider) { - this.connectionProvider = { - get: () => { - // even with the check "if (this.messageTransports)" the compiler requires the ! here - return Promise.resolve(this.messageTransports!); - } + protected async initMessageTransportWebSocket(lccOptions: WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl, + resolve: () => void, reject: (reason?: unknown) => void) { + const webSocket = lccOptions.$type === 'WebSocketDirect' ? lccOptions.webSocket : new WebSocket(createUrl(lccOptions)); + + const createMessageTransports = (transport: Worker | MessagePort | WebSocket) => { + const iWebSocket = toSocket(transport as WebSocket); + return { + reader: new WebSocketMessageReader(iWebSocket), + writer: new WebSocketMessageWriter(iWebSocket) }; + }; + + // if websocket is already open, then start the languageclient directly + if (webSocket.readyState === WebSocket.OPEN) { + await this.performLanguageClientStart(webSocket, createMessageTransports, resolve, reject); } + // otherwise start on open + webSocket.onopen = async () => { + await this.performLanguageClientStart(webSocket, createMessageTransports, resolve, reject); + }; + webSocket.onerror = (ev: Event) => { + const languageClientError: LanguageClientError = { + message: `languageClientWrapper (${this.name}): Websocket connection failed.`, + error: (ev as ErrorEvent).error ?? 'No error was provided.' + }; + reject(languageClientError); + }; + } + + protected async initMessageTransportWorker(lccOptions: WorkerConfigDirect | WorkerConfigOptions, resolve: () => void, reject: (reason?: unknown) => void) { + if (!this.worker) { + if (lccOptions.$type === 'WorkerConfig') { + const workerConfig = lccOptions as WorkerConfigOptions; + this.worker = new Worker(new URL(workerConfig.url, import.meta.url).href, { + type: workerConfig.type, + name: workerConfig.workerName + }); + + this.worker.onerror = (ev) => { + const languageClientError: LanguageClientError = { + message: `languageClientWrapper (${this.name}): Illegal worker configuration detected. Potentially the url is wrong.`, + error: ev.error ?? 'No error was provided.' + }; + reject(languageClientError); + }; + } else { + const workerDirectConfig = lccOptions as WorkerConfigDirect; + this.worker = workerDirectConfig.worker; + } + if (lccOptions.messagePort !== undefined) { + this.port = lccOptions.messagePort; + } + } + + const portOrWorker = this.port ? this.port : this.worker; + const createMessageTransports = (transport: Worker | MessagePort | WebSocket) => { + return { + reader: new BrowserMessageReader(transport), + writer: new BrowserMessageWriter(transport) + }; + }; + await this.performLanguageClientStart(portOrWorker, createMessageTransports, resolve, reject); + } + + protected async performLanguageClientStart(transport: Worker | MessagePort | WebSocket, + createMessageTransports: (transport: Worker | MessagePort | WebSocket) => MessageTransports, + resolve: () => void, reject: (reason?: unknown) => void) { + // do not perform another start attempt if already running + if (this.languageClient?.isRunning() ?? false) { + this.logger?.info('performLanguageClientStart: monaco-languageclient already running!'); + resolve(); + } const mlcConfig = { name: this.languageClientConfig?.name ?? 'Monaco Wrapper Language Client', @@ -201,18 +204,22 @@ export class LanguageClientWrapper { closed: () => ({ action: CloseAction.DoNotRestart }) } }, - - connectionProvider: this.connectionProvider! + connectionProvider: this.languageClientConfig?.connectionProvider ?? { + get: async () => createMessageTransports(transport) + } }; + this.languageClient = new MonacoLanguageClient(mlcConfig); + const messageTransports = await mlcConfig.connectionProvider.get('utf-8'); - const lcConfig = this.languageClientConfig?.options; + const lccOptions = this.languageClientConfig?.options; + this.initRestartConfiguration(messageTransports, lccOptions?.restartOptions); - this.messageTransports?.reader.onClose(async () => { + messageTransports.reader.onClose(async () => { await this.languageClient?.stop(); - if ((lcConfig?.$type === 'WebSocket' || lcConfig?.$type === 'WebSocketUrl') && lcConfig.stopOptions) { - const stopOptions = lcConfig.stopOptions; + if ((lccOptions?.$type === 'WebSocketParams' || lccOptions?.$type === 'WebSocketUrl') && lccOptions.stopOptions) { + const stopOptions = lccOptions.stopOptions; stopOptions.onCall(this.getLanguageClient()); if (stopOptions.reportStatus !== undefined) { this.logger?.info(this.reportStatus().join('\n')); @@ -223,8 +230,8 @@ export class LanguageClientWrapper { try { await this.languageClient.start(); - if ((lcConfig?.$type === 'WebSocket' || lcConfig?.$type === 'WebSocketUrl') && lcConfig.startOptions) { - const startOptions = lcConfig.startOptions; + if ((lccOptions?.$type === 'WebSocketParams' || lccOptions?.$type === 'WebSocketUrl') && lccOptions.startOptions) { + const startOptions = lccOptions.startOptions; startOptions.onCall(this.getLanguageClient()); if (startOptions.reportStatus !== undefined) { this.logger?.info(this.reportStatus().join('\n')); @@ -241,14 +248,47 @@ export class LanguageClientWrapper { resolve(); } - private disposeWorker(keepWorker?: boolean) { + protected initRestartConfiguration(messageTransports: MessageTransports, restartOptions?: LanguageClientRestartOptions) { + if (restartOptions !== undefined) { + let retry = 0; + + const readerOnError = messageTransports.reader.onError(() => restartLC); + const readerOnClose = messageTransports.reader.onClose(() => restartLC); + + const restartLC = async () => { + if (this.isStarted()) { + try { + readerOnError.dispose(); + readerOnClose.dispose(); + + await this.restartLanguageClient(this.worker, restartOptions.keepWorker); + } finally { + retry++; + if (retry > (restartOptions.retries) && !this.isStarted()) { + this.logger?.info('Disabling Language Client. Failed to start clangd after 5 retries'); + } else { + setTimeout(async () => { + await this.restartLanguageClient(this.worker, restartOptions.keepWorker); + }, restartOptions.timeout); + } + } + } + }; + } + const successCallback = messageTransports.reader.listen(() => { + this.logger?.info('MessageTransport Reader started listening.'); + successCallback.dispose(); + }); + } + + protected disposeWorker(keepWorker?: boolean) { if (keepWorker === undefined || keepWorker === false) { this.worker?.terminate(); this.worker = undefined; } } - public async disposeLanguageClient(keepWorker?: boolean): Promise { + async disposeLanguageClient(keepWorker?: boolean): Promise { // If there is no language client, try to terminate the worker if (!this.languageClient) { this.disposeWorker(keepWorker); diff --git a/packages/wrapper/src/utils.ts b/packages/wrapper/src/utils.ts index 04782788f..ae925f9e3 100644 --- a/packages/wrapper/src/utils.ts +++ b/packages/wrapper/src/utils.ts @@ -4,22 +4,22 @@ * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { WebSocketConfigOptions, WebSocketConfigOptionsUrl, WorkerConfigDirect, WorkerConfigOptions } from './commonTypes.js'; +import { WebSocketUrlParams, WebSocketUrlString, WorkerConfigDirect, WorkerConfigOptions } from 'monaco-languageclient'; import { CodePlusFileExt, CodePlusUri, CodeResources } from './editorAppBase.js'; import { EditorAppClassic } from './editorAppClassic.js'; import { UserConfig } from './userConfig.js'; import { EditorAppExtended } from './editorAppExtended.js'; -export const createUrl = (config: WebSocketConfigOptions | WebSocketConfigOptionsUrl) => { +export const createUrl = (config: WebSocketUrlParams | WebSocketUrlString) => { let buildUrl = ''; - if ((config as WebSocketConfigOptionsUrl).url) { - const options = config as WebSocketConfigOptionsUrl; + if ((config as WebSocketUrlString).url) { + const options = config as WebSocketUrlString; if (!options.url.startsWith('ws://') && !options.url.startsWith('wss://')) { throw new Error(`This is not a proper websocket url: ${options.url}`); } buildUrl = options.url; } else { - const options = config as WebSocketConfigOptions; + const options = config as WebSocketUrlParams; const protocol = options.secured ? 'wss' : 'ws'; buildUrl = `${protocol}://${options.host}`; if (options.port !== undefined) { diff --git a/packages/wrapper/test/utils.test.ts b/packages/wrapper/test/utils.test.ts index 7ab0e4536..5b07c0bd5 100644 --- a/packages/wrapper/test/utils.test.ts +++ b/packages/wrapper/test/utils.test.ts @@ -4,7 +4,8 @@ * ------------------------------------------------------------------------------------------ */ import { describe, expect, test } from 'vitest'; -import { WebSocketConfigOptions, WebSocketConfigOptionsUrl, createUrl } from 'monaco-editor-wrapper'; +import { WebSocketConfigOptionsParams, WebSocketConfigOptionsUrl } from 'monaco-languageclient'; +import { createUrl } from 'monaco-editor-wrapper'; describe('createUrl', () => { @@ -14,7 +15,7 @@ describe('createUrl', () => { host: 'localhost', port: 30000, path: 'sampleServer' - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('ws://localhost:30000/sampleServer'); }); @@ -25,7 +26,7 @@ describe('createUrl', () => { host: 'localhost', port: 30000, path: 'sampleServer' - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('wss://localhost:30000/sampleServer'); }); @@ -35,7 +36,7 @@ describe('createUrl', () => { secured: true, host: 'localhost', path: 'sampleServer' - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('wss://localhost/sampleServer'); }); @@ -45,7 +46,7 @@ describe('createUrl', () => { secured: true, host: 'localhost', port: 30000 - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('wss://localhost:30000'); }); @@ -54,7 +55,7 @@ describe('createUrl', () => { const url = createUrl({ secured: true, host: 'localhost' - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('wss://localhost'); }); @@ -64,7 +65,7 @@ describe('createUrl', () => { secured: false, host: 'localhost', port: 80 - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('ws://localhost'); }); @@ -75,7 +76,7 @@ describe('createUrl', () => { host: 'localhost', port: 80, path: 'sampleServer' - } as WebSocketConfigOptions); + } as WebSocketConfigOptionsParams); expect(url).toBe('ws://localhost/sampleServer'); }); diff --git a/packages/wrapper/tsconfig.build.json b/packages/wrapper/tsconfig.build.json index e41c77a38..0cb7e416d 100644 --- a/packages/wrapper/tsconfig.build.json +++ b/packages/wrapper/tsconfig.build.json @@ -2,11 +2,7 @@ "extends": "../../tsconfig.json", "compilerOptions": { "rootDir": "./build", - "noEmit": true, - // all other types are not needed here - "types": [ - "node" - ] + "noEmit": true }, "include": [ "build/**/*.ts", diff --git a/packages/wrapper/tsconfig.src.json b/packages/wrapper/tsconfig.src.json index 47c7bcf03..5f42eb538 100644 --- a/packages/wrapper/tsconfig.src.json +++ b/packages/wrapper/tsconfig.src.json @@ -7,10 +7,7 @@ // because vscode-jsonrpc requires DedicatedWorkerGlobalScope // we are required to include both DOM and WebWorker libs // the only way out currently is to disable lib checking - "skipLibCheck": true, - "types": [ - "vscode" - ] + "skipLibCheck": true }, "references": [{ "path": "../client/tsconfig.src.json", diff --git a/packages/wrapper/tsconfig.test.json b/packages/wrapper/tsconfig.test.json index 606d704d7..143bf44ec 100644 --- a/packages/wrapper/tsconfig.test.json +++ b/packages/wrapper/tsconfig.test.json @@ -2,7 +2,11 @@ "extends": "./tsconfig.src.json", "compilerOptions": { "noEmit": true, - "rootDir": "test" + "rootDir": "test", + // because vscode-jsonrpc requires DedicatedWorkerGlobalScope + // we are required to include both DOM and WebWorker libs + // the only way out currently is to disable lib checking + "skipLibCheck": true }, "references": [{ "path": "./tsconfig.src.json" diff --git a/tsconfig.json b/tsconfig.json index bc75e2d94..cc281bbe0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Bundler", + "module": "NodeNext", + "moduleResolution": "NodeNext", "lib": [ "ESNext", "DOM" From ce32c0f31b9a466eb21263010283573640ec2ea2 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Wed, 4 Sep 2024 15:40:42 +0200 Subject: [PATCH 3/7] Support multiple languageclients --- .../src/eclipse.jdt.ls/client/main.ts | 28 +- packages/examples/src/groovy/client/main.ts | 12 +- .../examples/src/json/client/wrapperWs.ts | 32 +- .../langium-dsl/config/classicConfig.ts | 12 +- .../langium-dsl/config/extendedConfig.ts | 18 +- .../config/wrapperStatemachineConfig.ts | 20 +- packages/examples/src/python/client/config.ts | 54 ++-- packages/examples/src/ts/wrapperAdvanced.ts | 285 +++++------------- packages/examples/wrapper_adv.html | 17 +- packages/examples/wrapper_ws.html | 2 +- packages/wrapper/src/userConfig.ts | 2 +- packages/wrapper/src/utils.ts | 19 +- packages/wrapper/src/wrapper.ts | 69 +++-- .../test/languageClientWrapper.test.ts | 62 ++-- packages/wrapper/test/wrapper.test.ts | 2 +- 15 files changed, 267 insertions(+), 367 deletions(-) diff --git a/packages/examples/src/eclipse.jdt.ls/client/main.ts b/packages/examples/src/eclipse.jdt.ls/client/main.ts index 48fe77d4a..467d3b04e 100644 --- a/packages/examples/src/eclipse.jdt.ls/client/main.ts +++ b/packages/examples/src/eclipse.jdt.ls/client/main.ts @@ -54,20 +54,22 @@ export const runEclipseJdtLsClient = () => { } } }, - languageClientConfig: { - languageId: 'java', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:30003/jdtls' - }, - clientOptions: { - documentSelector: ['java'], - workspaceFolder: { - index: 0, - name: 'workspace', - uri: vscode.Uri.parse(`${eclipseJdtLsConfig.basePath}/workspace`) + languageClientConfigs: { + java: { + languageId: 'java', + options: { + $type: 'WebSocketUrl', + url: 'ws://localhost:30003/jdtls' }, - }, + clientOptions: { + documentSelector: ['java'], + workspaceFolder: { + index: 0, + name: 'workspace', + uri: vscode.Uri.parse(`${eclipseJdtLsConfig.basePath}/workspace`) + } + } + } } }; diff --git a/packages/examples/src/groovy/client/main.ts b/packages/examples/src/groovy/client/main.ts index 9996c2e96..40489a2fe 100644 --- a/packages/examples/src/groovy/client/main.ts +++ b/packages/examples/src/groovy/client/main.ts @@ -50,11 +50,13 @@ const userConfig: UserConfig = { } } }, - languageClientConfig: { - languageId: 'groovy', - options: { - $type: 'WebSocketUrl', - url: `ws://localhost:${groovyConfig.port}${groovyConfig.path}` + languageClientConfigs: { + groovy: { + languageId: 'groovy', + options: { + $type: 'WebSocketUrl', + url: `ws://localhost:${groovyConfig.port}${groovyConfig.path}` + } } } }; diff --git a/packages/examples/src/json/client/wrapperWs.ts b/packages/examples/src/json/client/wrapperWs.ts index 3582b2f9d..2d8171aff 100644 --- a/packages/examples/src/json/client/wrapperWs.ts +++ b/packages/examples/src/json/client/wrapperWs.ts @@ -51,22 +51,24 @@ export const jsonClientUserConfig: UserConfig = { } } }, - languageClientConfig: { - languageId: 'json', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:30000/sampleServer', - startOptions: { - onCall: () => { - console.log('Connected to socket.'); + languageClientConfigs: { + json: { + languageId: 'json', + options: { + $type: 'WebSocketUrl', + url: 'ws://localhost:30000/sampleServer', + startOptions: { + onCall: () => { + console.log('Connected to socket.'); + }, + reportStatus: true }, - reportStatus: true - }, - stopOptions: { - onCall: () => { - console.log('Disconnected from socket.'); - }, - reportStatus: true + stopOptions: { + onCall: () => { + console.log('Disconnected from socket.'); + }, + reportStatus: true + } } } } diff --git a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts index 2cb396efa..629a2dda4 100644 --- a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts @@ -49,11 +49,13 @@ export const setupLangiumClientClassic = async (): Promise => { } } }, - languageClientConfig: { - languageId: 'langium', - options: { - $type: 'WorkerDirect', - worker: langiumWorker + languageClientConfigs: { + langium: { + languageId: 'langium', + options: { + $type: 'WorkerDirect', + worker: langiumWorker + } } } }; diff --git a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts index a1fe8bb6a..f9c66a6c4 100644 --- a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts @@ -76,14 +76,16 @@ export const setupLangiumClientExtended = async (): Promise => { } } }, - languageClientConfig: { - languageId: 'langium', - options: { - $type: 'WorkerDirect', - worker: langiumWorker - }, - connectionProvider: { - get: async () => ({ reader, writer }) + languageClientConfigs: { + langium: { + languageId: 'langium', + options: { + $type: 'WorkerDirect', + worker: langiumWorker + }, + connectionProvider: { + get: async () => ({ reader, writer }) + } } } }; diff --git a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts index cae12bddf..1f660fd0c 100644 --- a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts +++ b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts @@ -33,14 +33,16 @@ export const createLangiumGlobalConfig = async (params: { }; } - const languageClientConfig: LanguageClientConfig | undefined = params.useLanguageClient && params.worker ? { - languageId: 'statemachine', - options: { - $type: 'WorkerDirect', - worker: params.worker, - messagePort: params.messagePort, - }, - connectionProvider: params.connectionProvider + const languageClientConfigs: Record | undefined = params.useLanguageClient && params.worker ? { + statemachine: { + languageId: 'statemachine', + options: { + $type: 'WorkerDirect', + worker: params.worker, + messagePort: params.messagePort, + }, + connectionProvider: params.connectionProvider + } } : undefined; return { @@ -92,7 +94,7 @@ export const createLangiumGlobalConfig = async (params: { } } }, - languageClientConfig, + languageClientConfigs, loggerConfig: { enabled: true, debugEnabled: true diff --git a/packages/examples/src/python/client/config.ts b/packages/examples/src/python/client/config.ts index 961186c61..5be57b2e8 100644 --- a/packages/examples/src/python/client/config.ts +++ b/packages/examples/src/python/client/config.ts @@ -28,35 +28,37 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s const writer = new WebSocketMessageWriter(iWebSocket); return { - languageClientConfig: { - languageId: 'python', - name: 'Python Language Server Example', - options: { - $type: 'WebSocketDirect', - webSocket: webSocket, - startOptions: { - onCall: (languageClient?: MonacoLanguageClient) => { - setTimeout(() => { - ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { - vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { - languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + languageClientConfigs: { + python: { + languageId: 'python', + name: 'Python Language Server Example', + options: { + $type: 'WebSocketDirect', + webSocket: webSocket, + startOptions: { + onCall: (languageClient?: MonacoLanguageClient) => { + setTimeout(() => { + ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { + vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { + languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + }); }); - }); - }, 250); + }, 250); + }, + reportStatus: true, + } + }, + clientOptions: { + documentSelector: ['python'], + workspaceFolder: { + index: 0, + name: 'workspace', + uri: vscode.Uri.parse(workspaceRoot) }, - reportStatus: true, - } - }, - clientOptions: { - documentSelector: ['python'], - workspaceFolder: { - index: 0, - name: 'workspace', - uri: vscode.Uri.parse(workspaceRoot) }, - }, - connectionProvider: { - get: async () => ({ reader, writer }) + connectionProvider: { + get: async () => ({ reader, writer }) + } } }, wrapperConfig: { diff --git a/packages/examples/src/ts/wrapperAdvanced.ts b/packages/examples/src/ts/wrapperAdvanced.ts index dafa8931c..679ba6aa0 100644 --- a/packages/examples/src/ts/wrapperAdvanced.ts +++ b/packages/examples/src/ts/wrapperAdvanced.ts @@ -3,233 +3,114 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ +import * as vscode from 'vscode'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import '@codingame/monaco-vscode-standalone-languages'; -import '@codingame/monaco-vscode-standalone-typescript-language-features'; -import { EditorAppConfigClassic, LanguageClientError, MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +// this is required syntax highlighting +import '@codingame/monaco-vscode-json-default-extension'; +import '@codingame/monaco-vscode-python-default-extension'; +import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; +import { MonacoLanguageClient } from 'monaco-languageclient'; export const configureMonacoWorkers = () => { useWorkerFactory({ ignoreMapping: true, workerLoaders: { - editorWorkerService: () => new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), { type: 'module' }), - javascript: () => new Worker(new URL('monaco-editor-wrapper/workers/module/ts', import.meta.url), { type: 'module' }), + editorWorkerService: () => new Worker(new URL('monaco-editor/esm/vs/editor/editor.worker.js', import.meta.url), { type: 'module' }) } }); }; -const wrapper42 = new MonacoEditorLanguageClientWrapper(); -const wrapper43 = new MonacoEditorLanguageClientWrapper(); -const wrapper44 = new MonacoEditorLanguageClientWrapper(); - -const wrapper42Config: UserConfig = { - id: '42', - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride() - }, - debugLogging: true - }, - editorAppConfig: { - $type: 'classic', - codeResources: { - original: { - text: `This line is equal. -This number is different 2002 -Misspeelled! -Same again.`, - fileExt: 'txt' +export const runMultipleLanguageClientsExample = async () => { + const text = `{ + "$schema": "http://json.schemastore.org/coffeelint", + "line_endings": {"value": "unix"} +}`; + const userConfig: UserConfig = { + id: '42', + wrapperConfig: { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride() }, - main: { - text: `This line is equal. -This number is different 2022 -Misspelled! -Same again.`, - fileExt: 'txt' - } - }, - useDiffEditor: true, - } - }, - languageClientConfig: { - languageId: 'json', - name: 'wrapper42 language client', - options: { - $type: 'WebSocketParams', - host: 'localhost', - port: 30000, - path: 'sampleServer', - secured: false - } - } -}; - -const wrapper43Config: UserConfig = { - id: '43', - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride() + debugLogging: true }, - debugLogging: true - }, - editorAppConfig: { - $type: 'classic', - codeResources: { - original: { - text: 'This line is equal.\nThis number is different 3022.\nMisspelled!Same again.', - fileExt: 'txt' + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text, + fileExt: 'json' + } }, - main: { - text: 'This line is equal.\nThis number is different 3002.\nMisspelled!Same again.', - fileExt: 'txt' + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.wordBasedSuggestions': 'off' + }) } - }, - useDiffEditor: true, - editorOptions: { - lineNumbers: 'off' - }, - diffEditorOptions: { - lineNumbers: 'off' } - } - } -}; - -const wrapper44Config: UserConfig = { - id: '44', - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride() - }, - debugLogging: true }, - editorAppConfig: { - $type: 'classic', - codeResources: { - main: { - text: `function logMe() { - console.log('Hello monaco-editor-wrapper!'); -};`, - fileExt: 'js' + languageClientConfigs: { + json: { + languageId: 'json', + name: 'JSON Client', + options: { + $type: 'WebSocketParams', + host: 'localhost', + port: 30000, + path: 'sampleServer', + secured: false } }, - useDiffEditor: false, - editorOptions: { - minimap: { - enabled: true + python: { + languageId: 'python', + name: 'Python Client', + options: { + $type: 'WebSocketParams', + host: 'localhost', + port: 30001, + path: 'pyright', + secured: false, + extraParams: { + authorization: 'UserAuth' + }, + startOptions: { + onCall: (languageClient?: MonacoLanguageClient) => { + setTimeout(() => { + ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { + vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { + languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + }); + }); + }, 250); + }, + reportStatus: true, + } }, - theme: 'vs-dark' + clientOptions: { + documentSelector: ['python'], + workspaceFolder: { + index: 0, + name: 'workspace', + uri: vscode.Uri.parse('/workspace') + } + } } } - } -}; + }; -const startWrapper42 = async () => { - await wrapper42.initAndStart(wrapper42Config, document.getElementById('monaco-editor-root-42')); - console.log('wrapper42 was started.'); -}; + const htmlElement = document.getElementById('monaco-editor-root'); + const wrapper = new MonacoEditorLanguageClientWrapper(); -const startWrapper43 = async () => { - await wrapper43.initAndStart(wrapper43Config, document.getElementById('monaco-editor-root-43')); - console.log('wrapper43 was started.'); -}; -const startWrapper44 = async () => { - await wrapper44.initAndStart(wrapper44Config, document.getElementById('monaco-editor-root-44')); - console.log('wrapper44 was started.'); -}; - -const sleepOne = (milliseconds: number) => { - setTimeout(async () => { - alert(`Updating editors after ${milliseconds}ms`); - - await wrapper42.dispose(); - wrapper42Config.languageClientConfig = undefined; - const appConfig42 = wrapper42Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic; - appConfig42.codeResources = { - main: { - text: `function logMe() { - console.log('Hello swap editors!'); -};`, - fileExt: 'js' - } - }; - appConfig42.useDiffEditor = false; - const w42Start = wrapper42.initAndStart(wrapper42Config, document.getElementById('monaco-editor-root-42')); - - const w43Start = await wrapper43.updateCodeResources({ - main: { - text: 'text 5678', - fileExt: 'txt' - }, - original: { - text: 'text 1234', - fileExt: 'txt' - } - }); - - await wrapper44.dispose(); - const appConfig44 = wrapper44Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic; - appConfig44.useDiffEditor = true; - appConfig44.codeResources = { - original: { - text: 'oh la la la!', - fileExt: 'txt' - }, - main: { - text: 'oh lo lo lo!', - fileExt: 'txt' - } - }; - // This affects all editors globally and is only effective - // if it is not in contrast to one configured later - appConfig44.editorOptions = { - theme: 'vs-light' - }; - const w44Start = wrapper44.initAndStart(wrapper44Config, document.getElementById('monaco-editor-root-44')); - - await w42Start; - console.log('Restarted wrapper42.'); - await w43Start; - console.log('Updated diffmodel of wrapper43.'); - await w44Start; - console.log('Restarted wrapper44.'); - }, milliseconds); -}; - -const sleepTwo = (milliseconds: number) => { - setTimeout(async () => { - alert(`Updating last editor after ${milliseconds}ms`); - - await wrapper44.dispose(); - const appConfig44 = wrapper44Config.wrapperConfig.editorAppConfig as EditorAppConfigClassic; - appConfig44.useDiffEditor = false; - appConfig44.editorOptions = { - theme: 'vs-dark' - }; - await wrapper44.initAndStart(wrapper44Config, document.getElementById('monaco-editor-root-44')); - console.log('Restarted wrapper44.'); - }, milliseconds); -}; - -export const runAdvancedExample = async () => { try { - await startWrapper43(); - await startWrapper44(); - try { - await startWrapper42(); - } catch (e) { - console.log(`Catched expected connection error: ${(e as LanguageClientError).message}`); - } - - // change the editors config, content or swap normal and diff editors after five seconds - sleepOne(5000); - - // change last editor to regular mode - sleepTwo(10000); + document.querySelector('#button-start')?.addEventListener('click', async () => { + await wrapper.initAndStart(userConfig, htmlElement); + }); + document.querySelector('#button-dispose')?.addEventListener('click', async () => { + await wrapper.dispose(); + }); } catch (e) { console.error(e); } diff --git a/packages/examples/wrapper_adv.html b/packages/examples/wrapper_adv.html index e8e79f041..58085632b 100644 --- a/packages/examples/wrapper_adv.html +++ b/packages/examples/wrapper_adv.html @@ -2,25 +2,22 @@ + Multiple Language Clients - Multiple Editors -

Multiple Editors -
-
-
-
-
-
+

Multiple Language Clients

+ + +
diff --git a/packages/examples/wrapper_ws.html b/packages/examples/wrapper_ws.html index d1fd7c2f3..82472b9bc 100644 --- a/packages/examples/wrapper_ws.html +++ b/packages/examples/wrapper_ws.html @@ -2,9 +2,9 @@ + JSON Language Client & Language Server (Web Socket) - JSON Language Client & Language Server (Web Socket) diff --git a/packages/wrapper/src/userConfig.ts b/packages/wrapper/src/userConfig.ts index 463e5b74c..88a816a2e 100644 --- a/packages/wrapper/src/userConfig.ts +++ b/packages/wrapper/src/userConfig.ts @@ -18,5 +18,5 @@ export type UserConfig = { id?: string; loggerConfig?: LoggerConfig; wrapperConfig: WrapperConfig; - languageClientConfig?: LanguageClientConfig; + languageClientConfigs?: Record; } diff --git a/packages/wrapper/src/utils.ts b/packages/wrapper/src/utils.ts index ae925f9e3..034888486 100644 --- a/packages/wrapper/src/utils.ts +++ b/packages/wrapper/src/utils.ts @@ -4,7 +4,7 @@ * ------------------------------------------------------------------------------------------ */ import * as vscode from 'vscode'; -import { WebSocketUrlParams, WebSocketUrlString, WorkerConfigDirect, WorkerConfigOptions } from 'monaco-languageclient'; +import { WebSocketUrlParams, WebSocketUrlString } from 'monaco-languageclient'; import { CodePlusFileExt, CodePlusUri, CodeResources } from './editorAppBase.js'; import { EditorAppClassic } from './editorAppClassic.js'; import { UserConfig } from './userConfig.js'; @@ -119,23 +119,6 @@ export const isReInitRequired = (editorApp: EditorAppClassic | EditorAppExtended let mustReInit = false; const config = userConfig.wrapperConfig.editorAppConfig; const prevConfig = previousUserConfig.wrapperConfig.editorAppConfig; - const prevWorkerOptions = previousUserConfig.languageClientConfig?.options; - const currentWorkerOptions = userConfig.languageClientConfig?.options; - const prevIsWorker = (prevWorkerOptions?.$type === 'WorkerDirect'); - const currentIsWorker = (currentWorkerOptions?.$type === 'WorkerDirect'); - const prevIsWorkerConfig = (prevWorkerOptions?.$type === 'WorkerConfig'); - const currentIsWorkerConfig = (currentWorkerOptions?.$type === 'WorkerConfig'); - - // check if both are configs and the workers are both undefined - if (prevIsWorkerConfig && !prevIsWorker && currentIsWorkerConfig && !currentIsWorker) { - mustReInit = (prevWorkerOptions as WorkerConfigOptions).url !== (currentWorkerOptions as WorkerConfigOptions).url; - // check if both are workers and configs are both undefined - } else if (!prevIsWorkerConfig && prevIsWorker && !currentIsWorkerConfig && currentIsWorker) { - mustReInit = (prevWorkerOptions as WorkerConfigDirect).worker !== (currentWorkerOptions as WorkerConfigDirect).worker; - // previous was worker and current config is not or the other way around - } else if (prevIsWorker && currentIsWorkerConfig || prevIsWorkerConfig && currentIsWorker) { - mustReInit = true; - } if (prevConfig.$type !== config.$type) { mustReInit = true; diff --git a/packages/wrapper/src/wrapper.ts b/packages/wrapper/src/wrapper.ts index d9630bb75..07f3bb592 100644 --- a/packages/wrapper/src/wrapper.ts +++ b/packages/wrapper/src/wrapper.ts @@ -23,7 +23,7 @@ export class MonacoEditorLanguageClientWrapper { private id: string; private editorApp: EditorAppClassic | EditorAppExtended | undefined; - private languageClientWrapper?: LanguageClientWrapper; + private languageClientWrappers: Map = new Map(); private logger: Logger = new Logger(); private initDone = false; private starting?: Promise; @@ -70,12 +70,18 @@ export class MonacoEditorLanguageClientWrapper { logger: this.logger }); - if (userConfig.languageClientConfig) { - this.languageClientWrapper = new LanguageClientWrapper(); - await this.languageClientWrapper.init({ - languageClientConfig: userConfig.languageClientConfig, - logger: this.logger - }); + const lccs = userConfig.languageClientConfigs; + if (lccs !== undefined && Object.entries(lccs).length > 0) { + + for (const [languageId, lcc] of Object.entries(lccs)) { + const lcw = new LanguageClientWrapper(); + this.languageClientWrappers.set(languageId, lcw); + + lcw.init({ + languageClientConfig: lcc, + logger: this.logger + }); + } } this.initDone = true; @@ -104,9 +110,12 @@ export class MonacoEditorLanguageClientWrapper { await this.editorApp?.init(); await this.editorApp?.createEditors(htmlElement); - if (this.languageClientWrapper?.haveLanguageClientConfig() ?? false) { - await this.languageClientWrapper?.start(); + for (const lcw of this.languageClientWrappers.values()) { + if (lcw.haveLanguageClientConfig()) { + await lcw.start(); + } } + this.markStarted(); } @@ -135,14 +144,19 @@ export class MonacoEditorLanguageClientWrapper { return false; } - if (this.languageClientWrapper?.haveLanguageClient() ?? false) { - return this.languageClientWrapper?.isStarted() ?? false; + for (const lcw of this.languageClientWrappers.values()) { + if (lcw.haveLanguageClient()) { + // as soon as one is not started return + if (!lcw.isStarted()) { + return false; + } + } } return true; } - haveLanguageClient(): boolean { - return this.languageClientWrapper !== undefined; + haveLanguageClients(): boolean { + return this.languageClientWrappers.size > 0; } getMonacoEditorApp() { @@ -157,12 +171,12 @@ export class MonacoEditorLanguageClientWrapper { return this.editorApp?.getDiffEditor(); } - getLanguageClientWrapper(): LanguageClientWrapper | undefined { - return this.languageClientWrapper; + getLanguageClientWrapper(languageId: string): LanguageClientWrapper | undefined { + return this.languageClientWrappers.get(languageId); } - getLanguageClient(): MonacoLanguageClient | undefined { - return this.languageClientWrapper?.getLanguageClient(); + getLanguageClient(languageId: string): MonacoLanguageClient | undefined { + return this.languageClientWrappers.get(languageId)?.getLanguageClient(); } getTextContents(): TextContents | undefined { @@ -177,8 +191,8 @@ export class MonacoEditorLanguageClientWrapper { return this.editorApp?.getModelRefs(); } - getWorker(): Worker | undefined { - return this.languageClientWrapper?.getWorker(); + getWorker(languageId: string): Worker | undefined { + return this.languageClientWrappers.get(languageId)?.getWorker(); } async updateCodeResources(codeResources?: CodeResources): Promise { @@ -200,17 +214,20 @@ export class MonacoEditorLanguageClientWrapper { /** * Disposes all application and editor resources, plus the languageclient (if used). */ - async dispose(disposeLanguageClient: boolean = true): Promise { + async dispose(disposeLanguageClients: boolean = true): Promise { this.markStopping(); this.editorApp?.disposeApp(); - if ((disposeLanguageClient && this.languageClientWrapper?.haveLanguageClient()) ?? false) { - await this.languageClientWrapper?.disposeLanguageClient(false); - this.editorApp = undefined; - await Promise.resolve('Monaco editor and languageclient completed disposed.'); - } - else { + if (disposeLanguageClients) { + for (const lcw of this.languageClientWrappers.values()) { + if (lcw.haveLanguageClient()) { + await lcw.disposeLanguageClient(false); + } + this.editorApp = undefined; + await Promise.resolve('Monaco editor and languageclient completed disposed.'); + } + } else { await Promise.resolve('Monaco editor has been disposed.'); } this.initDone = false; diff --git a/packages/wrapper/test/languageClientWrapper.test.ts b/packages/wrapper/test/languageClientWrapper.test.ts index e73e00987..387b8145f 100644 --- a/packages/wrapper/test/languageClientWrapper.test.ts +++ b/packages/wrapper/test/languageClientWrapper.test.ts @@ -13,7 +13,7 @@ describe('Test LanguageClientWrapper', () => { const wrapper = new MonacoEditorLanguageClientWrapper(); await wrapper.init(createBaseConfig('extended')); - const languageClientWrapper = wrapper.getLanguageClientWrapper(); + const languageClientWrapper = wrapper.getLanguageClientWrapper('unknown'); expect(languageClientWrapper).toBeUndefined(); }); @@ -47,17 +47,19 @@ describe('Test LanguageClientWrapper', () => { // setup the wrapper const config = createBaseConfig('extended'); - config.languageClientConfig = { - languageId: 'javascript', - options: { - $type: 'WorkerDirect', - worker + config.languageClientConfigs = { + javascript: { + languageId: 'javascript', + options: { + $type: 'WorkerDirect', + worker + } } }; const wrapper = new MonacoEditorLanguageClientWrapper(); await wrapper.init(config); - const languageClientWrapper = wrapper.getLanguageClientWrapper(); + const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); // start up & verify (don't wait for start to finish, just roll past it, we only care about the worker) @@ -71,17 +73,19 @@ describe('Test LanguageClientWrapper', () => { test('Constructor: config', async () => { const config = createBaseConfig('extended'); - config.languageClientConfig = { - languageId: 'javascript', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:12345/Tester' + config.languageClientConfigs = { + javascript: { + languageId: 'javascript', + options: { + $type: 'WebSocketUrl', + url: 'ws://localhost:12345/Tester' + } } }; const wrapper = new MonacoEditorLanguageClientWrapper(); await wrapper.init(config); - const languageClientWrapper = wrapper.getLanguageClientWrapper(); + const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); @@ -89,18 +93,20 @@ describe('Test LanguageClientWrapper', () => { test('Start: unreachable url', async () => { const config = createBaseConfig('extended'); - config.languageClientConfig = { - languageId: 'javascript', - name: 'test-unreachable', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:12345/Tester' + config.languageClientConfigs = { + javascript: { + languageId: 'javascript', + name: 'test-unreachable', + options: { + $type: 'WebSocketUrl', + url: 'ws://localhost:12345/Tester' + } } }; const wrapper = new MonacoEditorLanguageClientWrapper(); await wrapper.init(config); - const languageClientWrapper = wrapper.getLanguageClientWrapper(); + const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); @@ -124,18 +130,20 @@ describe('Test LanguageClientWrapper', () => { test('Start: unreachable worker url', async () => { const config = createBaseConfig('extended'); - config.languageClientConfig = { - languageId: 'javascript', - options: { - $type: 'WorkerConfig', - url: new URL('http://localhost:20101'), - type: 'classic' + config.languageClientConfigs = { + javascript: { + languageId: 'javascript', + options: { + $type: 'WorkerConfig', + url: new URL('http://localhost:20101'), + type: 'classic' + } } }; const wrapper = new MonacoEditorLanguageClientWrapper(); await wrapper.init(config); - const languageClientWrapper = wrapper.getLanguageClientWrapper(); + const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); diff --git a/packages/wrapper/test/wrapper.test.ts b/packages/wrapper/test/wrapper.test.ts index 161080715..648d88646 100644 --- a/packages/wrapper/test/wrapper.test.ts +++ b/packages/wrapper/test/wrapper.test.ts @@ -13,7 +13,7 @@ describe('Test MonacoEditorLanguageClientWrapper', () => { test('New wrapper has undefined editor', () => { const wrapper = new MonacoEditorLanguageClientWrapper(); - expect(wrapper.haveLanguageClient()).toBeFalsy(); + expect(wrapper.haveLanguageClients()).toBeFalsy(); expect(wrapper.getEditor()).toBeUndefined(); }); From 62b85616510876783d7bd425cd947d2a11790261 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Thu, 5 Sep 2024 11:09:19 +0200 Subject: [PATCH 4/7] LanguageClient allows to specify MessageTransport directly --- .gitignore | 1 + packages/client/src/client.ts | 16 +- packages/client/src/commonTypes.ts | 2 +- packages/examples/src/bare/client.ts | 6 +- .../src/eclipse.jdt.ls/client/main.ts | 10 +- packages/examples/src/groovy/client/main.ts | 8 +- .../examples/src/json/client/wrapperWs.ts | 28 ++-- .../langium-dsl/config/classicConfig.ts | 8 +- .../langium-dsl/config/extendedConfig.ts | 12 +- .../config/wrapperStatemachineConfig.ts | 18 ++- .../examples/src/langium/statemachine/main.ts | 4 +- packages/examples/src/python/client/config.ts | 36 ++--- packages/examples/src/ts/wrapperAdvanced.ts | 56 +++---- packages/wrapper/src/index.ts | 88 ++--------- packages/wrapper/src/languageClientWrapper.ts | 143 +++++++----------- packages/wrapper/src/workerFactory.ts | 11 +- packages/wrapper/src/wrapper.ts | 10 +- packages/wrapper/test/helper.ts | 9 -- .../test/languageClientWrapper.test.ts | 80 +++++----- .../wrapper/test/worker/langium-server.ts | 24 +++ 20 files changed, 251 insertions(+), 319 deletions(-) create mode 100644 packages/wrapper/test/worker/langium-server.ts diff --git a/.gitignore b/.gitignore index 07e4af692..22a1810f5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ lib node_modules *.tsbuildinfo .angular +__screenshots__ diff --git a/packages/client/src/client.ts b/packages/client/src/client.ts index 1f84cfe66..9a3bd7ed9 100644 --- a/packages/client/src/client.ts +++ b/packages/client/src/client.ts @@ -5,26 +5,22 @@ import { BaseLanguageClient, MessageTransports, LanguageClientOptions } from 'vscode-languageclient/browser.js'; -export interface IConnectionProvider { - get(encoding: string): Promise; -} - export type MonacoLanguageClientOptions = { name: string; id?: string; clientOptions: LanguageClientOptions; - connectionProvider: IConnectionProvider; + messageTransports: MessageTransports; } export class MonacoLanguageClient extends BaseLanguageClient { - protected readonly connectionProvider: IConnectionProvider; + protected readonly messageTransports: MessageTransports; - constructor({ id, name, clientOptions, connectionProvider }: MonacoLanguageClientOptions) { + constructor({ id, name, clientOptions, messageTransports }: MonacoLanguageClientOptions) { super(id ?? name.toLowerCase(), name, clientOptions); - this.connectionProvider = connectionProvider; + this.messageTransports = messageTransports; } - protected override createMessageTransports(encoding: string): Promise { - return this.connectionProvider.get(encoding); + protected override createMessageTransports(_encoding: string): Promise { + return Promise.resolve(this.messageTransports); } } diff --git a/packages/client/src/commonTypes.ts b/packages/client/src/commonTypes.ts index f37e15d9a..fe8a9ccba 100644 --- a/packages/client/src/commonTypes.ts +++ b/packages/client/src/commonTypes.ts @@ -20,7 +20,7 @@ export type LanguageClientRestartOptions = { export type LanguageClientConfigType = 'WebSocket' | 'WebSocketUrl' | 'WebSocketDirect' | 'WorkerConfig' | 'Worker'; -export type LanguageClientConfigOptions = (WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl | WorkerConfigOptions | WorkerConfigDirect) & { +export type ConnetionConfigOptions = (WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl | WorkerConfigOptions | WorkerConfigDirect) & { restartOptions?: LanguageClientRestartOptions; } diff --git a/packages/examples/src/bare/client.ts b/packages/examples/src/bare/client.ts index faf6f0fe1..8e6ca5e1e 100644 --- a/packages/examples/src/bare/client.ts +++ b/packages/examples/src/bare/client.ts @@ -73,7 +73,7 @@ export const initWebSocketAndStartClient = (url: string): WebSocket => { return webSocket; }; -export const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => { +export const createLanguageClient = (messageTransports: MessageTransports): MonacoLanguageClient => { return new MonacoLanguageClient({ name: 'Sample Language Client', clientOptions: { @@ -86,8 +86,6 @@ export const createLanguageClient = (transports: MessageTransports): MonacoLangu } }, // create a language client connection from the JSON RPC connection on demand - connectionProvider: { - get: async () => (transports) - } + messageTransports }); }; diff --git a/packages/examples/src/eclipse.jdt.ls/client/main.ts b/packages/examples/src/eclipse.jdt.ls/client/main.ts index 467d3b04e..adcd3cbd3 100644 --- a/packages/examples/src/eclipse.jdt.ls/client/main.ts +++ b/packages/examples/src/eclipse.jdt.ls/client/main.ts @@ -57,11 +57,13 @@ export const runEclipseJdtLsClient = () => { languageClientConfigs: { java: { languageId: 'java', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:30003/jdtls' + connection: { + configOptions: { + $type: 'WebSocketUrl', + url: 'ws://localhost:30003/jdtls' + } }, - clientOptions: { + languageClientOptions: { documentSelector: ['java'], workspaceFolder: { index: 0, diff --git a/packages/examples/src/groovy/client/main.ts b/packages/examples/src/groovy/client/main.ts index 40489a2fe..97d873913 100644 --- a/packages/examples/src/groovy/client/main.ts +++ b/packages/examples/src/groovy/client/main.ts @@ -53,9 +53,11 @@ const userConfig: UserConfig = { languageClientConfigs: { groovy: { languageId: 'groovy', - options: { - $type: 'WebSocketUrl', - url: `ws://localhost:${groovyConfig.port}${groovyConfig.path}` + connection: { + configOptions: { + $type: 'WebSocketUrl', + url: `ws://localhost:${groovyConfig.port}${groovyConfig.path}` + } } } } diff --git a/packages/examples/src/json/client/wrapperWs.ts b/packages/examples/src/json/client/wrapperWs.ts index 2d8171aff..437b23b2b 100644 --- a/packages/examples/src/json/client/wrapperWs.ts +++ b/packages/examples/src/json/client/wrapperWs.ts @@ -54,20 +54,22 @@ export const jsonClientUserConfig: UserConfig = { languageClientConfigs: { json: { languageId: 'json', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:30000/sampleServer', - startOptions: { - onCall: () => { - console.log('Connected to socket.'); + connection: { + configOptions: { + $type: 'WebSocketUrl', + url: 'ws://localhost:30000/sampleServer', + startOptions: { + onCall: () => { + console.log('Connected to socket.'); + }, + reportStatus: true }, - reportStatus: true - }, - stopOptions: { - onCall: () => { - console.log('Disconnected from socket.'); - }, - reportStatus: true + stopOptions: { + onCall: () => { + console.log('Disconnected from socket.'); + }, + reportStatus: true + } } } } diff --git a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts index 629a2dda4..45119a349 100644 --- a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts @@ -52,9 +52,11 @@ export const setupLangiumClientClassic = async (): Promise => { languageClientConfigs: { langium: { languageId: 'langium', - options: { - $type: 'WorkerDirect', - worker: langiumWorker + connection: { + configOptions: { + $type: 'WorkerDirect', + worker: langiumWorker + } } } } diff --git a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts index f9c66a6c4..d31252bc3 100644 --- a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts @@ -79,12 +79,12 @@ export const setupLangiumClientExtended = async (): Promise => { languageClientConfigs: { langium: { languageId: 'langium', - options: { - $type: 'WorkerDirect', - worker: langiumWorker - }, - connectionProvider: { - get: async () => ({ reader, writer }) + connection: { + configOptions: { + $type: 'WorkerDirect', + worker: langiumWorker + }, + messageTransports: { reader, writer } } } } diff --git a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts index 1f660fd0c..6b383b980 100644 --- a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts +++ b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts @@ -6,12 +6,12 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; import getLifecycleServiceOverride from '@codingame/monaco-vscode-lifecycle-service-override'; import getLocalizationServiceOverride from '@codingame/monaco-vscode-localization-service-override'; -import { IConnectionProvider } from 'monaco-languageclient'; import { createDefaultLocaleConfiguration } from 'monaco-languageclient/vscode/services'; import { LanguageClientConfig, UserConfig } from 'monaco-editor-wrapper'; // cannot be imported with assert as json contains comments import statemachineLanguageConfig from './language-configuration.json?raw'; import responseStatemachineTm from '../syntaxes/statemachine.tmLanguage.json?raw'; +import { MessageTransports } from 'vscode-languageclient'; export const createLangiumGlobalConfig = async (params: { languageServerId: string, @@ -19,7 +19,7 @@ export const createLangiumGlobalConfig = async (params: { text?: string, worker?: Worker, messagePort?: MessagePort, - connectionProvider?: IConnectionProvider + messageTransports?: MessageTransports }): Promise => { const extensionFilesOrContents = new Map(); extensionFilesOrContents.set(`/${params.languageServerId}-statemachine-configuration.json`, statemachineLanguageConfig); @@ -36,12 +36,14 @@ export const createLangiumGlobalConfig = async (params: { const languageClientConfigs: Record | undefined = params.useLanguageClient && params.worker ? { statemachine: { languageId: 'statemachine', - options: { - $type: 'WorkerDirect', - worker: params.worker, - messagePort: params.messagePort, - }, - connectionProvider: params.connectionProvider + connection: { + configOptions: { + $type: 'WorkerDirect', + worker: params.worker, + messagePort: params.messagePort, + }, + messageTransports: params.messageTransports + } } } : undefined; diff --git a/packages/examples/src/langium/statemachine/main.ts b/packages/examples/src/langium/statemachine/main.ts index 48e5bc06c..a64819925 100644 --- a/packages/examples/src/langium/statemachine/main.ts +++ b/packages/examples/src/langium/statemachine/main.ts @@ -55,9 +55,7 @@ const startEditor = async () => { useLanguageClient: true, worker: stateMachineWorkerPort, messagePort: channel.port1, - connectionProvider: { - get: async () => ({ reader, writer }) - } + messageTransports: { reader, writer } }); await wrapper.initAndStart(langiumGlobalConfig, document.getElementById('monaco-editor-root')); diff --git a/packages/examples/src/python/client/config.ts b/packages/examples/src/python/client/config.ts index 5be57b2e8..96a183669 100644 --- a/packages/examples/src/python/client/config.ts +++ b/packages/examples/src/python/client/config.ts @@ -32,32 +32,32 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s python: { languageId: 'python', name: 'Python Language Server Example', - options: { - $type: 'WebSocketDirect', - webSocket: webSocket, - startOptions: { - onCall: (languageClient?: MonacoLanguageClient) => { - setTimeout(() => { - ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { - vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { - languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + connection: { + configOptions: { + $type: 'WebSocketDirect', + webSocket: webSocket, + startOptions: { + onCall: (languageClient?: MonacoLanguageClient) => { + setTimeout(() => { + ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { + vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { + languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + }); }); - }); - }, 250); - }, - reportStatus: true, - } + }, 250); + }, + reportStatus: true, + } + }, + messageTransports: { reader, writer } }, - clientOptions: { + languageClientOptions: { documentSelector: ['python'], workspaceFolder: { index: 0, name: 'workspace', uri: vscode.Uri.parse(workspaceRoot) }, - }, - connectionProvider: { - get: async () => ({ reader, writer }) } } }, diff --git a/packages/examples/src/ts/wrapperAdvanced.ts b/packages/examples/src/ts/wrapperAdvanced.ts index 679ba6aa0..a2d356759 100644 --- a/packages/examples/src/ts/wrapperAdvanced.ts +++ b/packages/examples/src/ts/wrapperAdvanced.ts @@ -56,40 +56,44 @@ export const runMultipleLanguageClientsExample = async () => { json: { languageId: 'json', name: 'JSON Client', - options: { - $type: 'WebSocketParams', - host: 'localhost', - port: 30000, - path: 'sampleServer', - secured: false + connection: { + configOptions: { + $type: 'WebSocketParams', + host: 'localhost', + port: 30000, + path: 'sampleServer', + secured: false + } } }, python: { languageId: 'python', name: 'Python Client', - options: { - $type: 'WebSocketParams', - host: 'localhost', - port: 30001, - path: 'pyright', - secured: false, - extraParams: { - authorization: 'UserAuth' - }, - startOptions: { - onCall: (languageClient?: MonacoLanguageClient) => { - setTimeout(() => { - ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { - vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { - languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); - }); - }); - }, 250); + connection: { + configOptions: { + $type: 'WebSocketParams', + host: 'localhost', + port: 30001, + path: 'pyright', + secured: false, + extraParams: { + authorization: 'UserAuth' }, - reportStatus: true, + startOptions: { + onCall: (languageClient?: MonacoLanguageClient) => { + setTimeout(() => { + ['pyright.restartserver', 'pyright.organizeimports'].forEach((cmdName) => { + vscode.commands.registerCommand(cmdName, (...args: unknown[]) => { + languageClient?.sendRequest('workspace/executeCommand', { command: cmdName, arguments: args }); + }); + }); + }, 250); + }, + reportStatus: true, + } } }, - clientOptions: { + languageClientOptions: { documentSelector: ['python'], workspaceFolder: { index: 0, diff --git a/packages/wrapper/src/index.ts b/packages/wrapper/src/index.ts index fcde29f30..091f5c51e 100644 --- a/packages/wrapper/src/index.ts +++ b/packages/wrapper/src/index.ts @@ -3,89 +3,21 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ -import { - EditorAppBase, -} from './editorAppBase.js'; +export type * from './editorAppBase.js'; +export * from './editorAppBase.js'; -import type { - EditorAppConfigBase, - EditorAppType, - CodeContent, - CodePlusUri, - CodePlusFileExt, - CodeResources, - ModelRefs, - TextModels, - TextContents -} from './editorAppBase.js'; +export type * from './editorAppClassic.js'; +export * from './editorAppClassic.js'; -import type { - EditorAppConfigClassic, -} from './editorAppClassic.js'; +export type * from './editorAppExtended.js'; +export * from './editorAppExtended.js'; -import { - EditorAppClassic -} from './editorAppClassic.js'; +export type * from './languageClientWrapper.js'; +export * from './languageClientWrapper.js'; -import type { - ExtensionConfig, - EditorAppConfigExtended, - RegisterExtensionResult, - RegisterLocalProcessExtensionResult, - UserConfiguration -} from './editorAppExtended.js'; +export type * from './userConfig.js'; -import { - EditorAppExtended -} from './editorAppExtended.js'; - -import type { - LanguageClientConfig, - LanguageClientError -} from './languageClientWrapper.js'; - -import { - LanguageClientWrapper, -} from './languageClientWrapper.js'; - -import type { - UserConfig, - WrapperConfig -} from './userConfig.js'; - -import { - MonacoEditorLanguageClientWrapper, -} from './wrapper.js'; - -export type { - WrapperConfig, - EditorAppConfigBase, - EditorAppType, - EditorAppConfigClassic, - ExtensionConfig, - EditorAppConfigExtended, - RegisterExtensionResult, - RegisterLocalProcessExtensionResult, - UserConfiguration, - LanguageClientConfig, - LanguageClientError, - UserConfig, - CodeContent, - CodePlusUri, - CodePlusFileExt, - CodeResources, - ModelRefs, - TextModels, - TextContents -}; - -export { - MonacoEditorLanguageClientWrapper, - LanguageClientWrapper, - EditorAppBase, - EditorAppClassic, - EditorAppExtended -}; +export * from './wrapper.js'; export * from './utils.js'; export type * from './utils.js'; diff --git a/packages/wrapper/src/languageClientWrapper.ts b/packages/wrapper/src/languageClientWrapper.ts index b5acdcab2..e63677cc2 100644 --- a/packages/wrapper/src/languageClientWrapper.ts +++ b/packages/wrapper/src/languageClientWrapper.ts @@ -3,19 +3,23 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ -import { MonacoLanguageClient, IConnectionProvider, WorkerConfigOptions, WorkerConfigDirect, LanguageClientConfigOptions, WebSocketConfigOptionsParams, WebSocketConfigOptionsDirect, WebSocketConfigOptionsUrl, LanguageClientRestartOptions } from 'monaco-languageclient'; +import { MonacoLanguageClient, WorkerConfigOptions, WorkerConfigDirect, LanguageClientRestartOptions, ConnetionConfigOptions } from 'monaco-languageclient'; import { Logger } from 'monaco-languageclient/tools'; import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageserver-protocol/browser.js'; import { CloseAction, ErrorAction, LanguageClientOptions, MessageTransports, State } from 'vscode-languageclient/browser.js'; import { createUrl } from './utils.js'; import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; +export type ConnectionConfig = { + configOptions: ConnetionConfigOptions; + messageTransports?: MessageTransports; +} + export type LanguageClientConfig = { - languageId: string; - options: LanguageClientConfigOptions; name?: string; - clientOptions?: LanguageClientOptions; - connectionProvider?: IConnectionProvider; + languageId: string; + connection: ConnectionConfig; + languageClientOptions?: LanguageClientOptions; } export type LanguageClientError = { @@ -26,14 +30,14 @@ export type LanguageClientError = { export class LanguageClientWrapper { private languageClient?: MonacoLanguageClient; - private languageClientConfig?: LanguageClientConfig; + private languageClientConfig: LanguageClientConfig; private languageId: string; private worker?: Worker; private port?: MessagePort; private name?: string; private logger: Logger | undefined; - async init(config: { + constructor(config: { languageClientConfig: LanguageClientConfig, logger?: Logger }) { @@ -47,10 +51,6 @@ export class LanguageClientWrapper { return this.languageClient !== undefined; } - haveLanguageClientConfig(): boolean { - return this.languageClientConfig !== undefined; - } - getLanguageClient(): MonacoLanguageClient | undefined { return this.languageClient; } @@ -63,16 +63,25 @@ export class LanguageClientWrapper { return this.languageClient !== undefined && this.languageClient.isRunning(); } - async start() { - if (this.languageClientConfig) { - return this.startLanguageClientConnection(); - } else { - const languageClientError: LanguageClientError = { - message: `languageClientWrapper (${this.name}): Unable to start monaco-languageclient. No configuration was provided.`, - error: 'No error was provided.' - }; - return Promise.reject(languageClientError); + async start(): Promise { + if (this.languageClient?.isRunning() ?? false) { + this.logger?.info('startLanguageClientConnection: monaco-languageclient already running!'); + return Promise.resolve(); } + + // eslint-disable-next-line no-async-promise-executor + return new Promise((resolve, reject) => { + const conConfig = this.languageClientConfig.connection; + const conOptions = conConfig.configOptions; + + if (conOptions.$type === 'WebSocketDirect' || conOptions.$type === 'WebSocketParams' || conOptions.$type === 'WebSocketUrl') { + const webSocket = conOptions.$type === 'WebSocketDirect' ? conOptions.webSocket : new WebSocket(createUrl(conOptions)); + this.initMessageTransportWebSocket(webSocket, resolve, reject); + } else { + // init of worker and start of languageclient can be handled directly, because worker available already + this.initMessageTransportWorker(conOptions, resolve, reject); + } + }); } /** @@ -85,60 +94,29 @@ export class LanguageClientWrapper { await this.disposeLanguageClient(keepWorker); this.worker = updatedWorker; - if (this.languageClientConfig) { - this.logger?.info('Re-Starting monaco-languageclient'); - return this.startLanguageClientConnection(); - } else { - const languageClientError: LanguageClientError = { - message: `languageClientWrapper (${this.name}): Unable to restart languageclient. No configuration was provided.`, - error: 'No error was provided.' - }; - return Promise.reject(languageClientError); - } + this.logger?.info('Re-Starting monaco-languageclient'); + return this.start(); } - protected async startLanguageClientConnection(): Promise { - if (this.languageClient?.isRunning() ?? false) { - this.logger?.info('startLanguageClientConnection: monaco-languageclient already running!'); - return Promise.resolve(); - } - const lccOptions = this.languageClientConfig?.options; - - // eslint-disable-next-line no-async-promise-executor - return new Promise((resolve, reject) => { - if (lccOptions === undefined) { - reject('Unable to start languageclient, because configuration options are not provided.'); - } else { - if (lccOptions.$type === 'WebSocketDirect' || lccOptions.$type === 'WebSocketParams' || lccOptions.$type === 'WebSocketUrl') { - this.initMessageTransportWebSocket(lccOptions, resolve, reject); - } else { - // init of worker and start of languageclient can be handled directly, because worker available already - this.initMessageTransportWorker(lccOptions, resolve, reject); - } - } - }); - } - - protected async initMessageTransportWebSocket(lccOptions: WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl, - resolve: () => void, reject: (reason?: unknown) => void) { - const webSocket = lccOptions.$type === 'WebSocketDirect' ? lccOptions.webSocket : new WebSocket(createUrl(lccOptions)); + protected async initMessageTransportWebSocket(webSocket: WebSocket, resolve: () => void, reject: (reason?: unknown) => void) { - const createMessageTransports = (transport: Worker | MessagePort | WebSocket) => { - const iWebSocket = toSocket(transport as WebSocket); - return { + let messageTransports = this.languageClientConfig.connection.messageTransports; + if (messageTransports === undefined) { + const iWebSocket = toSocket(webSocket); + messageTransports = { reader: new WebSocketMessageReader(iWebSocket), writer: new WebSocketMessageWriter(iWebSocket) }; - }; + } // if websocket is already open, then start the languageclient directly if (webSocket.readyState === WebSocket.OPEN) { - await this.performLanguageClientStart(webSocket, createMessageTransports, resolve, reject); + await this.performLanguageClientStart(messageTransports, resolve, reject); } // otherwise start on open webSocket.onopen = async () => { - await this.performLanguageClientStart(webSocket, createMessageTransports, resolve, reject); + await this.performLanguageClientStart(messageTransports, resolve, reject); }; webSocket.onerror = (ev: Event) => { const languageClientError: LanguageClientError = { @@ -175,28 +153,28 @@ export class LanguageClientWrapper { } const portOrWorker = this.port ? this.port : this.worker; - const createMessageTransports = (transport: Worker | MessagePort | WebSocket) => { - return { - reader: new BrowserMessageReader(transport), - writer: new BrowserMessageWriter(transport) + let messageTransports = this.languageClientConfig.connection.messageTransports; + if (messageTransports === undefined) { + messageTransports = { + reader: new BrowserMessageReader(portOrWorker), + writer: new BrowserMessageWriter(portOrWorker) }; - }; - await this.performLanguageClientStart(portOrWorker, createMessageTransports, resolve, reject); + } + + await this.performLanguageClientStart(messageTransports, resolve, reject); } - protected async performLanguageClientStart(transport: Worker | MessagePort | WebSocket, - createMessageTransports: (transport: Worker | MessagePort | WebSocket) => MessageTransports, - resolve: () => void, reject: (reason?: unknown) => void) { + protected async performLanguageClientStart(messageTransports: MessageTransports, resolve: () => void, reject: (reason?: unknown) => void) { // do not perform another start attempt if already running if (this.languageClient?.isRunning() ?? false) { this.logger?.info('performLanguageClientStart: monaco-languageclient already running!'); resolve(); } const mlcConfig = { - name: this.languageClientConfig?.name ?? 'Monaco Wrapper Language Client', + name: this.languageClientConfig.name ?? 'Monaco Wrapper Language Client', // allow to fully override the clientOptions - clientOptions: this.languageClientConfig?.clientOptions ?? { + clientOptions: this.languageClientConfig.languageClientOptions ?? { documentSelector: [this.languageId], // disable the default error handler errorHandler: { @@ -204,22 +182,19 @@ export class LanguageClientWrapper { closed: () => ({ action: CloseAction.DoNotRestart }) } }, - connectionProvider: this.languageClientConfig?.connectionProvider ?? { - get: async () => createMessageTransports(transport) - } + messageTransports }; this.languageClient = new MonacoLanguageClient(mlcConfig); - const messageTransports = await mlcConfig.connectionProvider.get('utf-8'); - const lccOptions = this.languageClientConfig?.options; - this.initRestartConfiguration(messageTransports, lccOptions?.restartOptions); + const conOptions = this.languageClientConfig.connection.configOptions; + this.initRestartConfiguration(messageTransports, conOptions.restartOptions); messageTransports.reader.onClose(async () => { await this.languageClient?.stop(); - if ((lccOptions?.$type === 'WebSocketParams' || lccOptions?.$type === 'WebSocketUrl') && lccOptions.stopOptions) { - const stopOptions = lccOptions.stopOptions; + if ((conOptions.$type === 'WebSocketParams' || conOptions.$type === 'WebSocketUrl') && conOptions.stopOptions !== undefined) { + const stopOptions = conOptions.stopOptions; stopOptions.onCall(this.getLanguageClient()); if (stopOptions.reportStatus !== undefined) { this.logger?.info(this.reportStatus().join('\n')); @@ -230,8 +205,8 @@ export class LanguageClientWrapper { try { await this.languageClient.start(); - if ((lccOptions?.$type === 'WebSocketParams' || lccOptions?.$type === 'WebSocketUrl') && lccOptions.startOptions) { - const startOptions = lccOptions.startOptions; + if ((conOptions.$type === 'WebSocketParams' || conOptions.$type === 'WebSocketUrl') && conOptions.startOptions !== undefined) { + const startOptions = conOptions.startOptions; startOptions.onCall(this.getLanguageClient()); if (startOptions.reportStatus !== undefined) { this.logger?.info(this.reportStatus().join('\n')); @@ -275,10 +250,6 @@ export class LanguageClientWrapper { } }; } - const successCallback = messageTransports.reader.listen(() => { - this.logger?.info('MessageTransport Reader started listening.'); - successCallback.dispose(); - }); } protected disposeWorker(keepWorker?: boolean) { diff --git a/packages/wrapper/src/workerFactory.ts b/packages/wrapper/src/workerFactory.ts index e4db1c1a0..b9f3f50d3 100644 --- a/packages/wrapper/src/workerFactory.ts +++ b/packages/wrapper/src/workerFactory.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See LICENSE in the package root for license information. * ------------------------------------------------------------------------------------------ */ +import { Logger } from 'monaco-languageclient/tools'; import { initEnhancedMonacoEnvironment } from 'monaco-languageclient/vscode/services'; export type WorkerOverrides = { @@ -60,7 +61,7 @@ export const defaultWorkerLoaders: Partial { +export const buildWorker = (config: WorkerConfig, workerOverrides?: WorkerOverrides, logger?: Logger): Worker => { if (workerOverrides?.rootPath !== undefined) { config.rootPath = workerOverrides.rootPath; } @@ -72,7 +73,7 @@ export const buildWorker = (config: WorkerConfig, workerOverrides?: WorkerOverri workerFile = `${config.basePath}/${config.workerFile}`; } const fullUrl = new URL(workerFile, config.rootPath).href; - console.log(`Creating worker: ${fullUrl}`); + logger?.info(`Creating worker: ${fullUrl}`); // default to 'module' if not specified const workerOptions = config.options ?? {}; @@ -85,11 +86,11 @@ export const buildWorker = (config: WorkerConfig, workerOverrides?: WorkerOverri return new Worker(URL.createObjectURL(blob), workerOptions); }; -export const useWorkerFactory = (workerOverrides?: WorkerOverrides) => { +export const useWorkerFactory = (workerOverrides?: WorkerOverrides, logger?: Logger) => { const envEnhanced = initEnhancedMonacoEnvironment(); const getWorker = (moduleId: string, label: string) => { - console.log(`getWorker: moduleId: ${moduleId} label: ${label}`); + logger?.info(`getWorker: moduleId: ${moduleId} label: ${label}`); let selector = label; let workerLoaders; @@ -116,7 +117,7 @@ export const useWorkerFactory = (workerOverrides?: WorkerOverrides) => { if (workerOrConfig) { const invoked = workerOrConfig(); if (Object.hasOwn(invoked, 'workerFile')) { - return buildWorker(invoked as WorkerConfig, workerOverrides); + return buildWorker(invoked as WorkerConfig, workerOverrides, logger); } else { return invoked as Worker; } diff --git a/packages/wrapper/src/wrapper.ts b/packages/wrapper/src/wrapper.ts index 07f3bb592..fb00f14f8 100644 --- a/packages/wrapper/src/wrapper.ts +++ b/packages/wrapper/src/wrapper.ts @@ -74,13 +74,11 @@ export class MonacoEditorLanguageClientWrapper { if (lccs !== undefined && Object.entries(lccs).length > 0) { for (const [languageId, lcc] of Object.entries(lccs)) { - const lcw = new LanguageClientWrapper(); - this.languageClientWrappers.set(languageId, lcw); - - lcw.init({ + const lcw = new LanguageClientWrapper({ languageClientConfig: lcc, logger: this.logger }); + this.languageClientWrappers.set(languageId, lcw); } } @@ -111,9 +109,7 @@ export class MonacoEditorLanguageClientWrapper { await this.editorApp?.createEditors(htmlElement); for (const lcw of this.languageClientWrappers.values()) { - if (lcw.haveLanguageClientConfig()) { - await lcw.start(); - } + await lcw.start(); } this.markStarted(); diff --git a/packages/wrapper/test/helper.ts b/packages/wrapper/test/helper.ts index 098dcb01d..5887c410d 100644 --- a/packages/wrapper/test/helper.ts +++ b/packages/wrapper/test/helper.ts @@ -35,12 +35,3 @@ export const createEditorAppConfig = (type: EditorAppType) => { useDiffEditor: false, }; }; - -/** - * Helper to generate a quick worker from a function blob - */ -export const createWorkerFromFunction = (fn: () => void): Worker => { - return new Worker(URL.createObjectURL( - new Blob([`(${fn.toString()})()`], { type: 'application/javascript' }) - )); -}; diff --git a/packages/wrapper/test/languageClientWrapper.test.ts b/packages/wrapper/test/languageClientWrapper.test.ts index 387b8145f..0fe55f52e 100644 --- a/packages/wrapper/test/languageClientWrapper.test.ts +++ b/packages/wrapper/test/languageClientWrapper.test.ts @@ -5,7 +5,7 @@ import { describe, expect, test } from 'vitest'; import { LanguageClientConfig, LanguageClientWrapper, MonacoEditorLanguageClientWrapper } from 'monaco-editor-wrapper'; -import { createBaseConfig, createWorkerFromFunction } from './helper.js'; +import { createBaseConfig } from './helper.js'; describe('Test LanguageClientWrapper', () => { @@ -19,30 +19,32 @@ describe('Test LanguageClientWrapper', () => { test('Constructor: no config', async () => { // create a web worker to pass to the wrapper - const worker = createWorkerFromFunction(() => { - console.info('Hello'); + const worker = new Worker('./worker/langium-server.ts', { + type: 'module', + name: 'Langium LS', }); const languageClientConfig: LanguageClientConfig = { languageId: 'javascript', - options: { - $type: 'WorkerDirect', - worker + connection: { + configOptions: { + $type: 'WorkerDirect', + worker + } } }; - const languageClientWrapper = new LanguageClientWrapper(); - languageClientWrapper.init({ + const languageClientWrapper = new LanguageClientWrapper({ languageClientConfig }); expect(languageClientWrapper).toBeDefined(); expect(languageClientWrapper.haveLanguageClient).toBeTruthy(); - expect(languageClientWrapper.haveLanguageClientConfig).toBeTruthy(); }); test('Dispose: direct worker is cleaned up afterwards', async () => { // create a web worker to pass to the wrapper - const worker = createWorkerFromFunction(() => { - console.info('Hello'); + const worker = new Worker('./worker/langium-server.ts', { + type: 'module', + name: 'Langium LS', }); // setup the wrapper @@ -50,9 +52,11 @@ describe('Test LanguageClientWrapper', () => { config.languageClientConfigs = { javascript: { languageId: 'javascript', - options: { - $type: 'WorkerDirect', - worker + connection: { + configOptions: { + $type: 'WorkerDirect', + worker + } } } }; @@ -62,13 +66,18 @@ describe('Test LanguageClientWrapper', () => { const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); - // start up & verify (don't wait for start to finish, just roll past it, we only care about the worker) - languageClientWrapper!.start(); - expect(languageClientWrapper!.getWorker()).toBeTruthy(); + expect(languageClientWrapper?.getWorker()).toBeFalsy(); + + // WA: for whatever reasons "await" kills the test, + // but the languageClientWrapper needs to be fully initialised as otherwise the follow up steps fail + languageClientWrapper?.start(); - // dispose & verify - languageClientWrapper!.disposeLanguageClient(); - expect(languageClientWrapper!.getWorker()).toBeUndefined(); + setTimeout(async () => { + // dispose & verify + await languageClientWrapper?.disposeLanguageClient(); + expect(languageClientWrapper?.getWorker()).toBeUndefined(); + }, 250); + expect(languageClientWrapper?.getWorker()).toBeTruthy(); }); test('Constructor: config', async () => { @@ -76,9 +85,11 @@ describe('Test LanguageClientWrapper', () => { config.languageClientConfigs = { javascript: { languageId: 'javascript', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:12345/Tester' + connection: { + configOptions: { + $type: 'WebSocketUrl', + url: 'ws://localhost:12345/Tester' + } } } }; @@ -87,8 +98,6 @@ describe('Test LanguageClientWrapper', () => { const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); - - expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); }); test('Start: unreachable url', async () => { @@ -97,9 +106,11 @@ describe('Test LanguageClientWrapper', () => { javascript: { languageId: 'javascript', name: 'test-unreachable', - options: { - $type: 'WebSocketUrl', - url: 'ws://localhost:12345/Tester' + connection: { + configOptions: { + $type: 'WebSocketUrl', + url: 'ws://localhost:12345/Tester' + } } } }; @@ -109,8 +120,6 @@ describe('Test LanguageClientWrapper', () => { const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); - expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); - console.log('start'); await expect(languageClientWrapper!.start()).rejects.toEqual({ message: 'languageClientWrapper (test-unreachable): Websocket connection failed.', error: 'No error was provided.' @@ -133,10 +142,12 @@ describe('Test LanguageClientWrapper', () => { config.languageClientConfigs = { javascript: { languageId: 'javascript', - options: { - $type: 'WorkerConfig', - url: new URL('http://localhost:20101'), - type: 'classic' + connection: { + configOptions: { + $type: 'WorkerConfig', + url: new URL('http://localhost:20101'), + type: 'classic' + } } } }; @@ -146,7 +157,6 @@ describe('Test LanguageClientWrapper', () => { const languageClientWrapper = wrapper.getLanguageClientWrapper('javascript'); expect(languageClientWrapper).toBeDefined(); - expect(languageClientWrapper!.haveLanguageClientConfig()).toBeTruthy(); await expect(languageClientWrapper!.start()).rejects.toEqual({ message: 'languageClientWrapper (unnamed): Illegal worker configuration detected. Potentially the url is wrong.', error: 'No error was provided.' diff --git a/packages/wrapper/test/worker/langium-server.ts b/packages/wrapper/test/worker/langium-server.ts new file mode 100644 index 000000000..be1c511f5 --- /dev/null +++ b/packages/wrapper/test/worker/langium-server.ts @@ -0,0 +1,24 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) 2018-2022 TypeFox and others. + * Licensed under the MIT License. See LICENSE in the package root for license information. + * ------------------------------------------------------------------------------------------ */ +/// + +import { EmptyFileSystem } from 'langium'; +import { DefaultSharedModuleContext, startLanguageServer } from 'langium/lsp'; +import { createLangiumGrammarServices } from 'langium/grammar'; +import { BrowserMessageReader, BrowserMessageWriter, createConnection } from 'vscode-languageserver/browser.js'; + +/* browser specific setup code */ +const messageReader = new BrowserMessageReader(self as DedicatedWorkerGlobalScope); +const messageWriter = new BrowserMessageWriter(self as DedicatedWorkerGlobalScope); + +// Inject the shared services and language-specific services +const context = { + connection: createConnection(messageReader, messageWriter), + ...EmptyFileSystem +} as unknown as DefaultSharedModuleContext; +const { shared } = createLangiumGrammarServices(context); + +// Start the language server with the shared services +startLanguageServer(shared); From f4da090ac8e0ee7a513d294a16b7119ae929b953 Mon Sep 17 00:00:00 2001 From: Kai Salmen Date: Thu, 5 Sep 2024 21:22:29 +0200 Subject: [PATCH 5/7] Naming enhancements, config restructuring - UserConfig is now WrapperConfig and one level less deep - Language client config has better names and is structured more logically - Introduce new two_langauge_clients.html example replacing wrapper_adv.html --- index.html | 7 +- packages/client/src/commonTypes.ts | 10 +- packages/examples/build/downloadResources.mts | 9 +- packages/examples/src/browser/main.ts | 44 ++++----- packages/examples/src/common/client/utils.ts | 11 +++ .../src/eclipse.jdt.ls/client/main.ts | 49 +++++----- packages/examples/src/groovy/client/main.ts | 46 +++++---- .../examples/src/json/client/wrapperWs.ts | 48 +++++----- .../langium-dsl/config/classicConfig.ts | 56 ++++++----- .../langium-dsl/config/extendedConfig.ts | 96 +++++++++---------- .../src/langium/langium-dsl/wrapperLangium.ts | 8 +- .../config/wrapperStatemachineConfig.ts | 92 +++++++++--------- .../src/langium/statemachine/main-react.tsx | 2 +- .../twoLanguageClients.ts} | 84 ++++++++++------ packages/examples/src/python/client/config.ts | 54 +++++------ .../src/python/client/reactPython.tsx | 2 +- packages/examples/src/ts/wrapperTs.ts | 68 +++++++------ ...per_adv.html => two_langauge_clients.html} | 7 +- packages/wrapper-react/src/index.tsx | 20 ++-- packages/wrapper-react/test/index.test.tsx | 60 ++++++------ packages/wrapper/src/editorAppClassic.ts | 10 +- packages/wrapper/src/editorAppExtended.ts | 12 +-- packages/wrapper/src/index.ts | 3 +- packages/wrapper/src/languageClientWrapper.ts | 21 ++-- packages/wrapper/src/userConfig.ts | 22 ----- packages/wrapper/src/utils.ts | 16 ++-- packages/wrapper/src/wrapper.ts | 39 ++++---- packages/wrapper/test/editorAppBase.test.ts | 6 +- .../wrapper/test/editorAppClassic.test.ts | 52 +++++----- .../wrapper/test/editorAppExtended.test.ts | 12 +-- packages/wrapper/test/helper.ts | 10 +- .../test/languageClientWrapper.test.ts | 10 +- packages/wrapper/test/wrapper.test.ts | 54 +++++------ vite.config.ts | 6 +- 34 files changed, 519 insertions(+), 527 deletions(-) create mode 100644 packages/examples/src/common/client/utils.ts rename packages/examples/src/{ts/wrapperAdvanced.ts => multi/twoLanguageClients.ts} (63%) rename packages/examples/{wrapper_adv.html => two_langauge_clients.html} (69%) delete mode 100644 packages/wrapper/src/userConfig.ts diff --git a/index.html b/index.html index b55d8fefd..39af637ec 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,11 @@

Groovy

Groovy Language Client & Language Server (Web Socket)
+

Multiple Languageclients

+ Please execute npm run start:example:server:python and npm run start:example:server:json beforehand:
+ Json & Python Languageclients & Language Server (Web Socket) +
+

Monaco Editor React

React: Langium Statemachine Language Client & Language Server (Worker)
@@ -65,8 +70,6 @@

Monaco Editor React

monaco-editor related examples

Monaco Editor Wrapper TypeScript Example
- Multiple Editors -

Verification

Angular

diff --git a/packages/client/src/commonTypes.ts b/packages/client/src/commonTypes.ts index fe8a9ccba..7a86ef88e 100644 --- a/packages/client/src/commonTypes.ts +++ b/packages/client/src/commonTypes.ts @@ -18,11 +18,7 @@ export type LanguageClientRestartOptions = { keepWorker?: boolean; } -export type LanguageClientConfigType = 'WebSocket' | 'WebSocketUrl' | 'WebSocketDirect' | 'WorkerConfig' | 'Worker'; - -export type ConnetionConfigOptions = (WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl | WorkerConfigOptions | WorkerConfigDirect) & { - restartOptions?: LanguageClientRestartOptions; -} +export type ConnetionConfigOptions = (WebSocketConfigOptionsDirect | WebSocketConfigOptionsParams | WebSocketConfigOptionsUrl | WorkerConfigOptionsParams | WorkerConfigOptionsDirect); export type WebSocketUrlParams = { secured: boolean; @@ -55,7 +51,7 @@ export type WebSocketConfigOptionsUrl = WebSocketUrlString & { stopOptions?: WebSocketCallOptions; } -export type WorkerConfigOptions = { +export type WorkerConfigOptionsParams = { $type: 'WorkerConfig' url: URL; type: 'classic' | 'module'; @@ -63,7 +59,7 @@ export type WorkerConfigOptions = { workerName?: string; }; -export type WorkerConfigDirect = { +export type WorkerConfigOptionsDirect = { $type: 'WorkerDirect'; worker: Worker; messagePort?: MessagePort; diff --git a/packages/examples/build/downloadResources.mts b/packages/examples/build/downloadResources.mts index d62f497af..8494dcbb6 100644 --- a/packages/examples/build/downloadResources.mts +++ b/packages/examples/build/downloadResources.mts @@ -28,5 +28,12 @@ const downloadVsix = async (url: string, targetDir: string, filename: string) => } }; +// Source: https://gist.github.com/wanglf/7acc591890dc0d8ceff1e7ec9af32a55?permalink_comment_id=4151555#gistcomment-4151555 +// https://marketplace.visualstudio.com/_apis/public/gallery/publishers/${publisher}/vsextensions/${extension}/${version}/vspackage + await downloadVsix('https://marketplace.visualstudio.com/_apis/public/gallery/publishers/GitHub/vsextensions/github-vscode-theme/6.3.4/vspackage', - resolve(getLocalDirectory(), '../resources/vsix/'), 'GitHub.github-vscode-theme-6.3.4.vsix'); + resolve(getLocalDirectory(), '../resources/vsix/'), 'github-vscode-theme.vsix'); + +// not yet used +await downloadVsix('https://marketplace.visualstudio.com/_apis/public/gallery/publishers/TypeFox/vsextensions/open-collaboration-tools/0.2.3/vspackage', + resolve(getLocalDirectory(), '../resources/vsix/'), 'open-collaboration-tools.vsix'); diff --git a/packages/examples/src/browser/main.ts b/packages/examples/src/browser/main.ts index 3b0ad381c..0ea6f7d31 100644 --- a/packages/examples/src/browser/main.ts +++ b/packages/examples/src/browser/main.ts @@ -10,7 +10,7 @@ import '@codingame/monaco-vscode-json-default-extension'; import { getLanguageService, TextDocument } from 'vscode-json-languageservice'; import { createConverter as createCodeConverter } from 'vscode-languageclient/lib/common/codeConverter.js'; import { createConverter as createProtocolConverter } from 'vscode-languageclient/lib/common/protocolConverter.js'; -import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; export const configureMonacoWorkers = () => { @@ -36,30 +36,28 @@ export const runBrowserEditor = async () => { const codeUri = '/workspace/model.json'; const wrapper = new MonacoEditorLanguageClientWrapper(); - const jsonClientUserConfig: UserConfig = { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride(), - }, - debugLogging: true + const jsonClientUserConfig: WrapperConfig = { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride(), }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text: code, - uri: codeUri - } - }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.lightbulb.enabled': 'On' - }) + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: code, + uri: codeUri } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.lightbulb.enabled': 'On' + }) } } }; diff --git a/packages/examples/src/common/client/utils.ts b/packages/examples/src/common/client/utils.ts new file mode 100644 index 000000000..f06902591 --- /dev/null +++ b/packages/examples/src/common/client/utils.ts @@ -0,0 +1,11 @@ +/* -------------------------------------------------------------------------------------------- + * Copyright (c) 2024 TypeFox and others. + * Licensed under the MIT License. See LICENSE in the package root for license information. + * ------------------------------------------------------------------------------------------ */ + +export const disableButton = (id: string, disabled: boolean) => { + const button = document.getElementById(id) as HTMLButtonElement | null; + if (button !== null) { + button.disabled = disabled; + } +}; diff --git a/packages/examples/src/eclipse.jdt.ls/client/main.ts b/packages/examples/src/eclipse.jdt.ls/client/main.ts index adcd3cbd3..f4d0ebc3d 100644 --- a/packages/examples/src/eclipse.jdt.ls/client/main.ts +++ b/packages/examples/src/eclipse.jdt.ls/client/main.ts @@ -7,7 +7,7 @@ import * as vscode from 'vscode'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; // this is required syntax highlighting import '@codingame/monaco-vscode-java-default-extension'; -import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; import { RegisteredFileSystemProvider, RegisteredMemoryFile, registerFileSystemOverlay } from '@codingame/monaco-vscode-files-service-override'; import { eclipseJdtLsConfig } from '../config.js'; @@ -28,42 +28,41 @@ export const runEclipseJdtLsClient = () => { fileSystemProvider.registerFile(new RegisteredMemoryFile(helloJavaUri, helloJavaCode)); registerFileSystemOverlay(1, fileSystemProvider); - const userConfig: UserConfig = { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride(), - }, - debugLogging: true + const userConfig: WrapperConfig = { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride(), }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text: helloJavaCode, - uri: `${eclipseJdtLsConfig.basePath}/workspace/hello.java` - } - }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.wordBasedSuggestions': 'off' - }) + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: helloJavaCode, + uri: `${eclipseJdtLsConfig.basePath}/workspace/hello.java` } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.wordBasedSuggestions': 'off' + }) } + }, languageClientConfigs: { java: { languageId: 'java', connection: { - configOptions: { + options: { $type: 'WebSocketUrl', url: 'ws://localhost:30003/jdtls' } }, - languageClientOptions: { + clientOptions: { documentSelector: ['java'], workspaceFolder: { index: 0, diff --git a/packages/examples/src/groovy/client/main.ts b/packages/examples/src/groovy/client/main.ts index 97d873913..95720250a 100644 --- a/packages/examples/src/groovy/client/main.ts +++ b/packages/examples/src/groovy/client/main.ts @@ -6,7 +6,7 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; // this is required syntax highlighting import '@codingame/monaco-vscode-groovy-default-extension'; -import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { groovyConfig } from '../config.js'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; @@ -24,37 +24,35 @@ import java.io.File; File file = new File("E:/Example.txt"); `; -const userConfig: UserConfig = { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride(), - }, - debugLogging: true +const userConfig: WrapperConfig = { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride(), }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text: code, - fileExt: 'groovy' - } - }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.wordBasedSuggestions': 'off' - }) + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: code, + fileExt: 'groovy' } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.wordBasedSuggestions': 'off' + }) } }, languageClientConfigs: { groovy: { languageId: 'groovy', connection: { - configOptions: { + options: { $type: 'WebSocketUrl', url: `ws://localhost:${groovyConfig.port}${groovyConfig.path}` } diff --git a/packages/examples/src/json/client/wrapperWs.ts b/packages/examples/src/json/client/wrapperWs.ts index 437b23b2b..7460bb188 100644 --- a/packages/examples/src/json/client/wrapperWs.ts +++ b/packages/examples/src/json/client/wrapperWs.ts @@ -6,7 +6,7 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; // this is required syntax highlighting import '@codingame/monaco-vscode-json-default-extension'; -import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; export const configureMonacoWorkers = () => { @@ -24,38 +24,36 @@ const text = `{ "line_endings": {"value": "unix"} }`; -export const jsonClientUserConfig: UserConfig = { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride(), - }, - debugLogging: true +export const jsonClientUserConfig: WrapperConfig = { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride(), }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text, - fileExt: 'json' - } - }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.lightbulb.enabled': 'On', - 'editor.wordBasedSuggestions': 'off' - }) + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text, + fileExt: 'json' } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.lightbulb.enabled': 'On', + 'editor.wordBasedSuggestions': 'off' + }) } }, languageClientConfigs: { json: { languageId: 'json', connection: { - configOptions: { + options: { $type: 'WebSocketUrl', url: 'ws://localhost:30000/sampleServer', startOptions: { diff --git a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts index 45119a349..488e76162 100644 --- a/packages/examples/src/langium/langium-dsl/config/classicConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/classicConfig.ts @@ -7,53 +7,51 @@ import getConfigurationServiceOverride from '@codingame/monaco-vscode-configurat import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; import { useOpenEditorStub } from 'monaco-editor-wrapper/vscode/services'; -import { UserConfig } from 'monaco-editor-wrapper'; +import { WrapperConfig } from 'monaco-editor-wrapper'; import { LangiumMonarchContent } from './langium.monarch.js'; import { loadLangiumWorker } from '../wrapperLangium.js'; import code from '../content/example.langium?raw'; -export const setupLangiumClientClassic = async (): Promise => { +export const setupLangiumClientClassic = async (): Promise => { const langiumWorker = loadLangiumWorker(); return { loggerConfig: { enabled: true, debugEnabled: true }, - wrapperConfig: { - serviceConfig: { - userServices: { - ...getConfigurationServiceOverride(), - ...getEditorServiceOverride(useOpenEditorStub), - ...getKeybindingsServiceOverride() - }, - debugLogging: true + serviceConfig: { + userServices: { + ...getConfigurationServiceOverride(), + ...getEditorServiceOverride(useOpenEditorStub), + ...getKeybindingsServiceOverride() }, - editorAppConfig: { - $type: 'classic', - codeResources: { - main: { - text: code, - fileExt: 'langium', - enforceLanguageId: 'langium' - } - }, - useDiffEditor: false, - editorOptions: { - 'semanticHighlighting.enabled': true, - wordBasedSuggestions: 'off', - theme: 'vs-dark' - }, - languageDef: { - monarchLanguage: LangiumMonarchContent, - languageExtensionConfig: { id: 'langium' }, + debugLogging: true + }, + editorAppConfig: { + $type: 'classic', + codeResources: { + main: { + text: code, + fileExt: 'langium', + enforceLanguageId: 'langium' } + }, + useDiffEditor: false, + editorOptions: { + 'semanticHighlighting.enabled': true, + wordBasedSuggestions: 'off', + theme: 'vs-dark' + }, + languageDef: { + monarchLanguage: LangiumMonarchContent, + languageExtensionConfig: { id: 'langium' }, } }, languageClientConfigs: { langium: { languageId: 'langium', connection: { - configOptions: { + options: { $type: 'WorkerDirect', worker: langiumWorker } diff --git a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts index d31252bc3..5cc60226b 100644 --- a/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts +++ b/packages/examples/src/langium/langium-dsl/config/extendedConfig.ts @@ -5,16 +5,16 @@ import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; -import '../../../../resources/vsix/GitHub.github-vscode-theme-6.3.4.vsix'; +import '../../../../resources/vsix/github-vscode-theme.vsix'; import { useOpenEditorStub } from 'monaco-editor-wrapper/vscode/services'; -import { UserConfig } from 'monaco-editor-wrapper'; +import { WrapperConfig } from 'monaco-editor-wrapper'; import { BrowserMessageReader, BrowserMessageWriter } from 'vscode-languageclient/browser.js'; import { loadLangiumWorker } from '../wrapperLangium.js'; import langiumLanguageConfig from './langium.configuration.json?raw'; import langiumTextmateGrammar from './langium.tmLanguage.json?raw'; import text from '../content/example.langium?raw'; -export const setupLangiumClientExtended = async (): Promise => { +export const setupLangiumClientExtended = async (): Promise => { const extensionFilesOrContents = new Map(); // vite build is easier with string content @@ -26,61 +26,59 @@ export const setupLangiumClientExtended = async (): Promise => { const writer = new BrowserMessageWriter(langiumWorker); return { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getEditorServiceOverride(useOpenEditorStub), - ...getKeybindingsServiceOverride() - }, - debugLogging: true + serviceConfig: { + userServices: { + ...getEditorServiceOverride(useOpenEditorStub), + ...getKeybindingsServiceOverride() + }, + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text, + fileExt: 'langium' + } }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text, - fileExt: 'langium' + useDiffEditor: false, + extensions: [{ + config: { + name: 'langium-example', + publisher: 'monaco-editor-wrapper-examples', + version: '1.0.0', + engines: { + vscode: '*' + }, + contributes: { + languages: [{ + id: 'langium', + extensions: ['.langium'], + aliases: ['langium', 'LANGIUM'], + configuration: './langium-configuration.json' + }], + grammars: [{ + language: 'langium', + scopeName: 'source.langium', + path: './langium-grammar.json' + }] } }, - useDiffEditor: false, - extensions: [{ - config: { - name: 'langium-example', - publisher: 'monaco-editor-wrapper-examples', - version: '1.0.0', - engines: { - vscode: '*' - }, - contributes: { - languages: [{ - id: 'langium', - extensions: ['.langium'], - aliases: ['langium', 'LANGIUM'], - configuration: './langium-configuration.json' - }], - grammars: [{ - language: 'langium', - scopeName: 'source.langium', - path: './langium-grammar.json' - }] - } - }, - filesOrContents: extensionFilesOrContents - }], - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'GitHub Dark High Contrast', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.wordBasedSuggestions': 'off' - }) - } + filesOrContents: extensionFilesOrContents + }], + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'GitHub Dark High Contrast', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.wordBasedSuggestions': 'off' + }) } }, languageClientConfigs: { langium: { languageId: 'langium', connection: { - configOptions: { + options: { $type: 'WorkerDirect', worker: langiumWorker }, diff --git a/packages/examples/src/langium/langium-dsl/wrapperLangium.ts b/packages/examples/src/langium/langium-dsl/wrapperLangium.ts index 20713d10e..9ca1aacae 100644 --- a/packages/examples/src/langium/langium-dsl/wrapperLangium.ts +++ b/packages/examples/src/langium/langium-dsl/wrapperLangium.ts @@ -8,6 +8,7 @@ import { setupLangiumClientExtended } from './config/extendedConfig.js'; import { setupLangiumClientClassic } from './config/classicConfig.js'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; import workerUrl from './worker/langium-server?worker&url'; +import { disableButton } from '../../common/client/utils.js'; let wrapper: MonacoEditorLanguageClientWrapper | undefined; let extended = false; @@ -67,13 +68,6 @@ const checkStarted = () => { return false; }; -const disableButton = (id: string, disabled: boolean) => { - const button = document.getElementById(id) as HTMLButtonElement | null; - if (button !== null) { - button.disabled = disabled; - } -}; - export const disposeEditor = async () => { if (!wrapper) return; wrapper.reportStatus(); diff --git a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts index 6b383b980..e54abc400 100644 --- a/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts +++ b/packages/examples/src/langium/statemachine/config/wrapperStatemachineConfig.ts @@ -7,7 +7,7 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings- import getLifecycleServiceOverride from '@codingame/monaco-vscode-lifecycle-service-override'; import getLocalizationServiceOverride from '@codingame/monaco-vscode-localization-service-override'; import { createDefaultLocaleConfiguration } from 'monaco-languageclient/vscode/services'; -import { LanguageClientConfig, UserConfig } from 'monaco-editor-wrapper'; +import { LanguageClientConfig, WrapperConfig } from 'monaco-editor-wrapper'; // cannot be imported with assert as json contains comments import statemachineLanguageConfig from './language-configuration.json?raw'; import responseStatemachineTm from '../syntaxes/statemachine.tmLanguage.json?raw'; @@ -20,7 +20,7 @@ export const createLangiumGlobalConfig = async (params: { worker?: Worker, messagePort?: MessagePort, messageTransports?: MessageTransports -}): Promise => { +}): Promise => { const extensionFilesOrContents = new Map(); extensionFilesOrContents.set(`/${params.languageServerId}-statemachine-configuration.json`, statemachineLanguageConfig); extensionFilesOrContents.set(`/${params.languageServerId}-statemachine-grammar.json`, responseStatemachineTm); @@ -37,7 +37,7 @@ export const createLangiumGlobalConfig = async (params: { statemachine: { languageId: 'statemachine', connection: { - configOptions: { + options: { $type: 'WorkerDirect', worker: params.worker, messagePort: params.messagePort, @@ -48,52 +48,50 @@ export const createLangiumGlobalConfig = async (params: { } : undefined; return { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride(), - ...getLifecycleServiceOverride(), - ...getLocalizationServiceOverride(createDefaultLocaleConfiguration()), - }, - debugLogging: true + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride(), + ...getLifecycleServiceOverride(), + ...getLocalizationServiceOverride(createDefaultLocaleConfiguration()), }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main - }, - useDiffEditor: false, - extensions: [{ - config: { - name: 'statemachine-example', - publisher: 'monaco-editor-wrapper-examples', - version: '1.0.0', - engines: { - vscode: '*' - }, - contributes: { - languages: [{ - id: 'statemachine', - extensions: ['.statemachine'], - aliases: ['statemachine', 'Statemachine'], - configuration: `./${params.languageServerId}-statemachine-configuration.json` - }], - grammars: [{ - language: 'statemachine', - scopeName: 'source.statemachine', - path: `./${params.languageServerId}-statemachine-grammar.json` - }] - } + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main + }, + useDiffEditor: false, + extensions: [{ + config: { + name: 'statemachine-example', + publisher: 'monaco-editor-wrapper-examples', + version: '1.0.0', + engines: { + vscode: '*' }, - filesOrContents: extensionFilesOrContents - }], - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.wordBasedSuggestions': 'off' - }) - } + contributes: { + languages: [{ + id: 'statemachine', + extensions: ['.statemachine'], + aliases: ['statemachine', 'Statemachine'], + configuration: `./${params.languageServerId}-statemachine-configuration.json` + }], + grammars: [{ + language: 'statemachine', + scopeName: 'source.statemachine', + path: `./${params.languageServerId}-statemachine-grammar.json` + }] + } + }, + filesOrContents: extensionFilesOrContents + }], + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.wordBasedSuggestions': 'off' + }) } }, languageClientConfigs, diff --git a/packages/examples/src/langium/statemachine/main-react.tsx b/packages/examples/src/langium/statemachine/main-react.tsx index 3ba131a42..3543116eb 100644 --- a/packages/examples/src/langium/statemachine/main-react.tsx +++ b/packages/examples/src/langium/statemachine/main-react.tsx @@ -48,7 +48,7 @@ export const runStatemachineReact = async () => {
+ wrapperConfig={langiumGlobalConfig} />
); }; diff --git a/packages/examples/src/ts/wrapperAdvanced.ts b/packages/examples/src/multi/twoLanguageClients.ts similarity index 63% rename from packages/examples/src/ts/wrapperAdvanced.ts rename to packages/examples/src/multi/twoLanguageClients.ts index a2d356759..d4085e14f 100644 --- a/packages/examples/src/ts/wrapperAdvanced.ts +++ b/packages/examples/src/multi/twoLanguageClients.ts @@ -8,9 +8,10 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings- // this is required syntax highlighting import '@codingame/monaco-vscode-json-default-extension'; import '@codingame/monaco-vscode-python-default-extension'; -import { MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { CodePlusFileExt, MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; import { MonacoLanguageClient } from 'monaco-languageclient'; +import { disableButton } from '../common/client/utils.js'; export const configureMonacoWorkers = () => { useWorkerFactory({ @@ -22,34 +23,44 @@ export const configureMonacoWorkers = () => { }; export const runMultipleLanguageClientsExample = async () => { - const text = `{ + disableButton('button-flip', true); + + const textJson = `{ "$schema": "http://json.schemastore.org/coffeelint", "line_endings": {"value": "unix"} }`; - const userConfig: UserConfig = { + + const textPython = `from hello2 import print_hello + +print_hello() +print("Hello Moon!") +`; + + let currentText = textJson; + let currenFileExt = 'json'; + + const wrapperConfig: WrapperConfig = { id: '42', - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride() - }, - debugLogging: true + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride() }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text, - fileExt: 'json' - } - }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.wordBasedSuggestions': 'off' - }) + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: currentText, + fileExt: currenFileExt } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.wordBasedSuggestions': 'off' + }) } }, languageClientConfigs: { @@ -57,7 +68,7 @@ export const runMultipleLanguageClientsExample = async () => { languageId: 'json', name: 'JSON Client', connection: { - configOptions: { + options: { $type: 'WebSocketParams', host: 'localhost', port: 30000, @@ -70,7 +81,7 @@ export const runMultipleLanguageClientsExample = async () => { languageId: 'python', name: 'Python Client', connection: { - configOptions: { + options: { $type: 'WebSocketParams', host: 'localhost', port: 30001, @@ -93,8 +104,8 @@ export const runMultipleLanguageClientsExample = async () => { } } }, - languageClientOptions: { - documentSelector: ['python'], + clientOptions: { + documentSelector: ['python', 'py'], workspaceFolder: { index: 0, name: 'workspace', @@ -110,10 +121,27 @@ export const runMultipleLanguageClientsExample = async () => { try { document.querySelector('#button-start')?.addEventListener('click', async () => { - await wrapper.initAndStart(userConfig, htmlElement); + if (wrapperConfig.editorAppConfig.codeResources?.main !== undefined) { + (wrapperConfig.editorAppConfig.codeResources.main as CodePlusFileExt).text = currentText; + (wrapperConfig.editorAppConfig.codeResources.main as CodePlusFileExt).fileExt = currenFileExt; + } + + await wrapper.initAndStart(wrapperConfig, htmlElement); + disableButton('button-flip', false); }); document.querySelector('#button-dispose')?.addEventListener('click', async () => { await wrapper.dispose(); + disableButton('button-flip', true); + }); + document.querySelector('#button-flip')?.addEventListener('click', async () => { + currentText = currentText === textJson ? textPython : textJson; + currenFileExt = currenFileExt === 'json' ? 'py' : 'json'; + wrapper.updateCodeResources({ + main: { + text: currentText, + fileExt: currenFileExt + } + }); }); } catch (e) { console.error(e); diff --git a/packages/examples/src/python/client/config.ts b/packages/examples/src/python/client/config.ts index 96a183669..72b5fa352 100644 --- a/packages/examples/src/python/client/config.ts +++ b/packages/examples/src/python/client/config.ts @@ -7,12 +7,12 @@ import * as vscode from 'vscode'; import getEditorServiceOverride from '@codingame/monaco-vscode-editor-service-override'; import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings-service-override'; import '@codingame/monaco-vscode-python-default-extension'; -import { createUrl, UserConfig } from 'monaco-editor-wrapper'; +import { createUrl, WrapperConfig } from 'monaco-editor-wrapper'; import { useOpenEditorStub } from 'monaco-editor-wrapper/vscode/services'; import { MonacoLanguageClient } from 'monaco-languageclient'; import { toSocket, WebSocketMessageReader, WebSocketMessageWriter } from 'vscode-ws-jsonrpc'; -export const createUserConfig = (workspaceRoot: string, code: string, codeUri: string): UserConfig => { +export const createUserConfig = (workspaceRoot: string, code: string, codeUri: string): WrapperConfig => { const url = createUrl({ secured: false, host: 'localhost', @@ -33,7 +33,7 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s languageId: 'python', name: 'Python Language Server Example', connection: { - configOptions: { + options: { $type: 'WebSocketDirect', webSocket: webSocket, startOptions: { @@ -51,7 +51,7 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s }, messageTransports: { reader, writer } }, - languageClientOptions: { + clientOptions: { documentSelector: ['python'], workspaceFolder: { index: 0, @@ -61,31 +61,29 @@ export const createUserConfig = (workspaceRoot: string, code: string, codeUri: s } } }, - wrapperConfig: { - serviceConfig: { - userServices: { - ...getEditorServiceOverride(useOpenEditorStub), - ...getKeybindingsServiceOverride() - }, - debugLogging: true + serviceConfig: { + userServices: { + ...getEditorServiceOverride(useOpenEditorStub), + ...getKeybindingsServiceOverride() }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text: code, - uri: codeUri - } - }, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'editor.guides.bracketPairsHorizontal': 'active', - 'editor.wordBasedSuggestions': 'off' - }) - }, - useDiffEditor: false - } + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: code, + uri: codeUri + } + }, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'editor.guides.bracketPairsHorizontal': 'active', + 'editor.wordBasedSuggestions': 'off' + }) + }, + useDiffEditor: false }, loggerConfig: { enabled: true, diff --git a/packages/examples/src/python/client/reactPython.tsx b/packages/examples/src/python/client/reactPython.tsx index b644d9c1e..38d35849a 100644 --- a/packages/examples/src/python/client/reactPython.tsx +++ b/packages/examples/src/python/client/reactPython.tsx @@ -41,7 +41,7 @@ export const runPythonReact = async () => { return (
{ diff --git a/packages/examples/src/ts/wrapperTs.ts b/packages/examples/src/ts/wrapperTs.ts index 5b10c12bb..56867bf12 100644 --- a/packages/examples/src/ts/wrapperTs.ts +++ b/packages/examples/src/ts/wrapperTs.ts @@ -8,7 +8,7 @@ import getKeybindingsServiceOverride from '@codingame/monaco-vscode-keybindings- import '@codingame/monaco-vscode-theme-defaults-default-extension'; import '@codingame/monaco-vscode-typescript-basics-default-extension'; import '@codingame/monaco-vscode-typescript-language-features-default-extension'; -import { CodePlusUri, MonacoEditorLanguageClientWrapper, UserConfig } from 'monaco-editor-wrapper'; +import { CodePlusUri, MonacoEditorLanguageClientWrapper, WrapperConfig } from 'monaco-editor-wrapper'; import { useWorkerFactory } from 'monaco-editor-wrapper/workerFactory'; export const configureMonacoWorkers = () => { @@ -31,39 +31,37 @@ export const runTsWrapper = async () => { return "Goodbye"; };`; - const userConfig: UserConfig = { - wrapperConfig: { - serviceConfig: { - userServices: { - ...getKeybindingsServiceOverride() - }, - enableExtHostWorker: true, - debugLogging: true + const wrapperConfig: WrapperConfig = { + serviceConfig: { + userServices: { + ...getKeybindingsServiceOverride() }, - editorAppConfig: { - $type: 'extended', - codeResources: { - main: { - text: code, - uri: codeUri - }, - original: { - text: codeOriginal, - uri: codeOriginalUri, - } + enableExtHostWorker: true, + debugLogging: true + }, + editorAppConfig: { + $type: 'extended', + codeResources: { + main: { + text: code, + uri: codeUri }, - useDiffEditor: false, - userConfiguration: { - json: JSON.stringify({ - 'workbench.colorTheme': 'Default Dark Modern', - 'typescript.tsserver.web.projectWideIntellisense.enabled': true, - 'typescript.tsserver.web.projectWideIntellisense.suppressSemanticErrors': false, - 'diffEditor.renderSideBySide': false, - 'editor.lightbulb.enabled': 'on', - 'editor.glyphMargin': true, - 'editor.guides.bracketPairsHorizontal': true - }) + original: { + text: codeOriginal, + uri: codeOriginalUri, } + }, + useDiffEditor: false, + userConfiguration: { + json: JSON.stringify({ + 'workbench.colorTheme': 'Default Dark Modern', + 'typescript.tsserver.web.projectWideIntellisense.enabled': true, + 'typescript.tsserver.web.projectWideIntellisense.suppressSemanticErrors': false, + 'diffEditor.renderSideBySide': false, + 'editor.lightbulb.enabled': 'on', + 'editor.glyphMargin': true, + 'editor.guides.bracketPairsHorizontal': true + }) } } }; @@ -73,7 +71,7 @@ export const runTsWrapper = async () => { try { document.querySelector('#button-start')?.addEventListener('click', async () => { - await wrapper.initAndStart(userConfig, htmlElement); + await wrapper.initAndStart(wrapperConfig, htmlElement); vscode.commands.getCommands().then((x) => { console.log(`Found ${x.length} commands`); @@ -112,9 +110,9 @@ export const runTsWrapper = async () => { }); document.querySelector('#button-diff')?.addEventListener('click', async () => { // ensure it is boolean value and not undefined - const useDiffEditor = userConfig.wrapperConfig.editorAppConfig.useDiffEditor ?? false; - userConfig.wrapperConfig.editorAppConfig.useDiffEditor = !useDiffEditor; - await wrapper.initAndStart(userConfig, htmlElement); + const useDiffEditor = wrapperConfig.editorAppConfig.useDiffEditor ?? false; + wrapperConfig.editorAppConfig.useDiffEditor = !useDiffEditor; + await wrapper.initAndStart(wrapperConfig, htmlElement); }); document.querySelector('#button-dispose')?.addEventListener('click', async () => { await wrapper.dispose(); diff --git a/packages/examples/wrapper_adv.html b/packages/examples/two_langauge_clients.html similarity index 69% rename from packages/examples/wrapper_adv.html rename to packages/examples/two_langauge_clients.html index 58085632b..a8d6b9d31 100644 --- a/packages/examples/wrapper_adv.html +++ b/packages/examples/two_langauge_clients.html @@ -2,19 +2,20 @@ - Multiple Language Clients + Json & Python Languageclients & Language Server (Web Socket) -

Multiple Language Clients

+

Json & Python Languageclients & Language Server (Web Socket)

+