Compare commits

..

No commits in common. "main" and "v0.54.0-dev-20251002" have entirely different histories.

308 changed files with 192999 additions and 223548 deletions

View file

@ -62,9 +62,6 @@ extends:
- script: npm ci
displayName: Install NPM dependencies
- script: npx playwright install --with-deps
displayName: Install Playwright Dependencies
- script: yarn ts-node ./scripts/ci/build-monaco-editor-core-pkg nightly
env:
VSCODE_REF: ${{ parameters.vscodeRef }}

View file

@ -50,9 +50,6 @@ extends:
- script: npm ci
displayName: Install NPM dependencies
- script: npx playwright install --with-deps
displayName: Install Playwright Dependencies
- script: yarn ts-node ./scripts/ci/build-monaco-editor-core-pkg stable
displayName: Setup, Build & Test monaco-editor-core

150
.github/commands.json vendored
View file

@ -1,150 +0,0 @@
[
{
"type": "comment",
"name": "question",
"allowUsers": [],
"action": "updateLabels",
"addLabel": "*question"
},
{
"type": "label",
"name": "*question",
"action": "close",
"reason": "not_planned",
"comment": "We closed this issue because it is a question about using Monaco Editor rather than an issue or feature request. Please search for help on [StackOverflow](https://stackoverflow.com/questions/tagged/monaco-editor), where the community has already answered many similar questions. See also our [issue reporting guidelines](https://github.com/microsoft/monaco-editor#contributing).\n\nHappy Coding!"
},
{
"type": "label",
"name": "*out-of-scope",
"action": "close",
"reason": "not_planned",
"comment": "We closed this issue because we don't plan to address it in the foreseeable future. If you disagree and feel that this issue is crucial: we are happy to listen and to reconsider.\n\nThanks for your understanding, and happy coding!"
},
{
"type": "label",
"name": "wont-fix",
"action": "close",
"reason": "not_planned",
"comment": "We closed this issue because we don't plan to address it.\n\nThanks for your understanding, and happy coding!"
},
{
"type": "comment",
"name": "duplicate",
"allowUsers": [],
"action": "updateLabels",
"addLabel": "*duplicate"
},
{
"type": "label",
"name": "*duplicate",
"action": "close",
"reason": "not_planned",
"comment": "Thanks for creating this issue! We figured it's covering the same as another one we already have. Thus, we closed this one as a duplicate. You can search for [existing issues](https://github.com/microsoft/monaco-editor/issues).\n\nHappy Coding!"
},
{
"type": "comment",
"name": "verified",
"allowUsers": [
"@author"
],
"action": "updateLabels",
"addLabel": "verified",
"removeLabel": "author-verification-requested",
"requireLabel": "author-verification-requested",
"disallowLabel": "unreleased"
},
{
"type": "comment",
"name": "confirm",
"allowUsers": [],
"action": "updateLabels",
"addLabel": "confirmed",
"removeLabel": "confirmation-pending"
},
{
"type": "comment",
"name": "confirmationPending",
"allowUsers": [],
"action": "updateLabels",
"addLabel": "confirmation-pending",
"removeLabel": "confirmed"
},
{
"type": "comment",
"name": "needsMoreInfo",
"allowUsers": [],
"action": "updateLabels",
"addLabel": "~info-needed"
},
{
"type": "comment",
"name": "closedWith",
"allowUsers": [],
"action": "close",
"reason": "completed",
"addLabel": "unreleased"
},
{
"type": "comment",
"name": "spam",
"allowUsers": [],
"action": "close",
"reason": "not_planned",
"addLabel": "invalid"
},
{
"__comment__": "Allows folks on the team to label issues by commenting: `\\label My-Label` ",
"type": "comment",
"name": "label",
"allowUsers": []
},
{
"type": "label",
"name": "~verification-steps-needed",
"action": "updateLabels",
"addLabel": "verification-steps-needed",
"removeLabel": "~verification-steps-needed",
"comment": "Friendly ping! Looks like this issue requires some further steps to be verified. Please provide us with the steps necessary to verify this issue."
},
{
"type": "label",
"name": "~info-needed",
"action": "updateLabels",
"addLabel": "info-needed",
"removeLabel": "~info-needed",
"comment": "Thanks for creating this issue! We figured it's missing some basic information or doesn't follow our issue reporting guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!"
},
{
"type": "label",
"name": "~version-info-needed",
"action": "updateLabels",
"addLabel": "info-needed",
"removeLabel": "~version-info-needed",
"comment": "Thanks for creating this issue! We figured it's missing some basic information, such as a version number. Please take the time to update the issue with the Monaco Editor version you're using.\n\nHappy Coding!"
},
{
"type": "label",
"name": "~confirmation-needed",
"action": "updateLabels",
"addLabel": "info-needed",
"removeLabel": "~confirmation-needed",
"comment": "Please try to reproduce this issue with the latest version of Monaco Editor. If the issue persists, please update the issue with confirmation.\n\nHappy Coding!"
},
{
"type": "comment",
"name": "gifPlease",
"allowUsers": [],
"action": "comment",
"addLabel": "info-needed",
"comment": "Thanks for reporting this issue! Unfortunately, it's hard for us to understand what issue you're seeing. Please help us out by providing a screen recording showing exactly what isn't working as expected. While we can work with most standard formats, `.gif` files are preferred as they are displayed inline on GitHub. You may find https://gifcap.dev helpful as a browser-based gif recording tool.\n\nHappy coding!"
},
{
"type": "comment",
"name": "upstream",
"allowUsers": [],
"action": "close",
"reason": "not_planned",
"addLabel": "upstream",
"comment": "This issue is caused by an upstream dependency (VS Code editor core). The fix needs to happen in the [VS Code repository](https://github.com/microsoft/vscode). Please check if there's already an issue filed there, or create one if not.\n\nHappy Coding!"
}
]

View file

@ -74,7 +74,13 @@ jobs:
run: npm run compile --prefix webpack-plugin
- name: Package using webpack plugin
run: npm run package-for-smoketest
run: npm run package-for-smoketest-webpack
- name: Package using esbuild
run: npm run package-for-smoketest-esbuild
- name: Package using vite
run: npm run package-for-smoketest-vite
# - name: Package using parcel
# run: npm run package-for-smoketest-parcel --prefix test/smoke/parcel
@ -83,18 +89,14 @@ jobs:
- name: Run smoke test
run: npm run smoketest
- name: Install website node modules
working-directory: website
run: npm ci
# - name: Install website node modules
# working-directory: website
# run: yarn install --frozen-lockfile
- name: Install most recent version of monaco-editor
working-directory: website
run: npm install monaco-editor
# - name: Build website
# working-directory: website
# run: yarn run build
- name: Build website
working-directory: website
run: npm run build
- name: Test website
working-directory: website
run: npm run test
# - name: Test website
# working-directory: website
# run: yarn test

View file

@ -39,26 +39,20 @@ jobs:
- name: execute `npm ci` (1)
if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }}
run: npm ci
# For TypeDoc
- name: Build
run: npm run build
run: npm run build-monaco-editor
- name: Install website node modules
working-directory: website
run: npm ci
run: yarn install --frozen-lockfile
- name: Install most recent version of monaco-editor
working-directory: website
run: npm install monaco-editor
run: yarn add monaco-editor
- name: Build website
working-directory: website
run: npm run build
- name: Test website
working-directory: website
run: npm run test
run: yarn run build
- name: Setup Pages
uses: actions/configure-pages@v5

View file

@ -1,2 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npm run pretty-quick

2
.nvmrc
View file

@ -1 +1 @@
22.21.1
22.18.0

View file

@ -12,6 +12,5 @@
"typescript.tsdk": "./node_modules/typescript/lib",
"git.branchProtection": ["main", "release/*"],
"git.branchProtectionPrompt": "alwaysCommitToNewBranch",
"git.branchRandomName.enable": true,
"editor.formatOnSave": true
"git.branchRandomName.enable": true
}

View file

@ -1,25 +1,5 @@
# Monaco Editor Changelog
## [0.55.1]
- Fixes missing language exports (monaco.json/typescript/...) due to wrong "types" path - [#5123](https://github.com/microsoft/monaco-editor/issues/5123)
## [0.55.0]
### Breaking Changes
- Moves nested namespaces (`languages.css`, `languages.html`, `languages.json`, `languages.typescript`) to top level namespaces (`css`, `html`, `json`, `typescript`) to simplify the build process and align with typescript recommendations.
### New Features
- Adds native LSP support (see new `lsp` namespace).
### Bug Fixes
- Updates dompurify to 3.2.7
## [0.54.0]
- Adds option `editor.mouseMiddleClickAction`
- Various bug fixes
## [0.53.0]
- :warning: This release deprecates the AMD build and ships with significant changes of the AMD build. The AMD build will still be shipped for a while, but we don't offer support for it anymore. Please migrate to the ESM build.

View file

@ -28,15 +28,6 @@ You will get:
:warning: The monaco editor also ships an `AMD` build for backwards-compatibility reasons, but the `AMD` support is deprecated and will be removed in future versions.
## Localization
To load the editor in a specific language, make sure that the corresponding nls script file is loaded before the main monaco editor script. For example, to load the editor in German, include the following script tag:
```html
<script src="path/to/monaco-editor/esm/nls.messages.de.js"></script>
```
Check the sources for available languages.
## Concepts
Monaco editor is best known for being the text editor that powers VS Code. However, it's a bit more nuanced. Some basic understanding about the underlying concepts is needed to use Monaco editor effectively.

View file

@ -4,5 +4,4 @@ export async function buildAmdMinDev() {
const rootPath = __dirname;
await run('npx vite build --mode development', { cwd: rootPath });
await run('npx vite build', { cwd: rootPath });
await run('npx rollup -c rollup-types.config.mjs', { cwd: rootPath });
}

View file

@ -1,44 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import nodeResolve from '@rollup/plugin-node-resolve';
import { join } from 'path';
import { defineConfig } from 'rollup';
import { dts } from 'rollup-plugin-dts';
import { dtsDeprecationWarning, mapModuleId } from '../shared.mjs';
export default defineConfig({
input: {
types: join(import.meta.dirname, './src/types.ts')
},
output: {
dir: join(import.meta.dirname, './out'),
format: 'es',
preserveModules: false,
entryFileNames: function (chunkInfo) {
const moduleId = chunkInfo.facadeModuleId;
if (moduleId) {
const m = mapModuleId(moduleId, '.d.ts');
if (m !== undefined) {
return m;
}
}
return '[name].d.ts';
}
},
external: [/.*\.css/],
plugins: [
nodeResolve(),
dts({
compilerOptions: {
stripInternal: true
},
includeExternal: ['monaco-editor-core', '@vscode/monaco-lsp-client']
}),
dtsDeprecationWarning(),
]
});

View file

@ -1,30 +1,10 @@
/// @ts-ignore
import * as require from 'require';
if (typeof (globalThis as any).require !== 'undefined' && typeof (globalThis as any).require.config === 'function') {
(globalThis as any).require.config({
ignoreDuplicateModules: [
'vscode-languageserver-types',
'vscode-languageserver-types/main',
'vscode-languageserver-textdocument',
'vscode-languageserver-textdocument/main',
'vscode-nls',
'vscode-nls/vscode-nls',
'jsonc-parser',
'jsonc-parser/main',
'vscode-uri',
'vscode-uri/index',
'vs/basic-languages/typescript/typescript'
]
});
}
self.MonacoEnvironment = {
getWorker: function (_moduleId, label) {
if (label === 'json') {
return new Worker(
getWorkerBootstrapUrl(
/// @ts-ignore
new URL('../../../src/language/json/json.worker.ts?worker', import.meta.url)
)
);
@ -32,7 +12,6 @@ self.MonacoEnvironment = {
if (label === 'css' || label === 'scss' || label === 'less') {
return new Worker(
getWorkerBootstrapUrl(
/// @ts-ignore
new URL('../../../src/language/css/css.worker.ts?worker', import.meta.url)
)
);
@ -40,7 +19,6 @@ self.MonacoEnvironment = {
if (label === 'html' || label === 'handlebars' || label === 'razor') {
return new Worker(
getWorkerBootstrapUrl(
/// @ts-ignore
new URL('../../../src/language/html/html.worker.ts?worker', import.meta.url)
)
);
@ -48,22 +26,17 @@ self.MonacoEnvironment = {
if (label === 'typescript' || label === 'javascript') {
return new Worker(
getWorkerBootstrapUrl(
/// @ts-ignore
new URL('../../../src/language/typescript/ts.worker.ts?worker', import.meta.url)
)
);
}
return new Worker(
/// @ts-ignore
getWorkerBootstrapUrl(new URL('../../../src/editor/editor.worker.ts?worker', import.meta.url))
);
}
};
function getWorkerBootstrapUrl(workerScriptUrl: string | URL) {
if (typeof workerScriptUrl !== 'string') {
workerScriptUrl = workerScriptUrl.toString();
}
function getWorkerBootstrapUrl(workerScriptUrl) {
const blob = new Blob(
[
[
@ -81,10 +54,11 @@ function getWorkerBootstrapUrl(workerScriptUrl: string | URL) {
}
import 'vs/nls.messages-loader!';
import * as monaco from '../../../src/editor/editor.main';
export * from '../../../src/editor/editor.main';
globalThis.monaco = monaco;
import '../../../src/basic-languages/monaco.contribution';
import '../../../src/language/css/monaco.contribution';
import '../../../src/language/html/monaco.contribution';
import '../../../src/language/json/monaco.contribution';
import '../../../src/language/typescript/monaco.contribution';
const styleSheetUrl = require.toUrl('vs/editor/editor.main.css');
@ -92,3 +66,5 @@ const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = styleSheetUrl;
document.head.appendChild(link);
export * as m from 'monaco-editor-core';

View file

@ -1,3 +0,0 @@
import * as m from './editor.main';
export { m };

View file

@ -1,26 +1,36 @@
import { readFileSync } from 'node:fs';
import { dirname, resolve } from 'node:path';
import { glob } from 'node:fs/promises';
import { basename, dirname, join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { defineConfig } from 'vite';
import { urlToEsmPlugin } from './plugin';
import { getNlsEntryPoints } from '../shared.mjs';
const __dirname = dirname(fileURLToPath(import.meta.url));
export default defineConfig(async (args) => {
const monacoEditorCoreDir = join(
dirname(require.resolve('monaco-editor-core/package.json')),
'esm'
);
const nlsEntries = {};
for await (const path of glob(`${monacoEditorCoreDir}/nls.messages.*.js`)) {
const entryName = basename(path).replace('.js', '');
nlsEntries[entryName] = path;
}
/** @type {import('vite').UserConfig} */
return {
base: './',
resolve: {
dedupe: ['monaco-editor-core']
define: {
AMD: false
},
build: {
lib: {
cssFileName: 'editor/editor.main',
entry: {
...getNlsEntryPoints(),
...nlsEntries,
'nls.messages-loader': resolve(__dirname, 'src/nls.messages-loader.js'),
'editor/editor.main': resolve(__dirname, 'src/editor.main.ts'),
'editor/editor.main': resolve(__dirname, 'src/editor.main.js'),
'basic-languages/monaco.contribution': resolve(
__dirname,
'../../src/basic-languages/monaco.contribution.ts'

127
build/build-languages.ts Normal file
View file

@ -0,0 +1,127 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import glob from 'glob';
import { runTsc, massageAndCopyDts, buildESM } from './utils';
import { removeDir } from './fs';
removeDir(`out/languages`);
runTsc(`src/tsconfig.json`);
//#region Type Defintion
massageAndCopyDts(
`out/languages/tsc/language/css/monaco.contribution.d.ts`,
`out/languages/bundled/css.d.ts`,
'monaco.languages.css'
);
massageAndCopyDts(
`out/languages/tsc/language/html/monaco.contribution.d.ts`,
`out/languages/bundled/html.d.ts`,
'monaco.languages.html'
);
massageAndCopyDts(
`out/languages/tsc/language/json/monaco.contribution.d.ts`,
`out/languages/bundled/json.d.ts`,
'monaco.languages.json'
);
massageAndCopyDts(
`out/languages/tsc/language/typescript/monaco.contribution.d.ts`,
`out/languages/bundled/typescript.d.ts`,
'monaco.languages.typescript'
);
//#endregion
//#region css
buildESM({
base: 'language/css',
entryPoints: [
'src/language/css/monaco.contribution.ts',
'src/language/css/cssMode.ts',
'src/language/css/css.worker.ts'
],
external: ['monaco-editor-core', '*/cssMode', '*/monaco.contribution']
});
//#endregion
//#region html
buildESM({
base: 'language/html',
entryPoints: [
'src/language/html/monaco.contribution.ts',
'src/language/html/htmlMode.ts',
'src/language/html/html.worker.ts'
],
external: ['monaco-editor-core', '*/htmlMode', '*/monaco.contribution']
});
//#endregion
//#region json
buildESM({
base: 'language/json',
entryPoints: [
'src/language/json/monaco.contribution.ts',
'src/language/json/jsonMode.ts',
'src/language/json/json.worker.ts'
],
external: ['monaco-editor-core', '*/jsonMode', '*/monaco.contribution']
});
//#endregion
//#region typescript
buildESM({
base: 'language/typescript',
entryPoints: [
'src/language/typescript/monaco.contribution.ts',
'src/language/typescript/tsMode.ts',
'src/language/typescript/ts.worker.ts'
],
external: ['monaco-editor-core', '*/tsMode', '*/monaco.contribution']
});
//#endregion
//#region basic-languages
glob('../src/basic-languages/*/*.contribution.ts', { cwd: __dirname }, function (err, files) {
if (err) {
console.error(err);
return;
}
const languages = files.map((file) => file.split('/')[3]);
// ESM
{
/** @type {string[]} */
const entryPoints = [
'src/basic-languages/monaco.contribution.ts',
'src/basic-languages/_.contribution.ts'
];
const external = ['monaco-editor-core', '*/_.contribution'];
for (const language of languages) {
entryPoints.push(`src/basic-languages/${language}/${language}.contribution.ts`);
entryPoints.push(`src/basic-languages/${language}/${language}.ts`);
external.push(`*/${language}.contribution`);
external.push(`*/${language}`);
}
buildESM({
base: 'basic-languages',
entryPoints,
external
});
}
});
//#endregion

View file

@ -5,30 +5,38 @@
import path = require('path');
import fs = require('fs');
import { REPO_ROOT, readFiles, writeFiles } from '../build/utils';
import { generateEsmMetadataJsAndDTs } from './releaseMetadata';
import { buildESM } from './esm/build.script';
import {
REPO_ROOT,
readFiles,
writeFiles,
IFile,
readFile,
build,
bundledFileHeader
} from '../build/utils';
import { removeDir } from '../build/fs';
import { generateMetadata } from './releaseMetadata';
import { buildAmdMinDev } from './amd/build.script';
import { rm } from 'fs/promises';
import ts = require('typescript');
async function run() {
await rm(path.join(REPO_ROOT, './out/monaco-editor'), { recursive: true, force: true });
removeDir(`out/monaco-editor`);
await buildESM();
await buildAmdMinDev();
buildAmdMinDev();
// copy types.d.ts from build/amd/out/ to out/monaco-editor/monaco.d.ts (and append `declare global { export import monaco = editor_main; }`)
(() => {
let contents = fs.readFileSync('build/amd/out/types.d.ts', { encoding: 'utf8' });
contents += '\n\ndeclare global { export import monaco = editor_main; }\n';
fs.writeFileSync('out/monaco-editor/monaco.d.ts', contents);
})();
// esm folder
ESM_release();
createThirdPartyNoticesDotTxt();
generateEsmMetadataJsAndDTs();
// monaco.d.ts, editor.api.d.ts
releaseDTS();
// package.json
(() => {
// ThirdPartyNotices.txt
releaseThirdPartyNotices();
// esm/metadata.d.ts, esm/metadata.js
generateMetadata();
// package.json
(() => {
const packageJSON = readFiles('package.json', { base: '' })[0];
const json = JSON.parse(packageJSON.contents.toString());
@ -37,9 +45,9 @@ async function run() {
packageJSON.contents = Buffer.from(JSON.stringify(json, null, ' '));
writeFiles([packageJSON], `out/monaco-editor`);
})();
})();
(() => {
(() => {
/** @type {IFile[]} */
let otherFiles = [];
@ -52,14 +60,306 @@ async function run() {
);
writeFiles(otherFiles, `out/monaco-editor`);
})();
})();
function ESM_release() {
const coreFiles = readFiles(`node_modules/monaco-editor-core/esm/**/*`, {
base: 'node_modules/monaco-editor-core/esm',
// we will create our own editor.api.d.ts which also contains the plugins API
ignore: ['node_modules/monaco-editor-core/esm/vs/editor/editor.api.d.ts']
});
ESM_addImportSuffix(coreFiles);
ESM_addPluginContribs(coreFiles);
writeFiles(coreFiles, `out/monaco-editor/esm`);
ESM_releasePlugins();
build({
entryPoints: ['src/editor/editor.main.ts', 'src/editor/editor.worker.ts'],
bundle: true,
target: 'esnext',
format: 'esm',
drop: ['debugger'],
define: {
AMD: 'false'
},
banner: {
js: bundledFileHeader
},
external: ['./src/basic-languages/*', './edcore.main.js', './editor.worker.start'],
alias: {
'monaco-editor-core/esm/vs/editor/editor.worker.start': './editor.worker.start',
'monaco-editor-core': './edcore.main.js'
},
outbase: `src/`,
outdir: `out/monaco-editor/esm/vs/`,
plugins: [
{
name: 'example',
setup(build) {
build.onResolve({ filter: /\/language\/|\/basic-languages\// }, (args) => {
if (args.path.includes('monaco-editor-core')) {
return undefined;
}
return { external: true };
});
}
}
]
});
}
/**
* Release a plugin to `esm`.
* Adds a dependency to 'vs/editor/editor.api' in contrib files in order for `monaco` to be defined.
* Rewrites imports for 'monaco-editor-core/**'
*/
function ESM_releasePlugins() {
const files = readFiles(`out/languages/bundled/esm/**/*`, { base: 'out/languages/bundled/esm/' });
for (const file of files) {
if (!/(\.js$)|(\.ts$)/.test(file.path)) {
continue;
}
let contents = file.contents.toString();
// WARNING: this only returns the first occurence of each imported file!
const info = ts.preProcessFile(contents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
let importText = info.importedFiles[i].fileName;
const pos = info.importedFiles[i].pos;
const end = info.importedFiles[i].end;
if (!/(^\.\/)|(^\.\.\/)/.test(importText)) {
// non-relative import
if (!/^monaco-editor-core/.test(importText)) {
console.error(`Non-relative import for unknown module: ${importText} in ${file.path}`);
process.exit(1);
}
if (importText === 'monaco-editor-core') {
importText = 'monaco-editor-core/esm/vs/editor/editor.api';
}
const importFilePath = importText.substring('monaco-editor-core/esm/'.length);
let relativePath = path
.relative(path.dirname(file.path), importFilePath)
.replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
contents = contents.substring(0, pos + 1) + relativePath + contents.substring(end + 1);
}
}
file.contents = Buffer.from(contents);
}
for (const file of files) {
if (!/monaco\.contribution\.js$/.test(file.path)) {
continue;
}
const apiFilePath = 'vs/editor/editor.api';
let relativePath = path.relative(path.dirname(file.path), apiFilePath).replace(/\\/g, '/');
if (!/(^\.\/)|(^\.\.\/)/.test(relativePath)) {
relativePath = './' + relativePath;
}
let contents = file.contents.toString();
contents = `import '${relativePath}';\n` + contents;
file.contents = Buffer.from(contents);
}
ESM_addImportSuffix(files);
writeFiles(files, `out/monaco-editor/esm`);
}
/**
* Adds `.js` to all import statements.
*/
function ESM_addImportSuffix(files: IFile[]) {
for (const file of files) {
if (!/\.js$/.test(file.path)) {
continue;
}
let contents = file.contents.toString();
const info = ts.preProcessFile(contents);
for (let i = info.importedFiles.length - 1; i >= 0; i--) {
const importText = info.importedFiles[i].fileName;
const pos = info.importedFiles[i].pos;
const end = info.importedFiles[i].end;
if (/(\.css)|(\.js)$/.test(importText)) {
// A CSS import or an import already using .js
continue;
}
contents = contents.substring(0, pos + 1) + importText + '.js' + contents.substring(end + 1);
}
file.contents = Buffer.from(contents);
}
}
/**
* - Rename esm/vs/editor/editor.main.js to esm/vs/editor/edcore.main.js
* - Create esm/vs/editor/editor.main.js that that stiches things together
*/
function ESM_addPluginContribs(files: IFile[]) {
for (const file of files) {
if (!/editor\.main\.js$/.test(file.path)) {
continue;
}
file.path = file.path.replace(/editor\.main/, 'edcore.main');
}
}
/**
* Edit monaco.d.ts:
* - append monaco.d.ts from plugins
*/
function releaseDTS() {
const monacodts = readFiles('node_modules/monaco-editor-core/monaco.d.ts', {
base: 'node_modules/monaco-editor-core'
})[0];
let contents = monacodts.contents.toString();
const additionalDtsFiles: Record<string, string> = {
'out/languages/tsc/common/workers.d.ts': 'monaco.editor'
};
Object.entries(additionalDtsFiles).forEach(([filePath, namespace]) => {
try {
const dtsFile = readFile(filePath);
let dtsContent = dtsFile.contents.toString();
// Remove imports
dtsContent = dtsContent.replace(/import .*\n/gm, '');
dtsContent = dtsContent.replace(/export declare function/gm, 'export function');
// Wrap in namespace if specified
if (namespace) {
dtsContent = `declare namespace ${namespace} {\n${dtsContent
.split('\n')
.map((line) => (line ? ` ${line}` : line))
.join('\n')}\n}`;
}
contents += '\n' + dtsContent;
} catch (error) {
console.warn(`Could not read d.ts file: ${filePath}`);
}
});
const extraContent = readFiles('out/languages/bundled/*.d.ts', {
base: 'out/languages/bundled/'
}).map((file) => {
return file.contents.toString().replace(/\/\/\/ <reference.*\n/m, '');
});
contents =
[
'/*!-----------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Type definitions for monaco-editor',
' * Released under the MIT license',
'*-----------------------------------------------------------*/'
].join('\n') +
'\n' +
contents +
'\n' +
extraContent.join('\n');
// Ensure consistent indentation and line endings
contents = cleanFile(contents);
monacodts.contents = Buffer.from(contents);
const editorapidts = {
path: 'esm/vs/editor/editor.api.d.ts',
contents: Buffer.from(toExternalDTS(contents))
};
writeFiles([monacodts, editorapidts], `out/monaco-editor`);
// fs.writeFileSync('website/typedoc/monaco.d.ts', contents);
}
/**
* Transforms a .d.ts which uses internal modules (namespaces) to one which is usable with external modules
* This function is duplicated in the `vscode` repo.
* @param {string} contents
*/
function toExternalDTS(contents) {
const lines = contents.split(/\r\n|\r|\n/);
let killNextCloseCurlyBrace = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (killNextCloseCurlyBrace) {
if ('}' === line) {
lines[i] = '';
killNextCloseCurlyBrace = false;
continue;
}
if (line.indexOf(' ') === 0) {
lines[i] = line.substr(4);
} else if (line.charAt(0) === '\t') {
lines[i] = line.substr(1);
}
continue;
}
if ('declare namespace monaco {' === line) {
lines[i] = '';
killNextCloseCurlyBrace = true;
continue;
}
if (line.indexOf('declare namespace monaco.') === 0) {
lines[i] = line.replace('declare namespace monaco.', 'export namespace ');
}
if (line.indexOf('declare var MonacoEnvironment') === 0) {
lines[i] = `declare global {\n var MonacoEnvironment: Environment | undefined;\n}`;
}
}
return lines.join('\n').replace(/\n\n\n+/g, '\n\n');
}
/**
* Normalize line endings and ensure consistent 4 spaces indentation
*/
function cleanFile(contents: string): string {
return contents
.split(/\r\n|\r|\n/)
.map(function (line) {
const m = line.match(/^(\t+)/);
if (!m) {
return line;
}
const tabsCount = m[1].length;
let newIndent = '';
for (let i = 0; i < 4 * tabsCount; i++) {
newIndent += ' ';
}
return newIndent + line.substring(tabsCount);
})
.join('\n');
}
/**
* Edit ThirdPartyNotices.txt:
* - append ThirdPartyNotices.txt from plugins
*/
function createThirdPartyNoticesDotTxt() {
function releaseThirdPartyNotices() {
const tpn = readFiles('node_modules/monaco-editor-core/ThirdPartyNotices.txt', {
base: 'node_modules/monaco-editor-core'
})[0];
@ -77,5 +377,3 @@ function createThirdPartyNoticesDotTxt() {
writeFiles([tpn], `out/monaco-editor`);
}
run();

View file

@ -1,7 +0,0 @@
import { run } from '../../scripts/lib/index';
export async function buildESM() {
const rootPath = __dirname;
await run('npx rollup -c rollup.config.mjs', { cwd: rootPath });
await run('npx rollup -c rollup-types.config.mjs', { cwd: rootPath });
}

View file

@ -1 +0,0 @@
!dist

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 Alexandr Yeskov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,2 +0,0 @@
See https://www.npmjs.com/package/rollup-plugin-keep-css-imports.
Compare index.mjs with index.original.mjs to see the patch.

View file

@ -1,25 +0,0 @@
import MagicString from "magic-string";
import type { RenderedChunk } from "rollup";
import { OutputOptions, KeepCssImportsPluginContext } from "./types";
interface ChunkDetails {
chunk: RenderedChunk;
bundleOutDir: string;
moduleRoot: string;
}
export declare class ImportUpdater {
private _outputOptions;
private _pluginContext;
constructor(pluginContext: KeepCssImportsPluginContext, outputOptions: OutputOptions);
getMagicId(id: string): string;
updateImports(code: string, chunk: RenderedChunk, bundleOutDir: string, moduleRoot: string): {
code: string;
map: import("magic-string").SourceMap;
};
updateMatchedImport(m: RegExpMatchArray, magicString: MagicString, chunkDetails: ChunkDetails): void;
private addImportAndGetNewId;
private updateChunk;
private saveAndGetUpdatedImportPath;
private shouldAddPrefixCurrentDir;
private resolveOutputPath;
}
export {};

View file

@ -1,31 +0,0 @@
import type { AsyncCompiler, Options } from "sass";
type SassAsyncCompiler = Pick<AsyncCompiler, "compileAsync" | "compileStringAsync">;
export type PostCssCompatible = {
process: (css: string, opt: {
from: string;
to: string;
map: {
prev: string;
inline: boolean;
} | null;
}) => string | {
css: string;
map?: string;
};
};
export interface CompilationOptions {
outputExt: string;
sass?: SassAsyncCompiler;
postProcessor?: (css: string, map: string) => Promise<PostCssCompatible | string | {
css: string;
map?: string;
}>;
loadPaths?: string[];
sourceMap?: boolean;
sassOptions: Options<"async">;
}
export declare const compileSass: (sassPath: string, outWatchList: string[] | undefined, { outputExt, sass, postProcessor, loadPaths, sourceMap, sassOptions }: CompilationOptions) => Promise<{
css: string;
map: string;
}>;
export {};

View file

@ -1,3 +0,0 @@
export declare const PLUGIN_NAME = "keep-css-imports";
export declare const FILE_URL_PREFIX: string;
export declare const KEY_EXT_STRING = ".[keep-css-imports-plugin-ext]";

View file

@ -1,52 +0,0 @@
import { EmitFile } from "rollup";
import { StylesMap } from "./types";
export declare const escapeRegex: (val: any) => any;
export declare const assertDuplicates: (stylesToEmit: StylesMap) => void;
export declare const assertLocation: (outDir: any, assetPath: any) => void;
export declare const ensureSourceMap: ({ css, map }: {
css?: string | Uint8Array;
map?: string | Uint8Array;
}, includeSourceMap: boolean | "inline" | undefined, fileName: string, onEmit: EmitFile) => string | Uint8Array;
export declare const formatProcessedToCSS: (input: string | {
css: string;
map?: string | object;
}, sourceMap: boolean) => {
css: string;
map: string;
};
export declare const requireSass: () => Promise<{
default: typeof import("sass");
AsyncCompiler: typeof import("sass").AsyncCompiler;
Compiler: typeof import("sass").Compiler;
compile: typeof import("sass").compile;
compileAsync: typeof import("sass").compileAsync;
compileString: typeof import("sass").compileString;
compileStringAsync: typeof import("sass").compileStringAsync;
initCompiler: typeof import("sass").initCompiler;
initAsyncCompiler: typeof import("sass").initAsyncCompiler;
Exception: typeof import("sass").Exception;
Logger: typeof import("sass").Logger;
CalculationInterpolation: typeof import("sass").CalculationInterpolation;
CalculationOperation: typeof import("sass").CalculationOperation;
SassArgumentList: typeof import("sass").SassArgumentList;
SassBoolean: typeof import("sass").SassBoolean;
SassCalculation: typeof import("sass").SassCalculation;
SassColor: typeof import("sass").SassColor;
SassFunction: typeof import("sass").SassFunction;
SassList: typeof import("sass").SassList;
SassMap: typeof import("sass").SassMap;
SassMixin: typeof import("sass").SassMixin;
SassNumber: typeof import("sass").SassNumber;
SassString: typeof import("sass").SassString;
Value: typeof import("sass").Value;
sassFalse: import("sass").SassBoolean;
sassNull: import("sass").Value;
sassTrue: import("sass").SassBoolean;
FALSE: import("sass").types.Boolean<false>;
NULL: import("sass").types.Null;
TRUE: import("sass").types.Boolean<true>;
types: typeof import("sass").types;
render: typeof import("sass").render;
renderSync: typeof import("sass").renderSync;
info: string;
}>;

View file

@ -1,4 +0,0 @@
import type { Plugin } from "rollup";
import { KeepCssImportsOptions } from "./types";
declare function keepCssImports({ outputExt, outputPath, skipCurrentFolderPart, includeRegexp, sass, postProcessor, sassOptions, ...options }?: KeepCssImportsOptions): Plugin;
export default keepCssImports;

View file

@ -1,487 +0,0 @@
import { readFile } from 'fs/promises';
import * as path from 'path';
import MagicString from 'magic-string';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
var __assign = function () {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var escapeRegex = function (val) { return val.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&"); };
var assertDuplicates = function (stylesToEmit) {
Object.values(stylesToEmit).forEach(function (v, i, all) {
if (all.some(function (av, ai) { return !!v.output && v.output === av.output && ai != i; })) {
throw new Error("Two or more assets have conflicting output path ".concat(v.output));
}
});
};
var assertLocation = function (outDir, assetPath) {
if (!path.normalize(assetPath).startsWith(path.normalize(outDir))) {
throw new Error("Output path ".concat(assetPath, " must be in output directory ").concat(outDir));
}
};
var ensureSourceMap = function (_a, includeSourceMap, fileName, onEmit) {
var css = _a.css, map = _a.map;
if (map) {
if (includeSourceMap === "inline") {
css += "\n/*# sourceMappingURL=data:application/json;base64,".concat((map instanceof Uint8Array ? Buffer.from(map) : Buffer.from(map, "utf8")).toString("base64"), "*/");
}
else if (includeSourceMap === true) {
css += "\n/*# sourceMappingURL=".concat(path.basename(fileName), ".map */");
}
if (includeSourceMap === true) {
onEmit({
type: "asset",
fileName: fileName + ".map",
source: map,
});
}
}
return css;
};
var formatProcessedToCSS = function (input, sourceMap) {
return typeof input === "string"
? { css: input, map: "" }
: typeof input === "object"
? {
css: input.css,
map: !sourceMap ? "" : typeof input.map === "object" ? JSON.stringify(input.map) : input.map,
}
: input;
};
var requireSass = function () {
try {
return import('sass');
}
catch (e) {
throw new Error("You have to install `sass` package! Try running\n\t" +
"npm install --save-dev sass\nor\nyarn add sass --dev\n" +
"or use `sass` option to pass processor");
}
};
function ensureCompiler(sass) {
return __awaiter(this, void 0, void 0, function () {
var sassProcessor, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = sass;
if (_a) return [3 /*break*/, 2];
return [4 /*yield*/, requireSass()];
case 1:
_a = (_b.sent());
_b.label = 2;
case 2:
sassProcessor = _a;
if (!("compileAsync" in sassProcessor)) {
throw new Error("You have to install `sass` package! Or provide an object which implements `compileAsync` as `sass` option");
}
return [2 /*return*/, sassProcessor];
}
});
});
}
var isPostCssCompatible = function (result) {
return result && typeof result === "object" && "process" in result && typeof result.process === "function";
};
var compileSass = function (sassPath, outWatchList, _a) {
var outputExt = _a.outputExt, sass = _a.sass, postProcessor = _a.postProcessor, loadPaths = _a.loadPaths, sourceMap = _a.sourceMap, sassOptions = _a.sassOptions;
return __awaiter(void 0, void 0, void 0, function () {
var sassProcessor, watchListNeeded, compiled, css, mapObject, sources, map, result, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!sassPath) {
return [2 /*return*/, { css: "", map: "" }];
}
return [4 /*yield*/, ensureCompiler(sass)];
case 1:
sassProcessor = _d.sent();
watchListNeeded = Array.isArray(outWatchList);
return [4 /*yield*/, sassProcessor.compileAsync(sassPath, __assign({ loadPaths: loadPaths, style: "expanded", sourceMap: !!sourceMap || watchListNeeded, sourceMapIncludeSources: !!sourceMap || watchListNeeded }, (sassOptions || [])))];
case 2:
compiled = _d.sent();
css = compiled.css.toString();
if (watchListNeeded && compiled.sourceMap && typeof compiled.sourceMap === "object") {
mapObject = "toJSON" in compiled.sourceMap && typeof compiled.sourceMap.toJSON === "function"
? compiled.sourceMap.toJSON()
: compiled.sourceMap;
sources = mapObject.sources || mapObject._sources;
outWatchList.push.apply(outWatchList, sources.filter(function (s) { return s && typeof s === "string"; }));
}
map = compiled.sourceMap
? typeof compiled.sourceMap === "object"
? JSON.stringify(compiled.sourceMap)
: compiled.sourceMap
: "";
if (!(typeof postProcessor === "function")) return [3 /*break*/, 7];
return [4 /*yield*/, postProcessor(css, map)];
case 3:
result = _d.sent();
if ((typeof result !== "string" && typeof result !== "object") || result === null) {
throw new Error("`postProcessor` must return string, object with `css` and `map` or PostCSS like object which implements `process` function");
}
_b = formatProcessedToCSS;
if (!isPostCssCompatible(result) // If PostCSS compatible result
) return [3 /*break*/, 5]; // If PostCSS compatible result
return [4 /*yield*/, Promise.resolve(result.process(css, {
from: sassPath,
to: path.parse(sassPath).name + outputExt,
map: map ? { prev: map, inline: false } : null,
}))];
case 4:
_c = _d.sent();
return [3 /*break*/, 6];
case 5:
_c = result;
_d.label = 6;
case 6: return [2 /*return*/, _b.apply(void 0, [_c, sourceMap])];
case 7: return [2 /*return*/, { css: css, map: sourceMap ? map : undefined }];
}
});
});
};
var PLUGIN_NAME = "keep-css-imports";
var FILE_URL_PREFIX = new URL("file://").toString();
var KEY_EXT_STRING = ".[keep-css-imports-plugin-ext]";
var createErrorMessage = function (message) { return "[".concat(PLUGIN_NAME, "] ").concat(message); };
var ImportUpdater = /** @class */ (function () {
function ImportUpdater(pluginContext, outputOptions) {
var _this = this;
this.addImportAndGetNewId = function (resolvedId) {
var moduleIndex = _this._pluginContext.allStyleImports.indexOf(resolvedId);
return !~moduleIndex ? _this._pluginContext.allStyleImports.push(resolvedId) - 1 : moduleIndex;
};
this._pluginContext = pluginContext;
this._outputOptions = outputOptions;
}
ImportUpdater.prototype.getMagicId = function (id) {
return "\0" + this.addImportAndGetNewId(id) + KEY_EXT_STRING;
};
ImportUpdater.prototype.updateImports = function (code, chunk, bundleOutDir, moduleRoot) {
var _this = this;
var magicString = new MagicString(code);
var matchRegex = new RegExp("\0([^\"']+)".concat(escapeRegex(KEY_EXT_STRING)), "g");
Array.from(code.matchAll(matchRegex))
.reverse()
.forEach(function (m) {
return _this.updateMatchedImport(m, magicString, {
chunk: chunk,
bundleOutDir: bundleOutDir,
moduleRoot: moduleRoot,
});
});
return {
code: magicString.toString(),
map: magicString.generateMap({ hires: true }),
};
};
ImportUpdater.prototype.updateMatchedImport = function (m, magicString, chunkDetails) {
var importId = m[0];
var assetId = this._pluginContext.allStyleImports[m[1]];
if (!assetId || typeof assetId !== "string" || !this._pluginContext.stylesToEmit[assetId]) {
return;
}
var updatedImport = this.saveAndGetUpdatedImportPath(assetId, chunkDetails);
var start = m.index;
var end = start + importId.length;
magicString.overwrite(start, end, updatedImport);
this.updateChunk(importId, updatedImport, chunkDetails.chunk);
};
ImportUpdater.prototype.updateChunk = function (importId, updatedImport, chunk) {
if (chunk.importedBindings[importId]) {
chunk.importedBindings[updatedImport] = chunk.importedBindings[importId];
if (updatedImport !== importId) {
delete chunk.importedBindings[importId];
}
}
var importIndex = chunk.imports.indexOf(importId);
if (~importIndex) {
chunk.imports[importIndex] = updatedImport;
}
};
ImportUpdater.prototype.saveAndGetUpdatedImportPath = function (assetId, _a) {
var bundleOutDir = _a.bundleOutDir, moduleRoot = _a.moduleRoot, chunk = _a.chunk;
var assetOutput = this.resolveOutputPath(bundleOutDir, assetId, moduleRoot);
var updatedImport = path
.relative(path.dirname(path.resolve(bundleOutDir, chunk.fileName)), assetOutput)
.replace(/\\/g, "/");
this._pluginContext.stylesToEmit[assetId].output = path.relative(path.resolve(bundleOutDir), assetOutput);
if (this.shouldAddPrefixCurrentDir(updatedImport) &&
!updatedImport.startsWith("./") &&
!updatedImport.startsWith("../") &&
!updatedImport.match(/^[a-zA-Z]:/)) {
updatedImport = "./" + updatedImport;
}
return updatedImport;
};
ImportUpdater.prototype.shouldAddPrefixCurrentDir = function (updatedImport) {
var skip = this._outputOptions.skipCurrentFolderPart;
return !skip || (skip instanceof RegExp && !skip.test(updatedImport));
};
ImportUpdater.prototype.resolveOutputPath = function (bundleOutDir, assetId, moduleRoot) {
var _a = this._outputOptions, outputPath = _a.outputPath, outputDir = _a.outputDir, outputExt = _a.outputExt;
var newPath = undefined;
if (typeof outputPath === "function") {
newPath = outputPath(assetId);
assertLocation(bundleOutDir, newPath);
}
else if (typeof outputPath === "string") {
newPath = path.resolve(bundleOutDir, outputDir, outputPath !== "keep" ? outputPath : path.relative(moduleRoot, assetId));
assertLocation(bundleOutDir, newPath);
}
else {
throw new Error(createErrorMessage("Invalid outputPath option value!"));
}
return newPath.replace(/\.s[ca]ss$/, outputExt);
};
return ImportUpdater;
}());
var ensureStylesInfo = function (stylesMap, importer, resolvedId) {
stylesMap[resolvedId] = stylesMap[resolvedId] || { importers: [], watchList: [] };
stylesMap[resolvedId].importers.push(importer);
return stylesMap[resolvedId];
};
var ensureCodeAndWatchList = function (filePath, stylesInfo, isWatch, compilerOptions) {
return __awaiter(void 0, void 0, void 0, function () {
var outWatchList, _a, _b, css, map;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
outWatchList = [];
if (!filePath.endsWith(".css")) return [3 /*break*/, 2];
_a = stylesInfo;
return [4 /*yield*/, readFile(filePath, "utf8")];
case 1:
_a.css = _c.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, compileSass(filePath, isWatch ? outWatchList : undefined, compilerOptions)];
case 3:
_b = _c.sent(), css = _b.css, map = _b.map;
stylesInfo.css = css;
stylesInfo.map = map;
_c.label = 4;
case 4:
outWatchList.push(filePath);
stylesInfo.watchList = outWatchList.map(function (watchFile) { return path.resolve(watchFile.replace(FILE_URL_PREFIX, "")); });
return [2 /*return*/];
}
});
});
};
function keepCssImports(_a) {
if (_a === void 0) { _a = {}; }
var _b = _a.outputExt, outputExt = _b === void 0 ? ".css" : _b, _c = _a.outputPath, outputPath = _c === void 0 ? "keep" : _c, _d = _a.skipCurrentFolderPart, skipCurrentFolderPart = _d === void 0 ? false : _d, _e = _a.includeRegexp, includeRegexp = _e === void 0 ? /\.(?:s[ca]|c)ss$/ : _e, sass = _a.sass, postProcessor = _a.postProcessor, sassOptions = _a.sassOptions, options = __rest(_a, ["outputExt", "outputPath", "skipCurrentFolderPart", "includeRegexp", "sass", "postProcessor", "sassOptions"]);
var stylesOutputOptions = {
outputPath: outputPath,
outputExt: outputExt,
outputDir: options.outputDir ? path.resolve(options.outputDir) : "./",
skipCurrentFolderPart: skipCurrentFolderPart,
};
var context = {
allStyleImports: [],
modulesWithCss: new Set(),
stylesToEmit: {},
};
var importUpdater = new ImportUpdater(context, stylesOutputOptions);
var loadPaths = options.includePaths || ["node_modules/"];
loadPaths.push(process.cwd());
loadPaths = loadPaths.filter(function (v, i, a) { return a.indexOf(v) === i; });
var compilerOptions = {
outputExt: outputExt,
sass: sass,
postProcessor: typeof postProcessor === "function"
? function (css, map) { return postProcessor(css, map, context.stylesToEmit); }
: undefined,
loadPaths: loadPaths,
sourceMap: !!options.sourceMap,
sassOptions: sassOptions,
};
return {
name: PLUGIN_NAME,
resolveId: function (source, importer, resolveOptions) {
return __awaiter(this, void 0, void 0, function () {
var _a, custom, _b, _c, _d, _e, _f, alreadyResolving, resolved, styleInfo;
var _g, _h;
var _this = this;
return __generator(this, function (_j) {
switch (_j.label) {
case 0:
if (!importer || !includeRegexp.test(source) || /\0/.test(source)) {
return [2 /*return*/, null];
}
_a = resolveOptions.custom, custom = _a === void 0 ? {} : _a;
_b = custom, _c = PLUGIN_NAME, _d = _b[_c], _e = _d === void 0 ? {} : _d, _f = _e.resolving, alreadyResolving = _f === void 0 ? false : _f;
if (alreadyResolving) {
return [2 /*return*/, null];
}
return [4 /*yield*/, this.resolve(source, importer, __assign(__assign({ skipSelf: true }, resolveOptions), { custom: __assign(__assign({}, custom), (_g = {}, _g[PLUGIN_NAME] = __assign(__assign({}, custom[PLUGIN_NAME]), { resolving: true }), _g)) }))];
case 1:
resolved = _j.sent();
if (!resolved || resolved.external) {
return [2 /*return*/, resolved];
}
context.modulesWithCss.add(importer);
styleInfo = ensureStylesInfo(context.stylesToEmit, importer, resolved.id);
return [4 /*yield*/, ensureCodeAndWatchList(resolved.id, styleInfo, this.meta.watchMode, compilerOptions)];
case 2:
_j.sent();
styleInfo.watchList.forEach(function (watchFile) {
_this.addWatchFile(watchFile);
});
return [2 /*return*/, {
id: importUpdater.getMagicId(resolved.id),
meta: (_h = {}, _h[PLUGIN_NAME] = { sourceId: resolved.id }, _h),
external: true,
}];
}
});
});
},
buildStart: function () {
var _this = this;
// Every rebuild will refresh watcher, so we need to reattach
if (this.meta.watchMode) {
var allWatched_1 = this.getWatchFiles();
Object.values(context.stylesToEmit).forEach(function (styleInfo) {
return styleInfo.watchList.forEach(function (watchFile) {
if (!allWatched_1.find(function (watched) { return path.normalize(watched) === path.normalize(watchFile); })) {
_this.addWatchFile(watchFile);
}
});
});
}
},
watchChange: function (id) {
return __awaiter(this, void 0, void 0, function () {
var resolvedId, filesToUpdate;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
resolvedId = path.resolve(id);
filesToUpdate = Object.entries(context.stylesToEmit).filter(function (_a) {
var styleInfo = _a[1];
return styleInfo.watchList.includes(resolvedId);
});
return [4 /*yield*/, Promise.all(filesToUpdate.map(function (_a) {
var fileName = _a[0], styleInfo = _a[1];
return ensureCodeAndWatchList(fileName, styleInfo, _this.meta.watchMode, compilerOptions);
}))];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
},
renderChunk: function (code, chunk, outputOptions) {
var bundleOutDir = path.resolve(outputOptions.dir || path.dirname(outputOptions.file));
// Always do it, otherwise some modules are missed
var moduleRoot = outputOptions.preserveModulesRoot || process.cwd();
return importUpdater.updateImports(code, chunk, bundleOutDir, moduleRoot);
},
generateBundle: function (_, __, isWrite) {
if (!isWrite) {
return;
}
assertDuplicates(context.stylesToEmit);
for (var file in context.stylesToEmit) {
var stylesInfo = context.stylesToEmit[file];
var fileName = stylesInfo.output;
var source = file.endsWith(".css")
? stylesInfo.css
: ensureSourceMap(stylesInfo, options.sourceMap || (sassOptions === null || sassOptions === void 0 ? void 0 : sassOptions.sourceMap), fileName, this.emitFile);
this.emitFile({
type: "asset",
fileName: fileName,
source: source,
});
}
},
};
}
export { keepCssImports as default };

View file

@ -1,489 +0,0 @@
import { readFile } from 'fs/promises';
import * as path from 'path';
import MagicString from 'magic-string';
/******************************************************************************
Copyright (c) Microsoft Corporation.
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
***************************************************************************** */
/* global Reflect, Promise, SuppressedError, Symbol */
var __assign = function () {
__assign = Object.assign || function __assign(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function __rest(s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
}
function __awaiter(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
}
function __generator(thisArg, body) {
var _ = { label: 0, sent: function () { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function () { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
}
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
var e = new Error(message);
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
};
var escapeRegex = function (val) { return val.replace(/[/\-\\^$*+?.()|[\]{}]/g, "\\$&"); };
var assertDuplicates = function (stylesToEmit) {
Object.values(stylesToEmit).forEach(function (v, i, all) {
if (all.some(function (av, ai) { return !!v.output && v.output === av.output && ai != i; })) {
throw new Error("Two or more assets have conflicting output path ".concat(v.output));
}
});
};
var assertLocation = function (outDir, assetPath) {
if (!path.normalize(assetPath).startsWith(path.normalize(outDir))) {
throw new Error("Output path ".concat(assetPath, " must be in output directory ").concat(outDir));
}
};
var ensureSourceMap = function (_a, includeSourceMap, fileName, onEmit) {
var css = _a.css, map = _a.map;
if (map) {
if (includeSourceMap === "inline") {
css += "\n/*# sourceMappingURL=data:application/json;base64,".concat((map instanceof Uint8Array ? Buffer.from(map) : Buffer.from(map, "utf8")).toString("base64"), "*/");
}
else if (includeSourceMap === true) {
css += "\n/*# sourceMappingURL=".concat(path.basename(fileName), ".map */");
}
if (includeSourceMap === true) {
onEmit({
type: "asset",
fileName: fileName + ".map",
source: map,
});
}
}
return css;
};
var formatProcessedToCSS = function (input, sourceMap) {
return typeof input === "string"
? { css: input, map: "" }
: typeof input === "object"
? {
css: input.css,
map: !sourceMap ? "" : typeof input.map === "object" ? JSON.stringify(input.map) : input.map,
}
: input;
};
var requireSass = function () {
try {
return import('sass');
}
catch (e) {
throw new Error("You have to install `sass` package! Try running\n\t" +
"npm install --save-dev sass\nor\nyarn add sass --dev\n" +
"or use `sass` option to pass processor");
}
};
function ensureCompiler(sass) {
return __awaiter(this, void 0, void 0, function () {
var sassProcessor, _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = sass;
if (_a) return [3 /*break*/, 2];
return [4 /*yield*/, requireSass()];
case 1:
_a = (_b.sent());
_b.label = 2;
case 2:
sassProcessor = _a;
if (!("compileAsync" in sassProcessor)) {
throw new Error("You have to install `sass` package! Or provide an object which implements `compileAsync` as `sass` option");
}
return [2 /*return*/, sassProcessor];
}
});
});
}
var isPostCssCompatible = function (result) {
return result && typeof result === "object" && "process" in result && typeof result.process === "function";
};
var compileSass = function (sassPath, outWatchList, _a) {
var outputExt = _a.outputExt, sass = _a.sass, postProcessor = _a.postProcessor, loadPaths = _a.loadPaths, sourceMap = _a.sourceMap, sassOptions = _a.sassOptions;
return __awaiter(void 0, void 0, void 0, function () {
var sassProcessor, watchListNeeded, compiled, css, mapObject, sources, map, result, _b, _c;
return __generator(this, function (_d) {
switch (_d.label) {
case 0:
if (!sassPath) {
return [2 /*return*/, { css: "", map: "" }];
}
return [4 /*yield*/, ensureCompiler(sass)];
case 1:
sassProcessor = _d.sent();
watchListNeeded = Array.isArray(outWatchList);
return [4 /*yield*/, sassProcessor.compileAsync(sassPath, __assign({ loadPaths: loadPaths, style: "expanded", sourceMap: !!sourceMap || watchListNeeded, sourceMapIncludeSources: !!sourceMap || watchListNeeded }, (sassOptions || [])))];
case 2:
compiled = _d.sent();
css = compiled.css.toString();
if (watchListNeeded && compiled.sourceMap && typeof compiled.sourceMap === "object") {
mapObject = "toJSON" in compiled.sourceMap && typeof compiled.sourceMap.toJSON === "function"
? compiled.sourceMap.toJSON()
: compiled.sourceMap;
sources = mapObject.sources || mapObject._sources;
outWatchList.push.apply(outWatchList, sources.filter(function (s) { return s && typeof s === "string"; }));
}
map = compiled.sourceMap
? typeof compiled.sourceMap === "object"
? JSON.stringify(compiled.sourceMap)
: compiled.sourceMap
: "";
if (!(typeof postProcessor === "function")) return [3 /*break*/, 7];
return [4 /*yield*/, postProcessor(css, map)];
case 3:
result = _d.sent();
if ((typeof result !== "string" && typeof result !== "object") || result === null) {
throw new Error("`postProcessor` must return string, object with `css` and `map` or PostCSS like object which implements `process` function");
}
_b = formatProcessedToCSS;
if (!isPostCssCompatible(result) // If PostCSS compatible result
) return [3 /*break*/, 5]; // If PostCSS compatible result
return [4 /*yield*/, Promise.resolve(result.process(css, {
from: sassPath,
to: path.parse(sassPath).name + outputExt,
map: map ? { prev: map, inline: false } : null,
}))];
case 4:
_c = _d.sent();
return [3 /*break*/, 6];
case 5:
_c = result;
_d.label = 6;
case 6: return [2 /*return*/, _b.apply(void 0, [_c, sourceMap])];
case 7: return [2 /*return*/, { css: css, map: sourceMap ? map : undefined }];
}
});
});
};
var PLUGIN_NAME = "keep-css-imports";
var FILE_URL_PREFIX = new URL("file://").toString();
var KEY_EXT_STRING = ".[keep-css-imports-plugin-ext]";
var createErrorMessage = function (message) { return "[".concat(PLUGIN_NAME, "] ").concat(message); };
var ImportUpdater = /** @class */ (function () {
function ImportUpdater(pluginContext, outputOptions) {
var _this = this;
this.addImportAndGetNewId = function (resolvedId) {
var moduleIndex = _this._pluginContext.allStyleImports.indexOf(resolvedId);
return !~moduleIndex ? _this._pluginContext.allStyleImports.push(resolvedId) - 1 : moduleIndex;
};
this._pluginContext = pluginContext;
this._outputOptions = outputOptions;
}
ImportUpdater.prototype.getMagicId = function (id) {
return "\0" + this.addImportAndGetNewId(id) + KEY_EXT_STRING;
};
ImportUpdater.prototype.updateImports = function (code, chunk, bundleOutDir, moduleRoot) {
var _this = this;
var magicString = new MagicString(code);
var matchRegex = new RegExp("\0([^\"']+)".concat(escapeRegex(KEY_EXT_STRING)), "g");
Array.from(code.matchAll(matchRegex))
.reverse()
.forEach(function (m) {
return _this.updateMatchedImport(m, magicString, {
chunk: chunk,
bundleOutDir: bundleOutDir,
moduleRoot: moduleRoot,
});
});
return {
code: magicString.toString(),
map: magicString.generateMap({ hires: true }),
};
};
ImportUpdater.prototype.updateMatchedImport = function (m, magicString, chunkDetails) {
var importId = m[0];
var assetId = this._pluginContext.allStyleImports[m[1]];
if (!assetId || typeof assetId !== "string" || !this._pluginContext.stylesToEmit[assetId]) {
return;
}
var updatedImport = this.saveAndGetUpdatedImportPath(assetId, chunkDetails);
var start = m.index;
var end = start + importId.length;
magicString.overwrite(start, end, updatedImport);
this.updateChunk(importId, updatedImport, chunkDetails.chunk);
};
ImportUpdater.prototype.updateChunk = function (importId, updatedImport, chunk) {
if (chunk.importedBindings[importId]) {
chunk.importedBindings[updatedImport] = chunk.importedBindings[importId];
if (updatedImport !== importId) {
delete chunk.importedBindings[importId];
}
}
var importIndex = chunk.imports.indexOf(importId);
if (~importIndex) {
chunk.imports[importIndex] = updatedImport;
}
};
ImportUpdater.prototype.saveAndGetUpdatedImportPath = function (assetId, _a) {
var bundleOutDir = _a.bundleOutDir, moduleRoot = _a.moduleRoot, chunk = _a.chunk;
var assetOutput = this.resolveOutputPath(bundleOutDir, assetId, moduleRoot);
var updatedImport = path
.relative(path.dirname(path.resolve(bundleOutDir, chunk.fileName)), assetOutput)
.replace(/\\/g, "/");
this._pluginContext.stylesToEmit[assetId].output = path.relative(path.resolve(bundleOutDir), assetOutput);
if (this.shouldAddPrefixCurrentDir(updatedImport) &&
!updatedImport.startsWith("./") &&
!updatedImport.startsWith("../") &&
!updatedImport.match(/^[a-zA-Z]:/)) {
updatedImport = "./" + updatedImport;
}
return updatedImport;
};
ImportUpdater.prototype.shouldAddPrefixCurrentDir = function (updatedImport) {
var skip = this._outputOptions.skipCurrentFolderPart;
return !skip || (skip instanceof RegExp && !skip.test(updatedImport));
};
ImportUpdater.prototype.resolveOutputPath = function (bundleOutDir, assetId, moduleRoot) {
var _a = this._outputOptions, outputPath = _a.outputPath, outputDir = _a.outputDir, outputExt = _a.outputExt;
var newPath = undefined;
if (typeof outputPath === "function") {
newPath = outputPath(assetId);
assertLocation(bundleOutDir, newPath);
}
else if (typeof outputPath === "string") {
newPath = path.resolve(bundleOutDir, outputDir, outputPath !== "keep" ? outputPath : path.relative(moduleRoot, assetId));
assertLocation(bundleOutDir, newPath);
}
else {
throw new Error(createErrorMessage("Invalid outputPath option value!"));
}
return newPath.replace(/\.s[ca]ss$/, outputExt);
};
return ImportUpdater;
}());
var ensureStylesInfo = function (stylesMap, importer, resolvedId) {
stylesMap[resolvedId] = stylesMap[resolvedId] || { importers: [], watchList: [] };
stylesMap[resolvedId].importers.push(importer);
return stylesMap[resolvedId];
};
var ensureCodeAndWatchList = function (filePath, stylesInfo, isWatch, compilerOptions) {
return __awaiter(void 0, void 0, void 0, function () {
var outWatchList, _a, _b, css, map;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
outWatchList = [];
if (!filePath.endsWith(".css")) return [3 /*break*/, 2];
_a = stylesInfo;
return [4 /*yield*/, readFile(filePath, "utf8")];
case 1:
_a.css = _c.sent();
return [3 /*break*/, 4];
case 2: return [4 /*yield*/, compileSass(filePath, isWatch ? outWatchList : undefined, compilerOptions)];
case 3:
_b = _c.sent(), css = _b.css, map = _b.map;
stylesInfo.css = css;
stylesInfo.map = map;
_c.label = 4;
case 4:
outWatchList.push(filePath);
stylesInfo.watchList = outWatchList.map(function (watchFile) { return path.resolve(watchFile.replace(FILE_URL_PREFIX, "")); });
return [2 /*return*/];
}
});
});
};
function keepCssImports(_a) {
if (_a === void 0) { _a = {}; }
var _b = _a.outputExt, outputExt = _b === void 0 ? ".css" : _b, _c = _a.outputPath, outputPath = _c === void 0 ? "keep" : _c, _d = _a.skipCurrentFolderPart, skipCurrentFolderPart = _d === void 0 ? false : _d, _e = _a.includeRegexp, includeRegexp = _e === void 0 ? /\.(?:s[ca]|c)ss$/ : _e, sass = _a.sass, postProcessor = _a.postProcessor, sassOptions = _a.sassOptions, options = __rest(_a, ["outputExt", "outputPath", "skipCurrentFolderPart", "includeRegexp", "sass", "postProcessor", "sassOptions"]);
var stylesOutputOptions = {
outputPath: outputPath,
outputExt: outputExt,
outputDir: options.outputDir ? path.resolve(options.outputDir) : "./",
skipCurrentFolderPart: skipCurrentFolderPart,
};
var context = {
allStyleImports: [],
modulesWithCss: new Set(),
stylesToEmit: {},
};
var importUpdater = new ImportUpdater(context, stylesOutputOptions);
var loadPaths = options.includePaths || ["node_modules/"];
loadPaths.push(process.cwd());
loadPaths = loadPaths.filter(function (v, i, a) { return a.indexOf(v) === i; });
var compilerOptions = {
outputExt: outputExt,
sass: sass,
postProcessor: typeof postProcessor === "function"
? function (css, map) { return postProcessor(css, map, context.stylesToEmit); }
: undefined,
loadPaths: loadPaths,
sourceMap: !!options.sourceMap,
sassOptions: sassOptions,
};
return {
name: PLUGIN_NAME,
resolveId: function (source, importer, resolveOptions) {
return __awaiter(this, void 0, void 0, function () {
var _a, custom, _b, _c, _d, _e, _f, alreadyResolving, resolved, styleInfo;
var _g, _h;
var _this = this;
return __generator(this, function (_j) {
switch (_j.label) {
case 0:
if (!importer || !includeRegexp.test(source) || /\0/.test(source)) {
return [2 /*return*/, null];
}
_a = resolveOptions.custom, custom = _a === void 0 ? {} : _a;
_b = custom, _c = PLUGIN_NAME, _d = _b[_c], _e = _d === void 0 ? {} : _d, _f = _e.resolving, alreadyResolving = _f === void 0 ? false : _f;
if (alreadyResolving) {
return [2 /*return*/, null];
}
return [4 /*yield*/, this.resolve(source, importer, __assign(__assign({ skipSelf: true }, resolveOptions), { custom: __assign(__assign({}, custom), (_g = {}, _g[PLUGIN_NAME] = __assign(__assign({}, custom[PLUGIN_NAME]), { resolving: true }), _g)) }))];
case 1:
resolved = _j.sent();
if (!resolved || resolved.external) {
return [2 /*return*/, resolved];
}
context.modulesWithCss.add(importer);
styleInfo = ensureStylesInfo(context.stylesToEmit, importer, resolved.id);
return [4 /*yield*/, ensureCodeAndWatchList(resolved.id, styleInfo, this.meta.watchMode, compilerOptions)];
case 2:
_j.sent();
styleInfo.watchList.forEach(function (watchFile) {
_this.addWatchFile(watchFile);
});
return [2 /*return*/, {
id: importUpdater.getMagicId(resolved.id),
meta: (_h = {}, _h[PLUGIN_NAME] = { sourceId: resolved.id }, _h),
external: true,
}];
}
});
});
},
buildStart: function () {
var _this = this;
// Every rebuild will refresh watcher, so we need to reattach
if (this.meta.watchMode) {
var allWatched_1 = this.getWatchFiles();
Object.values(context.stylesToEmit).forEach(function (styleInfo) {
return styleInfo.watchList.forEach(function (watchFile) {
if (!allWatched_1.find(function (watched) { return path.normalize(watched) === path.normalize(watchFile); })) {
_this.addWatchFile(watchFile);
}
});
});
}
},
watchChange: function (id) {
return __awaiter(this, void 0, void 0, function () {
var resolvedId, filesToUpdate;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
resolvedId = path.resolve(id);
filesToUpdate = Object.entries(context.stylesToEmit).filter(function (_a) {
var styleInfo = _a[1];
return styleInfo.watchList.includes(resolvedId);
});
return [4 /*yield*/, Promise.all(filesToUpdate.map(function (_a) {
var fileName = _a[0], styleInfo = _a[1];
return ensureCodeAndWatchList(fileName, styleInfo, _this.meta.watchMode, compilerOptions);
}))];
case 1:
_a.sent();
return [2 /*return*/];
}
});
});
},
renderChunk: function (code, chunk, outputOptions) {
var bundleOutDir = path.resolve(outputOptions.dir || path.dirname(outputOptions.file));
if (code && chunk.modules && Object.keys(chunk.modules).some(function (m) { return context.modulesWithCss.has(m); })) {
var moduleRoot = outputOptions.preserveModulesRoot || process.cwd();
return importUpdater.updateImports(code, chunk, bundleOutDir, moduleRoot);
}
return null;
},
generateBundle: function (_, __, isWrite) {
if (!isWrite) {
return;
}
assertDuplicates(context.stylesToEmit);
for (var file in context.stylesToEmit) {
var stylesInfo = context.stylesToEmit[file];
var fileName = stylesInfo.output;
var source = file.endsWith(".css")
? stylesInfo.css
: ensureSourceMap(stylesInfo, options.sourceMap || (sassOptions === null || sassOptions === void 0 ? void 0 : sassOptions.sourceMap), fileName, this.emitFile);
this.emitFile({
type: "asset",
fileName: fileName,
source: source,
});
}
},
};
}
export { keepCssImports as default };

View file

@ -1,110 +0,0 @@
import { type CompilationOptions, PostCssCompatible } from "./compileSass";
import type { Options as SassOptions } from "sass";
export type KeepCssImportsOptions = OutputOptions & InputOptions & Extensions;
export type StyleRefInfo = {
/**
* Collection of files with reference to the current file
*/
importers: string[];
/**
* List of files which are used to render the current file
*/
watchList: string[];
/**
* Emit path
*/
output?: string;
/**
* Processed CSS content to emit
*/
css?: string | Uint8Array;
/**
* Processed CSS content map to emit
*/
map?: string | Uint8Array;
};
export type StylesMap = Record<string, StyleRefInfo>;
interface Extensions {
/**
* Customised SASS (SCSS) processor. If not provided plugin will try to
* import locally installed `sass` if required
*/
sass?: CompilationOptions["sass"];
/**
* An optional object that allows to provide additional options for the
* SASS compiler.
*/
sassOptions?: SassOptions<"async">;
/**
* Specifies the list of include paths for SASS to search when resolving imports.
*
* Default: `["node_modules/"]`
*/
includePaths?: string[];
/**
* An optional function that allows you to perform additional processing on the
* generated CSS, such as applying PostCSS plugins.
*/
postProcessor?: (css: string, map: string, stylesMap: StylesMap) => Promise<PostCssCompatible | string | {
css: string;
map?: string;
}>;
}
type OutputPath = string | "keep" | ((assetId: string) => string);
export interface OutputOptions {
/**
* Specifies the file extension for the output CSS files.
*
* Default: `".css"`
*/
outputExt?: string;
/**
* Specifies the output directory for the generated CSS files.
* Relative to Rollup output folder.
*
* Default: `"./"`
*/
outputDir?: string;
/**
* Specifies the output path relative to `outputDir` for the generated CSS
* files.
* The default value, "keep", preserves the original file paths. It is also
* possible to provide a custom function to generate output paths based on
* the input file.
*
* Default: `"keep"`
*/
outputPath?: OutputPath;
/**
* Specifies whether to generate source maps for the compiled CSS.
* Use `"inline"` to inline source maps into CSS files.
*
* Default: `false`
*/
sourceMap?: boolean | "inline";
/**
* By default CSS paths will be prefixed with current folder mark `./`.
* To avoid this for CSS files use `true` or specify RegExp filter.
*
* If RegExp filter matches `./` won't be added to the path.
* This option may be helpful if you have some issues with external
* modules imports from `node_modules`
*
* Default: `false`
*/
skipCurrentFolderPart?: boolean | RegExp;
}
interface InputOptions {
/**
* Regular expression to test if an import should be processed by this plugin
*
* Default: `/\.(?:s[ca]|c)ss$/`
*/
includeRegexp?: RegExp;
}
export interface KeepCssImportsPluginContext {
allStyleImports: string[];
modulesWithCss: Set<string>;
stylesToEmit: StylesMap;
}
export {};

View file

@ -1,74 +0,0 @@
{
"name": "rollup-plugin-keep-css-imports",
"version": "1.0.0",
"description": "Rollup plugin that allows to maintain the original structure of style imports without altering them during the bundling process",
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.ts",
"default": "./dist/index.cjs"
}
}
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"keywords": [
"rollup",
"rollup-plugin",
"css-modules",
"sass",
"scss",
"keep",
"preserve",
"imports"
],
"homepage": "https://github.com/SLTKA/rollup-plugin-keep-css-imports",
"author": "Alexandr Yeskov",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/SLTKA/rollup-plugin-keep-css-imports"
},
"bugs": {
"url": "https://github.com/SLTKA/rollup-plugin-keep-css-imports/issues"
},
"scripts": {
"prebuild": "rimraf -rf dist/",
"build": "rollup -c",
"test": "mocha",
"pretest": "yarn build"
},
"devDependencies": {
"@rollup/plugin-typescript": "^11.1.6",
"@types/mocha": "^10.0.6",
"@types/node": "^20.11.24",
"@typescript-eslint/eslint-plugin": "^7.1.1",
"@typescript-eslint/parser": "^7.1.1",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-mocha": "^10.2.0",
"eslint-plugin-prettier": "^5.1.3",
"mocha": "^10.2.0",
"prettier": "^3.2.4",
"rimraf": "^5.0.5",
"rollup": "^4.9.5",
"rollup-plugin-dts": "^6.1.0",
"sass": "^1.70.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"typescript-eslint": "^7.1.1"
},
"dependencies": {
"magic-string": "^0.30.5"
}
}

View file

@ -1,47 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import nodeResolve from '@rollup/plugin-node-resolve';
import { join } from 'path';
import { defineConfig } from 'rollup';
import { dts } from "rollup-plugin-dts";
import { dtsDeprecationWarning, mapModuleId } from '../shared.mjs';
const root = join(import.meta.dirname, '../../');
export default defineConfig({
input: {
entry: join(root, './src/editor/editor.main.ts'),
editorApi: join(root, './src/editor/editor.api.ts'),
},
output: {
dir: join(root, './out/monaco-editor/esm'),
format: 'es',
preserveModules: false,
entryFileNames: function (chunkInfo) {
const moduleId = chunkInfo.facadeModuleId;
if (moduleId) {
const m = mapModuleId(moduleId, '.d.ts');
if (m !== undefined) {
return m;
}
}
return '[name].d.ts';
},
},
external: [/.*\.css/],
plugins: [
nodeResolve(),
dts({
compilerOptions: {
stripInternal: true,
},
includeExternal: ['monaco-editor-core', '@vscode/monaco-lsp-client']
}),
dtsDeprecationWarning(f => f.endsWith('editor.api.d.ts')),
],
});

View file

@ -1,63 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* @type {() => import('rollup').Plugin}
*/
export function urlToEsmPlugin() {
return {
name: 'import-meta-url',
async transform(code, id) {
if (this.environment?.mode === 'dev') {
return;
}
// Look for `new URL(..., import.meta.url)` patterns.
const regex = /new\s+URL\s*\(\s*(['"`])(.*?)\1\s*,\s*import\.meta\.url\s*\)?/g;
let match;
let modified = false;
let result = code;
let offset = 0;
while ((match = regex.exec(code)) !== null) {
let path = match[2];
if (!path.startsWith('.') && !path.startsWith('/')) {
path = `./${path}`;
}
const resolved = await this.resolve(path, id);
if (!resolved) {
continue;
}
// Add the file as an entry point
const refId = this.emitFile({
type: 'chunk',
id: resolved.id,
});
const start = match.index;
const end = start + match[0].length;
const replacement = `import.meta.ROLLUP_FILE_URL_OBJ_${refId}`;
result = result.slice(0, start + offset) + replacement + result.slice(end + offset);
offset += replacement.length - (end - start);
modified = true;
}
if (!modified) {
return null;
}
return {
code: result,
map: null
};
}
};
}

View file

@ -1,86 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import { join, relative } from 'path';
import { defineConfig } from 'rollup';
import esbuild from 'rollup-plugin-esbuild';
//import { urlToEsmPlugin } from '../rollup-url-to-module-plugin/index.mjs';
import del from 'rollup-plugin-delete';
import keepCssImports from './rollup-plugin-keep-css-imports/dist/index.mjs';
import nodeResolve from '@rollup/plugin-node-resolve';
import { urlToEsmPlugin } from './rollup-url-to-module-plugin/index.mjs';
import { getNlsEntryPoints, mapModuleId } from '../shared.mjs';
import { readFileSync } from 'fs';
const root = join(import.meta.dirname, '../../');
const outDir = join(root, './out/monaco-editor/esm');
export default defineConfig({
input: {
entry: join(root, './src/editor/editor.main.ts'),
editorAll: join(root, './src/editor/editor.all.ts'),
edcoreMain: join(root, './src/editor/edcore.main.ts'),
editorApi: join(root, './src/editor/editor.api.ts'),
editorWorker: join(root, './src/editor/editor.worker.ts'),
...getNlsEntryPoints(),
},
output: {
dir: outDir,
format: 'es',
entryFileNames: function (chunkInfo) {
const moduleId = chunkInfo.facadeModuleId;
if (moduleId) {
const r = mapModuleId(moduleId, '.js');
if (r !== undefined) {
return r;
}
}
return '[name].js';
},
preserveModules: true,
},
plugins: [
del({ targets: outDir, force: true }),
{
name: 'copy-codicon-font',
generateBundle() {
this.emitFile({
type: 'asset',
fileName: 'vs/base/browser/ui/codicons/codicon/codicon.ttf',
source: readFileSync(join(root, 'node_modules/monaco-editor-core/esm/vs/base/browser/ui/codicons/codicon/codicon.ttf'))
});
}
},
urlToEsmPlugin(),
esbuild(),
keepCssImports({
/**
* @param {string} assetId
*/
outputPath: (assetId) => {
const r = mapModuleId(assetId, '.css');
if (r !== undefined) {
return join(outDir, r);
}
const relativePath = join(outDir, relative(root, assetId));
return relativePath.replace(/(\.s[ca]ss)$/, ".min$1")
},
}),
nodeResolve({
dedupe: ['monaco-editor-core', '@vscode/monaco-lsp-client'],
browser: true,
}),
],
});

View file

@ -0,0 +1,46 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export interface Options {
locale?: string;
cacheLanguageResolution?: boolean;
}
export interface LocalizeInfo {
key: string;
comment: string[];
}
export interface LocalizeFunc {
(info: LocalizeInfo, message: string, ...args: any[]): string;
(key: string, message: string, ...args: any[]): string;
}
export interface LoadFunc {
(file?: string): LocalizeFunc;
}
function format(message: string, args: any[]): string {
let result: string;
if (args.length === 0) {
result = message;
} else {
result = message.replace(/\{(\d+)\}/g, (match, rest) => {
let index = rest[0];
return typeof args[index] !== 'undefined' ? args[index] : match;
});
}
return result;
}
function localize(key: string | LocalizeInfo, message: string, ...args: any[]): string {
return format(message, args);
}
export function loadMessageBundle(file?: string): LocalizeFunc {
return localize;
}
export function config(opt?: Options | string): LoadFunc {
return loadMessageBundle;
}

View file

@ -23,12 +23,25 @@ export function ensureDir(dirname: string) {
if (!existingDirCache.has(dir)) {
try {
fs.mkdirSync(dir);
} catch (err) { }
} catch (err) {}
existingDirCache.add(dir);
}
});
}
/**
* Copy a file.
*/
export function copyFile(_source: string, _destination: string) {
const source = path.join(REPO_ROOT, _source);
const destination = path.join(REPO_ROOT, _destination);
ensureDir(path.dirname(destination));
fs.writeFileSync(destination, fs.readFileSync(source));
console.log(`Copied ${_source} to ${_destination}`);
}
/**
* Remove a directory and all its contents.
*/

View file

@ -103,7 +103,7 @@ function getAdvancedLanguages(): Promise<
}
}
export function generateEsmMetadataJsAndDTs() {
export function generateMetadata() {
return Promise.all([getBasicLanguages(), getAdvancedLanguages()]).then(
([basicLanguages, advancedLanguages]) => {
basicLanguages.sort((a, b) => strcmp(a.entry, b.entry));

View file

@ -1,96 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
// @ts-check
import { dirname, join } from 'path';
import { fileURLToPath } from 'url';
import { readdirSync } from 'fs';
/**
* @param {string} filePath
* @param {string} newExt
*/
export function changeExt(filePath, newExt) {
const idx = filePath.lastIndexOf('.');
if (idx === -1) {
return filePath + newExt;
} else {
return filePath.substring(0, idx) + newExt;
}
}
export function getNlsEntryPoints() {
const nlsDir = dirname(fileURLToPath(import.meta.resolve('monaco-editor-core/esm/nls.messages.en.js')));
const nlsFiles = readdirSync(nlsDir)
.filter(file => file.startsWith('nls.messages.') && file.endsWith('.js'))
.reduce((acc, file) => {
// @ts-ignore
acc[file] = join(nlsDir, file);
return acc;
}, {});
return nlsFiles;
}
const root = join(import.meta.dirname, '../');
const mappedPaths = {
[join(root, 'node_modules/monaco-editor-core/esm/')]: '.',
[join(root, 'node_modules/')]: 'external/',
[join(root, 'monaco-lsp-client/')]: 'external/monaco-lsp-client/',
[join(root, 'src/')]: 'vs/',
};
/**
* @param {string} moduleId
* @param {string} newExt (with leading .)
* @returns {string | undefined}
*/
export function mapModuleId(moduleId, newExt) {
for (const [key, val] of Object.entries(mappedPaths)) {
if (moduleId.startsWith(key)) {
const relativePath = moduleId.substring(key.length);
return changeExt(join(val, relativePath), newExt);
}
}
return undefined;
}
/**
* @param {(moduleId: string) => boolean} [filter]
* @return {import('rollup').Plugin}
*/
export function dtsDeprecationWarning(filter) {
return {
name: 'add-dts-deprecation-warning',
generateBundle(options, bundle) {
for (const fileName in bundle) {
if (filter && !filter(fileName)) {
continue;
}
const file = bundle[fileName];
if (file.type === 'chunk' && fileName.endsWith('.d.ts')) {
let content = file.code.toString();
content = content + `
declare namespace languages {
/** @deprecated Use the new top level "css" namespace instead. */
export const css: { deprecated: true };
/** @deprecated Use the new top level "html" namespace instead. */
export const html: { deprecated: true };
/** @deprecated Use the new top level "json" namespace instead. */
export const json: { deprecated: true };
/** @deprecated Use the new top level "typescript" namespace instead. */
export const typescript: { deprecated: true };
}
`;
file.code = content;
}
}
}
};
}

View file

@ -5,11 +5,190 @@
import * as fs from 'fs';
import * as path from 'path';
import * as cp from 'child_process';
import * as esbuild from 'esbuild';
import alias from 'esbuild-plugin-alias';
import * as glob from 'glob';
import { ensureDir } from './fs';
export const REPO_ROOT = path.join(__dirname, '../');
/**
* Launch the typescript compiler synchronously over a project.
*/
export function runTsc(_projectPath: string) {
const projectPath = path.join(REPO_ROOT, _projectPath);
console.log(`Launching compiler at ${_projectPath}...`);
const res = cp.spawnSync(
process.execPath,
[path.join(__dirname, '../node_modules/typescript/lib/tsc.js'), '-p', projectPath],
{ stdio: 'inherit' }
);
console.log(`Compiled ${_projectPath}`);
if (res.status !== 0) {
process.exit(res.status);
}
}
/**
* Launch prettier on a specific file.
*/
export function prettier(_filePath: string) {
const filePath = path.join(REPO_ROOT, _filePath);
cp.spawnSync(
process.execPath,
[path.join(__dirname, '../node_modules/prettier/bin-prettier.js'), '--write', filePath],
{ stdio: 'inherit' }
);
console.log(`Ran prettier over ${_filePath}`);
}
/**
* Transform an external .d.ts file to an internal .d.ts file
*/
export function massageAndCopyDts(source: string, destination: string, namespace: string) {
const absoluteSource = path.join(REPO_ROOT, source);
const absoluteDestination = path.join(REPO_ROOT, destination);
const lines = fs
.readFileSync(absoluteSource)
.toString()
.split(/\r\n|\r|\n/);
let result = [
`/*---------------------------------------------------------------------------------------------`,
` * Copyright (c) Microsoft Corporation. All rights reserved.`,
` * Licensed under the MIT License. See License.txt in the project root for license information.`,
` *--------------------------------------------------------------------------------------------*/`,
``,
`declare namespace ${namespace} {`
];
for (let line of lines) {
if (/^import/.test(line)) {
continue;
}
if (line === 'export {};') {
continue;
}
line = line.replace(/ /g, '\t');
line = line.replace(/declare /g, '');
if (line.length > 0) {
line = `\t${line}`;
result.push(line);
}
}
result.push(`}`);
result.push(``);
ensureDir(path.dirname(absoluteDestination));
fs.writeFileSync(absoluteDestination, result.join('\n'));
prettier(destination);
}
export function build(options: import('esbuild').BuildOptions) {
esbuild.build(options).then((result) => {
if (result.errors.length > 0) {
console.error(result.errors);
}
if (result.warnings.length > 0) {
console.error(result.warnings);
}
});
}
export function buildESM(options: { base: string; entryPoints: string[]; external: string[] }) {
build({
entryPoints: options.entryPoints,
bundle: true,
target: 'esnext',
format: 'esm',
drop: ['debugger'],
define: {
AMD: 'false'
},
banner: {
js: bundledFileHeader
},
external: options.external,
outbase: `src/${options.base}`,
outdir: `out/languages/bundled/esm/vs/${options.base}/`,
plugins: [
alias({
'vscode-nls': path.join(__dirname, 'fillers/vscode-nls.ts')
})
]
});
}
function getGitVersion() {
const git = path.join(REPO_ROOT, '.git');
const headPath = path.join(git, 'HEAD');
let head;
try {
head = fs.readFileSync(headPath, 'utf8').trim();
} catch (e) {
return void 0;
}
if (/^[0-9a-f]{40}$/i.test(head)) {
return head;
}
const refMatch = /^ref: (.*)$/.exec(head);
if (!refMatch) {
return void 0;
}
const ref = refMatch[1];
const refPath = path.join(git, ref);
try {
return fs.readFileSync(refPath, 'utf8').trim();
} catch (e) {
// noop
}
const packedRefsPath = path.join(git, 'packed-refs');
let refsRaw;
try {
refsRaw = fs.readFileSync(packedRefsPath, 'utf8').trim();
} catch (e) {
return void 0;
}
const refsRegex = /^([0-9a-f]{40})\s+(.+)$/gm;
let refsMatch;
const refs = {};
while ((refsMatch = refsRegex.exec(refsRaw))) {
refs[refsMatch[2]] = refsMatch[1];
}
return refs[ref];
}
export const bundledFileHeader = (() => {
const sha1 = getGitVersion();
const semver = require('../package.json').version;
const headerVersion = semver + '(' + sha1 + ')';
const BUNDLED_FILE_HEADER = [
'/*!-----------------------------------------------------------------------------',
' * Copyright (c) Microsoft Corporation. All rights reserved.',
' * Version: ' + headerVersion,
' * Released under the MIT license',
' * https://github.com/microsoft/monaco-editor/blob/main/LICENSE.txt',
' *-----------------------------------------------------------------------------*/',
''
].join('\n');
return BUNDLED_FILE_HEADER;
})();
export interface IFile {
path: string;

View file

@ -1,5 +0,0 @@
# Monaco LSP Client
Provides a Language Server Protocol (LSP) client for the Monaco Editor.
This package is in alpha stage and might contain many bugs.

View file

@ -1,687 +0,0 @@
import * as fs from 'fs';
import * as path from 'path';
/**
* Utility class for writing formatted code with proper indentation
*/
class LineWriter {
private lines: string[] = [];
private indentLevel: number = 0;
private indentStr: string = ' '; // 4 spaces
/**
* Write a line with current indentation
*/
writeLine(line: string = ''): void {
if (line.trim() === '') {
this.lines.push('');
} else {
this.lines.push(this.indentStr.repeat(this.indentLevel) + line);
}
}
/**
* Write text without adding a new line
*/
write(text: string): void {
if (this.lines.length === 0) {
this.lines.push('');
}
const lastIndex = this.lines.length - 1;
if (this.lines[lastIndex] === '') {
this.lines[lastIndex] = this.indentStr.repeat(this.indentLevel) + text;
} else {
this.lines[lastIndex] += text;
}
}
/**
* Increase indentation level
*/
indent(): void {
this.indentLevel++;
}
/**
* Decrease indentation level
*/
outdent(): void {
if (this.indentLevel > 0) {
this.indentLevel--;
}
}
/**
* Get the generated content as a string
*/
toString(): string {
return this.lines.join('\n');
}
/**
* Clear all content and reset indentation
*/
clear(): void {
this.lines = [];
this.indentLevel = 0;
}
}
/**
* Interface definitions based on the metaModel schema
*/
interface MetaModel {
metaData: MetaData;
requests: Request[];
notifications: Notification[];
structures: Structure[];
enumerations: Enumeration[];
typeAliases: TypeAlias[];
}
interface MetaData {
version: string;
}
interface Request {
method: string;
result: Type;
messageDirection: MessageDirection;
params?: Type | Type[];
partialResult?: Type;
errorData?: Type;
registrationOptions?: Type;
registrationMethod?: string;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface Notification {
method: string;
messageDirection: MessageDirection;
params?: Type | Type[];
registrationOptions?: Type;
registrationMethod?: string;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface Structure {
name: string;
properties: Property[];
extends?: Type[];
mixins?: Type[];
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface Property {
name: string;
type: Type;
optional?: boolean;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface Enumeration {
name: string;
type: EnumerationType;
values: EnumerationEntry[];
supportsCustomValues?: boolean;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface EnumerationEntry {
name: string;
value: string | number;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface EnumerationType {
kind: 'base';
name: 'string' | 'integer' | 'uinteger';
}
interface TypeAlias {
name: string;
type: Type;
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
type MessageDirection = 'clientToServer' | 'serverToClient' | 'both';
type Type =
| BaseType
| ReferenceType
| ArrayType
| MapType
| AndType
| OrType
| TupleType
| StructureLiteralType
| StringLiteralType
| IntegerLiteralType
| BooleanLiteralType;
interface BaseType {
kind: 'base';
name:
| 'URI'
| 'DocumentUri'
| 'integer'
| 'uinteger'
| 'decimal'
| 'RegExp'
| 'string'
| 'boolean'
| 'null';
}
interface ReferenceType {
kind: 'reference';
name: string;
}
interface ArrayType {
kind: 'array';
element: Type;
}
interface MapType {
kind: 'map';
key: Type;
value: Type;
}
interface AndType {
kind: 'and';
items: Type[];
}
interface OrType {
kind: 'or';
items: Type[];
}
interface TupleType {
kind: 'tuple';
items: Type[];
}
interface StructureLiteralType {
kind: 'literal';
value: StructureLiteral;
}
interface StructureLiteral {
properties: Property[];
documentation?: string;
since?: string;
proposed?: boolean;
deprecated?: string;
}
interface StringLiteralType {
kind: 'stringLiteral';
value: string;
}
interface IntegerLiteralType {
kind: 'integerLiteral';
value: number;
}
interface BooleanLiteralType {
kind: 'booleanLiteral';
value: boolean;
}
/**
* TypeScript types generator for LSP client
*/
class LSPTypesGenerator {
private writer = new LineWriter();
/**
* Load and parse the metaModel.json file
*/
private loadMetaModel(): MetaModel {
const metaModelPath = path.join(__dirname, '..', 'metaModel.json');
const content = fs.readFileSync(metaModelPath, 'utf-8');
return JSON.parse(content) as MetaModel;
}
/**
* Convert Type to TypeScript type string
*/
private typeToTypeScript(type: Type): string {
switch (type.kind) {
case 'base':
switch (type.name) {
case 'string':
case 'DocumentUri':
case 'URI':
return 'string';
case 'integer':
case 'uinteger':
case 'decimal':
return 'number';
case 'boolean':
return 'boolean';
case 'null':
return 'null';
case 'RegExp':
return 'RegExp';
default:
return 'any';
}
case 'reference':
return type.name;
case 'array':
return `(${this.typeToTypeScript(type.element)})[]`;
case 'map':
return `{ [key: ${this.typeToTypeScript(type.key)}]: ${this.typeToTypeScript(
type.value
)} }`;
case 'and':
return type.items.map((item) => this.typeToTypeScript(item)).join(' & ');
case 'or':
return type.items.map((item) => this.typeToTypeScript(item)).join(' | ');
case 'tuple':
return `[${type.items.map((item) => this.typeToTypeScript(item)).join(', ')}]`;
case 'literal':
return this.structureLiteralToTypeScript(type.value);
case 'stringLiteral':
return `'${type.value}'`;
case 'integerLiteral':
return type.value.toString();
case 'booleanLiteral':
return type.value.toString();
default:
return 'any';
}
}
/**
* Convert structure literal to TypeScript interface
*/
private structureLiteralToTypeScript(literal: StructureLiteral): string {
const properties = literal.properties.map((prop) => {
const optional = prop.optional ? '?' : '';
return `${prop.name}${optional}: ${this.typeToTypeScript(prop.type)}`;
});
return `{\n ${properties.join(';\n ')}\n}`;
}
/**
* Generate TypeScript interface for a structure
*/
private generateStructure(structure: Structure): void {
if (structure.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${structure.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
// Build extends clause combining extends and mixins
const allParents: string[] = [];
if (structure.extends && structure.extends.length > 0) {
allParents.push(...structure.extends.map((type) => this.typeToTypeScript(type)));
}
if (structure.mixins && structure.mixins.length > 0) {
allParents.push(...structure.mixins.map((type) => this.typeToTypeScript(type)));
}
const extendsClause = allParents.length > 0 ? ` extends ${allParents.join(', ')}` : '';
this.writer.writeLine(`export interface ${structure.name}${extendsClause} {`);
this.writer.indent();
// Add properties
for (const property of structure.properties) {
if (property.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${property.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
const optional = property.optional ? '?' : '';
this.writer.writeLine(
`${property.name}${optional}: ${this.typeToTypeScript(property.type)};`
);
}
this.writer.outdent();
this.writer.writeLine('}');
this.writer.writeLine('');
}
/**
* Generate TypeScript enum for an enumeration
*/
private generateEnumeration(enumeration: Enumeration): void {
if (enumeration.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${enumeration.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
this.writer.writeLine(`export enum ${enumeration.name} {`);
this.writer.indent();
for (let i = 0; i < enumeration.values.length; i++) {
const entry = enumeration.values[i];
if (entry.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${entry.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
const isLast = i === enumeration.values.length - 1;
const comma = isLast ? '' : ',';
if (typeof entry.value === 'string') {
this.writer.writeLine(`${entry.name} = '${entry.value}'${comma}`);
} else {
this.writer.writeLine(`${entry.name} = ${entry.value}${comma}`);
}
}
this.writer.outdent();
this.writer.writeLine('}');
this.writer.writeLine('');
}
/**
* Generate TypeScript type alias
*/
private generateTypeAlias(typeAlias: TypeAlias): void {
if (typeAlias.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${typeAlias.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
this.writer.writeLine(
`export type ${typeAlias.name} = ${this.typeToTypeScript(typeAlias.type)};`
);
this.writer.writeLine('');
}
/**
* Generate the Capability class
*/
private generateCapabilityClass(): void {
this.writer.writeLine('/**');
this.writer.writeLine(
' * Represents a capability with its associated method and registration options type'
);
this.writer.writeLine(' */');
this.writer.writeLine('export class Capability<T> {');
this.writer.indent();
this.writer.writeLine('constructor(public readonly method: string) {}');
this.writer.outdent();
this.writer.writeLine('}');
this.writer.writeLine('');
}
/**
* Generate the capabilities map
*/
private generateCapabilitiesMap(metaModel: MetaModel): void {
this.writer.writeLine('/**');
this.writer.writeLine(' * Map of all LSP capabilities with their registration options');
this.writer.writeLine(' */');
this.writer.writeLine('export const capabilities = {');
this.writer.indent();
// Collect all requests and notifications with registration options
const itemsWithRegistration: Array<{ method: string; registrationOptions?: Type }> = [];
for (const request of metaModel.requests) {
if (request.registrationOptions) {
itemsWithRegistration.push({
method: request.method,
registrationOptions: request.registrationOptions
});
}
}
for (const notification of metaModel.notifications) {
if (notification.registrationOptions) {
itemsWithRegistration.push({
method: notification.method,
registrationOptions: notification.registrationOptions
});
}
}
// Generate capability entries
for (const item of itemsWithRegistration) {
const methodIdentifier = this.methodToIdentifier(item.method);
const registrationType = item.registrationOptions
? this.typeToTypeScript(item.registrationOptions)
: 'unknown';
this.writer.writeLine(
`${methodIdentifier}: new Capability<${registrationType}>('${item.method}'),`
);
}
this.writer.outdent();
this.writer.writeLine('};');
this.writer.writeLine('');
}
/**
* Convert LSP method name to valid JavaScript identifier
*/
private methodToIdentifier(method: string): string {
const parts = method
.replace(/\$/g, '') // Remove $ characters
.split('/') // Split on forward slashes
.filter((part) => part.length > 0); // Remove empty parts
return parts
.map((part, index) => {
// Convert kebab-case to camelCase for each part
const camelCase = part.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase());
// Capitalize first letter of all parts except the first non-empty part
return index === 0 ? camelCase : camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
})
.join('');
}
/**
* Generate the API contract object
*/
private generateApiContract(metaModel: MetaModel): void {
this.writer.writeLine('/**');
this.writer.writeLine(' * LSP API Contract');
this.writer.writeLine(' */');
this.writer.writeLine('export const api = contract({');
this.writer.indent();
this.writer.writeLine('name: "LSP",');
// Helper function to generate request entries
const generateRequest = (request: Request, isOptional: boolean = false) => {
const methodIdentifier = this.methodToIdentifier(request.method);
const paramsType = this.getParamsType(request.params);
const resultType = this.typeToTypeScript(request.result);
if (request.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${request.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
const optional = isOptional ? '.optional()' : '';
this.writer.writeLine(
`${methodIdentifier}: unverifiedRequest<${paramsType}, ${resultType}>({ method: "${request.method}" })${optional},`
);
};
// Helper function to generate notification entries
const generateNotification = (notification: Notification) => {
const methodIdentifier = this.methodToIdentifier(notification.method);
const paramsType = this.getParamsType(notification.params);
if (notification.documentation) {
this.writer.writeLine('/**');
this.writer.writeLine(` * ${notification.documentation.replace(/\n/g, '\n * ')}`);
this.writer.writeLine(' */');
}
this.writer.writeLine(
`${methodIdentifier}: unverifiedNotification<${paramsType}>({ method: "${notification.method}" }),`
);
};
// Server section
this.writer.writeLine('server: {');
this.writer.indent();
// Server requests (sent from client to server)
for (const request of metaModel.requests) {
if (request.messageDirection === 'clientToServer' || request.messageDirection === 'both') {
generateRequest(request);
}
}
// Server notifications (sent from client to server)
for (const notification of metaModel.notifications) {
if (
notification.messageDirection === 'clientToServer' ||
notification.messageDirection === 'both'
) {
generateNotification(notification);
}
}
this.writer.outdent();
this.writer.writeLine('},');
// Client section
this.writer.writeLine('client: {');
this.writer.indent();
// Client requests (handled by server)
for (const request of metaModel.requests) {
if (request.messageDirection === 'serverToClient' || request.messageDirection === 'both') {
generateRequest(request, true); // serverToClient requests are optional
}
}
// Client notifications (sent from server to client)
for (const notification of metaModel.notifications) {
if (
notification.messageDirection === 'serverToClient' ||
notification.messageDirection === 'both'
) {
generateNotification(notification);
}
}
this.writer.outdent();
this.writer.writeLine('}');
this.writer.outdent();
this.writer.writeLine('});');
this.writer.writeLine('');
}
/**
* Helper method to get parameter type
*/
private getParamsType(params?: Type | Type[]): string {
if (!params) {
return 'void';
}
if (Array.isArray(params)) {
const paramTypes = params.map((p) => this.typeToTypeScript(p));
return `[${paramTypes.join(', ')}]`;
} else {
return this.typeToTypeScript(params);
}
}
/**
* Generate the complete TypeScript types
*/
generate(): void {
const metaModel = this.loadMetaModel();
this.writer.clear();
this.writer.writeLine('// Generated TypeScript definitions for LSP');
this.writer.writeLine(`// Protocol version: ${metaModel.metaData.version}`);
this.writer.writeLine('// This file is auto-generated. Do not edit manually.');
this.writer.writeLine('');
// Import contract types from @hediet/json-rpc
this.writer.writeLine('import {');
this.writer.indent();
this.writer.writeLine('contract,');
this.writer.writeLine('Contract,');
this.writer.writeLine('unverifiedRequest,');
this.writer.writeLine('unverifiedNotification,');
this.writer.outdent();
this.writer.writeLine('} from "@hediet/json-rpc";');
this.writer.writeLine('');
// Generate enumerations
for (const enumeration of metaModel.enumerations) {
this.generateEnumeration(enumeration);
}
// Generate type aliases
for (const typeAlias of metaModel.typeAliases) {
this.generateTypeAlias(typeAlias);
}
// Generate structures
for (const structure of metaModel.structures) {
this.generateStructure(structure);
}
// Generate Capability class
this.generateCapabilityClass();
// Generate capabilities map
this.generateCapabilitiesMap(metaModel);
// Generate API contract
this.generateApiContract(metaModel);
// Write types file
const srcDir = path.join(__dirname, '..', 'src');
if (!fs.existsSync(srcDir)) {
fs.mkdirSync(srcDir, { recursive: true });
}
fs.writeFileSync(path.join(srcDir, 'types.ts'), this.writer.toString());
console.log('Generated LSP types file: src/types.ts');
}
}
// Run the generator
if (require.main === module) {
const generator = new LSPTypesGenerator();
generator.generate();
}

File diff suppressed because it is too large Load diff

View file

@ -1,26 +0,0 @@
{
"name": "@vscode/monaco-lsp-client",
"description": "description",
"authors": "vscode",
"version": "0.1.0",
"main": "out/index.js",
"types": "out/index.d.ts",
"dependencies": {
"@hediet/json-rpc": "^0.5.0",
"@hediet/json-rpc-browser": "^0.5.1",
"@hediet/json-rpc-websocket": "^0.5.1"
},
"peerDependencies": {
"monaco-editor-core": "^0.54.0-dev-20250929"
},
"devDependencies": {
"rolldown": "^1.0.0-beta.41",
"rolldown-plugin-dts": "^0.16.11",
"rollup-plugin-delete": "^3.0.1"
},
"scripts": {
"build": "npx rolldown -c rolldown.config.mjs",
"dev": "npx rolldown -c rolldown.config.mjs --watch",
"generate": "tsx generator/index.ts"
}
}

View file

@ -1,33 +0,0 @@
// @ts-check
import { join } from 'path';
import { defineConfig } from 'rolldown';
import { dts } from 'rolldown-plugin-dts';
import del from 'rollup-plugin-delete';
import alias from '@rollup/plugin-alias';
export default defineConfig({
input: {
index: join(import.meta.dirname, './src/index.ts')
},
output: {
dir: join(import.meta.dirname, './out'),
format: 'es'
},
external: ['monaco-editor-core'],
plugins: [
del({ targets: 'out/*' }),
alias({
entries: {
ws: 'undefined'
}
}),
dts({
tsconfig: false,
compilerOptions: {
stripInternal: true
},
resolve: true
})
]
});

View file

@ -1,40 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { Position, Range, TextDocumentIdentifier } from '../../src/types';
export interface ITextModelBridge {
translate(
textModel: monaco.editor.ITextModel,
monacoPos: monaco.Position
): {
textDocument: TextDocumentIdentifier;
position: Position;
};
translateRange(textModel: monaco.editor.ITextModel, monacoRange: monaco.Range): Range;
translateBack(
textDocument: TextDocumentIdentifier,
position: Position
): {
textModel: monaco.editor.ITextModel;
position: monaco.Position;
};
translateBackRange(
textDocument: TextDocumentIdentifier,
range: Range
): {
textModel: monaco.editor.ITextModel;
range: monaco.Range;
};
}
export function assertTargetTextModel<T extends { textModel: monaco.editor.ITextModel }>(
input: T,
expectedTextModel: monaco.editor.ITextModel
): T {
if (input.textModel !== expectedTextModel) {
throw new Error(`Expected text model to be ${expectedTextModel}, but got ${input.textModel}`);
}
return input;
}

View file

@ -1,254 +0,0 @@
import { TypedChannel } from '@hediet/json-rpc';
import { ClientCapabilities, Capability, ServerCapabilities, api, capabilities, TextDocumentChangeRegistrationOptions, TextDocumentSyncKind } from '../../src/types';
import { IDisposable, Disposable } from '../utils';
export interface ILspCapabilitiesRegistry {
addStaticClientCapabilities(capability: ClientCapabilities): IDisposable;
registerCapabilityHandler<T>(capability: Capability<T>, handleStaticCapability: boolean, handler: (capability: T) => IDisposable): IDisposable;
}
export class LspCapabilitiesRegistry extends Disposable implements ILspCapabilitiesRegistry {
private readonly _staticCapabilities = new Set<{ cap: ClientCapabilities; }>();
private readonly _dynamicFromStatic = DynamicFromStaticOptions.create();
private readonly _registrations = new Map<Capability<any>, CapabilityInfo<any>>();
private _serverCapabilities: ServerCapabilities | undefined = undefined;
constructor(
private readonly _connection: TypedChannel
) {
super();
this._register(this._connection.registerRequestHandler(api.client.clientRegisterCapability, async (params) => {
for (const registration of params.registrations) {
const capability = getCapabilityByMethod(registration.method);
const r = new CapabilityRegistration(registration.id, capability, registration.registerOptions, false);
this._registerCapabilityOptions(r);
}
return { ok: null };
}));
this._register(this._connection.registerRequestHandler(api.client.clientUnregisterCapability, async (params) => {
for (const unregistration of params.unregisterations) {
const capability = getCapabilityByMethod(unregistration.method);
const info = this._registrations.get(capability);
const handlerInfo = info?.registrations.get(unregistration.id);
if (!handlerInfo) {
throw new Error(`No registration for method ${unregistration.method} with id ${unregistration.id}`);
}
handlerInfo?.handlerDisposables.forEach(d => d.dispose());
info?.registrations.delete(unregistration.id);
}
return { ok: null };
}));
}
private _registerCapabilityOptions<T>(registration: CapabilityRegistration<T>) {
let registrationForMethod = this._registrations.get(registration.capability);
if (!registrationForMethod) {
registrationForMethod = new CapabilityInfo();
this._registrations.set(registration.capability, registrationForMethod);
}
if (registrationForMethod.registrations.has(registration.id)) {
throw new Error(`Handler for method ${registration.capability.method} with id ${registration.id} already registered`);
}
registrationForMethod.registrations.set(registration.id, registration);
for (const h of registrationForMethod.handlers) {
if (!h.handleStaticCapability && registration.isFromStatic) {
continue;
}
registration.handlerDisposables.set(h, h.handler(registration.options));
}
}
setServerCapabilities(serverCapabilities: ServerCapabilities) {
if (this._serverCapabilities) {
throw new Error('Server capabilities already set');
}
this._serverCapabilities = serverCapabilities;
for (const cap of Object.values(capabilities)) {
const options = this._dynamicFromStatic.getOptions(cap, serverCapabilities);
if (options) {
this._registerCapabilityOptions(new CapabilityRegistration(cap.method, cap, options, true));
}
}
}
getClientCapabilities(): ClientCapabilities {
const result: ClientCapabilities = {};
for (const c of this._staticCapabilities) {
deepAssign(result, c.cap);
}
return result;
}
addStaticClientCapabilities(capability: ClientCapabilities): IDisposable {
const obj = { cap: capability };
this._staticCapabilities.add(obj);
return {
dispose: () => {
this._staticCapabilities.delete(obj);
}
};
}
registerCapabilityHandler<T>(capability: Capability<T>, handleStaticCapability: boolean, handler: (capability: T) => IDisposable): IDisposable {
let info = this._registrations.get(capability);
if (!info) {
info = new CapabilityInfo();
this._registrations.set(capability, info);
}
const handlerInfo = new CapabilityHandler(capability, handleStaticCapability, handler);
info.handlers.add(handlerInfo);
for (const registration of info.registrations.values()) {
if (!handlerInfo.handleStaticCapability && registration.isFromStatic) {
continue;
}
registration.handlerDisposables.set(handlerInfo, handler(registration.options));
}
return {
dispose: () => {
info.handlers.delete(handlerInfo);
for (const registration of info.registrations.values()) {
const disposable = registration.handlerDisposables.get(handlerInfo);
if (disposable) {
disposable.dispose();
registration.handlerDisposables.delete(handlerInfo);
}
}
}
};
}
}
class CapabilityHandler<T> {
constructor(
public readonly capability: Capability<T>,
public readonly handleStaticCapability: boolean,
public readonly handler: (capabilityOptions: T) => IDisposable
) { }
}
class CapabilityRegistration<T> {
public readonly handlerDisposables = new Map<CapabilityHandler<any>, IDisposable>();
constructor(
public readonly id: string,
public readonly capability: Capability<T>,
public readonly options: T,
public readonly isFromStatic: boolean
) { }
}
const capabilitiesByMethod = new Map([...Object.values(capabilities)].map(c => [c.method, c]));
function getCapabilityByMethod(method: string): Capability<any> {
const c = capabilitiesByMethod.get(method);
if (!c) {
throw new Error(`No capability found for method ${method}`);
}
return c;
}
class CapabilityInfo<T> {
public readonly handlers = new Set<CapabilityHandler<T>>();
public readonly registrations = new Map</* id */ string, CapabilityRegistration<T>>();
}
class DynamicFromStaticOptions {
private readonly _mappings = new Map</* method */ string, (serverCapabilities: ServerCapabilities) => any>();
public static create(): DynamicFromStaticOptions {
const o = new DynamicFromStaticOptions();
o.set(capabilities.textDocumentDidChange, s => {
if (s.textDocumentSync === undefined) {
return undefined;
}
if (typeof s.textDocumentSync === 'object') {
return {
syncKind: s.textDocumentSync.change ?? TextDocumentSyncKind.None,
documentSelector: null,
} satisfies TextDocumentChangeRegistrationOptions;
} else {
return {
syncKind: s.textDocumentSync,
documentSelector: null,
} satisfies TextDocumentChangeRegistrationOptions;
}
return null!;
});
o.set(capabilities.textDocumentCompletion, s => s.completionProvider);
o.set(capabilities.textDocumentHover, s => s.hoverProvider);
o.set(capabilities.textDocumentSignatureHelp, s => s.signatureHelpProvider);
o.set(capabilities.textDocumentDefinition, s => s.definitionProvider);
o.set(capabilities.textDocumentReferences, s => s.referencesProvider);
o.set(capabilities.textDocumentDocumentHighlight, s => s.documentHighlightProvider);
o.set(capabilities.textDocumentDocumentSymbol, s => s.documentSymbolProvider);
o.set(capabilities.textDocumentCodeAction, s => s.codeActionProvider);
o.set(capabilities.textDocumentCodeLens, s => s.codeLensProvider);
o.set(capabilities.textDocumentDocumentLink, s => s.documentLinkProvider);
o.set(capabilities.textDocumentFormatting, s => s.documentFormattingProvider);
o.set(capabilities.textDocumentRangeFormatting, s => s.documentRangeFormattingProvider);
o.set(capabilities.textDocumentOnTypeFormatting, s => s.documentOnTypeFormattingProvider);
o.set(capabilities.textDocumentRename, s => s.renameProvider);
o.set(capabilities.textDocumentFoldingRange, s => s.foldingRangeProvider);
o.set(capabilities.textDocumentDeclaration, s => s.declarationProvider);
o.set(capabilities.textDocumentTypeDefinition, s => s.typeDefinitionProvider);
o.set(capabilities.textDocumentImplementation, s => s.implementationProvider);
o.set(capabilities.textDocumentDocumentColor, s => s.colorProvider);
o.set(capabilities.textDocumentSelectionRange, s => s.selectionRangeProvider);
o.set(capabilities.textDocumentLinkedEditingRange, s => s.linkedEditingRangeProvider);
o.set(capabilities.textDocumentPrepareCallHierarchy, s => s.callHierarchyProvider);
o.set(capabilities.textDocumentSemanticTokensFull, s => s.semanticTokensProvider);
o.set(capabilities.textDocumentInlayHint, s => s.inlayHintProvider);
o.set(capabilities.textDocumentInlineValue, s => s.inlineValueProvider);
o.set(capabilities.textDocumentDiagnostic, s => s.diagnosticProvider);
o.set(capabilities.textDocumentMoniker, s => s.monikerProvider);
o.set(capabilities.textDocumentPrepareTypeHierarchy, s => s.typeHierarchyProvider);
o.set(capabilities.workspaceSymbol, s => s.workspaceSymbolProvider);
o.set(capabilities.workspaceExecuteCommand, s => s.executeCommandProvider);
return o;
}
set<T>(capability: Capability<T>, getOptionsFromStatic: (serverCapabilities: ServerCapabilities) => T | boolean | undefined): void {
if (this._mappings.has(capability.method)) {
throw new Error(`Capability for method ${capability.method} already registered`);
}
this._mappings.set(capability.method, getOptionsFromStatic);
}
getOptions<T>(capability: Capability<T>, serverCapabilities: ServerCapabilities): T | undefined {
const getter = this._mappings.get(capability.method);
if (!getter) {
return undefined;
}
const result = getter(serverCapabilities);
return result;
}
}
function deepAssign(target: any, source: any) {
for (const key of Object.keys(source)) {
const srcValue = source[key];
if (srcValue === undefined) {
continue;
}
const tgtValue = target[key];
if (tgtValue === undefined) {
target[key] = srcValue;
continue;
}
if (typeof srcValue !== 'object' || srcValue === null) {
target[key] = srcValue;
continue;
}
if (typeof tgtValue !== 'object' || tgtValue === null) {
target[key] = srcValue;
continue;
}
deepAssign(tgtValue, srcValue);
}
}

View file

@ -1,90 +0,0 @@
import { IMessageTransport, TypedChannel } from "@hediet/json-rpc";
import { LspCompletionFeature } from "./languageFeatures/LspCompletionFeature";
import { LspHoverFeature } from "./languageFeatures/LspHoverFeature";
import { LspSignatureHelpFeature } from "./languageFeatures/LspSignatureHelpFeature";
import { LspDefinitionFeature } from "./languageFeatures/LspDefinitionFeature";
import { LspDeclarationFeature } from "./languageFeatures/LspDeclarationFeature";
import { LspTypeDefinitionFeature } from "./languageFeatures/LspTypeDefinitionFeature";
import { LspImplementationFeature } from "./languageFeatures/LspImplementationFeature";
import { LspReferencesFeature } from "./languageFeatures/LspReferencesFeature";
import { LspDocumentHighlightFeature } from "./languageFeatures/LspDocumentHighlightFeature";
import { LspDocumentSymbolFeature } from "./languageFeatures/LspDocumentSymbolFeature";
import { LspRenameFeature } from "./languageFeatures/LspRenameFeature";
import { LspCodeActionFeature } from "./languageFeatures/LspCodeActionFeature";
import { LspCodeLensFeature } from "./languageFeatures/LspCodeLensFeature";
import { LspDocumentLinkFeature } from "./languageFeatures/LspDocumentLinkFeature";
import { LspFormattingFeature } from "./languageFeatures/LspFormattingFeature";
import { LspRangeFormattingFeature } from "./languageFeatures/LspRangeFormattingFeature";
import { LspOnTypeFormattingFeature } from "./languageFeatures/LspOnTypeFormattingFeature";
import { LspFoldingRangeFeature } from "./languageFeatures/LspFoldingRangeFeature";
import { LspSelectionRangeFeature } from "./languageFeatures/LspSelectionRangeFeature";
import { LspInlayHintsFeature } from "./languageFeatures/LspInlayHintsFeature";
import { LspSemanticTokensFeature } from "./languageFeatures/LspSemanticTokensFeature";
import { LspDiagnosticsFeature } from "./languageFeatures/LspDiagnosticsFeature";
import { api } from "../../src/types";
import { LspConnection } from "./LspConnection";
import { LspCapabilitiesRegistry } from './LspCapabilitiesRegistry';
import { TextDocumentSynchronizer } from "./TextDocumentSynchronizer";
import { DisposableStore, IDisposable } from "../utils";
export class MonacoLspClient {
private _connection: LspConnection;
private readonly _capabilitiesRegistry: LspCapabilitiesRegistry;
private readonly _bridge: TextDocumentSynchronizer;
private _initPromise: Promise<void>;
constructor(transport: IMessageTransport) {
const c = TypedChannel.fromTransport(transport);
const s = api.getServer(c, {});
c.startListen();
this._capabilitiesRegistry = new LspCapabilitiesRegistry(c);
this._bridge = new TextDocumentSynchronizer(s.server, this._capabilitiesRegistry);
this._connection = new LspConnection(s.server, this._bridge, this._capabilitiesRegistry, c);
this.createFeatures();
this._initPromise = this._init();
}
private async _init() {
const result = await this._connection.server.initialize({
processId: null,
capabilities: this._capabilitiesRegistry.getClientCapabilities(),
rootUri: null,
});
this._connection.server.initialized({});
this._capabilitiesRegistry.setServerCapabilities(result.capabilities);
}
protected createFeatures(): IDisposable {
const store = new DisposableStore();
store.add(new LspCompletionFeature(this._connection));
store.add(new LspHoverFeature(this._connection));
store.add(new LspSignatureHelpFeature(this._connection));
store.add(new LspDefinitionFeature(this._connection));
store.add(new LspDeclarationFeature(this._connection));
store.add(new LspTypeDefinitionFeature(this._connection));
store.add(new LspImplementationFeature(this._connection));
store.add(new LspReferencesFeature(this._connection));
store.add(new LspDocumentHighlightFeature(this._connection));
store.add(new LspDocumentSymbolFeature(this._connection));
store.add(new LspRenameFeature(this._connection));
store.add(new LspCodeActionFeature(this._connection));
store.add(new LspCodeLensFeature(this._connection));
store.add(new LspDocumentLinkFeature(this._connection));
store.add(new LspFormattingFeature(this._connection));
store.add(new LspRangeFormattingFeature(this._connection));
store.add(new LspOnTypeFormattingFeature(this._connection));
store.add(new LspFoldingRangeFeature(this._connection));
store.add(new LspSelectionRangeFeature(this._connection));
store.add(new LspInlayHintsFeature(this._connection));
store.add(new LspSemanticTokensFeature(this._connection));
store.add(new LspDiagnosticsFeature(this._connection));
return store;
}
}

View file

@ -1,13 +0,0 @@
import { TypedChannel } from '@hediet/json-rpc';
import { api } from '../../src/types';
import { ITextModelBridge } from './ITextModelBridge';
import { LspCapabilitiesRegistry } from './LspCapabilitiesRegistry';
export class LspConnection {
constructor(
public readonly server: typeof api.TServerInterface,
public readonly bridge: ITextModelBridge,
public readonly capabilities: LspCapabilitiesRegistry,
public readonly connection: TypedChannel,
) { }
}

View file

@ -1,183 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { api, capabilities, Position, Range, TextDocumentContentChangeEvent, TextDocumentIdentifier } from '../../src/types';
import { Disposable } from '../utils';
import { ITextModelBridge } from './ITextModelBridge';
import { ILspCapabilitiesRegistry } from './LspCapabilitiesRegistry';
export class TextDocumentSynchronizer extends Disposable implements ITextModelBridge {
private readonly _managedModels = new Map<monaco.editor.ITextModel, ManagedModel>();
private readonly _managedModelsReverse = new Map</* uri */ string, monaco.editor.ITextModel>();
private _started = false;
constructor(
private readonly _server: typeof api.TServerInterface,
private readonly _capabilities: ILspCapabilitiesRegistry,
) {
super();
this._register(this._capabilities.addStaticClientCapabilities({
textDocument: {
synchronization: {
dynamicRegistration: true,
willSave: false,
willSaveWaitUntil: false,
didSave: false,
}
}
}));
this._register(_capabilities.registerCapabilityHandler(capabilities.textDocumentDidChange, true, e => {
if (this._started) {
return {
dispose: () => {
}
}
}
this._started = true;
this._register(monaco.editor.onDidCreateModel(m => {
this._getOrCreateManagedModel(m);
}));
for (const m of monaco.editor.getModels()) {
this._getOrCreateManagedModel(m);
}
return {
dispose: () => {
}
}
}));
}
private _getOrCreateManagedModel(m: monaco.editor.ITextModel) {
if (!this._started) {
throw new Error('Not started');
}
const uriStr = m.uri.toString(true).toLowerCase();
let mm = this._managedModels.get(m);
if (!mm) {
mm = new ManagedModel(m, this._server);
this._managedModels.set(m, mm);
this._managedModelsReverse.set(uriStr, m);
}
m.onWillDispose(() => {
mm!.dispose();
this._managedModels.delete(m);
this._managedModelsReverse.delete(uriStr);
});
return mm;
}
translateBack(textDocument: TextDocumentIdentifier, position: Position): { textModel: monaco.editor.ITextModel; position: monaco.Position; } {
const uri = textDocument.uri.toLowerCase();
const textModel = this._managedModelsReverse.get(uri);
if (!textModel) {
throw new Error(`No text model for uri ${uri}`);
}
const monacoPosition = new monaco.Position(position.line + 1, position.character + 1);
return { textModel, position: monacoPosition };
}
translateBackRange(textDocument: TextDocumentIdentifier, range: Range): { textModel: monaco.editor.ITextModel; range: monaco.Range; } {
const uri = textDocument.uri.toLowerCase();
const textModel = this._managedModelsReverse.get(uri);
if (!textModel) {
throw new Error(`No text model for uri ${uri}`);
}
const monacoRange = new monaco.Range(
range.start.line + 1,
range.start.character + 1,
range.end.line + 1,
range.end.character + 1
);
return { textModel, range: monacoRange };
}
translate(textModel: monaco.editor.ITextModel, monacoPos: monaco.Position): { textDocument: TextDocumentIdentifier; position: Position; } {
return {
textDocument: {
uri: textModel.uri.toString(true),
},
position: {
line: monacoPos.lineNumber - 1,
character: monacoPos.column - 1,
}
};
}
translateRange(textModel: monaco.editor.ITextModel, monacoRange: monaco.Range): Range {
return {
start: {
line: monacoRange.startLineNumber - 1,
character: monacoRange.startColumn - 1,
},
end: {
line: monacoRange.endLineNumber - 1,
character: monacoRange.endColumn - 1,
}
};
}
}
class ManagedModel extends Disposable {
constructor(
private readonly _textModel: monaco.editor.ITextModel,
private readonly _api: typeof api.TServerInterface
) {
super();
const uri = _textModel.uri.toString(true).toLowerCase();
this._api.textDocumentDidOpen({
textDocument: {
languageId: _textModel.getLanguageId(),
uri: uri,
version: _textModel.getVersionId(),
text: _textModel.getValue(),
}
});
this._register(_textModel.onDidChangeContent(e => {
const contentChanges = e.changes.map(c => toLspTextDocumentContentChangeEvent(c));
this._api.textDocumentDidChange({
textDocument: {
uri: uri,
version: _textModel.getVersionId(),
},
contentChanges: contentChanges
});
}));
this._register({
dispose: () => {
this._api.textDocumentDidClose({
textDocument: {
uri: uri,
}
});
}
});
}
}
function toLspTextDocumentContentChangeEvent(change: monaco.editor.IModelContentChange): TextDocumentContentChangeEvent {
return {
range: toLspRange(change.range),
rangeLength: change.rangeLength,
text: change.text,
};
}
function toLspRange(range: monaco.IRange): Range {
return {
start: {
line: range.startLineNumber - 1,
character: range.startColumn - 1,
},
end: {
line: range.endLineNumber - 1,
character: range.endColumn - 1,
}
};
}

View file

@ -1,169 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, CodeActionRegistrationOptions, Command, WorkspaceEdit, CodeAction } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { lspCodeActionKindToMonacoCodeActionKind, toMonacoCodeActionKind, toLspDiagnosticSeverity, toLspCodeActionTriggerKind, toMonacoCommand } from './common';
export class LspCodeActionFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
codeAction: {
dynamicRegistration: true,
codeActionLiteralSupport: {
codeActionKind: {
valueSet: Array.from(lspCodeActionKindToMonacoCodeActionKind.keys()),
}
},
isPreferredSupport: true,
disabledSupport: true,
dataSupport: true,
resolveSupport: {
properties: ['edit'],
},
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentCodeAction, true, capability => {
return monaco.languages.registerCodeActionProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspCodeActionProvider(this._connection, capability),
);
}));
}
}
interface ExtendedCodeAction extends monaco.languages.CodeAction {
_lspAction?: CodeAction;
}
class LspCodeActionProvider implements monaco.languages.CodeActionProvider {
public readonly resolveCodeAction;
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: CodeActionRegistrationOptions,
) {
if (_capabilities.resolveProvider) {
this.resolveCodeAction = async (codeAction: ExtendedCodeAction, token: monaco.CancellationToken): Promise<ExtendedCodeAction> => {
if (codeAction._lspAction) {
const resolved = await this._client.server.codeActionResolve(codeAction._lspAction);
if (resolved.edit) {
codeAction.edit = toMonacoWorkspaceEdit(resolved.edit, this._client);
}
if (resolved.command) {
codeAction.command = toMonacoCommand(resolved.command);
}
}
return codeAction;
};
}
}
async provideCodeActions(
model: monaco.editor.ITextModel,
range: monaco.Range,
context: monaco.languages.CodeActionContext,
token: monaco.CancellationToken
): Promise<monaco.languages.CodeActionList | null> {
const translated = this._client.bridge.translate(model, range.getStartPosition());
const result = await this._client.server.textDocumentCodeAction({
textDocument: translated.textDocument,
range: this._client.bridge.translateRange(model, range),
context: {
diagnostics: context.markers.map(marker => ({
range: this._client.bridge.translateRange(model, monaco.Range.lift(marker)),
message: marker.message,
severity: toLspDiagnosticSeverity(marker.severity),
})),
triggerKind: toLspCodeActionTriggerKind(context.trigger),
},
});
if (!result) {
return null;
}
const actions = Array.isArray(result) ? result : [result];
return {
actions: actions.map(action => {
if ('title' in action && !('kind' in action)) {
// Command
const cmd = action as Command;
const monacoAction: ExtendedCodeAction = {
title: cmd.title,
command: toMonacoCommand(cmd),
};
return monacoAction;
} else {
// CodeAction
const codeAction = action as CodeAction;
const monacoAction: ExtendedCodeAction = {
title: codeAction.title,
kind: toMonacoCodeActionKind(codeAction.kind),
isPreferred: codeAction.isPreferred,
disabled: codeAction.disabled?.reason,
edit: codeAction.edit ? toMonacoWorkspaceEdit(codeAction.edit, this._client) : undefined,
command: toMonacoCommand(codeAction.command),
_lspAction: codeAction,
};
return monacoAction;
}
}),
dispose: () => { },
};
}
}
function toMonacoWorkspaceEdit(
edit: WorkspaceEdit,
client: LspConnection
): monaco.languages.WorkspaceEdit {
const edits: monaco.languages.IWorkspaceTextEdit[] = [];
if (edit.changes) {
for (const uri in edit.changes) {
const textEdits = edit.changes[uri];
for (const textEdit of textEdits) {
const translated = client.bridge.translateBackRange({ uri }, textEdit.range);
edits.push({
resource: translated.textModel.uri,
versionId: undefined,
textEdit: {
range: translated.range,
text: textEdit.newText,
},
});
}
}
}
if (edit.documentChanges) {
for (const change of edit.documentChanges) {
if ('textDocument' in change) {
const uri = change.textDocument.uri;
for (const textEdit of change.edits) {
const translated = client.bridge.translateBackRange({ uri }, textEdit.range);
edits.push({
resource: translated.textModel.uri,
versionId: change.textDocument.version ?? undefined,
textEdit: {
range: translated.range,
text: textEdit.newText,
},
});
}
}
}
}
return { edits };
}

View file

@ -1,90 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, CodeLensRegistrationOptions, CodeLens } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { assertTargetTextModel } from '../ITextModelBridge';
import { toMonacoCommand } from './common';
export class LspCodeLensFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
codeLens: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentCodeLens, true, capability => {
return monaco.languages.registerCodeLensProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspCodeLensProvider(this._connection, capability),
);
}));
}
}
interface ExtendedCodeLens extends monaco.languages.CodeLens {
_lspCodeLens?: CodeLens;
}
class LspCodeLensProvider implements monaco.languages.CodeLensProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: CodeLensRegistrationOptions,
) { }
async provideCodeLenses(
model: monaco.editor.ITextModel,
token: monaco.CancellationToken
): Promise<monaco.languages.CodeLensList | null> {
const translated = this._client.bridge.translate(model, new monaco.Position(1, 1));
const result = await this._client.server.textDocumentCodeLens({
textDocument: translated.textDocument,
});
if (!result) {
return null;
}
return {
lenses: result.map(lens => {
const monacoLens: ExtendedCodeLens = {
range: assertTargetTextModel(this._client.bridge.translateBackRange(translated.textDocument, lens.range), model).range,
command: toMonacoCommand(lens.command),
_lspCodeLens: lens,
};
return monacoLens;
}),
dispose: () => { },
};
}
async resolveCodeLens(
model: monaco.editor.ITextModel,
codeLens: ExtendedCodeLens,
token: monaco.CancellationToken
): Promise<monaco.languages.CodeLens> {
if (!this._capabilities.resolveProvider || !codeLens._lspCodeLens) {
return codeLens;
}
const resolved = await this._client.server.codeLensResolve(codeLens._lspCodeLens);
if (resolved.command) {
codeLens.command = {
id: resolved.command.command,
title: resolved.command.title,
arguments: resolved.command.arguments,
};
}
return codeLens;
}
}

View file

@ -1,202 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, CompletionRegistrationOptions, MarkupContent, CompletionItem, TextDocumentPositionParams } from '../../../src/types';
import { assertTargetTextModel, ITextModelBridge } from '../ITextModelBridge';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import {
lspCompletionItemKindToMonacoCompletionItemKind,
lspCompletionItemTagToMonacoCompletionItemTag,
toMonacoCompletionItemKind,
toMonacoCompletionItemTag,
toLspCompletionTriggerKind,
toMonacoInsertTextRules,
toMonacoCommand,
} from './common';
export class LspCompletionFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
completion: {
dynamicRegistration: true,
contextSupport: true,
completionItemKind: {
valueSet: Array.from(lspCompletionItemKindToMonacoCompletionItemKind.keys()),
},
completionItem: {
tagSupport: {
valueSet: Array.from(lspCompletionItemTagToMonacoCompletionItemTag.keys()),
},
commitCharactersSupport: true,
deprecatedSupport: true,
preselectSupport: true,
}
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentCompletion, true, capability => {
return monaco.languages.registerCompletionItemProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspCompletionProvider(this._connection, capability),
);
}));
}
}
interface ExtendedCompletionItem extends monaco.languages.CompletionItem {
_lspItem: CompletionItem;
_translated: TextDocumentPositionParams;
_model: monaco.editor.ITextModel;
}
class LspCompletionProvider implements monaco.languages.CompletionItemProvider {
public readonly resolveCompletionItem;
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: CompletionRegistrationOptions,
) {
if (_capabilities.resolveProvider) {
this.resolveCompletionItem = async (item: ExtendedCompletionItem, token: monaco.CancellationToken): Promise<ExtendedCompletionItem> => {
const resolved = await this._client.server.completionItemResolve(item._lspItem);
applyLspCompletionItemProperties(item, resolved, this._client.bridge, item._translated, item._model);
return item;
}
}
}
get triggerCharacters(): string[] | undefined {
return this._capabilities.triggerCharacters;
}
async provideCompletionItems(
model: monaco.editor.ITextModel,
position: monaco.Position,
context: monaco.languages.CompletionContext,
token: monaco.CancellationToken
): Promise<monaco.languages.CompletionList & { suggestions: ExtendedCompletionItem[] }> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentCompletion({
textDocument: translated.textDocument,
position: translated.position,
context: context.triggerCharacter ? {
triggerKind: toLspCompletionTriggerKind(context.triggerKind),
triggerCharacter: context.triggerCharacter,
} : undefined,
});
if (!result) {
return { suggestions: [] };
}
const items = Array.isArray(result) ? result : result.items;
return {
suggestions: items.map<ExtendedCompletionItem>(i => {
const item: ExtendedCompletionItem = {
...convertLspToMonacoCompletionItem(
i,
this._client.bridge,
translated,
model,
position
),
_lspItem: i,
_translated: translated,
_model: model,
};
return item;
})
};
}
}
function convertLspToMonacoCompletionItem(
lspItem: CompletionItem,
bridge: ITextModelBridge,
translated: TextDocumentPositionParams,
model: monaco.editor.ITextModel,
position: monaco.Position
): monaco.languages.CompletionItem {
let insertText = lspItem.insertText || lspItem.label;
let range: monaco.IRange | monaco.languages.CompletionItemRanges | undefined = undefined;
if (lspItem.textEdit) {
if ('range' in lspItem.textEdit) {
insertText = lspItem.textEdit.newText;
range = assertTargetTextModel(bridge.translateBackRange(translated.textDocument, lspItem.textEdit.range), model).range;
} else {
insertText = lspItem.textEdit.newText;
range = {
insert: assertTargetTextModel(bridge.translateBackRange(translated.textDocument, lspItem.textEdit.insert), model).range,
replace: assertTargetTextModel(bridge.translateBackRange(translated.textDocument, lspItem.textEdit.replace), model).range,
};
}
}
if (!range) {
range = monaco.Range.fromPositions(position, position);
}
const item: monaco.languages.CompletionItem = {
label: lspItem.label,
kind: toMonacoCompletionItemKind(lspItem.kind),
insertText,
sortText: lspItem.sortText,
filterText: lspItem.filterText,
preselect: lspItem.preselect,
commitCharacters: lspItem.commitCharacters,
range: range,
};
applyLspCompletionItemProperties(item, lspItem, bridge, translated, model);
return item;
}
function applyLspCompletionItemProperties(
monacoItem: monaco.languages.CompletionItem,
lspItem: CompletionItem,
bridge: ITextModelBridge,
translated: TextDocumentPositionParams,
targetModel: monaco.editor.ITextModel
): void {
if (lspItem.detail !== undefined) {
monacoItem.detail = lspItem.detail;
}
if (lspItem.documentation !== undefined) {
monacoItem.documentation = toMonacoDocumentation(lspItem.documentation);
}
if (lspItem.insertTextFormat !== undefined) {
const insertTextRules = toMonacoInsertTextRules(lspItem.insertTextFormat);
monacoItem.insertTextRules = insertTextRules;
}
if (lspItem.tags && lspItem.tags.length > 0) {
monacoItem.tags = lspItem.tags.map(toMonacoCompletionItemTag).filter((tag): tag is monaco.languages.CompletionItemTag => tag !== undefined);
}
if (lspItem.additionalTextEdits && lspItem.additionalTextEdits.length > 0) {
monacoItem.additionalTextEdits = lspItem.additionalTextEdits.map(edit => ({
range: assertTargetTextModel(bridge.translateBackRange(translated.textDocument, edit.range), targetModel).range,
text: edit.newText,
}));
}
if (lspItem.command) {
monacoItem.command = toMonacoCommand(lspItem.command);
}
}
function toMonacoDocumentation(doc: string | MarkupContent | undefined): string | monaco.IMarkdownString | undefined {
if (!doc) return undefined;
if (typeof doc === 'string') return doc;
return {
value: doc.value,
isTrusted: true,
};
}

View file

@ -1,60 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DeclarationRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoLocation } from "./common";
export class LspDeclarationFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
declaration: {
dynamicRegistration: true,
linkSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentDeclaration, true, capability => {
return monaco.languages.registerDeclarationProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDeclarationProvider(this._connection, capability),
);
}));
}
}
class LspDeclarationProvider implements monaco.languages.DeclarationProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DeclarationRegistrationOptions,
) { }
async provideDeclaration(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.Definition | monaco.languages.LocationLink[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentDeclaration({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
if (Array.isArray(result)) {
return result.map(loc => toMonacoLocation(loc, this._client));
}
return toMonacoLocation(result, this._client);
}
}

View file

@ -1,60 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DefinitionRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoLocation } from "./common";
export class LspDefinitionFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
definition: {
dynamicRegistration: true,
linkSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentDefinition, true, capability => {
return monaco.languages.registerDefinitionProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDefinitionProvider(this._connection, capability),
);
}));
}
}
class LspDefinitionProvider implements monaco.languages.DefinitionProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DefinitionRegistrationOptions,
) { }
async provideDefinition(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.Definition | monaco.languages.LocationLink[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentDefinition({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
if (Array.isArray(result)) {
return result.map(loc => toMonacoLocation(loc, this._client));
}
return toMonacoLocation(result, this._client);
}
}

View file

@ -1,208 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { api, capabilities, Diagnostic, DiagnosticRegistrationOptions, DocumentDiagnosticReport, PublishDiagnosticsParams } from '../../../src/types';
import { Disposable, DisposableStore } from '../../utils';
import { LspConnection } from '../LspConnection';
import { lspDiagnosticTagToMonacoMarkerTag, matchesDocumentSelector, toDiagnosticMarker } from './common';
export class LspDiagnosticsFeature extends Disposable {
private readonly _diagnosticsMarkerOwner = 'lsp';
private readonly _pullDiagnosticProviders = new Map<monaco.editor.ITextModel, ModelDiagnosticProvider>();
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
publishDiagnostics: {
relatedInformation: true,
tagSupport: {
valueSet: [...lspDiagnosticTagToMonacoMarkerTag.keys()],
},
versionSupport: true,
codeDescriptionSupport: true,
dataSupport: true,
},
diagnostic: {
dynamicRegistration: true,
relatedDocumentSupport: true,
}
}
}));
debugger;
this._register(this._connection.connection.registerNotificationHandler(
api.client.textDocumentPublishDiagnostics,
(params) => this._handlePublishDiagnostics(params)
));
this._register(this._connection.capabilities.registerCapabilityHandler(
capabilities.textDocumentDiagnostic,
true,
(capability) => {
const disposables = new DisposableStore();
for (const model of monaco.editor.getModels()) {
this._addPullDiagnosticProvider(model, capability, disposables);
}
disposables.add(monaco.editor.onDidCreateModel(model => {
this._addPullDiagnosticProvider(model, capability, disposables);
}));
return disposables;
}
));
}
private _addPullDiagnosticProvider(
model: monaco.editor.ITextModel,
capability: DiagnosticRegistrationOptions,
disposables: DisposableStore
): void {
// Check if model matches the document selector
const languageId = model.getLanguageId();
if (!matchesDocumentSelector(model, capability.documentSelector)) {
return;
}
const provider = new ModelDiagnosticProvider(
model,
this._connection,
this._diagnosticsMarkerOwner,
capability
);
this._pullDiagnosticProviders.set(model, provider);
disposables.add(provider);
disposables.add(model.onWillDispose(() => {
this._pullDiagnosticProviders.delete(model);
}));
}
private _handlePublishDiagnostics(params: PublishDiagnosticsParams): void {
const uri = params.uri;
try {
const translated = this._connection.bridge.translateBack({ uri }, { line: 0, character: 0 });
const model = translated.textModel;
if (!model || model.isDisposed()) {
return;
}
const markers = params.diagnostics.map(diagnostic =>
toDiagnosticMarker(diagnostic)
);
monaco.editor.setModelMarkers(model, this._diagnosticsMarkerOwner, markers);
} catch (error) {
// Model not found or already disposed - this is normal when files are closed
console.debug(`Could not set diagnostics for ${uri}:`, error);
}
}
}
/**
* Manages pull diagnostics for a single text model
*/
class ModelDiagnosticProvider extends Disposable {
private _updateHandle: number | undefined;
private _previousResultId: string | undefined;
constructor(
private readonly _model: monaco.editor.ITextModel,
private readonly _connection: LspConnection,
private readonly _markerOwner: string,
private readonly _capability: DiagnosticRegistrationOptions,
) {
super();
this._register(this._model.onDidChangeContent(() => {
this._scheduleDiagnosticUpdate();
}));
this._scheduleDiagnosticUpdate();
}
private _scheduleDiagnosticUpdate(): void {
if (this._updateHandle !== undefined) {
clearTimeout(this._updateHandle);
}
this._updateHandle = window.setTimeout(() => {
this._updateHandle = undefined;
this._requestDiagnostics();
}, 500);
}
private async _requestDiagnostics(): Promise<void> {
if (this._model.isDisposed()) {
return;
}
try {
const translated = this._connection.bridge.translate(this._model, new monaco.Position(1, 1));
const result = await this._connection.server.textDocumentDiagnostic({
textDocument: translated.textDocument,
identifier: this._capability.identifier,
previousResultId: this._previousResultId,
});
if (this._model.isDisposed()) {
return;
}
this._handleDiagnosticReport(result);
} catch (error) {
console.error('Error requesting diagnostics:', error);
}
}
private _handleDiagnosticReport(report: DocumentDiagnosticReport): void {
if (report.kind === 'full') {
// Full diagnostic report
this._previousResultId = report.resultId;
const markers = report.items.map(diagnostic => toDiagnosticMarker(diagnostic));
monaco.editor.setModelMarkers(this._model, this._markerOwner, markers);
// Handle related documents if present
if ('relatedDocuments' in report && report.relatedDocuments) {
this._handleRelatedDocuments(report.relatedDocuments);
}
} else if (report.kind === 'unchanged') {
// Unchanged report - diagnostics are still valid
this._previousResultId = report.resultId;
// No need to update markers
}
}
private _handleRelatedDocuments(relatedDocuments: { [key: string]: any }): void {
for (const [uri, report] of Object.entries(relatedDocuments)) {
try {
const translated = this._connection.bridge.translateBack({ uri }, { line: 0, character: 0 });
const model = translated.textModel;
if (!model || model.isDisposed()) {
continue;
}
if (report.kind === 'full') {
const markers = report.items.map((diagnostic: Diagnostic) => toDiagnosticMarker(diagnostic));
monaco.editor.setModelMarkers(model, this._markerOwner, markers);
}
} catch (error) {
// Model not found - this is normal
console.debug(`Could not set related diagnostics for ${uri}:`, error);
}
}
}
override dispose(): void {
if (this._updateHandle !== undefined) {
clearTimeout(this._updateHandle);
this._updateHandle = undefined;
}
super.dispose();
}
}

View file

@ -1,58 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentHighlightRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoDocumentHighlightKind } from './common';
export class LspDocumentHighlightFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
documentHighlight: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentDocumentHighlight, true, capability => {
return monaco.languages.registerDocumentHighlightProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDocumentHighlightProvider(this._connection, capability),
);
}));
}
}
class LspDocumentHighlightProvider implements monaco.languages.DocumentHighlightProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentHighlightRegistrationOptions,
) { }
async provideDocumentHighlights(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.DocumentHighlight[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentDocumentHighlight({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
return result.map(highlight => ({
range: this._client.bridge.translateBackRange(translated.textDocument, highlight.range).range,
kind: toMonacoDocumentHighlightKind(highlight.kind),
}));
}
}

View file

@ -1,71 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentLinkRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspDocumentLinkFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
documentLink: {
dynamicRegistration: true,
tooltipSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentDocumentLink, true, capability => {
return monaco.languages.registerLinkProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDocumentLinkProvider(this._connection, capability),
);
}));
}
}
class LspDocumentLinkProvider implements monaco.languages.LinkProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentLinkRegistrationOptions,
) { }
async provideLinks(
model: monaco.editor.ITextModel,
token: monaco.CancellationToken
): Promise<monaco.languages.ILinksList | null> {
const translated = this._client.bridge.translate(model, new monaco.Position(1, 1));
const result = await this._client.server.textDocumentDocumentLink({
textDocument: translated.textDocument,
});
if (!result) {
return null;
}
return {
links: result.map(link => ({
range: this._client.bridge.translateBackRange(translated.textDocument, link.range).range,
url: link.target,
tooltip: link.tooltip,
})),
};
}
async resolveLink(
link: monaco.languages.ILink,
token: monaco.CancellationToken
): Promise<monaco.languages.ILink> {
if (!this._capabilities.resolveProvider) {
return link;
}
// TODO: Implement resolve
return link;
}
}

View file

@ -1,101 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentSymbolRegistrationOptions, DocumentSymbol, SymbolInformation, SymbolTag } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { lspSymbolKindToMonacoSymbolKind, toMonacoSymbolKind, toMonacoSymbolTag } from './common';
export class LspDocumentSymbolFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
documentSymbol: {
dynamicRegistration: true,
hierarchicalDocumentSymbolSupport: true,
symbolKind: {
valueSet: Array.from(lspSymbolKindToMonacoSymbolKind.keys()),
},
tagSupport: {
valueSet: [SymbolTag.Deprecated],
},
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentDocumentSymbol, true, capability => {
return monaco.languages.registerDocumentSymbolProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDocumentSymbolProvider(this._connection, capability),
);
}));
}
}
class LspDocumentSymbolProvider implements monaco.languages.DocumentSymbolProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentSymbolRegistrationOptions,
) { }
async provideDocumentSymbols(
model: monaco.editor.ITextModel,
token: monaco.CancellationToken
): Promise<monaco.languages.DocumentSymbol[] | null> {
const translated = this._client.bridge.translate(model, new monaco.Position(1, 1));
const result = await this._client.server.textDocumentDocumentSymbol({
textDocument: translated.textDocument,
});
if (!result) {
return null;
}
if (Array.isArray(result) && result.length > 0) {
if ('location' in result[0]) {
// SymbolInformation[]
return (result as SymbolInformation[]).map(symbol => toMonacoSymbolInformation(symbol, this._client));
} else {
// DocumentSymbol[]
return (result as DocumentSymbol[]).map(symbol => toMonacoDocumentSymbol(symbol, this._client, translated.textDocument));
}
}
return [];
}
}
function toMonacoDocumentSymbol(
symbol: DocumentSymbol,
client: LspConnection,
textDocument: { uri: string }
): monaco.languages.DocumentSymbol {
return {
name: symbol.name,
detail: symbol.detail || '',
kind: toMonacoSymbolKind(symbol.kind),
tags: symbol.tags?.map(tag => toMonacoSymbolTag(tag)).filter((t): t is monaco.languages.SymbolTag => t !== undefined) || [],
range: client.bridge.translateBackRange(textDocument, symbol.range).range,
selectionRange: client.bridge.translateBackRange(textDocument, symbol.selectionRange).range,
children: symbol.children?.map(child => toMonacoDocumentSymbol(child, client, textDocument)) || [],
};
}
function toMonacoSymbolInformation(
symbol: SymbolInformation,
client: LspConnection
): monaco.languages.DocumentSymbol {
return {
name: symbol.name,
detail: '',
kind: toMonacoSymbolKind(symbol.kind),
tags: symbol.tags?.map(tag => toMonacoSymbolTag(tag)).filter((t): t is monaco.languages.SymbolTag => t !== undefined) || [],
range: client.bridge.translateBackRange({ uri: symbol.location.uri }, symbol.location.range).range,
selectionRange: client.bridge.translateBackRange({ uri: symbol.location.uri }, symbol.location.range).range,
children: [],
};
}

View file

@ -1,63 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, FoldingRangeRegistrationOptions, FoldingRangeKind } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoFoldingRangeKind } from './common';
export class LspFoldingRangeFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
foldingRange: {
dynamicRegistration: true,
rangeLimit: 5000,
lineFoldingOnly: false,
foldingRangeKind: {
valueSet: [FoldingRangeKind.Comment, FoldingRangeKind.Imports, FoldingRangeKind.Region],
},
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentFoldingRange, true, capability => {
return monaco.languages.registerFoldingRangeProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspFoldingRangeProvider(this._connection, capability),
);
}));
}
}
class LspFoldingRangeProvider implements monaco.languages.FoldingRangeProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: FoldingRangeRegistrationOptions,
) { }
async provideFoldingRanges(
model: monaco.editor.ITextModel,
context: monaco.languages.FoldingContext,
token: monaco.CancellationToken
): Promise<monaco.languages.FoldingRange[] | null> {
const translated = this._client.bridge.translate(model, new monaco.Position(1, 1));
const result = await this._client.server.textDocumentFoldingRange({
textDocument: translated.textDocument,
});
if (!result) {
return null;
}
return result.map(range => ({
start: range.startLine + 1,
end: range.endLine + 1,
kind: toMonacoFoldingRangeKind(range.kind),
}));
}
}

View file

@ -1,60 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentFormattingRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspFormattingFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
formatting: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentFormatting, true, capability => {
return monaco.languages.registerDocumentFormattingEditProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDocumentFormattingProvider(this._connection, capability),
);
}));
}
}
class LspDocumentFormattingProvider implements monaco.languages.DocumentFormattingEditProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentFormattingRegistrationOptions,
) { }
async provideDocumentFormattingEdits(
model: monaco.editor.ITextModel,
options: monaco.languages.FormattingOptions,
token: monaco.CancellationToken
): Promise<monaco.languages.TextEdit[] | null> {
const translated = this._client.bridge.translate(model, new monaco.Position(1, 1));
const result = await this._client.server.textDocumentFormatting({
textDocument: translated.textDocument,
options: {
tabSize: options.tabSize,
insertSpaces: options.insertSpaces,
},
});
if (!result) {
return null;
}
return result.map(edit => ({
range: this._client.bridge.translateBackRange(translated.textDocument, edit.range).range,
text: edit.newText,
}));
}
}

View file

@ -1,79 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, HoverRegistrationOptions, MarkupContent, MarkedString, MarkupKind } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspHoverFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
hover: {
dynamicRegistration: true,
contentFormat: [MarkupKind.Markdown, MarkupKind.PlainText],
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentHover, true, capability => {
return monaco.languages.registerHoverProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspHoverProvider(this._connection, capability),
);
}));
}
}
class LspHoverProvider implements monaco.languages.HoverProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: HoverRegistrationOptions,
) { }
async provideHover(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.Hover | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentHover({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result || !result.contents) {
return null;
}
return {
contents: toMonacoMarkdownString(result.contents),
range: result.range ? this._client.bridge.translateBackRange(translated.textDocument, result.range).range : undefined,
};
}
}
function toMonacoMarkdownString(
contents: MarkupContent | MarkedString | MarkedString[]
): monaco.IMarkdownString[] {
if (Array.isArray(contents)) {
return contents.map(c => toSingleMarkdownString(c));
}
return [toSingleMarkdownString(contents)];
}
function toSingleMarkdownString(content: MarkupContent | MarkedString): monaco.IMarkdownString {
if (typeof content === 'string') {
return { value: content, isTrusted: true };
}
if ('kind' in content) {
// MarkupContent
return { value: content.value, isTrusted: true };
}
// MarkedString with language
return { value: `\`\`\`${content.language}\n${content.value}\n\`\`\``, isTrusted: true };
}

View file

@ -1,60 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, ImplementationRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoLocation } from "./common";
export class LspImplementationFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
implementation: {
dynamicRegistration: true,
linkSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentImplementation, true, capability => {
return monaco.languages.registerImplementationProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspImplementationProvider(this._connection, capability),
);
}));
}
}
class LspImplementationProvider implements monaco.languages.ImplementationProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: ImplementationRegistrationOptions,
) { }
async provideImplementation(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.Definition | monaco.languages.LocationLink[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentImplementation({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
if (Array.isArray(result)) {
return result.map(loc => toMonacoLocation(loc, this._client));
}
return toMonacoLocation(result, this._client);
}
}

View file

@ -1,212 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, InlayHintRegistrationOptions, InlayHint, MarkupContent, api } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { assertTargetTextModel } from '../ITextModelBridge';
import { toMonacoCommand, toMonacoInlayHintKind } from './common';
export class LspInlayHintsFeature extends Disposable {
private readonly _providers = new Set<LspInlayHintsProvider>();
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
inlayHint: {
dynamicRegistration: true,
resolveSupport: {
properties: ['tooltip', 'textEdits', 'label.tooltip', 'label.location', 'label.command'],
},
}
},
workspace: {
inlayHint: {
refreshSupport: true,
}
}
}));
this._register(this._connection.connection.registerRequestHandler(api.client.workspaceInlayHintRefresh, async () => {
// Fire onDidChangeInlayHints for all providers
for (const provider of this._providers) {
provider.refresh();
}
return { ok: null };
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentInlayHint, true, capability => {
const provider = new LspInlayHintsProvider(this._connection, capability);
this._providers.add(provider);
const disposable = monaco.languages.registerInlayHintsProvider(
toMonacoLanguageSelector(capability.documentSelector),
provider,
);
return {
dispose: () => {
this._providers.delete(provider);
disposable.dispose();
}
};
}));
}
}
interface ExtendedInlayHint extends monaco.languages.InlayHint {
_lspInlayHint: InlayHint;
_targetUri: string;
}
class LspInlayHintsProvider implements monaco.languages.InlayHintsProvider {
private readonly _onDidChangeInlayHints = new monaco.Emitter<void>();
public readonly onDidChangeInlayHints = this._onDidChangeInlayHints.event;
public readonly resolveInlayHint;
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: InlayHintRegistrationOptions,
) {
if (_capabilities.resolveProvider) {
this.resolveInlayHint = async (hint: ExtendedInlayHint, token: monaco.CancellationToken): Promise<monaco.languages.InlayHint> => {
const resolved = await this._client.server.inlayHintResolve(hint._lspInlayHint);
if (resolved.tooltip) {
hint.tooltip = toMonacoTooltip(resolved.tooltip);
}
if (resolved.label !== hint._lspInlayHint.label) {
hint.label = toLspInlayHintLabel(resolved.label);
}
if (resolved.textEdits) {
hint.textEdits = resolved.textEdits.map(edit => {
const translated = this._client.bridge.translateBackRange(
{ uri: hint._targetUri },
edit.range
);
return {
range: translated.range,
text: edit.newText,
};
});
}
return hint;
};
}
}
public refresh(): void {
this._onDidChangeInlayHints.fire();
}
async provideInlayHints(
model: monaco.editor.ITextModel,
range: monaco.Range,
token: monaco.CancellationToken
): Promise<monaco.languages.InlayHintList | null> {
const translated = this._client.bridge.translate(model, range.getStartPosition());
const result = await retryOnContentModified(async () =>
await this._client.server.textDocumentInlayHint({
textDocument: translated.textDocument,
range: this._client.bridge.translateRange(model, range),
})
);
if (!result) {
return null;
}
return {
hints: result.map(hint => {
const monacoHint: ExtendedInlayHint = {
label: toLspInlayHintLabel(hint.label),
position: assertTargetTextModel(
this._client.bridge.translateBack(translated.textDocument, hint.position),
model
).position,
kind: toMonacoInlayHintKind(hint.kind),
tooltip: toMonacoTooltip(hint.tooltip),
paddingLeft: hint.paddingLeft,
paddingRight: hint.paddingRight,
textEdits: hint.textEdits?.map(edit => ({
range: assertTargetTextModel(
this._client.bridge.translateBackRange(translated.textDocument, edit.range),
model
).range,
text: edit.newText,
})),
_lspInlayHint: hint,
_targetUri: translated.textDocument.uri,
};
return monacoHint;
}),
dispose: () => { },
};
}
}
async function retryOnContentModified<T>(cb: () => Promise<T>): Promise<T> {
const nRetries = 3;
for (let triesLeft = nRetries; ; triesLeft--) {
try {
return await cb();
} catch (e: any) {
if (e.message === 'content modified' && triesLeft > 0) {
continue;
}
throw e;
}
}
}
function toLspInlayHintLabel(label: string | any[]): string | monaco.languages.InlayHintLabelPart[] {
if (typeof label === 'string') {
return label;
}
return label.map(part => {
const monacoLabelPart: monaco.languages.InlayHintLabelPart = {
label: part.value,
tooltip: toMonacoTooltip(part.tooltip),
command: toMonacoCommand(part.command),
};
if (part.location) {
monacoLabelPart.location = {
uri: monaco.Uri.parse(part.location.uri),
range: new monaco.Range(
part.location.range.start.line + 1,
part.location.range.start.character + 1,
part.location.range.end.line + 1,
part.location.range.end.character + 1
),
};
}
return monacoLabelPart;
});
}
function toMonacoTooltip(tooltip: string | MarkupContent | undefined): string | monaco.IMarkdownString | undefined {
if (!tooltip) {
return undefined;
}
if (typeof tooltip === 'string') {
return tooltip;
}
return {
value: tooltip.value,
isTrusted: true,
};
}

View file

@ -1,71 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentOnTypeFormattingRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspOnTypeFormattingFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
onTypeFormatting: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentOnTypeFormatting, true, capability => {
return monaco.languages.registerOnTypeFormattingEditProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspOnTypeFormattingProvider(this._connection, capability),
);
}));
}
}
class LspOnTypeFormattingProvider implements monaco.languages.OnTypeFormattingEditProvider {
public readonly autoFormatTriggerCharacters: string[];
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentOnTypeFormattingRegistrationOptions,
) {
this.autoFormatTriggerCharacters = [
_capabilities.firstTriggerCharacter,
...(_capabilities.moreTriggerCharacter || [])
];
}
async provideOnTypeFormattingEdits(
model: monaco.editor.ITextModel,
position: monaco.Position,
ch: string,
options: monaco.languages.FormattingOptions,
token: monaco.CancellationToken
): Promise<monaco.languages.TextEdit[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentOnTypeFormatting({
textDocument: translated.textDocument,
position: translated.position,
ch,
options: {
tabSize: options.tabSize,
insertSpaces: options.insertSpaces,
},
});
if (!result) {
return null;
}
return result.map(edit => ({
range: this._client.bridge.translateBackRange(translated.textDocument, edit.range).range,
text: edit.newText,
}));
}
}

View file

@ -1,62 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, DocumentRangeFormattingRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspRangeFormattingFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
rangeFormatting: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentRangeFormatting, true, capability => {
return monaco.languages.registerDocumentRangeFormattingEditProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspDocumentRangeFormattingProvider(this._connection, capability),
);
}));
}
}
class LspDocumentRangeFormattingProvider implements monaco.languages.DocumentRangeFormattingEditProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: DocumentRangeFormattingRegistrationOptions,
) { }
async provideDocumentRangeFormattingEdits(
model: monaco.editor.ITextModel,
range: monaco.Range,
options: monaco.languages.FormattingOptions,
token: monaco.CancellationToken
): Promise<monaco.languages.TextEdit[] | null> {
const translated = this._client.bridge.translate(model, range.getStartPosition());
const result = await this._client.server.textDocumentRangeFormatting({
textDocument: translated.textDocument,
range: this._client.bridge.translateRange(model, range),
options: {
tabSize: options.tabSize,
insertSpaces: options.insertSpaces,
},
});
if (!result) {
return null;
}
return result.map(edit => ({
range: this._client.bridge.translateBackRange(translated.textDocument, edit.range).range,
text: edit.newText,
}));
}
}

View file

@ -1,64 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, ReferenceRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspReferencesFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
references: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentReferences, true, capability => {
return monaco.languages.registerReferenceProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspReferenceProvider(this._connection, capability),
);
}));
}
}
class LspReferenceProvider implements monaco.languages.ReferenceProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: ReferenceRegistrationOptions,
) { }
async provideReferences(
model: monaco.editor.ITextModel,
position: monaco.Position,
context: monaco.languages.ReferenceContext,
token: monaco.CancellationToken
): Promise<monaco.languages.Location[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentReferences({
textDocument: translated.textDocument,
position: translated.position,
context: {
includeDeclaration: context.includeDeclaration,
},
});
if (!result) {
return null;
}
return result.map(loc => {
const translated = this._client.bridge.translateBackRange({ uri: loc.uri }, loc.range);
return {
uri: translated.textModel.uri,
range: translated.range,
};
});
}
}

View file

@ -1,142 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, RenameRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspRenameFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
rename: {
dynamicRegistration: true,
prepareSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentRename, true, capability => {
return monaco.languages.registerRenameProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspRenameProvider(this._connection, capability),
);
}));
}
}
class LspRenameProvider implements monaco.languages.RenameProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: RenameRegistrationOptions,
) { }
async provideRenameEdits(
model: monaco.editor.ITextModel,
position: monaco.Position,
newName: string,
token: monaco.CancellationToken
): Promise<monaco.languages.WorkspaceEdit | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentRename({
textDocument: translated.textDocument,
position: translated.position,
newName,
});
if (!result) {
return null;
}
return toMonacoWorkspaceEdit(result, this._client);
}
async resolveRenameLocation(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.RenameLocation | null> {
if (!this._capabilities.prepareProvider) {
return null;
}
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentPrepareRename({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
if ('range' in result && 'placeholder' in result) {
return {
range: this._client.bridge.translateBackRange(translated.textDocument, result.range).range,
text: result.placeholder,
};
} else if ('defaultBehavior' in result) {
return null;
} else if ('start' in result && 'end' in result) {
const range = this._client.bridge.translateBackRange(translated.textDocument, result).range;
return {
range,
text: model.getValueInRange(range),
};
}
return null;
}
}
function toMonacoWorkspaceEdit(
edit: any,
client: LspConnection
): monaco.languages.WorkspaceEdit {
const edits: monaco.languages.IWorkspaceTextEdit[] = [];
if (edit.changes) {
for (const uri in edit.changes) {
const textEdits = edit.changes[uri];
for (const textEdit of textEdits) {
const translated = client.bridge.translateBackRange({ uri }, textEdit.range);
edits.push({
resource: translated.textModel.uri,
versionId: undefined,
textEdit: {
range: translated.range,
text: textEdit.newText,
},
});
}
}
}
if (edit.documentChanges) {
for (const change of edit.documentChanges) {
if ('textDocument' in change) {
// TextDocumentEdit
const uri = change.textDocument.uri;
for (const textEdit of change.edits) {
const translated = client.bridge.translateBackRange({ uri }, textEdit.range);
edits.push({
resource: translated.textModel.uri,
versionId: change.textDocument.version,
textEdit: {
range: translated.range,
text: textEdit.newText,
},
});
}
}
// TODO: Handle CreateFile, RenameFile, DeleteFile
}
}
return { edits };
}

View file

@ -1,71 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, SelectionRangeRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspSelectionRangeFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
selectionRange: {
dynamicRegistration: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentSelectionRange, true, capability => {
return monaco.languages.registerSelectionRangeProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspSelectionRangeProvider(this._connection, capability),
);
}));
}
}
class LspSelectionRangeProvider implements monaco.languages.SelectionRangeProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: SelectionRangeRegistrationOptions,
) { }
async provideSelectionRanges(
model: monaco.editor.ITextModel,
positions: monaco.Position[],
token: monaco.CancellationToken
): Promise<monaco.languages.SelectionRange[][] | null> {
const translated = this._client.bridge.translate(model, positions[0]);
const result = await this._client.server.textDocumentSelectionRange({
textDocument: translated.textDocument,
positions: positions.map(pos => this._client.bridge.translate(model, pos).position),
});
if (!result) {
return null;
}
return result.map(selRange => this.convertSelectionRange(selRange, translated.textDocument));
}
private convertSelectionRange(
range: any,
textDocument: { uri: string }
): monaco.languages.SelectionRange[] {
const result: monaco.languages.SelectionRange[] = [];
let current = range;
while (current) {
result.push({
range: this._client.bridge.translateBackRange(textDocument, current.range).range,
});
current = current.parent;
}
return result;
}
}

View file

@ -1,130 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, SemanticTokensRegistrationOptions, TokenFormat } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
export class LspSemanticTokensFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
semanticTokens: {
dynamicRegistration: true,
requests: {
range: true,
full: {
delta: true,
},
},
tokenTypes: [
'namespace', 'type', 'class', 'enum', 'interface', 'struct',
'typeParameter', 'parameter', 'variable', 'property', 'enumMember',
'event', 'function', 'method', 'macro', 'keyword', 'modifier',
'comment', 'string', 'number', 'regexp', 'operator', 'decorator'
],
tokenModifiers: [
'declaration', 'definition', 'readonly', 'static', 'deprecated',
'abstract', 'async', 'modification', 'documentation', 'defaultLibrary'
],
formats: [TokenFormat.Relative],
overlappingTokenSupport: false,
multilineTokenSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentSemanticTokensFull, true, capability => {
return monaco.languages.registerDocumentSemanticTokensProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspSemanticTokensProvider(this._connection, capability),
);
}));
}
}
class LspSemanticTokensProvider implements monaco.languages.DocumentSemanticTokensProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: SemanticTokensRegistrationOptions,
) { }
getLegend(): monaco.languages.SemanticTokensLegend {
return {
tokenTypes: this._capabilities.legend.tokenTypes,
tokenModifiers: this._capabilities.legend.tokenModifiers,
};
}
releaseDocumentSemanticTokens(resultId: string | undefined): void {
// Monaco will call this when it's done with a result
// We can potentially notify the server if needed
}
async provideDocumentSemanticTokens(
model: monaco.editor.ITextModel,
lastResultId: string | null,
token: monaco.CancellationToken
): Promise<monaco.languages.SemanticTokens | monaco.languages.SemanticTokensEdits | null> {
const translated = this._client.bridge.translate(model, model.getPositionAt(0));
// Try delta request if we have a previous result and server supports it
const full = this._capabilities.full;
if (lastResultId && full && typeof full === 'object' && full.delta) {
const deltaResult = await this._client.server.textDocumentSemanticTokensFullDelta({
textDocument: translated.textDocument,
previousResultId: lastResultId,
});
if (!deltaResult) {
return null;
}
// Check if it's a delta or full result
if ('edits' in deltaResult) {
// It's a delta
return {
resultId: deltaResult.resultId,
edits: deltaResult.edits.map(edit => ({
start: edit.start,
deleteCount: edit.deleteCount,
data: edit.data ? new Uint32Array(edit.data) : undefined,
})),
};
} else {
// It's a full result
return {
resultId: deltaResult.resultId,
data: new Uint32Array(deltaResult.data),
};
}
}
// Full request
const result = await this._client.server.textDocumentSemanticTokensFull({
textDocument: translated.textDocument,
});
if (!result) {
return null;
}
return {
resultId: result.resultId,
data: new Uint32Array(result.data),
};
}
async provideDocumentSemanticTokensEdits?(
model: monaco.editor.ITextModel,
previousResultId: string,
token: monaco.CancellationToken
): Promise<monaco.languages.SemanticTokens | monaco.languages.SemanticTokensEdits | null> {
// This method is called when Monaco wants to use delta updates
// We can delegate to provideDocumentSemanticTokens which handles both
return this.provideDocumentSemanticTokens(model, previousResultId, token);
}
}

View file

@ -1,101 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, SignatureHelpRegistrationOptions, MarkupContent, MarkupKind } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toLspSignatureHelpTriggerKind } from './common';
export class LspSignatureHelpFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
signatureHelp: {
dynamicRegistration: true,
contextSupport: true,
signatureInformation: {
documentationFormat: [MarkupKind.Markdown, MarkupKind.PlainText],
parameterInformation: {
labelOffsetSupport: true,
},
activeParameterSupport: true,
}
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentSignatureHelp, true, capability => {
return monaco.languages.registerSignatureHelpProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspSignatureHelpProvider(this._connection, capability),
);
}));
}
}
class LspSignatureHelpProvider implements monaco.languages.SignatureHelpProvider {
public readonly signatureHelpTriggerCharacters?: readonly string[];
public readonly signatureHelpRetriggerCharacters?: readonly string[];
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: SignatureHelpRegistrationOptions,
) {
this.signatureHelpTriggerCharacters = _capabilities.triggerCharacters;
this.signatureHelpRetriggerCharacters = _capabilities.retriggerCharacters;
}
async provideSignatureHelp(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken,
context: monaco.languages.SignatureHelpContext
): Promise<monaco.languages.SignatureHelpResult | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentSignatureHelp({
textDocument: translated.textDocument,
position: translated.position,
context: {
triggerKind: toLspSignatureHelpTriggerKind(context.triggerKind),
triggerCharacter: context.triggerCharacter,
isRetrigger: context.isRetrigger,
},
});
if (!result) {
return null;
}
return {
value: {
signatures: result.signatures.map(sig => ({
label: sig.label,
documentation: toMonacoDocumentation(sig.documentation),
parameters: sig.parameters?.map(param => ({
label: param.label,
documentation: toMonacoDocumentation(param.documentation),
})) || [],
activeParameter: sig.activeParameter,
})),
activeSignature: result.activeSignature || 0,
activeParameter: result.activeParameter || 0,
},
dispose: () => { },
};
}
}
function toMonacoDocumentation(
doc: string | MarkupContent | undefined
): string | monaco.IMarkdownString | undefined {
if (!doc) return undefined;
if (typeof doc === 'string') return doc;
return {
value: doc.value,
isTrusted: true,
};
}

View file

@ -1,60 +0,0 @@
import * as monaco from 'monaco-editor-core';
import { capabilities, TypeDefinitionRegistrationOptions } from '../../../src/types';
import { Disposable } from '../../utils';
import { LspConnection } from '../LspConnection';
import { toMonacoLanguageSelector } from './common';
import { toMonacoLocation } from "./common";
export class LspTypeDefinitionFeature extends Disposable {
constructor(
private readonly _connection: LspConnection,
) {
super();
this._register(this._connection.capabilities.addStaticClientCapabilities({
textDocument: {
typeDefinition: {
dynamicRegistration: true,
linkSupport: true,
}
}
}));
this._register(this._connection.capabilities.registerCapabilityHandler(capabilities.textDocumentTypeDefinition, true, capability => {
return monaco.languages.registerTypeDefinitionProvider(
toMonacoLanguageSelector(capability.documentSelector),
new LspTypeDefinitionProvider(this._connection, capability),
);
}));
}
}
class LspTypeDefinitionProvider implements monaco.languages.TypeDefinitionProvider {
constructor(
private readonly _client: LspConnection,
private readonly _capabilities: TypeDefinitionRegistrationOptions,
) { }
async provideTypeDefinition(
model: monaco.editor.ITextModel,
position: monaco.Position,
token: monaco.CancellationToken
): Promise<monaco.languages.Definition | monaco.languages.LocationLink[] | null> {
const translated = this._client.bridge.translate(model, position);
const result = await this._client.server.textDocumentTypeDefinition({
textDocument: translated.textDocument,
position: translated.position,
});
if (!result) {
return null;
}
if (Array.isArray(result)) {
return result.map(loc => toMonacoLocation(loc, this._client));
}
return toMonacoLocation(result, this._client);
}
}

View file

@ -1,401 +0,0 @@
import * as monaco from 'monaco-editor-core';
import {
CodeActionKind,
CodeActionTriggerKind,
Command,
CompletionItemKind,
CompletionItemTag,
CompletionTriggerKind,
Diagnostic,
DiagnosticSeverity,
DiagnosticTag,
DocumentHighlightKind,
DocumentSelector,
FoldingRangeKind,
InlayHintKind,
InsertTextFormat,
Location,
LocationLink,
SignatureHelpTriggerKind,
SymbolKind,
SymbolTag,
} from '../../../src/types';
import { LspConnection } from '../LspConnection';
// ============================================================================
// Code Action Kind
// ============================================================================
export const lspCodeActionKindToMonacoCodeActionKind = new Map<CodeActionKind, string>([
[CodeActionKind.Empty, ''],
[CodeActionKind.QuickFix, 'quickfix'],
[CodeActionKind.Refactor, 'refactor'],
[CodeActionKind.RefactorExtract, 'refactor.extract'],
[CodeActionKind.RefactorInline, 'refactor.inline'],
[CodeActionKind.RefactorRewrite, 'refactor.rewrite'],
[CodeActionKind.Source, 'source'],
[CodeActionKind.SourceOrganizeImports, 'source.organizeImports'],
[CodeActionKind.SourceFixAll, 'source.fixAll'],
]);
export function toMonacoCodeActionKind(kind: CodeActionKind | undefined): string | undefined {
if (!kind) {
return undefined;
}
return lspCodeActionKindToMonacoCodeActionKind.get(kind) ?? kind;
}
// ============================================================================
// Code Action Trigger Kind
// ============================================================================
export const monacoCodeActionTriggerTypeToLspCodeActionTriggerKind = new Map<monaco.languages.CodeActionTriggerType, CodeActionTriggerKind>([
[monaco.languages.CodeActionTriggerType.Invoke, CodeActionTriggerKind.Invoked],
[monaco.languages.CodeActionTriggerType.Auto, CodeActionTriggerKind.Automatic],
]);
export function toLspCodeActionTriggerKind(monacoTrigger: monaco.languages.CodeActionTriggerType): CodeActionTriggerKind {
return monacoCodeActionTriggerTypeToLspCodeActionTriggerKind.get(monacoTrigger) ?? CodeActionTriggerKind.Invoked;
}
// ============================================================================
// Completion Item Kind
// ============================================================================
export const lspCompletionItemKindToMonacoCompletionItemKind = new Map<CompletionItemKind, monaco.languages.CompletionItemKind>([
[CompletionItemKind.Text, monaco.languages.CompletionItemKind.Text],
[CompletionItemKind.Method, monaco.languages.CompletionItemKind.Method],
[CompletionItemKind.Function, monaco.languages.CompletionItemKind.Function],
[CompletionItemKind.Constructor, monaco.languages.CompletionItemKind.Constructor],
[CompletionItemKind.Field, monaco.languages.CompletionItemKind.Field],
[CompletionItemKind.Variable, monaco.languages.CompletionItemKind.Variable],
[CompletionItemKind.Class, monaco.languages.CompletionItemKind.Class],
[CompletionItemKind.Interface, monaco.languages.CompletionItemKind.Interface],
[CompletionItemKind.Module, monaco.languages.CompletionItemKind.Module],
[CompletionItemKind.Property, monaco.languages.CompletionItemKind.Property],
[CompletionItemKind.Unit, monaco.languages.CompletionItemKind.Unit],
[CompletionItemKind.Value, monaco.languages.CompletionItemKind.Value],
[CompletionItemKind.Enum, monaco.languages.CompletionItemKind.Enum],
[CompletionItemKind.Keyword, monaco.languages.CompletionItemKind.Keyword],
[CompletionItemKind.Snippet, monaco.languages.CompletionItemKind.Snippet],
[CompletionItemKind.Color, monaco.languages.CompletionItemKind.Color],
[CompletionItemKind.File, monaco.languages.CompletionItemKind.File],
[CompletionItemKind.Reference, monaco.languages.CompletionItemKind.Reference],
[CompletionItemKind.Folder, monaco.languages.CompletionItemKind.Folder],
[CompletionItemKind.EnumMember, monaco.languages.CompletionItemKind.EnumMember],
[CompletionItemKind.Constant, monaco.languages.CompletionItemKind.Constant],
[CompletionItemKind.Struct, monaco.languages.CompletionItemKind.Struct],
[CompletionItemKind.Event, monaco.languages.CompletionItemKind.Event],
[CompletionItemKind.Operator, monaco.languages.CompletionItemKind.Operator],
[CompletionItemKind.TypeParameter, monaco.languages.CompletionItemKind.TypeParameter],
]);
export function toMonacoCompletionItemKind(kind: CompletionItemKind | undefined): monaco.languages.CompletionItemKind {
if (!kind) {
return monaco.languages.CompletionItemKind.Text;
}
return lspCompletionItemKindToMonacoCompletionItemKind.get(kind) ?? monaco.languages.CompletionItemKind.Text;
}
// ============================================================================
// Completion Item Tag
// ============================================================================
export const lspCompletionItemTagToMonacoCompletionItemTag = new Map<CompletionItemTag, monaco.languages.CompletionItemTag>([
[CompletionItemTag.Deprecated, monaco.languages.CompletionItemTag.Deprecated],
]);
export function toMonacoCompletionItemTag(tag: CompletionItemTag): monaco.languages.CompletionItemTag | undefined {
return lspCompletionItemTagToMonacoCompletionItemTag.get(tag);
}
// ============================================================================
// Completion Trigger Kind
// ============================================================================
export const monacoCompletionTriggerKindToLspCompletionTriggerKind = new Map<monaco.languages.CompletionTriggerKind, CompletionTriggerKind>([
[monaco.languages.CompletionTriggerKind.Invoke, CompletionTriggerKind.Invoked],
[monaco.languages.CompletionTriggerKind.TriggerCharacter, CompletionTriggerKind.TriggerCharacter],
[monaco.languages.CompletionTriggerKind.TriggerForIncompleteCompletions, CompletionTriggerKind.TriggerForIncompleteCompletions],
]);
export function toLspCompletionTriggerKind(monacoKind: monaco.languages.CompletionTriggerKind): CompletionTriggerKind {
return monacoCompletionTriggerKindToLspCompletionTriggerKind.get(monacoKind) ?? CompletionTriggerKind.Invoked;
}
// ============================================================================
// Insert Text Format
// ============================================================================
export const lspInsertTextFormatToMonacoInsertTextRules = new Map<InsertTextFormat, monaco.languages.CompletionItemInsertTextRule>([
[InsertTextFormat.Snippet, monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet],
]);
export function toMonacoInsertTextRules(format: InsertTextFormat | undefined): monaco.languages.CompletionItemInsertTextRule | undefined {
if (!format) {
return undefined;
}
return lspInsertTextFormatToMonacoInsertTextRules.get(format);
}
// ============================================================================
// Symbol Kind
// ============================================================================
export const lspSymbolKindToMonacoSymbolKind = new Map<SymbolKind, monaco.languages.SymbolKind>([
[SymbolKind.File, monaco.languages.SymbolKind.File],
[SymbolKind.Module, monaco.languages.SymbolKind.Module],
[SymbolKind.Namespace, monaco.languages.SymbolKind.Namespace],
[SymbolKind.Package, monaco.languages.SymbolKind.Package],
[SymbolKind.Class, monaco.languages.SymbolKind.Class],
[SymbolKind.Method, monaco.languages.SymbolKind.Method],
[SymbolKind.Property, monaco.languages.SymbolKind.Property],
[SymbolKind.Field, monaco.languages.SymbolKind.Field],
[SymbolKind.Constructor, monaco.languages.SymbolKind.Constructor],
[SymbolKind.Enum, monaco.languages.SymbolKind.Enum],
[SymbolKind.Interface, monaco.languages.SymbolKind.Interface],
[SymbolKind.Function, monaco.languages.SymbolKind.Function],
[SymbolKind.Variable, monaco.languages.SymbolKind.Variable],
[SymbolKind.Constant, monaco.languages.SymbolKind.Constant],
[SymbolKind.String, monaco.languages.SymbolKind.String],
[SymbolKind.Number, monaco.languages.SymbolKind.Number],
[SymbolKind.Boolean, monaco.languages.SymbolKind.Boolean],
[SymbolKind.Array, monaco.languages.SymbolKind.Array],
[SymbolKind.Object, monaco.languages.SymbolKind.Object],
[SymbolKind.Key, monaco.languages.SymbolKind.Key],
[SymbolKind.Null, monaco.languages.SymbolKind.Null],
[SymbolKind.EnumMember, monaco.languages.SymbolKind.EnumMember],
[SymbolKind.Struct, monaco.languages.SymbolKind.Struct],
[SymbolKind.Event, monaco.languages.SymbolKind.Event],
[SymbolKind.Operator, monaco.languages.SymbolKind.Operator],
[SymbolKind.TypeParameter, monaco.languages.SymbolKind.TypeParameter],
]);
export function toMonacoSymbolKind(kind: SymbolKind): monaco.languages.SymbolKind {
return lspSymbolKindToMonacoSymbolKind.get(kind) ?? monaco.languages.SymbolKind.File;
}
// ============================================================================
// Symbol Tag
// ============================================================================
export const lspSymbolTagToMonacoSymbolTag = new Map<SymbolTag, monaco.languages.SymbolTag>([
[SymbolTag.Deprecated, monaco.languages.SymbolTag.Deprecated],
]);
export function toMonacoSymbolTag(tag: SymbolTag): monaco.languages.SymbolTag | undefined {
return lspSymbolTagToMonacoSymbolTag.get(tag);
}
// ============================================================================
// Document Highlight Kind
// ============================================================================
export const lspDocumentHighlightKindToMonacoDocumentHighlightKind = new Map<DocumentHighlightKind, monaco.languages.DocumentHighlightKind>([
[DocumentHighlightKind.Text, monaco.languages.DocumentHighlightKind.Text],
[DocumentHighlightKind.Read, monaco.languages.DocumentHighlightKind.Read],
[DocumentHighlightKind.Write, monaco.languages.DocumentHighlightKind.Write],
]);
export function toMonacoDocumentHighlightKind(kind: DocumentHighlightKind | undefined): monaco.languages.DocumentHighlightKind {
if (!kind) {
return monaco.languages.DocumentHighlightKind.Text;
}
return lspDocumentHighlightKindToMonacoDocumentHighlightKind.get(kind) ?? monaco.languages.DocumentHighlightKind.Text;
}
// ============================================================================
// Folding Range Kind
// ============================================================================
export const lspFoldingRangeKindToMonacoFoldingRangeKind = new Map<FoldingRangeKind, monaco.languages.FoldingRangeKind>([
[FoldingRangeKind.Comment, monaco.languages.FoldingRangeKind.Comment],
[FoldingRangeKind.Imports, monaco.languages.FoldingRangeKind.Imports],
[FoldingRangeKind.Region, monaco.languages.FoldingRangeKind.Region],
]);
export function toMonacoFoldingRangeKind(kind: FoldingRangeKind | undefined): monaco.languages.FoldingRangeKind | undefined {
if (!kind) {
return undefined;
}
return lspFoldingRangeKindToMonacoFoldingRangeKind.get(kind);
}
// ============================================================================
// Diagnostic Severity
// ============================================================================
export const monacoMarkerSeverityToLspDiagnosticSeverity = new Map<monaco.MarkerSeverity, DiagnosticSeverity>([
[monaco.MarkerSeverity.Error, DiagnosticSeverity.Error],
[monaco.MarkerSeverity.Warning, DiagnosticSeverity.Warning],
[monaco.MarkerSeverity.Info, DiagnosticSeverity.Information],
[monaco.MarkerSeverity.Hint, DiagnosticSeverity.Hint],
]);
export function toLspDiagnosticSeverity(severity: monaco.MarkerSeverity): DiagnosticSeverity {
return monacoMarkerSeverityToLspDiagnosticSeverity.get(severity) ?? DiagnosticSeverity.Error;
}
export const lspDiagnosticSeverityToMonacoMarkerSeverity = new Map<DiagnosticSeverity, monaco.MarkerSeverity>([
[DiagnosticSeverity.Error, monaco.MarkerSeverity.Error],
[DiagnosticSeverity.Warning, monaco.MarkerSeverity.Warning],
[DiagnosticSeverity.Information, monaco.MarkerSeverity.Info],
[DiagnosticSeverity.Hint, monaco.MarkerSeverity.Hint],
]);
export function toMonacoDiagnosticSeverity(severity: DiagnosticSeverity | undefined): monaco.MarkerSeverity {
if (!severity) {
return monaco.MarkerSeverity.Error;
}
return lspDiagnosticSeverityToMonacoMarkerSeverity.get(severity) ?? monaco.MarkerSeverity.Error;
}
// ============================================================================
// Diagnostic Tag
// ============================================================================
export const lspDiagnosticTagToMonacoMarkerTag = new Map<DiagnosticTag, monaco.MarkerTag>([
[DiagnosticTag.Unnecessary, monaco.MarkerTag.Unnecessary],
[DiagnosticTag.Deprecated, monaco.MarkerTag.Deprecated],
]);
export function toMonacoDiagnosticTag(tag: DiagnosticTag): monaco.MarkerTag | undefined {
return lspDiagnosticTagToMonacoMarkerTag.get(tag);
}
// ============================================================================
// Signature Help Trigger Kind
// ============================================================================
export const monacoSignatureHelpTriggerKindToLspSignatureHelpTriggerKind = new Map<monaco.languages.SignatureHelpTriggerKind, SignatureHelpTriggerKind>([
[monaco.languages.SignatureHelpTriggerKind.Invoke, SignatureHelpTriggerKind.Invoked],
[monaco.languages.SignatureHelpTriggerKind.TriggerCharacter, SignatureHelpTriggerKind.TriggerCharacter],
[monaco.languages.SignatureHelpTriggerKind.ContentChange, SignatureHelpTriggerKind.ContentChange],
]);
export function toLspSignatureHelpTriggerKind(monacoKind: monaco.languages.SignatureHelpTriggerKind): SignatureHelpTriggerKind {
return monacoSignatureHelpTriggerKindToLspSignatureHelpTriggerKind.get(monacoKind) ?? SignatureHelpTriggerKind.Invoked;
}
// ============================================================================
// Command
// ============================================================================
export function toMonacoCommand(command: Command | undefined): monaco.languages.Command | undefined {
if (!command) {
return undefined;
}
return {
id: command.command,
title: command.title,
arguments: command.arguments,
};
}
// ============================================================================
// Inlay Hint Kind
// ============================================================================
export const lspInlayHintKindToMonacoInlayHintKind = new Map<InlayHintKind, monaco.languages.InlayHintKind>([
[InlayHintKind.Type, monaco.languages.InlayHintKind.Type],
[InlayHintKind.Parameter, monaco.languages.InlayHintKind.Parameter],
]);
export function toMonacoInlayHintKind(kind: InlayHintKind | undefined): monaco.languages.InlayHintKind {
if (!kind) {
return monaco.languages.InlayHintKind.Type;
}
return lspInlayHintKindToMonacoInlayHintKind.get(kind) ?? monaco.languages.InlayHintKind.Type;
} export function toMonacoLocation(
location: Location | LocationLink,
client: LspConnection
): monaco.languages.Location | monaco.languages.LocationLink {
if ('targetUri' in location) {
// LocationLink
const translatedRange = client.bridge.translateBackRange({ uri: location.targetUri }, location.targetRange);
return {
uri: translatedRange.textModel.uri,
range: translatedRange.range,
originSelectionRange: location.originSelectionRange
? client.bridge.translateBackRange({ uri: location.targetUri }, location.originSelectionRange).range
: undefined,
targetSelectionRange: location.targetSelectionRange
? client.bridge.translateBackRange({ uri: location.targetUri }, location.targetSelectionRange).range
: undefined,
};
} else {
// Location
const translatedRange = client.bridge.translateBackRange({ uri: location.uri }, location.range);
return {
uri: translatedRange.textModel.uri,
range: translatedRange.range,
};
}
}
export function toMonacoLanguageSelector(s: DocumentSelector | null): monaco.languages.LanguageSelector {
if (!s || s.length === 0) {
return { language: '*' };
}
return s.map<monaco.languages.LanguageFilter>(s => {
if ('notebook' in s) {
if (typeof s.notebook === 'string') {
return { notebookType: s.notebook, language: s.language };
} else {
return { notebookType: s.notebook.notebookType, language: s.language, pattern: s.notebook.pattern, scheme: s.notebook.scheme };
}
} else {
return { language: s.language, pattern: s.pattern, scheme: s.scheme };
}
});
}
export function matchesDocumentSelector(model: monaco.editor.ITextModel, selector: DocumentSelector | null): boolean {
if (!selector) {
return true;
}
const languageId = model.getLanguageId();
const uri = model.uri.toString(true);
if (!selector || selector.length === 0) {
return true;
}
for (const filter of selector) {
if (filter.language && filter.language !== '*' && filter.language !== languageId) {
continue;
}
return true;
}
return false;
}
export function toDiagnosticMarker(diagnostic: Diagnostic): monaco.editor.IMarkerData {
const marker: monaco.editor.IMarkerData = {
severity: toMonacoDiagnosticSeverity(diagnostic.severity),
startLineNumber: diagnostic.range.start.line + 1,
startColumn: diagnostic.range.start.character + 1,
endLineNumber: diagnostic.range.end.line + 1,
endColumn: diagnostic.range.end.character + 1,
message: diagnostic.message,
source: diagnostic.source,
code: typeof diagnostic.code === 'string' ? diagnostic.code : diagnostic.code?.toString(),
};
if (diagnostic.tags) {
marker.tags = diagnostic.tags.map(tag => toMonacoDiagnosticTag(tag)).filter((tag): tag is monaco.MarkerTag => tag !== undefined);
}
if (diagnostic.relatedInformation) {
marker.relatedInformation = diagnostic.relatedInformation.map(info => ({
resource: monaco.Uri.parse(info.location.uri),
startLineNumber: info.location.range.start.line + 1,
startColumn: info.location.range.start.character + 1,
endLineNumber: info.location.range.end.line + 1,
endColumn: info.location.range.end.character + 1,
message: info.message,
}));
}
return marker;
}

View file

@ -1,5 +0,0 @@
import { MonacoLspClient } from './adapters/LspClient';
import { WebSocketTransport } from '@hediet/json-rpc-websocket';
import { createTransportToWorker, createTransportToIFrame } from '@hediet/json-rpc-browser';
export { MonacoLspClient, WebSocketTransport, createTransportToWorker, createTransportToIFrame };

File diff suppressed because it is too large Load diff

View file

@ -1,75 +0,0 @@
export interface IDisposable {
dispose(): void;
}
export class Disposable implements IDisposable {
static None = Object.freeze<IDisposable>({ dispose() { } });
private _store = new DisposableStore();
constructor() { }
public dispose(): void {
this._store.dispose();
}
protected _register<T extends IDisposable>(t: T): T {
if ((t as any) === this) {
throw new Error('Cannot register a disposable on itself!');
}
return this._store.add(t);
}
}
export class DisposableStore implements IDisposable {
static DISABLE_DISPOSED_WARNING = false;
private _toDispose = new Set<IDisposable>();
private _isDisposed = false;
public dispose(): void {
if (this._isDisposed) {
return;
}
this._isDisposed = true;
this.clear();
}
public clear(): void {
if (this._toDispose.size === 0) {
return;
}
try {
for (const item of this._toDispose) {
item.dispose();
}
} finally {
this._toDispose.clear();
}
}
public add<T extends IDisposable>(t: T): T {
if (!t) {
return t;
}
if ((t as any) === this) {
throw new Error('Cannot register a disposable on itself!');
}
if (this._isDisposed) {
if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
console.warn(
new Error(
'Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!'
).stack
);
}
} else {
this._toDispose.add(t);
}
return t;
}
}

3778
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,7 @@
{
"name": "monaco-editor",
"version": "0.55.1",
"vscodeRef": "86f5a62f058e3905f74a9fa65d04b2f3b533408e",
"version": "0.53.0",
"vscodeRef": "e296bdfe0313d571a6f58399b22afd199f6da454",
"private": true,
"description": "A browser based code editor",
"homepage": "https://github.com/microsoft/monaco-editor",
@ -14,49 +14,33 @@
"prettier": "prettier --write .",
"pretty-quick": "pretty-quick --staged",
"simpleserver": "ts-node ./build/simpleserver",
"package-for-smoketest": "npm run package-for-smoketest-webpack && npm run package-for-smoketest-esbuild && npm run package-for-smoketest-vite",
"package-for-smoketest-webpack": "ts-node ./test/smoke/package-webpack",
"package-for-smoketest-webpack-cross-origin": "ts-node ./test/smoke/package-webpack --cross-origin",
"package-for-smoketest-esbuild": "ts-node ./test/smoke/package-esbuild",
"package-for-smoketest-vite": "ts-node ./test/smoke/package-vite",
"smoketest": "playwright test --config=test/smoke/playwright.config.ts",
"smoketest-debug": "playwright test --config=test/smoke/playwright.config.ts --debug",
"smoketest-ui": "playwright test --config=test/smoke/playwright.config.ts --ui",
"smoketest-headed": "playwright test --config=test/smoke/playwright.config.ts --headed",
"smoketest": "node ./test/smoke/runner.js",
"smoketest-debug": "node ./test/smoke/runner.js --debug-tests",
"test": "ts-node ./build/check-samples",
"deps-all-remove": "ts-node ./build/npm/removeAll",
"deps-all-install": "ts-node ./build/npm/installAll",
"update-actions": "pin-github-action ./.github/workflows/website.yml",
"watch": "tsc -w -p ./src",
"build-all": "npm run build-lsp && npm run build-monaco-editor && npm run package-for-smoketest",
"build": "npm run build-lsp && npm run build-monaco-editor",
"build-languages": "ts-node ./build/build-languages",
"build-monaco-editor": "ts-node ./build/build-monaco-editor",
"build-lsp": "cd monaco-lsp-client && npm install && npm run build"
"build": "npm run build-languages && npm run build-monaco-editor"
},
"typings": "./esm/vs/editor/editor.main.d.ts",
"main": "./min/vs/editor/editor.main.js",
"typings": "./esm/vs/editor/editor.api.d.ts",
"module": "./esm/vs/editor/editor.main.js",
"exports": {
".": {
"types": "./esm/vs/editor/editor.main.d.ts",
"import": "./esm/vs/editor/editor.main.js",
"require": "./min/vs/editor/editor.main.js"
},
"./*": "./*"
},
"repository": {
"type": "git",
"url": "https://github.com/microsoft/monaco-editor"
},
"devDependencies": {
"@playwright/test": "^1.56.1",
"@rollup/plugin-alias": "^5.1.1",
"@rollup/plugin-node-resolve": "^16.0.2",
"@types/mocha": "^10.0.10",
"@playwright/test": "^1.53.2",
"@types/mocha": "^9.1.0",
"@types/shelljs": "^0.8.11",
"@types/trusted-types": "^1.0.6",
"@typescript/vfs": "^1.3.5",
"@vscode/monaco-lsp-client": "file:./monaco-lsp-client",
"chai": "^4.3.6",
"clean-css": "^5.2.4",
"css-loader": "^6.7.1",
@ -68,26 +52,19 @@
"husky": "^7.0.4",
"jsdom": "^19.0.0",
"jsonc-parser": "^3.0.0",
"mocha": "^11.7.4",
"monaco-editor-core": "^0.55.0-rc",
"mocha": "^9.2.0",
"monaco-editor-core": "^0.54.0-dev-20250926",
"parcel": "^2.7.0",
"pin-github-action": "^1.8.0",
"postcss-url": "^10.1.3",
"prettier": "^2.5.1",
"pretty-quick": "^3.1.3",
"requirejs": "^2.3.7",
"rollup": "^4.52.4",
"rollup-plugin-delete": "^3.0.1",
"rollup-plugin-dts": "^6.2.3",
"rollup-plugin-esbuild": "^6.2.1",
"rollup-plugin-import-css": "^4.0.2",
"rollup-plugin-keep-css-imports": "^1.0.0",
"shelljs": "^0.8.5",
"style-loader": "^3.3.1",
"terser": "^5.14.2",
"ts-node": "^10.6.0",
"typescript": "^5.9.3",
"vite": "^7.1.11",
"typescript": "^5.4.5",
"vite": "^7.1.5",
"vscode-css-languageservice": "6.2.14",
"vscode-html-languageservice": "5.2.0",
"vscode-json-languageservice": "5.3.11",

File diff suppressed because it is too large Load diff

View file

@ -15,6 +15,6 @@
"@types/react-dom": "^17.0.11",
"@vitejs/plugin-react": "^1.1.4",
"typescript": "^5.4.5",
"vite": "^5.4.21"
"vite": "^2.9.17"
}
}

View file

@ -1,2 +0,0 @@
dist
src/**/*.js

View file

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>browser-esm-vite</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/main.ts"></script>
</body>
</html>

View file

@ -1,13 +0,0 @@
import './style.css';
import * as monaco from '../../src/editor/editor.main';
monaco.languages.register({ id: 'typescript' });
const tm = monaco.editor.createModel(`class Test {}`, 'typescript',
monaco.Uri.parse('file:///main.ts'));
const editor = monaco.editor.create(document.getElementById('root')!, {
model: tm,
language: 'typescript',
theme: 'vs-dark',
});

File diff suppressed because it is too large Load diff

View file

@ -1,14 +0,0 @@
{
"name": "browser-esm-vite",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"serve": "vite preview"
},
"dependencies": {},
"devDependencies": {
"monaco-editor": "^0.54.0",
"typescript": "^5.9.3",
"vite": "^7.1.11"
}
}

View file

@ -1,8 +0,0 @@
html,
body,
#root {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}

View file

@ -1,11 +0,0 @@
{
"compilerOptions": {
"target": "ESNext",
"strict": true,
"module": "ESNext",
"jsx": "react-jsx",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true
},
"include": ["./"]
}

View file

@ -1,19 +0,0 @@
import { defineConfig } from 'vite';
import { join } from 'path';
export default defineConfig({
server: {
fs: {
allow: ['../../', '../../../vscode']
}
},
resolve: {
alias: [{
find: 'monaco-editor-core/esm/vs',
replacement: join(__dirname, '../../../vscode/src/vs')
}, {
find: 'monaco-editor-core',
replacement: join(__dirname, '../../../vscode/src/vs/editor/editor.main.ts')
}],
}
});

View file

@ -721,7 +721,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -748,7 +747,6 @@
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",
@ -964,7 +962,6 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"caniuse-lite": "^1.0.30001737",
"electron-to-chromium": "^1.5.211",
@ -1262,34 +1259,35 @@
}
},
"node_modules/compression": {
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"version": "1.7.4",
"dev": true,
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"compressible": "~2.0.18",
"accepts": "~1.3.5",
"bytes": "3.0.0",
"compressible": "~2.0.16",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"on-headers": "~1.0.2",
"safe-buffer": "5.1.2",
"vary": "~1.1.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/compression/node_modules/negotiator": {
"version": "0.6.4",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
"integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
"node_modules/compression/node_modules/bytes": {
"version": "3.0.0",
"dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.6"
"node": ">= 0.8"
}
},
"node_modules/compression/node_modules/safe-buffer": {
"version": "5.1.2",
"dev": true,
"license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"dev": true,
@ -2186,21 +2184,6 @@
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@ -3177,12 +3160,11 @@
}
},
"node_modules/monaco-editor": {
"version": "0.53.0",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0.tgz",
"integrity": "sha512-0WNThgC6CMWNXXBxTbaYYcunj08iB5rnx4/G56UOPeL9UVIUGGHA1GR0EWIh9Ebabj7NpCRawQ5b0hfN1jQmYQ==",
"version": "0.53.0-dev-20250905",
"resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.53.0-dev-20250905.tgz",
"integrity": "sha512-dz8KD6kfIkQ9vQgUlUPBf24zFiDo/tPbI6HmDoBToURc3vXSlmxnR3NccA5NAaG/k3SrYZ+m4Pd+sjGtc7SV8w==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/trusted-types": "^1.0.6"
}
@ -3259,9 +3241,9 @@
}
},
"node_modules/node-forge": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
"integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"dev": true,
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
@ -3361,9 +3343,7 @@
}
},
"node_modules/on-headers": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"version": "1.0.2",
"dev": true,
"license": "MIT",
"engines": {
@ -3581,7 +3561,6 @@
"url": "https://github.com/sponsors/ai"
}
],
"peer": true,
"dependencies": {
"nanoid": "^3.3.6",
"picocolors": "^1.0.0",
@ -4013,7 +3992,6 @@
"version": "6.12.6",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
@ -4745,8 +4723,7 @@
"node_modules/tslib": {
"version": "2.4.0",
"dev": true,
"license": "0BSD",
"peer": true
"license": "0BSD"
},
"node_modules/tunnel": {
"version": "0.0.6",
@ -4788,7 +4765,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"dev": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -4918,7 +4894,6 @@
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.8",
@ -4966,7 +4941,6 @@
"version": "4.10.0",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",

View file

@ -81,6 +81,7 @@ async function buildAndTest() {
// Run checks and compilation
await run('npm run gulp hygiene', { cwd: vscodePath });
await run('npm run valid-layers-check', { cwd: vscodePath });
await run('npm run compile', { cwd: join(vscodePath, 'build') });
await run('npm run eslint', { cwd: vscodePath });
await run('npm run monaco-compile-check', { cwd: vscodePath });
await run('npm run --max_old_space_size=4095 compile', { cwd: vscodePath });
@ -88,8 +89,10 @@ async function buildAndTest() {
// Build editor distribution
await run('npm run gulp editor-distro', { cwd: vscodePath });
return; // To save CI time.
// Run browser tests
await run('npm run test-browser -- --browser chromium', { cwd: vscodePath });
await run('npm run test-browser --browser chromium', { cwd: vscodePath });
// TypeScript typings test
await run('mkdir typings-test', { cwd: vscodePath });
@ -107,4 +110,6 @@ async function buildAndTest() {
await run('npm test', { cwd: testMonacoDir });
}
//buildAndTest();
//prepareMonacoEditorCoreRelease('0.99.0', 'main');
prepareMonacoEditorCoreReleaseStableOrNightly();

View file

@ -43,32 +43,36 @@ async function prepareMonacoEditorRelease(monacoEditorCoreVersion: string) {
});
await group('Set Version & Update monaco-editor-core Version', async () => {
const packageJson = JSON.parse(await readFile(monacoEditorPackageJsonPath, { encoding: 'utf-8' })) as PackageJson;
const packageJson = JSON.parse(
await readFile(monacoEditorPackageJsonPath, { encoding: 'utf-8' })
) as PackageJson;
packageJson.version = monacoEditorCoreVersion;
packageJson.devDependencies['monaco-editor-core'] = monacoEditorCoreVersion;
await writeJsonFile(monacoEditorPackageJsonPath, packageJson);
});
await group('npm install to pick up monaco-editor-core', async () => {
await run('npm install', { cwd: rootPath });
});
const monacoEditorCorePackageJson = JSON.parse(
await readFile(monacoEditorCorePackageJsonPath, { encoding: 'utf-8' })
) as PackageJson;
await group('Pick up monaco-editor-core dependencies for CVE tracking', async () => {
const packageJson = JSON.parse(await readFile(monacoEditorPackageJsonPath, { encoding: 'utf-8' })) as PackageJson;
const monacoEditorCorePackageJson = JSON.parse(await readFile(monacoEditorCorePackageJsonPath, { encoding: 'utf-8' })) as PackageJson;
if (monacoEditorCorePackageJson.dependencies) {
if (!packageJson.dependencies) {
packageJson.dependencies = {};
}
objectMergeThrowIfSet(
packageJson.dependencies,
monacoEditorCorePackageJson.dependencies,
'dependencies'
);
}
await writeJsonFile(monacoEditorPackageJsonPath, packageJson);
});
await group('npm install to pick up monaco-editor-core', async () => {
await run('npm install', { cwd: rootPath });
});
await group('Setting vscode commitId from monaco-editor-core', async () => {
const monacoEditorCorePackageJson = JSON.parse(
await readFile(monacoEditorCorePackageJsonPath, { encoding: 'utf-8' })

View file

@ -54,7 +54,6 @@ export async function gitShallowClone(
await run(`git fetch --depth 1 origin ${ref}`, options);
await run(`git checkout ${ref}`, options);
const commitId = await gitCommitId(targetPath);
console.log(`Cloned ${repositoryUrl} (${commitId}) to ${targetPath}`);
return { commitId };
}

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { languages, editor } from 'monaco-editor-core';
import { languages, editor } from '../fillers/monaco-editor-core';
interface ILang extends languages.ILanguageExtensionPoint {
loader: () => Promise<ILangImpl>;

View file

@ -5,9 +5,20 @@
import { registerLanguage } from '../_.contribution';
declare var AMD: any;
declare var require: any;
registerLanguage({
id: 'abap',
extensions: ['.abap'],
aliases: ['abap', 'ABAP'],
loader: () => import('./abap')
loader: () => {
if (AMD) {
return new Promise((resolve, reject) => {
require(['vs/basic-languages/abap/abap'], resolve, reject);
});
} else {
return import('./abap');
}
}
});

View file

@ -14,7 +14,7 @@
*
*--------------------------------------------------------------------------------------------*/
import type { languages } from 'monaco-editor-core';
import type { languages } from '../../fillers/monaco-editor-core';
export const conf: languages.LanguageConfiguration = {
comments: {

View file

@ -5,10 +5,21 @@
import { registerLanguage } from '../_.contribution';
declare var AMD: any;
declare var require: any;
registerLanguage({
id: 'apex',
extensions: ['.cls'],
aliases: ['Apex', 'apex'],
mimetypes: ['text/x-apex-source', 'text/x-apex'],
loader: () => import('./apex')
loader: () => {
if (AMD) {
return new Promise((resolve, reject) => {
require(['vs/basic-languages/apex/apex'], resolve, reject);
});
} else {
return import('./apex');
}
}
});

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { languages } from 'monaco-editor-core';
import type { languages } from '../../fillers/monaco-editor-core';
export const conf: languages.LanguageConfiguration = {
// the default separators except `@$`

View file

@ -5,9 +5,20 @@
import { registerLanguage } from '../_.contribution';
declare var AMD: any;
declare var require: any;
registerLanguage({
id: 'azcli',
extensions: ['.azcli'],
aliases: ['Azure CLI', 'azcli'],
loader: () => import('./azcli')
loader: () => {
if (AMD) {
return new Promise((resolve, reject) => {
require(['vs/basic-languages/azcli/azcli'], resolve, reject);
});
} else {
return import('./azcli');
}
}
});

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { languages } from 'monaco-editor-core';
import type { languages } from '../../fillers/monaco-editor-core';
export const conf: languages.LanguageConfiguration = {
comments: {

View file

@ -5,9 +5,20 @@
import { registerLanguage } from '../_.contribution';
declare var AMD: any;
declare var require: any;
registerLanguage({
id: 'bat',
extensions: ['.bat', '.cmd'],
aliases: ['Batch', 'bat'],
loader: () => import('./bat')
loader: () => {
if (AMD) {
return new Promise((resolve, reject) => {
require(['vs/basic-languages/bat/bat'], resolve, reject);
});
} else {
return import('./bat');
}
}
});

View file

@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import type { languages } from 'monaco-editor-core';
import type { languages } from '../../fillers/monaco-editor-core';
export const conf: languages.LanguageConfiguration = {
comments: {

View file

@ -5,9 +5,20 @@
import { registerLanguage } from '../_.contribution';
declare var AMD: any;
declare var require: any;
registerLanguage({
id: 'bicep',
extensions: ['.bicep'],
aliases: ['Bicep'],
loader: () => import('./bicep')
loader: () => {
if (AMD) {
return new Promise((resolve, reject) => {
require(['vs/basic-languages/bicep/bicep'], resolve, reject);
});
} else {
return import('./bicep');
}
}
});

Some files were not shown because too many files have changed in this diff Show more