mirror of
https://github.com/microsoft/monaco-editor.git
synced 2025-12-22 09:20:10 +01:00
Merge branch 'main' into fix-memory-leak-webpack-plugin
This commit is contained in:
commit
956a90ae86
283 changed files with 220819 additions and 193076 deletions
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
|
|
@ -74,13 +74,7 @@ jobs:
|
|||
run: npm run compile --prefix webpack-plugin
|
||||
|
||||
- name: Package using webpack plugin
|
||||
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
|
||||
run: npm run package-for-smoketest
|
||||
|
||||
# - name: Package using parcel
|
||||
# run: npm run package-for-smoketest-parcel --prefix test/smoke/parcel
|
||||
|
|
|
|||
6
.github/workflows/website.yml
vendored
6
.github/workflows/website.yml
vendored
|
|
@ -44,15 +44,15 @@ jobs:
|
|||
|
||||
- name: Install website node modules
|
||||
working-directory: website
|
||||
run: yarn install --frozen-lockfile
|
||||
run: npm ci
|
||||
|
||||
- name: Install most recent version of monaco-editor
|
||||
working-directory: website
|
||||
run: yarn add monaco-editor
|
||||
run: npm install monaco-editor
|
||||
|
||||
- name: Build website
|
||||
working-directory: website
|
||||
run: yarn run build
|
||||
run: npm run build
|
||||
|
||||
- name: Setup Pages
|
||||
uses: actions/configure-pages@v5
|
||||
|
|
|
|||
|
|
@ -1,4 +1,2 @@
|
|||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run pretty-quick
|
||||
|
|
|
|||
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
|
@ -12,5 +12,6 @@
|
|||
"typescript.tsdk": "./node_modules/typescript/lib",
|
||||
"git.branchProtection": ["main", "release/*"],
|
||||
"git.branchProtectionPrompt": "alwaysCommitToNewBranch",
|
||||
"git.branchRandomName.enable": true
|
||||
"git.branchRandomName.enable": true,
|
||||
"editor.formatOnSave": true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,10 @@
|
|||
# Monaco Editor Changelog
|
||||
|
||||
## [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.
|
||||
|
|
|
|||
|
|
@ -4,4 +4,5 @@ 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 });
|
||||
}
|
||||
|
|
|
|||
66
build/amd/rollup-types.config.mjs
Normal file
66
build/amd/rollup-types.config.mjs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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';
|
||||
|
||||
const root = join(import.meta.dirname, '../../');
|
||||
const outDir = join(import.meta.dirname, './out');
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} newExt
|
||||
*/
|
||||
function changeExt(filePath, newExt) {
|
||||
const idx = filePath.lastIndexOf('.');
|
||||
if (idx === -1) {
|
||||
return filePath + newExt;
|
||||
} else {
|
||||
return filePath.substring(0, idx) + newExt;
|
||||
}
|
||||
}
|
||||
|
||||
const mappedPaths = {
|
||||
[join(root, 'node_modules/monaco-editor-core/esm/')]: '.',
|
||||
[join(root, 'node_modules/')]: 'external/',
|
||||
[join(root, 'src/')]: 'vs/'
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
input: {
|
||||
types: join(import.meta.dirname, './src/types.ts')
|
||||
},
|
||||
output: {
|
||||
dir: outDir,
|
||||
format: 'es',
|
||||
preserveModules: false,
|
||||
entryFileNames: function (chunkInfo) {
|
||||
const moduleId = chunkInfo.facadeModuleId;
|
||||
if (moduleId) {
|
||||
for (const [key, val] of Object.entries(mappedPaths)) {
|
||||
if (moduleId.startsWith(key)) {
|
||||
const relativePath = moduleId.substring(key.length);
|
||||
return changeExt(join(val, relativePath), '.d.ts');
|
||||
}
|
||||
}
|
||||
}
|
||||
return '[name].d.ts';
|
||||
}
|
||||
},
|
||||
external: [/.*\.css/],
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
dts({
|
||||
compilerOptions: {
|
||||
stripInternal: true
|
||||
},
|
||||
includeExternal: ['monaco-editor-core', '@vscode/monaco-lsp-client']
|
||||
})
|
||||
]
|
||||
});
|
||||
|
|
@ -1,10 +1,30 @@
|
|||
/// @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)
|
||||
)
|
||||
);
|
||||
|
|
@ -12,6 +32,7 @@ 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)
|
||||
)
|
||||
);
|
||||
|
|
@ -19,6 +40,7 @@ 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)
|
||||
)
|
||||
);
|
||||
|
|
@ -26,17 +48,22 @@ 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) {
|
||||
function getWorkerBootstrapUrl(workerScriptUrl: string | URL) {
|
||||
if (typeof workerScriptUrl !== 'string') {
|
||||
workerScriptUrl = workerScriptUrl.toString();
|
||||
}
|
||||
const blob = new Blob(
|
||||
[
|
||||
[
|
||||
|
|
@ -54,11 +81,10 @@ function getWorkerBootstrapUrl(workerScriptUrl) {
|
|||
}
|
||||
|
||||
import 'vs/nls.messages-loader!';
|
||||
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';
|
||||
import * as monaco from '../../../src/editor/editor.main';
|
||||
export * from '../../../src/editor/editor.main';
|
||||
|
||||
globalThis.monaco = monaco;
|
||||
|
||||
const styleSheetUrl = require.toUrl('vs/editor/editor.main.css');
|
||||
|
||||
|
|
@ -66,5 +92,3 @@ const link = document.createElement('link');
|
|||
link.rel = 'stylesheet';
|
||||
link.href = styleSheetUrl;
|
||||
document.head.appendChild(link);
|
||||
|
||||
export * as m from 'monaco-editor-core';
|
||||
3
build/amd/src/types.ts
Normal file
3
build/amd/src/types.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import * as m from './editor.main';
|
||||
|
||||
export { m };
|
||||
|
|
@ -21,8 +21,8 @@ export default defineConfig(async (args) => {
|
|||
/** @type {import('vite').UserConfig} */
|
||||
return {
|
||||
base: './',
|
||||
define: {
|
||||
AMD: false
|
||||
resolve: {
|
||||
dedupe: ['monaco-editor-core']
|
||||
},
|
||||
build: {
|
||||
lib: {
|
||||
|
|
@ -30,7 +30,7 @@ export default defineConfig(async (args) => {
|
|||
entry: {
|
||||
...nlsEntries,
|
||||
'nls.messages-loader': resolve(__dirname, 'src/nls.messages-loader.js'),
|
||||
'editor/editor.main': resolve(__dirname, 'src/editor.main.js'),
|
||||
'editor/editor.main': resolve(__dirname, 'src/editor.main.ts'),
|
||||
'basic-languages/monaco.contribution': resolve(
|
||||
__dirname,
|
||||
'../../src/basic-languages/monaco.contribution.ts'
|
||||
|
|
|
|||
|
|
@ -1,127 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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
|
||||
|
|
@ -5,35 +5,27 @@
|
|||
|
||||
import path = require('path');
|
||||
import fs = require('fs');
|
||||
import {
|
||||
REPO_ROOT,
|
||||
readFiles,
|
||||
writeFiles,
|
||||
IFile,
|
||||
readFile,
|
||||
build,
|
||||
bundledFileHeader
|
||||
} from '../build/utils';
|
||||
import { removeDir } from '../build/fs';
|
||||
import { generateMetadata } from './releaseMetadata';
|
||||
import { REPO_ROOT, readFiles, writeFiles } from '../build/utils';
|
||||
import { generateEsmMetadataJsAndDTs } from './releaseMetadata';
|
||||
import { buildESM } from './esm/build.script';
|
||||
import { removeDir } from './fs';
|
||||
import { buildAmdMinDev } from './amd/build.script';
|
||||
import ts = require('typescript');
|
||||
|
||||
async function run() {
|
||||
removeDir(`out/monaco-editor`);
|
||||
|
||||
buildAmdMinDev();
|
||||
await buildESM();
|
||||
await buildAmdMinDev();
|
||||
|
||||
// esm folder
|
||||
ESM_release();
|
||||
// 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);
|
||||
})();
|
||||
|
||||
// monaco.d.ts, editor.api.d.ts
|
||||
releaseDTS();
|
||||
|
||||
// ThirdPartyNotices.txt
|
||||
releaseThirdPartyNotices();
|
||||
|
||||
// esm/metadata.d.ts, esm/metadata.js
|
||||
generateMetadata();
|
||||
createThirdPartyNoticesDotTxt();
|
||||
generateEsmMetadataJsAndDTs();
|
||||
|
||||
// package.json
|
||||
(() => {
|
||||
|
|
@ -61,305 +53,13 @@ generateMetadata();
|
|||
|
||||
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 releaseThirdPartyNotices() {
|
||||
function createThirdPartyNoticesDotTxt() {
|
||||
const tpn = readFiles('node_modules/monaco-editor-core/ThirdPartyNotices.txt', {
|
||||
base: 'node_modules/monaco-editor-core'
|
||||
})[0];
|
||||
|
|
@ -377,3 +77,5 @@ function releaseThirdPartyNotices() {
|
|||
|
||||
writeFiles([tpn], `out/monaco-editor`);
|
||||
}
|
||||
|
||||
run();
|
||||
|
|
|
|||
7
build/esm/build.script.ts
Normal file
7
build/esm/build.script.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
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 });
|
||||
}
|
||||
1
build/esm/rollup-plugin-keep-css-imports/.gitignore
vendored
Normal file
1
build/esm/rollup-plugin-keep-css-imports/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
!dist
|
||||
21
build/esm/rollup-plugin-keep-css-imports/LICENSE
Normal file
21
build/esm/rollup-plugin-keep-css-imports/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
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.
|
||||
2
build/esm/rollup-plugin-keep-css-imports/README.md
Normal file
2
build/esm/rollup-plugin-keep-css-imports/README.md
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
See https://www.npmjs.com/package/rollup-plugin-keep-css-imports.
|
||||
Compare index.mjs with index.original.mjs to see the patch.
|
||||
25
build/esm/rollup-plugin-keep-css-imports/dist/ImportUpdater.d.ts
vendored
Normal file
25
build/esm/rollup-plugin-keep-css-imports/dist/ImportUpdater.d.ts
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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 {};
|
||||
31
build/esm/rollup-plugin-keep-css-imports/dist/compileSass.d.ts
vendored
Normal file
31
build/esm/rollup-plugin-keep-css-imports/dist/compileSass.d.ts
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
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 {};
|
||||
3
build/esm/rollup-plugin-keep-css-imports/dist/constants.d.ts
vendored
Normal file
3
build/esm/rollup-plugin-keep-css-imports/dist/constants.d.ts
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
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]";
|
||||
52
build/esm/rollup-plugin-keep-css-imports/dist/helpers.d.ts
vendored
Normal file
52
build/esm/rollup-plugin-keep-css-imports/dist/helpers.d.ts
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
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;
|
||||
}>;
|
||||
4
build/esm/rollup-plugin-keep-css-imports/dist/index.d.ts
vendored
Normal file
4
build/esm/rollup-plugin-keep-css-imports/dist/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
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;
|
||||
487
build/esm/rollup-plugin-keep-css-imports/dist/index.mjs
vendored
Normal file
487
build/esm/rollup-plugin-keep-css-imports/dist/index.mjs
vendored
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
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 };
|
||||
489
build/esm/rollup-plugin-keep-css-imports/dist/index.original.mjs
vendored
Normal file
489
build/esm/rollup-plugin-keep-css-imports/dist/index.original.mjs
vendored
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
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 };
|
||||
110
build/esm/rollup-plugin-keep-css-imports/dist/types.d.ts
vendored
Normal file
110
build/esm/rollup-plugin-keep-css-imports/dist/types.d.ts
vendored
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
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 {};
|
||||
74
build/esm/rollup-plugin-keep-css-imports/package.json
Normal file
74
build/esm/rollup-plugin-keep-css-imports/package.json
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
67
build/esm/rollup-types.config.mjs
Normal file
67
build/esm/rollup-types.config.mjs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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";
|
||||
|
||||
const root = join(import.meta.dirname, '../../');
|
||||
const outDir = join(root, './out/monaco-editor/esm');
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} newExt
|
||||
* @returns {string}
|
||||
*/
|
||||
function changeExt(filePath, newExt) {
|
||||
const idx = filePath.lastIndexOf('.');
|
||||
if (idx === -1) {
|
||||
return filePath + newExt;
|
||||
} else {
|
||||
return filePath.substring(0, idx) + newExt;
|
||||
}
|
||||
}
|
||||
|
||||
const mappedPaths = {
|
||||
[join(root, 'node_modules/monaco-editor-core/esm/')]: '.',
|
||||
[join(root, 'node_modules/')]: 'external/',
|
||||
[join(root, 'src/')]: 'vs/',
|
||||
};
|
||||
|
||||
export default defineConfig({
|
||||
input: {
|
||||
entry: join(root, './src/editor/editor.main.ts'),
|
||||
},
|
||||
output: {
|
||||
dir: outDir,
|
||||
format: 'es',
|
||||
preserveModules: false,
|
||||
entryFileNames: function (chunkInfo) {
|
||||
const moduleId = chunkInfo.facadeModuleId;
|
||||
if (moduleId) {
|
||||
for (const [key, val] of Object.entries(mappedPaths)) {
|
||||
if (moduleId.startsWith(key)) {
|
||||
const relativePath = moduleId.substring(key.length);
|
||||
return changeExt(join(val, relativePath), '.d.ts');
|
||||
}
|
||||
}
|
||||
}
|
||||
return '[name].d.ts';
|
||||
},
|
||||
},
|
||||
external: [/.*\.css/],
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
dts({
|
||||
compilerOptions: {
|
||||
stripInternal: true,
|
||||
},
|
||||
includeExternal: ['monaco-editor-core', '@vscode/monaco-lsp-client']
|
||||
}),
|
||||
],
|
||||
});
|
||||
63
build/esm/rollup-url-to-module-plugin/index.mjs
Normal file
63
build/esm/rollup-url-to-module-plugin/index.mjs
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
109
build/esm/rollup.config.mjs
Normal file
109
build/esm/rollup.config.mjs
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { copyFileSync, mkdirSync } from 'fs';
|
||||
import { dirname } from 'path';
|
||||
|
||||
const root = join(import.meta.dirname, '../../');
|
||||
const outDir = join(root, './out/monaco-editor/esm');
|
||||
|
||||
/**
|
||||
* @param {string} filePath
|
||||
* @param {string} newExt
|
||||
* @returns {string}
|
||||
*/
|
||||
function changeExt(filePath, newExt) {
|
||||
const idx = filePath.lastIndexOf('.');
|
||||
if (idx === -1) {
|
||||
return filePath + newExt;
|
||||
} else {
|
||||
return filePath.substring(0, idx) + newExt;
|
||||
}
|
||||
}
|
||||
|
||||
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/',
|
||||
};
|
||||
|
||||
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'),
|
||||
},
|
||||
|
||||
output: {
|
||||
dir: outDir,
|
||||
format: 'es',
|
||||
|
||||
entryFileNames: function (chunkInfo) {
|
||||
const moduleId = chunkInfo.facadeModuleId;
|
||||
if (moduleId) {
|
||||
for (const [key, val] of Object.entries(mappedPaths)) {
|
||||
if (moduleId.startsWith(key)) {
|
||||
const relativePath = moduleId.substring(key.length);
|
||||
return changeExt(join(val, relativePath), '.js');
|
||||
}
|
||||
}
|
||||
}
|
||||
return '[name].js';
|
||||
},
|
||||
preserveModules: true,
|
||||
},
|
||||
|
||||
|
||||
plugins: [
|
||||
del({ targets: outDir, force: true }),
|
||||
|
||||
{
|
||||
name: 'copy-codicon-font',
|
||||
buildEnd() {
|
||||
const codiconSource = join(root, 'node_modules/monaco-editor-core/esm/vs/base/browser/ui/codicons/codicon/codicon.ttf');
|
||||
const codiconDest = join(outDir, 'vs/base/browser/ui/codicons/codicon/codicon.ttf');
|
||||
mkdirSync(dirname(codiconDest), { recursive: true });
|
||||
copyFileSync(codiconSource, codiconDest);
|
||||
}
|
||||
},
|
||||
|
||||
urlToEsmPlugin(),
|
||||
esbuild(),
|
||||
|
||||
keepCssImports({
|
||||
/**
|
||||
* @param {string} assetId
|
||||
*/
|
||||
outputPath: (assetId) => {
|
||||
for (const [key, val] of Object.entries(mappedPaths)) {
|
||||
if (assetId.startsWith(key)) {
|
||||
const relativePath = assetId.substring(key.length);
|
||||
return changeExt(join(outDir, val, relativePath), '.css');
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* 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;
|
||||
}
|
||||
|
|
@ -103,7 +103,7 @@ function getAdvancedLanguages(): Promise<
|
|||
}
|
||||
}
|
||||
|
||||
export function generateMetadata() {
|
||||
export function generateEsmMetadataJsAndDTs() {
|
||||
return Promise.all([getBasicLanguages(), getAdvancedLanguages()]).then(
|
||||
([basicLanguages, advancedLanguages]) => {
|
||||
basicLanguages.sort((a, b) => strcmp(a.entry, b.entry));
|
||||
|
|
|
|||
179
build/utils.ts
179
build/utils.ts
|
|
@ -5,190 +5,11 @@
|
|||
|
||||
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;
|
||||
|
|
|
|||
5
monaco-lsp-client/README.md
Normal file
5
monaco-lsp-client/README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# 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.
|
||||
687
monaco-lsp-client/generator/index.ts
Normal file
687
monaco-lsp-client/generator/index.ts
Normal file
|
|
@ -0,0 +1,687 @@
|
|||
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();
|
||||
}
|
||||
1564
monaco-lsp-client/package-lock.json
generated
Normal file
1564
monaco-lsp-client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
26
monaco-lsp-client/package.json
Normal file
26
monaco-lsp-client/package.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
33
monaco-lsp-client/rolldown.config.mjs
Normal file
33
monaco-lsp-client/rolldown.config.mjs
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
// @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
|
||||
})
|
||||
]
|
||||
});
|
||||
40
monaco-lsp-client/src/adapters/ITextModelBridge.ts
Normal file
40
monaco-lsp-client/src/adapters/ITextModelBridge.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
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;
|
||||
}
|
||||
254
monaco-lsp-client/src/adapters/LspCapabilitiesRegistry.ts
Normal file
254
monaco-lsp-client/src/adapters/LspCapabilitiesRegistry.ts
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
90
monaco-lsp-client/src/adapters/LspClient.ts
Normal file
90
monaco-lsp-client/src/adapters/LspClient.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
13
monaco-lsp-client/src/adapters/LspConnection.ts
Normal file
13
monaco-lsp-client/src/adapters/LspConnection.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
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,
|
||||
) { }
|
||||
}
|
||||
183
monaco-lsp-client/src/adapters/TextDocumentSynchronizer.ts
Normal file
183
monaco-lsp-client/src/adapters/TextDocumentSynchronizer.ts
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
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,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
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 };
|
||||
}
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
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,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,208 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
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),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
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: [],
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
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),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
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 };
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
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,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
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,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
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,
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
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 };
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
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,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
401
monaco-lsp-client/src/adapters/languageFeatures/common.ts
Normal file
401
monaco-lsp-client/src/adapters/languageFeatures/common.ts
Normal file
|
|
@ -0,0 +1,401 @@
|
|||
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;
|
||||
}
|
||||
|
||||
5
monaco-lsp-client/src/index.ts
Normal file
5
monaco-lsp-client/src/index.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
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 };
|
||||
7514
monaco-lsp-client/src/types.ts
Normal file
7514
monaco-lsp-client/src/types.ts
Normal file
File diff suppressed because it is too large
Load diff
75
monaco-lsp-client/src/utils.ts
Normal file
75
monaco-lsp-client/src/utils.ts
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
3745
package-lock.json
generated
3745
package-lock.json
generated
File diff suppressed because it is too large
Load diff
47
package.json
47
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "monaco-editor",
|
||||
"version": "0.53.0",
|
||||
"vscodeRef": "e296bdfe0313d571a6f58399b22afd199f6da454",
|
||||
"version": "0.54.0",
|
||||
"vscodeRef": "484fdf69b8509c1c9370d913b32e9f6d3a68cc99",
|
||||
"private": true,
|
||||
"description": "A browser based code editor",
|
||||
"homepage": "https://github.com/microsoft/monaco-editor",
|
||||
|
|
@ -14,33 +14,49 @@
|
|||
"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": "node ./test/smoke/runner.js",
|
||||
"smoketest-debug": "node ./test/smoke/runner.js --debug-tests",
|
||||
"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",
|
||||
"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-languages": "ts-node ./build/build-languages",
|
||||
"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-monaco-editor": "ts-node ./build/build-monaco-editor",
|
||||
"build": "npm run build-languages && npm run build-monaco-editor"
|
||||
"build-lsp": "cd monaco-lsp-client && npm install && npm run build"
|
||||
},
|
||||
"typings": "./esm/vs/editor/editor.api.d.ts",
|
||||
"main": "./min/vs/editor/editor.main.js",
|
||||
"module": "./esm/vs/editor/editor.main.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./esm/vs/editor/editor.api.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.53.2",
|
||||
"@types/mocha": "^9.1.0",
|
||||
"@playwright/test": "^1.56.1",
|
||||
"@rollup/plugin-alias": "^5.1.1",
|
||||
"@rollup/plugin-node-resolve": "^16.0.2",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@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",
|
||||
|
|
@ -52,19 +68,26 @@
|
|||
"husky": "^7.0.4",
|
||||
"jsdom": "^19.0.0",
|
||||
"jsonc-parser": "^3.0.0",
|
||||
"mocha": "^9.2.0",
|
||||
"monaco-editor-core": "^0.54.0-dev-20250926",
|
||||
"mocha": "^11.7.4",
|
||||
"monaco-editor-core": "^0.55.0-dev-20251008",
|
||||
"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.4.5",
|
||||
"vite": "^7.1.5",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.11",
|
||||
"vscode-css-languageservice": "6.2.14",
|
||||
"vscode-html-languageservice": "5.2.0",
|
||||
"vscode-json-languageservice": "5.3.11",
|
||||
|
|
|
|||
1733
samples/browser-esm-vite-react/package-lock.json
generated
1733
samples/browser-esm-vite-react/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,6 @@
|
|||
"@types/react-dom": "^17.0.11",
|
||||
"@vitejs/plugin-react": "^1.1.4",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^2.9.17"
|
||||
"vite": "^5.4.20"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,36 +43,32 @@ 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);
|
||||
});
|
||||
|
||||
const monacoEditorCorePackageJson = JSON.parse(
|
||||
await readFile(monacoEditorCorePackageJsonPath, { encoding: 'utf-8' })
|
||||
) as PackageJson;
|
||||
await group('npm install to pick up monaco-editor-core', async () => {
|
||||
await run('npm install', { cwd: rootPath });
|
||||
});
|
||||
|
||||
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' })
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { languages, editor } from '../fillers/monaco-editor-core';
|
||||
import { languages, editor } from 'monaco-editor-core';
|
||||
|
||||
interface ILang extends languages.ILanguageExtensionPoint {
|
||||
loader: () => Promise<ILangImpl>;
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'abap',
|
||||
extensions: ['.abap'],
|
||||
aliases: ['abap', 'ABAP'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/abap/abap'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./abap');
|
||||
}
|
||||
}
|
||||
loader: () => import('./abap')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
*
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,21 +5,10 @@
|
|||
|
||||
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: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/apex/apex'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./apex');
|
||||
}
|
||||
}
|
||||
loader: () => import('./apex')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
// the default separators except `@$`
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'azcli',
|
||||
extensions: ['.azcli'],
|
||||
aliases: ['Azure CLI', 'azcli'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/azcli/azcli'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./azcli');
|
||||
}
|
||||
}
|
||||
loader: () => import('./azcli')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'bat',
|
||||
extensions: ['.bat', '.cmd'],
|
||||
aliases: ['Batch', 'bat'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/bat/bat'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./bat');
|
||||
}
|
||||
}
|
||||
loader: () => import('./bat')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'bicep',
|
||||
extensions: ['.bicep'],
|
||||
aliases: ['Bicep'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/bicep/bicep'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./bicep');
|
||||
}
|
||||
}
|
||||
loader: () => import('./bicep')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
const bounded = (text: string) => `\\b${text}\\b`;
|
||||
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'cameligo',
|
||||
extensions: ['.mligo'],
|
||||
aliases: ['Cameligo'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/cameligo/cameligo'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./cameligo');
|
||||
}
|
||||
}
|
||||
loader: () => import('./cameligo')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'clojure',
|
||||
extensions: ['.clj', '.cljs', '.cljc', '.edn'],
|
||||
aliases: ['clojure', 'Clojure'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/clojure/clojure'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./clojure');
|
||||
}
|
||||
}
|
||||
loader: () => import('./clojure')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,21 +5,10 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'coffeescript',
|
||||
extensions: ['.coffee'],
|
||||
aliases: ['CoffeeScript', 'coffeescript', 'coffee'],
|
||||
mimetypes: ['text/x-coffeescript', 'text/coffeescript'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/coffee/coffee'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./coffee');
|
||||
}
|
||||
}
|
||||
loader: () => import('./coffee')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
wordPattern:
|
||||
|
|
|
|||
|
|
@ -5,34 +5,15 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'c',
|
||||
extensions: ['.c', '.h'],
|
||||
aliases: ['C', 'c'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/cpp/cpp'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./cpp');
|
||||
}
|
||||
}
|
||||
loader: () => import('./cpp')
|
||||
});
|
||||
registerLanguage({
|
||||
id: 'cpp',
|
||||
extensions: ['.cpp', '.cc', '.cxx', '.hpp', '.hh', '.hxx'],
|
||||
aliases: ['C++', 'Cpp', 'cpp'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/cpp/cpp'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./cpp');
|
||||
}
|
||||
}
|
||||
loader: () => import('./cpp')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'csharp',
|
||||
extensions: ['.cs', '.csx', '.cake'],
|
||||
aliases: ['C#', 'csharp'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/csharp/csharp'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./csharp');
|
||||
}
|
||||
}
|
||||
loader: () => import('./csharp')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
wordPattern:
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'csp',
|
||||
extensions: ['.csp'],
|
||||
aliases: ['CSP', 'csp'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/csp/csp'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./csp');
|
||||
}
|
||||
}
|
||||
loader: () => import('./csp')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
brackets: [],
|
||||
|
|
|
|||
|
|
@ -5,21 +5,10 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'css',
|
||||
extensions: ['.css'],
|
||||
aliases: ['CSS', 'css'],
|
||||
mimetypes: ['text/css'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/css/css'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./css');
|
||||
}
|
||||
}
|
||||
loader: () => import('./css')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import type { languages } from '../../fillers/monaco-editor-core';
|
||||
import type { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
wordPattern: /(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,
|
||||
|
|
|
|||
|
|
@ -5,20 +5,9 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'cypher',
|
||||
extensions: ['.cypher', '.cyp'],
|
||||
aliases: ['Cypher', 'OpenCypher'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/cypher/cypher'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./cypher');
|
||||
}
|
||||
}
|
||||
loader: () => import('./cypher')
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { languages } from '../../fillers/monaco-editor-core';
|
||||
import { languages } from 'monaco-editor-core';
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
|
|
|
|||
|
|
@ -5,21 +5,10 @@
|
|||
|
||||
import { registerLanguage } from '../_.contribution';
|
||||
|
||||
declare var AMD: any;
|
||||
declare var require: any;
|
||||
|
||||
registerLanguage({
|
||||
id: 'dart',
|
||||
extensions: ['.dart'],
|
||||
aliases: ['Dart', 'dart'],
|
||||
mimetypes: ['text/x-dart-source', 'text/x-dart'],
|
||||
loader: () => {
|
||||
if (AMD) {
|
||||
return new Promise((resolve, reject) => {
|
||||
require(['vs/basic-languages/dart/dart'], resolve, reject);
|
||||
});
|
||||
} else {
|
||||
return import('./dart');
|
||||
}
|
||||
}
|
||||
loader: () => import('./dart')
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue