diff --git a/.gitignore b/.gitignore index ca8dba1b28f..7e23d14ae32 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ vscode.db product.overrides.json *.snap.actual .vscode-test +.void # Void added these: .tmp/ diff --git a/.vscode/settings.json b/.vscode/settings.json index 80c37853fe7..491841e3746 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ { "editor.insertSpaces": false, + "editor.renderWhitespace": "all", "files.trimTrailingWhitespace": true, "files.exclude": { ".git": true, diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0ca8d2993a3..d4decd45814 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -29,7 +29,7 @@ } } }, - { // Void added this + { // Void added this "type": "npm", "script": "watchreactd", "label": "React - Build", @@ -227,7 +227,13 @@ "windows": { "command": ".\\scripts\\code-server.bat" }, - "args": ["--no-launch", "--connection-token", "dev-token", "--port", "8080"], + "args": [ + "--no-launch", + "--connection-token", + "dev-token", + "--port", + "8080" + ], "label": "Run code server", "isBackground": true, "problemMatcher": { @@ -249,7 +255,12 @@ "windows": { "command": ".\\scripts\\code-web.bat" }, - "args": ["--port", "8080", "--browser", "none"], + "args": [ + "--port", + "8080", + "--browser", + "none" + ], "label": "Run code web", "isBackground": true, "problemMatcher": { @@ -275,26 +286,43 @@ }, { "type": "shell", - "command": "node build/lib/preLaunch.js", - "label": "Ensure Prelaunch Dependencies", + "label": "Lint (eslint: CLI)", + "command": "mkdir -p /tmp/tsc-problems-logs/; npx eslint \"src/**/*.{ts,tsx,js}\" --format stylish 2>&1 | tee -a /tmp/tsc-problems-logs/eslint.log", + "problemMatcher": [ + "$eslint-stylish" + ], + "group": "build", "presentation": { - "reveal": "silent", - "close": true + "reveal": "always" } }, { - "type": "npm", - "script": "tsec-compile-check", - "problemMatcher": [ - { - "base": "$tsc", - "applyTo": "allDocuments", - "owner": "tsec" - } + "label": "all problems check", + "dependsOn": [ + "Lint (eslint: CLI)", + "TS: typecheck (noEmit, non-blocking)" ], "group": "build", - "label": "npm: tsec-compile-check", - "detail": "node_modules/tsec/bin/tsec -p src/tsconfig.json --noEmit" + "dependsOrder": "sequence", + "presentation": { + "reveal": "always" + }, + "problemMatcher": [] + }, + { + "type": "shell", + "label": "TS: typecheck (noEmit, non-blocking)", + "command": "bash ${workspaceFolder}/scripts/tsc-problems.sh", + "problemMatcher": "$tsc", + "group": "build", + "options": { + "shell": { + "executable": "/bin/bash", + "args": [ + "-lc" + ] + } + } }, { // Used for monaco editor playground launch config diff --git a/HOW_TO_CONTRIBUTE.md b/HOW_TO_CONTRIBUTE.md index 443e3540acf..35a012c7cf9 100644 --- a/HOW_TO_CONTRIBUTE.md +++ b/HOW_TO_CONTRIBUTE.md @@ -117,6 +117,14 @@ If you're certain you want to build a local executible of Void, follow these ste Make sure you've already entered Developer Mode with Void first, then run one of the following commands. This will create a folder named `VSCode-darwin-arm64` or similar outside of the void/ repo (see below). +You can optionally tune the local executable build with these environment variables: + +- `VSCODE_MANGLE_MAX_WORKERS` (default: `4`) controls how many parallel mangler workers are used during build. Lower it on low-memory machines. +- `VSCODE_TSC_IGNORE_UNUSED` (set to any non-empty value, for example `1`) disables `noUnusedLocals` and `noUnusedParameters` checks for faster local builds. + +Example: + +- `VSCODE_MANGLE_MAX_WORKERS=2 VSCODE_TSC_IGNORE_UNUSED=1 npm run gulp vscode-linux-x64` ##### Mac - `npm run gulp vscode-darwin-arm64` - most common (Apple Silicon) @@ -131,6 +139,17 @@ Make sure you've already entered Developer Mode with Void first, then run one of - `npm run gulp vscode-linux-arm64` +##### Linux: `vscode-reh-linux-x64` Component + +`vscode-reh-linux-x64` builds the server-side REH package (Remote Extension Host), not the full desktop app UI. + +- Use it when you only need the server component (for remote/SSH-style setups). +- Output folder: `../vscode-reh-linux-x64/` +- Main server launcher inside that folder: `bin/void-server` +- Useful variant for faster packaging in CI flows: + +`VSCODE_MANGLE_MAX_WORKERS=8 VSCODE_TSC_IGNORE_UNUSED=1 npm run gulp vscode-reh-linux-x64 --max-old-space-size=48000` + ##### Local Executible Output The local executible will be located in a folder outside of `void/`: @@ -151,5 +170,3 @@ workspace/ - Please don't use AI to write your PR 🙂 - - diff --git a/build/buildfile.js b/build/buildfile.js index 9430fb3d7be..5c9c42c256f 100644 --- a/build/buildfile.js +++ b/build/buildfile.js @@ -13,7 +13,7 @@ function createModuleDescription(name) { }; } -exports.workerEditor = createModuleDescription('vs/editor/common/services/editorWebWorkerMain'); +exports.workerEditor = createModuleDescription('vs/editor/common/language/services/editorWebWorkerMain'); exports.workerExtensionHost = createModuleDescription('vs/workbench/api/worker/extensionHostWorkerMain'); exports.workerNotebook = createModuleDescription('vs/workbench/contrib/notebook/common/services/notebookWebWorkerMain'); exports.workerLanguageDetection = createModuleDescription('vs/workbench/services/languageDetection/browser/languageDetectionWebWorkerMain'); diff --git a/build/filters.js b/build/filters.js index 4f18bf6607a..50ec6925064 100644 --- a/build/filters.js +++ b/build/filters.js @@ -37,7 +37,7 @@ module.exports.unicodeFilter = [ '!LICENSES.chromium.html', '!**/LICENSE', - '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,woff,eot,template,ico,icns,opus,wasm}', + '!**/*.{dll,exe,png,bmp,jpg,scpt,cur,ttf,otf,woff,woff2,eot,template,ico,icns,opus,wasm}', '!**/test/**', '!**/*.test.ts', '!**/*.{d.ts,json,md}', @@ -123,7 +123,7 @@ module.exports.indentationFilter = [ '!src/vs/*/**/*.d.ts', '!src/typings/**/*.d.ts', '!extensions/**/*.d.ts', - '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,woff,eot,md,ps1,psm1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus,admx,adml,wasm}', + '!**/*.{svg,exe,png,bmp,jpg,scpt,bat,cmd,cur,ttf,otf,woff,woff2,eot,md,ps1,psm1,template,yaml,yml,d.ts.recipe,ico,icns,plist,opus,admx,adml,wasm}', '!build/{lib,download,linux,darwin}/**/*.js', '!build/**/*.sh', '!build/azure-pipelines/**/*.js', @@ -164,6 +164,13 @@ module.exports.copyrightFilter = [ '!**/*.opts', '!**/*.disabled', '!**/*.code-workspace', + + '!**/*.ttf', + '!**/*.otf', + '!**/*.woff', + '!**/*.woff2', + '!**/*.eot', + '!**/*.js.map', '!**/*.wasm', '!build/**/*.init', diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 4787605d068..505aedb2c8d 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -43,7 +43,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { entryPoints: [ 'vs/editor/editor.main', 'vs/editor/editor.worker.start', - 'vs/editor/common/services/editorWebWorkerMain', + 'vs/editor/common/language/services/editorWebWorkerMain', ], inlineEntryPoints: [ apiusages, diff --git a/build/gulpfile.js b/build/gulpfile.js index 7894398c2ea..a86645d3b66 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -27,15 +27,37 @@ gulp.task(transpileClientSWCTask); const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), transpileTask('src', 'out'))); gulp.task(transpileClientTask); -// Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); +// Fast compile for development time (use esbuild for bundling dependencies) +const compileClientTask = task.define('compile-client', task.series( + util.rimraf('out'), + compileApiProposalNamesTask, + transpileClientSWCTask +)); gulp.task(compileClientTask); -const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); +// Copy Terminalia fonts after dev compile +const copyVoidFontsDev = () => { + return gulp.src('src/vs/code/electron-sandbox/workbench/assets/fonts/*.{woff2,ttf}') + .pipe(gulp.dest('out/vs/code/electron-sandbox/workbench/assets/fonts')); +}; + +const copyPatchLibDev = () => { + return gulp.src('src/vs/workbench/contrib/void/browser/lib/diff-match-patch.js') + .pipe(gulp.dest('out/vs/workbench/contrib/void/browser/lib')); +}; + +const watchClientTask = task.define('watch-client', task.series( + util.rimraf('out'), + task.parallel(watchTask('out', false), watchApiProposalNamesTask) +)); gulp.task(watchClientTask); // All -const _compileTask = task.define('compile', task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask, compileExtensionMediaTask)); +const _compileTask = task.define('compile', task.series( + task.parallel(monacoTypecheckTask, compileClientTask, compileExtensionsTask, compileExtensionMediaTask), + copyVoidFontsDev, + copyPatchLibDev +)); gulp.task(_compileTask); gulp.task(task.define('watch', task.parallel(/* monacoTypecheckWatchTask, */ watchClientTask, watchExtensionsTask))); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 7046ee004bb..e59bc8e4bd3 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -58,6 +58,7 @@ const vscodeResourceIncludes = [ // Workbench 'out-build/vs/code/electron-sandbox/workbench/workbench.html', + 'out-build/vs/code/electron-sandbox/workbench/assets/fonts/*.{woff2,ttf}', // Electron Preload 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', diff --git a/build/hygiene.js b/build/hygiene.js index 84982259068..a38ca62f47e 100644 --- a/build/hygiene.js +++ b/build/hygiene.js @@ -13,11 +13,19 @@ const pall = require('p-all'); const { all, copyrightFilter, unicodeFilter, indentationFilter, tsFormattingFilter, eslintFilter, stylelintFilter } = require('./filters'); -const copyrightHeaderLines = [ - '/*---------------------------------------------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' * Licensed under the MIT License. See License.txt in the project root for license information.', - ' *--------------------------------------------------------------------------------------------*/', +const copyrightHeaderVariants = [ + [ + '/*---------------------------------------------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' * Licensed under the MIT License. See License.txt in the project root for license information.', + ' *--------------------------------------------------------------------------------------------*/', + ], + [ + '/*--------------------------------------------------------------------------------------', + ' * Copyright 2025 Glass Devtools, Inc. All rights reserved.', + ' * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information.', + ' *--------------------------------------------------------------------------------------*/', + ], ]; function hygiene(some, linting = true) { @@ -100,12 +108,13 @@ function hygiene(some, linting = true) { const copyrights = es.through(function (file) { const lines = file.__lines; - for (let i = 0; i < copyrightHeaderLines.length; i++) { - if (lines[i] !== copyrightHeaderLines[i]) { - console.error(file.relative + ': Missing or bad copyright statement'); - errorCount++; - break; - } + const hasValidHeader = copyrightHeaderVariants.some(header => + header.every((expectedLine, i) => lines[i] === expectedLine) + ); + + if (!hasValidHeader) { + console.error(file.relative + ': Missing or bad copyright statement'); + errorCount++; } this.emit('data', file); @@ -261,54 +270,55 @@ function createGitIndexVinyls(paths) { return pall(fns, { concurrency: 4 }).then((r) => r.filter((p) => !!p)); } -// Void - NO PRE COMMIT HOOKS!!!! for now... - Void team -// // this allows us to run hygiene as a git pre-commit hook -// if (require.main === module) { -// const cp = require('child_process'); - -// process.on('unhandledRejection', (reason, p) => { -// console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); -// process.exit(1); -// }); - -// if (process.argv.length > 2) { -// hygiene(process.argv.slice(2)).on('error', (err) => { -// console.error(); -// console.error(err); -// process.exit(1); -// }); -// } else { -// cp.exec( -// 'git diff --cached --name-only', -// { maxBuffer: 2000 * 1024 }, -// (err, out) => { -// if (err) { -// console.error(); -// console.error(err); -// process.exit(1); -// } - -// const some = out.split(/\r?\n/).filter((l) => !!l); - -// if (some.length > 0) { -// console.log('Reading git index versions...'); - -// createGitIndexVinyls(some) -// .then( -// (vinyls) => -// new Promise((c, e) => -// hygiene(es.readArray(vinyls).pipe(filter(all))) -// .on('end', () => c()) -// .on('error', e) -// ) -// ) -// .catch((err) => { -// console.error(); -// console.error(err); -// process.exit(1); -// }); -// } -// } -// ); -// } -// } +// this allows us to run hygiene as a git pre-commit hook +if (require.main === module) { + const cp = require('child_process'); + + process.on('unhandledRejection', (reason, p) => { + console.log('Unhandled Rejection at: Promise', p, 'reason:', reason); + process.exit(1); + }); + + if (process.argv.length > 2) { + hygiene(process.argv.slice(2)).on('error', (err) => { + console.error(); + console.error(err); + process.exit(1); + }); + } else { + cp.exec( + 'git diff --cached --name-only', + { maxBuffer: 2000 * 1024 }, + (err, out) => { + if (err) { + console.error(); + console.error(err); + process.exit(1); + } + + const some = out.split(/\r?\n/).filter((l) => !!l); + + if (some.length > 0) { + console.log('Reading git index versions...'); + + createGitIndexVinyls(some) + .then( + (vinyls) => + new Promise((c, e) => + hygiene(es.readArray(vinyls).pipe(filter(all))) + .on('end', () => c()) + .on('error', e) + ) + ) + .catch((err) => { + console.error(); + console.error(err); + process.exit(1); + }); + } else { + console.log('No staged files, skipping hygiene.'); + } + } + ); + } +} diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 55fb148097c..9f0888954c5 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -83,6 +83,10 @@ function createCompile(src, { build, emitError, transpileOnly, preserveEnglish } const sourcemaps = require('gulp-sourcemaps'); const projectPath = path_1.default.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; + if (process.env['VSCODE_TSC_IGNORE_UNUSED']) { + overrideOptions.noUnusedLocals = false; + overrideOptions.noUnusedParameters = false; + } if (!build) { overrideOptions.inlineSourceMap = true; } @@ -341,4 +345,4 @@ exports.watchApiProposalNamesTask = task.define('watch-api-proposal-names', () = .pipe(util.debounce(task)) .pipe(gulp_1.default.dest('src')); }); -//# sourceMappingURL=compilation.js.map \ No newline at end of file +//# sourceMappingURL=compilation.js.map diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 625fc430a94..c981402be2d 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -56,6 +56,10 @@ export function createCompile(src: string, { build, emitError, transpileOnly, pr const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; + if (process.env['VSCODE_TSC_IGNORE_UNUSED']) { + overrideOptions.noUnusedLocals = false; + overrideOptions.noUnusedParameters = false; + } if (!build) { overrideOptions.inlineSourceMap = true; } diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index ce744642551..c3fdd76b350 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -18,6 +18,16 @@ const url_1 = require("url"); const workerpool_1 = __importDefault(require("workerpool")); const staticLanguageServiceHost_1 = require("./staticLanguageServiceHost"); const buildfile = require('../../buildfile'); +function parsePositiveInt(value) { + if (!value) { + return undefined; + } + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed <= 0) { + return undefined; + } + return parsed; +} class ShortIdent { prefix; static _keywords = new Set(['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', @@ -353,10 +363,20 @@ class Mangler { this.projectPath = projectPath; this.log = log; this.config = config; + const maxWorkers = parsePositiveInt(process.env['VSCODE_MANGLE_MAX_WORKERS']) ?? 4; + const workerMaxOldSpaceSizeMb = parsePositiveInt(process.env['VSCODE_MANGLE_WORKER_MAX_OLD_SPACE_SIZE']) + ?? parsePositiveInt(process.env['npm_config_max_old_space_size']); + const execArgv = [...process.execArgv]; + if (workerMaxOldSpaceSizeMb && !execArgv.some(arg => arg.startsWith('--max-old-space-size='))) { + execArgv.push(`--max-old-space-size=${workerMaxOldSpaceSizeMb}`); + } this.renameWorkerPool = workerpool_1.default.pool(path_1.default.join(__dirname, 'renameWorker.js'), { - maxWorkers: 4, - minWorkers: 'max' + workerType: 'process', + maxWorkers, + minWorkers: 'max', + forkOpts: { execArgv } }); + this.log(`[mangler] Rename worker pool: type=process workers=${maxWorkers} max-old-space-size=${workerMaxOldSpaceSizeMb ?? 'inherit'}`); } async computeNewFileContents(strictImplicitPublicHandling) { const service = typescript_1.default.createLanguageService(new staticLanguageServiceHost_1.StaticLanguageServiceHost(this.projectPath)); @@ -478,6 +498,11 @@ class Mangler { // STEP: prepare rename edits this.log(`Starting prepare rename edits`); const editsByFile = new Map(); + const projectSourceRoot = normalize(path_1.default.dirname(this.projectPath)) + '/'; + const shouldApplyRenameToFile = (fileName) => { + const normalizedFileName = normalize(fileName); + return normalizedFileName.startsWith(projectSourceRoot) && !normalizedFileName.endsWith('.d.ts'); + }; const appendEdit = (fileName, edit) => { const edits = editsByFile.get(fileName); if (!edits) { @@ -488,6 +513,9 @@ class Mangler { } }; const appendRename = (newText, loc) => { + if (!shouldApplyRenameToFile(loc.fileName)) { + return; + } appendEdit(loc.fileName, { newText: (loc.prefixText || '') + newText + (loc.suffixText || ''), offset: loc.textSpan.start, @@ -660,4 +688,4 @@ async function _run() { if (__filename === process_1.argv[1]) { _run(); } -//# sourceMappingURL=index.js.map \ No newline at end of file +//# sourceMappingURL=index.js.map diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index 4cbbd3cdadd..50372384916 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -14,6 +14,17 @@ import workerpool from 'workerpool'; import { StaticLanguageServiceHost } from './staticLanguageServiceHost'; const buildfile = require('../../buildfile'); +function parsePositiveInt(value: string | undefined): number | undefined { + if (!value) { + return undefined; + } + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed <= 0) { + return undefined; + } + return parsed; +} + class ShortIdent { private static _keywords = new Set(['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', @@ -405,11 +416,21 @@ export class Mangler { private readonly log: typeof console.log = () => { }, private readonly config: { readonly manglePrivateFields: boolean; readonly mangleExports: boolean }, ) { + const maxWorkers = parsePositiveInt(process.env['VSCODE_MANGLE_MAX_WORKERS']) ?? 4; + const workerMaxOldSpaceSizeMb = parsePositiveInt(process.env['VSCODE_MANGLE_WORKER_MAX_OLD_SPACE_SIZE']) + ?? parsePositiveInt(process.env['npm_config_max_old_space_size']); + const execArgv = [...process.execArgv]; + if (workerMaxOldSpaceSizeMb && !execArgv.some(arg => arg.startsWith('--max-old-space-size='))) { + execArgv.push(`--max-old-space-size=${workerMaxOldSpaceSizeMb}`); + } this.renameWorkerPool = workerpool.pool(path.join(__dirname, 'renameWorker.js'), { - maxWorkers: 4, - minWorkers: 'max' + workerType: 'process', + maxWorkers, + minWorkers: 'max', + forkOpts: { execArgv } }); + this.log(`[mangler] Rename worker pool: type=process workers=${maxWorkers} max-old-space-size=${workerMaxOldSpaceSizeMb ?? 'inherit'}`); } async computeNewFileContents(strictImplicitPublicHandling?: Set): Promise> { @@ -555,6 +576,11 @@ export class Mangler { type Edit = { newText: string; offset: number; length: number }; const editsByFile = new Map(); + const projectSourceRoot = normalize(path.dirname(this.projectPath)) + '/'; + const shouldApplyRenameToFile = (fileName: string): boolean => { + const normalizedFileName = normalize(fileName); + return normalizedFileName.startsWith(projectSourceRoot) && !normalizedFileName.endsWith('.d.ts'); + }; const appendEdit = (fileName: string, edit: Edit) => { const edits = editsByFile.get(fileName); @@ -565,6 +591,9 @@ export class Mangler { } }; const appendRename = (newText: string, loc: ts.RenameLocation) => { + if (!shouldApplyRenameToFile(loc.fileName)) { + return; + } appendEdit(loc.fileName, { newText: (loc.prefixText || '') + newText + (loc.suffixText || ''), offset: loc.textSpan.start, diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 105dc02a79d..84331e1694f 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -128,7 +128,9 @@ function bundleESMTask(opts) { plugins: [contentsMapper, externalOverride], target: ['es2022'], loader: { - '.ttf': 'file', + '.woff2': 'file', + '.woff': 'file', + '.ttf': 'file', '.svg': 'file', '.png': 'file', '.sh': 'file', diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 88a2a25dae4..eaea989f50c 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -129,6 +129,8 @@ function bundleESMTask(opts: IBundleESMTaskOpts): NodeJS.ReadWriteStream { plugins: [contentsMapper, externalOverride], target: ['es2022'], loader: { + '.woff2': 'file', + '.woff': 'file', '.ttf': 'file', '.svg': 'file', '.png': 'file', diff --git a/build/lib/propertyInitOrderChecker.ts b/build/lib/propertyInitOrderChecker.ts index dc18213566f..73cfdbf6913 100644 --- a/build/lib/propertyInitOrderChecker.ts +++ b/build/lib/propertyInitOrderChecker.ts @@ -26,11 +26,11 @@ const ignored = new Set([ 'vs/base/common/arrays.ts', 'vs/platform/extensionManagement/common/extensionsScannerService.ts', 'vs/platform/configuration/common/configurations.ts', - 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', - 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', - 'vs/editor/common/model/textModelTokens.ts', - 'vs/editor/common/model/tokenizationTextModelPart.ts', - 'vs/editor/common/core/textEdit.ts', + 'vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', + 'vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', + 'vs/editor/common/language/model/textModelTokens.ts', + 'vs/editor/common/language/model/tokenizationTextModelPart.ts', + 'vs/editor/common/language/core/textEdit.ts', 'vs/workbench/contrib/debug/common/debugStorage.ts', 'vs/workbench/contrib/debug/common/debugModel.ts', 'vs/workbench/api/common/extHostCommands.ts', diff --git a/build/monaco/monaco.d.ts.recipe b/build/monaco/monaco.d.ts.recipe index cb4c9082a11..6274d3cf03d 100644 --- a/build/monaco/monaco.d.ts.recipe +++ b/build/monaco/monaco.d.ts.recipe @@ -77,15 +77,15 @@ declare namespace monaco { #include(vs/base/common/cancellation): CancellationTokenSource, CancellationToken #include(vs/base/common/uri): URI, UriComponents #include(vs/base/common/keyCodes): KeyCode -#include(vs/editor/common/services/editorBaseApi): KeyMod +#include(vs/editor/common/language/services/editorBaseApi): KeyMod #include(vs/base/common/htmlContent): IMarkdownString, MarkdownStringTrustedOptions #include(vs/base/browser/keyboardEvent): IKeyboardEvent #include(vs/base/browser/mouseEvent): IMouseEvent #include(vs/editor/common/editorCommon): IScrollEvent -#include(vs/editor/common/core/position): IPosition, Position -#include(vs/editor/common/core/range): IRange, Range -#include(vs/editor/common/core/selection): ISelection, Selection, SelectionDirection -#include(vs/editor/common/languages): Token +#include(vs/editor/common/language/core/position): IPosition, Position +#include(vs/editor/common/language/core/range): IRange, Range +#include(vs/editor/common/language/core/selection): ISelection, Selection, SelectionDirection +#include(vs/editor/common/language/languages): Token } declare namespace monaco.editor { @@ -110,13 +110,13 @@ export interface ICommandMetadata { #include(vs/editor/standalone/browser/colorizer): IColorizerOptions, IColorizerElementOptions #include(vs/base/common/scrollable): ScrollbarVisibility #include(vs/base/common/themables): ThemeColor, ThemeIcon -#include(vs/editor/common/core/editOperation): ISingleEditOperation -#include(vs/editor/common/core/wordHelper): IWordAtPosition -#includeAll(vs/editor/common/model): IScrollEvent +#include(vs/editor/common/language/core/editOperation): ISingleEditOperation +#include(vs/editor/common/language/core/wordHelper): IWordAtPosition +#includeAll(vs/editor/common/language/model): IScrollEvent #include(vs/editor/common/diff/legacyLinesDiffComputer): IChange, ICharChange, ILineChange -#include(vs/editor/common/core/dimension): IDimension +#include(vs/editor/common/language/core/dimension): IDimension #includeAll(vs/editor/common/editorCommon): IScrollEvent -#includeAll(vs/editor/common/textModelEvents): +#includeAll(vs/editor/common/language/textModelEvents): #includeAll(vs/editor/common/cursorEvents): #include(vs/platform/accessibility/common/accessibility): AccessibilitySupport #includeAll(vs/editor/common/config/editorOptions): @@ -133,19 +133,19 @@ export type IModel = ITextModel; declare namespace monaco.languages { #include(vs/base/common/glob): IRelativePattern -#include(vs/editor/common/languageSelector): LanguageSelector, LanguageFilter +#include(vs/editor/common/language/languageSelector): LanguageSelector, LanguageFilter #includeAll(vs/editor/standalone/browser/standaloneLanguages;languages.=>;editorCommon.=>editor.;model.=>editor.;IMarkerData=>editor.IMarkerData): #includeAll(vs/editor/common/languages/languageConfiguration): -#includeAll(vs/editor/common/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.;ThemeIcon=>editor.ThemeIcon): Token -#include(vs/editor/common/languages/language): ILanguageExtensionPoint +#includeAll(vs/editor/common/language/languages;IMarkerData=>editor.IMarkerData;ISingleEditOperation=>editor.ISingleEditOperation;model.=>editor.;ThemeIcon=>editor.ThemeIcon): Token +#include(vs/editor/common/language/language): ILanguageExtensionPoint #includeAll(vs/editor/standalone/common/monarch/monarchTypes): } declare namespace monaco.worker { -#include(vs/editor/common/model/mirrorTextModel): IMirrorTextModel -#includeAll(vs/editor/common/services/editorWebWorker;): +#include(vs/editor/common/language/model/mirrorTextModel): IMirrorTextModel +#includeAll(vs/editor/common/language/services/editorWebWorker;): } diff --git a/build/npm/run-gulp.js b/build/npm/run-gulp.js new file mode 100644 index 00000000000..10b451d8625 --- /dev/null +++ b/build/npm/run-gulp.js @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cp = require('child_process'); +const path = require('path'); + +const DEFAULT_MAX_OLD_SPACE_SIZE_MB = 8192; + +/** + * @param {string | undefined} value + * @returns {number | undefined} + */ +function parseHeapLimit(value) { + if (!value) { + return undefined; + } + + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed <= 0) { + return undefined; + } + + return parsed; +} + +/** + * @param {string[]} argv + * @returns {{ argv: string[]; heapLimitMb: number | undefined }} + */ +function extractHeapLimitFromArgv(argv) { + /** @type {string[]} */ + const cleanedArgv = []; + let heapLimitMb; + + for (let i = 0; i < argv.length; i++) { + const arg = argv[i]; + + if (arg.startsWith('--max-old-space-size=')) { + heapLimitMb = parseHeapLimit(arg.slice('--max-old-space-size='.length)); + continue; + } + + if (arg === '--max-old-space-size') { + heapLimitMb = parseHeapLimit(argv[i + 1]); + i++; + continue; + } + + cleanedArgv.push(arg); + } + + return { argv: cleanedArgv, heapLimitMb }; +} + +const argInfo = extractHeapLimitFromArgv(process.argv.slice(2)); +const heapLimitMb = argInfo.heapLimitMb + ?? parseHeapLimit(process.env['npm_config_max_old_space_size']) + ?? DEFAULT_MAX_OLD_SPACE_SIZE_MB; +const heapLimitSource = argInfo.heapLimitMb !== undefined + ? 'cli' + : parseHeapLimit(process.env['npm_config_max_old_space_size']) !== undefined + ? 'npm_config_max_old_space_size' + : 'default'; + +const gulpCliPath = path.join(__dirname, '..', '..', 'node_modules', 'gulp', 'bin', 'gulp.js'); + +console.log(`[run-gulp] Using --max-old-space-size=${heapLimitMb} (${heapLimitSource})`); + +const child = cp.spawn(process.execPath, [ + `--max-old-space-size=${heapLimitMb}`, + gulpCliPath, + ...argInfo.argv +], { + stdio: 'inherit', + env: process.env +}); + +child.on('error', err => { + console.error(err); + process.exit(1); +}); + +child.on('exit', (code, signal) => { + if (signal) { + process.kill(process.pid, signal); + return; + } + process.exit(code ?? 0); +}); diff --git a/build/tsconfig.build.json b/build/tsconfig.build.json index 4534420208f..6e3a0f7fdda 100644 --- a/build/tsconfig.build.json +++ b/build/tsconfig.build.json @@ -3,7 +3,8 @@ "compilerOptions": { "allowJs": false, "checkJs": false, - "noEmit": false, + "noEmit": true, + "strict": true, "skipLibCheck": true }, "include": [ diff --git a/build/tsconfig.json b/build/tsconfig.json index f3ad981d62f..fd6b19c2e74 100644 --- a/build/tsconfig.json +++ b/build/tsconfig.json @@ -20,7 +20,7 @@ "noUnusedLocals": true, "noUnusedParameters": true, "newLine": "lf", - "noEmit": true + "noEmit": true, }, "include": [ "**/*.ts", diff --git a/eslint.config.js b/eslint.config.js index 4f014565d2c..3be4e08a127 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -804,6 +804,8 @@ export default tseslint.config( 'console', 'cookie', 'crypto', + 'ws', + '@agentclientprotocol/sdk', 'dns', 'events', 'fs', diff --git a/extensions/configuration-editing/package-lock.json b/extensions/configuration-editing/package-lock.json index cba3a0fece6..4770f36dbfb 100644 --- a/extensions/configuration-editing/package-lock.json +++ b/extensions/configuration-editing/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@octokit/rest": "^21.1.1", "jsonc-parser": "^3.2.0", - "tunnel": "^0.0.6" + "https-proxy-agent": "^7.0.5" }, "devDependencies": { "@types/node": "20.x" @@ -210,14 +210,6 @@ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -229,6 +221,52 @@ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "license": "ISC" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" } } } diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 531ba21fe06..fa00c4356a9 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -26,8 +26,8 @@ }, "dependencies": { "@octokit/rest": "^21.1.1", - "jsonc-parser": "^3.2.0", - "tunnel": "^0.0.6" + "https-proxy-agent": "^7.0.5", + "jsonc-parser": "^3.2.0" }, "capabilities": { "virtualWorkspaces": true, diff --git a/extensions/configuration-editing/src/node/net.ts b/extensions/configuration-editing/src/node/net.ts index 4bbc6c69475..68d20624c2f 100644 --- a/extensions/configuration-editing/src/node/net.ts +++ b/extensions/configuration-editing/src/node/net.ts @@ -5,7 +5,7 @@ import { Agent, globalAgent } from 'https'; import { URL } from 'url'; -import { httpsOverHttp } from 'tunnel'; +import { HttpsProxyAgent } from 'https-proxy-agent'; import { window } from 'vscode'; export const agent = getAgent(); @@ -19,9 +19,8 @@ function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent { return globalAgent; } try { - const { hostname, port, username, password } = new URL(url); - const auth = username && password && `${username}:${password}`; - return httpsOverHttp({ proxy: { host: hostname, port, proxyAuth: auth } }); + const proxyUrl = new URL(url); + return new HttpsProxyAgent(proxyUrl); } catch (e) { window.showErrorMessage(`HTTPS_PROXY environment variable ignored: ${e.message}`); return globalAgent; diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index 2629a940444..b161dc4f7b9 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -11,14 +11,12 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", - "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/picomatch": "2.3.0", @@ -166,15 +164,6 @@ "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" }, - "node_modules/@types/byline": { - "version": "4.2.31", - "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.31.tgz", - "integrity": "sha1-DmH8ucA+BH0hxEllVMcRYperYM0= sha512-TC6Ljn7tALesQMQyTNoMWoM44SNvWtCLkJDrA/TxcwE5ILkWt4zi5wbEokqiDk42S75eykAY1onPImWDybOkmQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/mocha": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", @@ -216,14 +205,6 @@ "vscode": "^1.75.0" } }, - "node_modules/byline": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", - "integrity": "sha1-dBxSFkaOrcRXsDQQEYrXfejB3bE= sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/file-type": { "version": "16.5.4", "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", diff --git a/extensions/git/package.json b/extensions/git/package.json index 6761f02ce5b..0c8de9a1b3b 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3568,14 +3568,12 @@ "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", "@vscode/extension-telemetry": "^0.9.8", - "byline": "^5.0.0", "file-type": "16.5.4", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/picomatch": "2.3.0", diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index e42ca1e00d8..d0735149cad 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -14,8 +14,7 @@ import * as filetype from 'file-type'; import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant, relativePath } from './util'; import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode'; import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery as ApiRefQuery, InitOptions } from './api/git'; -import * as byline from 'byline'; -import { StringDecoder } from 'string_decoder'; +import { createInterface } from 'readline'; // https://github.com/microsoft/vscode/issues/65693 const MAX_CLI_LENGTH = 30000; @@ -429,14 +428,12 @@ export class Git { await mkdirp(options.parentPath); const onSpawn = (child: cp.ChildProcess) => { - const decoder = new StringDecoder('utf8'); - const lineStream = new byline.LineStream({ encoding: 'utf8' }); - child.stderr!.on('data', (buffer: Buffer) => lineStream.write(decoder.write(buffer))); + const lineStream = createInterface({ input: child.stderr!, crlfDelay: Infinity }); let totalProgress = 0; let previousProgress = 0; - lineStream.on('data', (line: string) => { + lineStream.on('line', (line: string) => { let match: RegExpExecArray | null = null; if (match = /Counting objects:\s*(\d+)%/i.exec(line)) { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 7dd1cbafbdf..679997bda7e 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -7,7 +7,7 @@ import { Event, Disposable, EventEmitter, SourceControlHistoryItemRef, l10n, wor import { dirname, sep, relative } from 'path'; import { Readable } from 'stream'; import { promises as fs, createReadStream } from 'fs'; -import byline from 'byline'; +import { createInterface } from 'readline'; export const isMacintosh = process.platform === 'darwin'; export const isWindows = process.platform === 'win32'; @@ -211,16 +211,33 @@ export function find(array: T[], fn: (t: T) => boolean): T | undefined { export async function grep(filename: string, pattern: RegExp): Promise { return new Promise((c, e) => { const fileStream = createReadStream(filename, { encoding: 'utf8' }); - const stream = byline(fileStream); - stream.on('data', (line: string) => { + const stream = createInterface({ input: fileStream, crlfDelay: Infinity }); + let done = false; + + const complete = (result: boolean) => { + if (!done) { + done = true; + c(result); + } + }; + + const fail = (err: Error) => { + if (!done) { + done = true; + e(err); + } + }; + + stream.on('line', (line: string) => { if (pattern.test(line)) { - fileStream.close(); - c(true); + stream.close(); + fileStream.destroy(); + complete(true); } }); - stream.on('error', e); - stream.on('end', () => c(false)); + stream.on('close', () => complete(false)); + fileStream.on('error', fail); }); } diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index cf7317a40a4..c07a2d22e1c 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -13,7 +13,7 @@ "@octokit/graphql-schema": "14.4.0", "@octokit/rest": "21.1.0", "@vscode/extension-telemetry": "^0.9.8", - "tunnel": "^0.0.6" + "https-proxy-agent": "^7.0.5" }, "devDependencies": { "@types/node": "20.x" @@ -381,14 +381,6 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", @@ -400,6 +392,52 @@ "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", "license": "ISC" + }, + "node_modules/https-proxy-agent": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", + "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/agent-base": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", + "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "dependencies": { + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" } } } diff --git a/extensions/github/package.json b/extensions/github/package.json index 86adc2ddc4e..e9c476cd994 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -230,7 +230,7 @@ "@octokit/graphql": "8.2.0", "@octokit/graphql-schema": "14.4.0", "@octokit/rest": "21.1.0", - "tunnel": "^0.0.6", + "https-proxy-agent": "^7.0.5", "@vscode/extension-telemetry": "^0.9.8" }, "devDependencies": { diff --git a/extensions/github/src/auth.ts b/extensions/github/src/auth.ts index e7be2637da0..1e12f243658 100644 --- a/extensions/github/src/auth.ts +++ b/extensions/github/src/auth.ts @@ -7,7 +7,7 @@ import { AuthenticationSession, authentication, window } from 'vscode'; import { Agent, globalAgent } from 'https'; import { graphql } from '@octokit/graphql/dist-types/types'; import { Octokit } from '@octokit/rest'; -import { httpsOverHttp } from 'tunnel'; +import { HttpsProxyAgent } from 'https-proxy-agent'; import { URL } from 'url'; export class AuthenticationError extends Error { } @@ -18,9 +18,8 @@ function getAgent(url: string | undefined = process.env.HTTPS_PROXY): Agent { } try { - const { hostname, port, username, password } = new URL(url); - const auth = username && password && `${username}:${password}`; - return httpsOverHttp({ proxy: { host: hostname, port, proxyAuth: auth } }); + const proxyUrl = new URL(url); + return new HttpsProxyAgent(proxyUrl); } catch (e) { window.showErrorMessage(`HTTPS_PROXY environment variable ignored: ${e.message}`); return globalAgent; diff --git a/extensions/vscode-colorize-perf-tests/test/colorize-fixtures/test-treeView.ts b/extensions/vscode-colorize-perf-tests/test/colorize-fixtures/test-treeView.ts index 612ee41bc2a..6f87b186888 100644 --- a/extensions/vscode-colorize-perf-tests/test/colorize-fixtures/test-treeView.ts +++ b/extensions/vscode-colorize-perf-tests/test/colorize-fixtures/test-treeView.ts @@ -76,7 +76,7 @@ import { parseLinkedText } from '../../../../base/common/linkedText.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IAccessibleViewInformationService } from '../../../services/accessibility/common/accessibleViewInformationService.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../src/vs/editor/common/language/language.ts'; export class TreeViewPane extends ViewPane { diff --git a/package-lock.json b/package-lock.json index 9fc60e62c84..f29cff2c40d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@agentclientprotocol/sdk": "^0.14.1", "@anthropic-ai/sdk": "^0.40.0", "@c4312/eventsource-umd": "^3.0.5", "@floating-ui/react": "^0.27.8", @@ -29,6 +30,7 @@ "@vscode/sqlite3": "5.1.8-vscode", "@vscode/sudo-prompt": "9.3.1", "@vscode/tree-sitter-wasm": "^0.1.4", + "@vscode/vsce-sign": "^2.0.6", "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", @@ -99,6 +101,7 @@ "@types/wicg-file-system-access": "^2020.9.6", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", + "@types/ws": "^8.18.1", "@types/yauzl": "^2.10.0", "@types/yazl": "^2.4.2", "@typescript-eslint/utils": "^8.8.0", @@ -184,8 +187,8 @@ "tsec": "0.2.7", "tslib": "^2.6.3", "tsup": "^8.4.0", - "typescript": "^5.8.0-dev.20250207", - "typescript-eslint": "^8.8.0", + "typescript": "5.8.2", + "typescript-eslint": "^8.55.0", "util": "^0.12.4", "webpack": "^5.94.0", "webpack-cli": "^5.1.4", @@ -197,6 +200,15 @@ "windows-foreground-love": "0.5.0" } }, + "node_modules/@agentclientprotocol/sdk": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.14.1.tgz", + "integrity": "sha512-b6r3PS3Nly+Wyw9U+0nOr47bV8tfS476EgyEMhoKvJCZLbgqoDFN7DJwkxL88RR0aiOqOYV1ZnESHqb+RmdH8w==", + "license": "Apache-2.0", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -1644,25 +1656,30 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^3.3.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, + "funding": { + "url": "https://opencollective.com/eslint" + }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -4121,6 +4138,16 @@ "integrity": "sha1-kdZxDlNtNFucmwF8V0z2qNpkxRg= sha512-c4m/hnOI1j34i8hXlkZzelE6SXfOqaTWhBp0UgBuwmpiafh22OpsE261Rlg//agZtQHIY5cMgbkX8bnthUFrmA==", "dev": true }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -4139,14 +4166,139 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.55.0.tgz", + "integrity": "sha512-1y/MVSz0NglV1ijHC8OT49mPJ4qhPYjiK08YUQVbIOyu+5k862LKUHFkpKHWu//zmr7hDR2rhwUm6gnCGNmGBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/type-utils": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.55.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.55.0.tgz", + "integrity": "sha512-4z2nCSBfVIMnbuu8uinj+f0o4qOeggYJLbjpPHka3KH1om7e+H9yLKTYgksTaHcGco+NClhhY2vyO3HsMH1RGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.55.0.tgz", + "integrity": "sha512-zRcVVPFUYWa3kNnjaZGXSu3xkKV1zXy8M4nO/pElzQhFweb7PPtluDLQtKArEOGmjXoRjnUZ29NjOiF0eCDkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.55.0", + "@typescript-eslint/types": "^8.55.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz", - "integrity": "sha512-EL8eaGC6gx3jDd8GwEFEV091210U97J0jeEHrAYvIYosmEGet4wJ+g0SYmLu+oRiAwbSA5AVrt6DxLHfdd+bUg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.55.0.tgz", + "integrity": "sha512-fVu5Omrd3jeqeQLiB9f1YsuK/iHFOwb04bCtY4BSCLgjNbOD33ZdV6KyEqplHr+IlpgT0QTZ/iJ+wT7hvTx49Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.55.0.tgz", + "integrity": "sha512-1R9cXqY7RQd7WuqSN47PK9EDpgFUK3VqdmbYrvWJZYDd0cavROGn+74ktWBlmJ13NXUQKlZ/iAEQHI/V0kKe0Q==", "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.55.0.tgz", + "integrity": "sha512-x1iH2unH4qAt6I37I2CGlsNs+B9WGxurP2uyZLRz6UJoZWDBx9cJL1xVN/FiOmHEONEg6RIufdvyT0TEYIgC5g==", + "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.0", - "@typescript-eslint/visitor-keys": "8.8.0" + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4154,13 +4306,18 @@ "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz", - "integrity": "sha512-QJwc50hRCgBd/k12sTykOJbESe1RrzmX6COk8Y525C9l7oweZ+1lw9JiU56im7Amm8swlz00DRIlxMYLizr2Vw==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.55.0.tgz", + "integrity": "sha512-ujT0Je8GI5BJWi+/mMoR0wxwVEQaxM+pi30xuMiJETlX80OPovb2p9E8ss87gnSVtYXtJoU9U1Cowcr6w2FE0w==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4170,19 +4327,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.8.0.tgz", - "integrity": "sha512-ZaMJwc/0ckLz5DaAZ+pNLmHv8AMVGtfWxZe/x2JVEkD5LnmhWiQMMcYT7IY7gkdJuzJ9P14fRy28lUrlDSWYdw==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.55.0.tgz", + "integrity": "sha512-EwrH67bSWdx/3aRQhCoxDaHM+CrZjotc2UCCpEDVqfCE+7OjKAGWNY2HsCSTEVvWH2clYQK8pdeLp42EVs+xQw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.0", - "@typescript-eslint/visitor-keys": "8.8.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/project-service": "8.55.0", + "@typescript-eslint/tsconfig-utils": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/visitor-keys": "8.55.0", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4191,17 +4350,16 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -4211,6 +4369,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -4222,15 +4381,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz", - "integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.55.0.tgz", + "integrity": "sha512-BqZEsnPGdYpgyEIkDC1BadNY8oMwckftxBT+C8W0g1iKPdeqKZBtTfnvcq0nf60u7MkjFO8RBvpRGZBPw4L2ow==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.0", - "@typescript-eslint/types": "8.8.0", - "@typescript-eslint/typescript-estree": "8.8.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.55.0", + "@typescript-eslint/types": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4240,17 +4400,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz", - "integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.55.0.tgz", + "integrity": "sha512-AxNRwEie8Nn4eFS1FzDMJWIISMGoXMb037sgCBJ3UR6o0fQTzr2tqN9WT+DkWJPhIdQCfV7T6D387566VtnCJA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.55.0", + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4260,6 +4422,19 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@typespec/ts-http-runtime": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.2.2.tgz", @@ -4751,6 +4926,101 @@ "integrity": "sha512-3EvQak7EIOLyIGz+IP9qSwRmP08ZRWgTeoRgAXPVkkDXZ8riqJ7LDtkgx++uHBiJ3MUaSdlUYPZcLFFw7E6zGg==", "dev": true }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.6.tgz", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "vsce-sign": "bin/vsce-sign" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "dependencies": { + "@vscode/windows-registry": "^1.1.2" + } + }, "node_modules/@vscode/vscode-languagedetection": { "version": "1.0.21", "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz", @@ -4823,10 +5093,11 @@ } }, "node_modules/@vscode/windows-registry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.0.tgz", - "integrity": "sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw==", - "hasInstallScript": true + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.2.tgz", + "integrity": "sha512-/eDRmGNe6g11wHckOyiVLvK/mEE5UBZFeoRlBosIL343LDrSKUL5JDAcFeAZqOXnlTtZ3UZtj5yezKiAz99NcA==", + "hasInstallScript": true, + "license": "MIT" }, "node_modules/@webassemblyjs/ast": { "version": "1.12.1", @@ -7661,9 +7932,9 @@ "dev": true }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -9581,11 +9852,14 @@ } }, "node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -11122,12 +11396,6 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true - }, "node_modules/groq-sdk": { "version": "0.20.1", "resolved": "https://registry.npmjs.org/groq-sdk/-/groq-sdk-0.20.1.tgz", @@ -19650,9 +19918,9 @@ } }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -21935,14 +22203,14 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -21952,9 +22220,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { @@ -22121,15 +22389,16 @@ } }, "node_modules/ts-api-utils": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", - "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=16" + "node": ">=18.12" }, "peerDependencies": { - "typescript": ">=4.2.0" + "typescript": ">=4.8.4" } }, "node_modules/ts-interface-checker": { @@ -22560,9 +22829,9 @@ "dev": true }, "node_modules/typescript": { - "version": "5.8.0-dev.20250207", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20250207.tgz", - "integrity": "sha512-bRCO1GkVxTLd/UFJWOg9R1oRiSMidcfpICzuQlDJlHspv6hlcJvvIJP0BvQxrBYpu4dbzqp/Fh8rRYIkEjbSlQ==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -22574,72 +22843,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.8.0.tgz", - "integrity": "sha512-BjIT/VwJ8+0rVO01ZQ2ZVnjE1svFBiRczcpr1t1Yxt7sT25VSbPfrJtDsQ8uQTy2pilX5nI9gwxhUyLULNentw==", - "dev": true, - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.8.0", - "@typescript-eslint/parser": "8.8.0", - "@typescript-eslint/utils": "8.8.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.8.0.tgz", - "integrity": "sha512-wORFWjU30B2WJ/aXBfOm1LX9v9nyt9D3jsSOxC3cCaTQGCW5k4jNpmjFv3U7p/7s4yvdjHzwtv2Sd2dOyhjS0A==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.8.0", - "@typescript-eslint/type-utils": "8.8.0", - "@typescript-eslint/utils": "8.8.0", - "@typescript-eslint/visitor-keys": "8.8.0", - "graphemer": "^1.4.0", - "ignore": "^5.3.1", - "natural-compare": "^1.4.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.8.0.tgz", - "integrity": "sha512-uEFUsgR+tl8GmzmLjRqz+VrDv4eoaMqMXW7ruXfgThaAShO9JTciKpEsB+TvnfFfbg5IpujgMXVV36gOJRLtZg==", + "version": "8.55.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.55.0.tgz", + "integrity": "sha512-HE4wj+r5lmDVS9gdaN0/+iqNvPZwGfnJ5lZuz7s5vLlg9ODw0bIiiETaios9LvFI1U94/VBXGm3CB2Y5cNFMpw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.8.0", - "@typescript-eslint/types": "8.8.0", - "@typescript-eslint/typescript-estree": "8.8.0", - "@typescript-eslint/visitor-keys": "8.8.0", - "debug": "^4.3.4" + "@typescript-eslint/eslint-plugin": "8.55.0", + "@typescript-eslint/parser": "8.55.0", + "@typescript-eslint/typescript-estree": "8.55.0", + "@typescript-eslint/utils": "8.55.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -22649,36 +22862,8 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.8.0.tgz", - "integrity": "sha512-IKwJSS7bCqyCeG4NVGxnOP6lLT9Okc3Zj8hLO96bpMkJab+10HIfJbMouLrlpyOr3yrQ1cA413YPFiGd1mW9/Q==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "8.8.0", - "@typescript-eslint/utils": "8.8.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typical": { @@ -24008,21 +24193,21 @@ } }, "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", + "version": "3.25.0", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", + "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", "license": "ISC", "peerDependencies": { - "zod": "^3.24.1" + "zod": "^3.25 || ^4" } } } diff --git a/package.json b/package.json index e6341c0903b..cf092a30c0c 100644 --- a/package.json +++ b/package.json @@ -2,26 +2,26 @@ "name": "code-oss-dev", "version": "1.99.3", "distro": "21c8d8ea1e46d97c5639a7cabda6c0e063cc8dd5", - "author": { - "name": "Microsoft Corporation" - }, + "author": "Microsoft Corporation", "license": "MIT", "main": "./out/main.js", "type": "module", "private": true, "scripts": { + "build-acp-vendor": "node scripts/build-acp-vendor.mjs", "buildreact": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js && cd ../../../../../../../", "watchreact": "cd ./src/vs/workbench/contrib/void/browser/react/ && node build.js --watch && cd ../../../../../../../", "watchreactd": "deemon npm run watchreact", "test": "echo Please run any of the test scripts from the scripts folder.", "test-browser": "npx playwright install && node test/unit/browser/index.js", "test-browser-no-install": "node test/unit/browser/index.js", + "test-browser-void-quick": "node test/unit/browser/index.js --voidQuick --browser chromium", "test-node": "mocha test/unit/node/index.js --delay --ui=tdd --timeout=5000 --exit", "test-extension": "vscode-test", "preinstall": "node build/npm/preinstall.js", - "postinstall": "node build/npm/postinstall.js", - "compile": "node ./node_modules/gulp/bin/gulp.js compile", - "watch": "npm-run-all -lp watch-client watch-extensions", + "postinstall": "npm run build-acp-vendor && node build/npm/postinstall.js", + "compile": "node ./node_modules/gulp/bin/gulp.js compile && npm run build-acp-vendor", + "watch": "npm run build-acp-vendor && npm-run-all -lp watch-client watch-extensions", "watchd": "deemon npm run watch", "watch-webd": "deemon npm run watch-web", "kill-watchd": "deemon --kill npm run watch", @@ -34,8 +34,8 @@ "watch-extensions": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js watch-extensions watch-extension-media", "watch-extensionsd": "deemon npm run watch-extensions", "kill-watch-extensionsd": "deemon --kill npm run watch-extensions", - "precommit": "node build/hygiene.js", - "gulp": "node --max-old-space-size=8192 ./node_modules/gulp/bin/gulp.js", + "precommit": "node build/hygiene.js && node test/unit/node/index.js && npm run -s test-browser-void-quick", + "gulp": "node build/npm/run-gulp.js", "electron": "node build/lib/electron", "7z": "7z", "update-grammars": "node build/npm/update-all-grammars.mjs", @@ -72,6 +72,7 @@ "update-build-ts-version": "npm install typescript@next && tsc -p ./build/tsconfig.build.json" }, "dependencies": { + "@agentclientprotocol/sdk": "^0.14.1", "@anthropic-ai/sdk": "^0.40.0", "@c4312/eventsource-umd": "^3.0.5", "@floating-ui/react": "^0.27.8", @@ -91,6 +92,7 @@ "@vscode/sqlite3": "5.1.8-vscode", "@vscode/sudo-prompt": "9.3.1", "@vscode/tree-sitter-wasm": "^0.1.4", + "@vscode/vsce-sign": "^2.0.6", "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", @@ -161,6 +163,7 @@ "@types/wicg-file-system-access": "^2020.9.6", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", + "@types/ws": "^8.18.1", "@types/yauzl": "^2.10.0", "@types/yazl": "^2.4.2", "@typescript-eslint/utils": "^8.8.0", @@ -246,8 +249,8 @@ "tsec": "0.2.7", "tslib": "^2.6.3", "tsup": "^8.4.0", - "typescript": "^5.8.0-dev.20250207", - "typescript-eslint": "^8.8.0", + "typescript": "5.8.2", + "typescript-eslint": "^8.55.0", "util": "^0.12.4", "webpack": "^5.94.0", "webpack-cli": "^5.1.4", diff --git a/remote/package-lock.json b/remote/package-lock.json index 7b52643c3f3..e5c9b7dfdaf 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -40,6 +40,7 @@ "native-watchdog": "^1.4.1", "node-pty": "^1.1.0-beta33", "tas-client-umd": "0.2.0", + "tslib": "^2.8.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.2.0", @@ -1403,6 +1404,12 @@ "resolved": "https://registry.npmjs.org/tas-client-umd/-/tas-client-umd-0.2.0.tgz", "integrity": "sha512-oezN7mJVm5qZDVEby7OzxCLKUpUN5of0rY4dvOWaDF2JZBlGpd3BXceFN8B53qlTaIkVSzP65aAMT0Vc+/N25Q==" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", diff --git a/remote/package.json b/remote/package.json index 203f38da292..a27f746364c 100644 --- a/remote/package.json +++ b/remote/package.json @@ -35,6 +35,7 @@ "native-watchdog": "^1.4.1", "node-pty": "^1.1.0-beta33", "tas-client-umd": "0.2.0", + "tslib": "^2.8.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.2.0", diff --git a/scripts/build-acp-vendor.mjs b/scripts/build-acp-vendor.mjs new file mode 100644 index 00000000000..50dc490182b --- /dev/null +++ b/scripts/build-acp-vendor.mjs @@ -0,0 +1,45 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { build } from 'esbuild'; +import { mkdir } from 'node:fs/promises'; +import { dirname, join, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const here = dirname(fileURLToPath(import.meta.url)); +const repoRoot = resolve(here, '..'); +const outDir = resolve(repoRoot, 'out/vs/platform/acp/electron-main/vendor'); + +const external = [ + 'node:*', 'fs', 'path', 'net', 'tls', 'http', 'https', 'zlib', 'stream', 'crypto', 'url', 'events', + 'bufferutil', 'utf-8-validate' +]; + +await mkdir(outDir, { recursive: true }); + +await build({ + entryPoints: [resolve(repoRoot, 'scripts/vendor/entry-acp-sdk.ts')], + bundle: true, + platform: 'node', + format: 'esm', + target: 'es2020', + outfile: join(outDir, 'acp-sdk.vendored.js'), + external +}); + +await build({ + entryPoints: [resolve(repoRoot, 'scripts/vendor/entry-ws.ts')], + bundle: true, + platform: 'node', + format: 'esm', + target: 'es2020', + outfile: join(outDir, 'ws.vendored.js'), + external, + banner: { + js: 'import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);' + } +}); + +console.log('[acp-vendor] built:', outDir); diff --git a/scripts/tsc-problems.sh b/scripts/tsc-problems.sh new file mode 100755 index 00000000000..6edb88e97e8 --- /dev/null +++ b/scripts/tsc-problems.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +set +e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +LOG_DIR="/tmp/tsc-problems-logs" +mkdir -p "$LOG_DIR" +LOG="$LOG_DIR/tsc-problems-$(date +%s).log" +MAX_OLD_SPACE_SIZE="${TSC_MAX_OLD_SPACE_SIZE:-12288}" +NODE_OPTS="--max-old-space-size=${MAX_OLD_SPACE_SIZE}" + +if [ -n "${NODE_OPTIONS:-}" ]; then + NODE_OPTS="${NODE_OPTS} ${NODE_OPTIONS}" +fi + +TSC_BIN="$ROOT/node_modules/typescript/bin/tsc" + +CFG="$ROOT/scripts/tsconfig.problems.src.json" + +echo "TS problems run started: $(date)" | tee "$LOG" +echo "Repo root: $ROOT" | tee -a "$LOG" +echo "Node options: $NODE_OPTS" | tee -a "$LOG" + +if [ -f "$CFG" ]; then + echo "\n--- Running tsc for $CFG ---" | tee -a "$LOG" + if [ -f "$TSC_BIN" ]; then + NODE_OPTIONS="$NODE_OPTS" node "$TSC_BIN" -p "$CFG" --noEmit --pretty false 2>&1 | tee -a "$LOG" + else + echo "Local tsc not found at $TSC_BIN, falling back to npx tsc" | tee -a "$LOG" + NODE_OPTIONS="$NODE_OPTS" npx tsc -p "$CFG" --noEmit --pretty false 2>&1 | tee -a "$LOG" + fi + echo "--- Finished $CFG ---\n" | tee -a "$LOG" +else + echo "Skipping missing config: $CFG" | tee -a "$LOG" +fi + +echo "TS problems run finished: $(date)" | tee -a "$LOG" + +exit 0 diff --git a/scripts/tsconfig.problems.json b/scripts/tsconfig.problems.json new file mode 100644 index 00000000000..3f65b51d564 --- /dev/null +++ b/scripts/tsconfig.problems.json @@ -0,0 +1,10 @@ +{ + "extends": "../src/tsconfig.json", + "compilerOptions": { + "noUnusedLocals": false, + "noUnusedParameters": false, + "jsx": "react-jsx" + } +} + + diff --git a/scripts/tsconfig.problems.src.json b/scripts/tsconfig.problems.src.json new file mode 100644 index 00000000000..10d882836a4 --- /dev/null +++ b/scripts/tsconfig.problems.src.json @@ -0,0 +1,17 @@ +{ + "extends": "./tsconfig.problems.json", + "include": [ + "../src/**/*.ts", + "../src/**/*.tsx" + ], + "exclude": [ + "../src/vs/workbench/contrib/void/browser/react/src2/**", + "../node_modules", + "../out", + "../out-vscode", + "../out-build", + "../dist", + "**/*.d.ts", + "**/node_modules/**" + ] +} diff --git a/scripts/vendor/entry-acp-sdk.ts b/scripts/vendor/entry-acp-sdk.ts new file mode 100644 index 00000000000..574350d17fc --- /dev/null +++ b/scripts/vendor/entry-acp-sdk.ts @@ -0,0 +1,6 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export * from '@agentclientprotocol/sdk'; diff --git a/scripts/vendor/entry-ws.ts b/scripts/vendor/entry-ws.ts new file mode 100644 index 00000000000..3d08f196e75 --- /dev/null +++ b/scripts/vendor/entry-ws.ts @@ -0,0 +1,6 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export { WebSocket, WebSocketServer } from 'ws'; diff --git a/src/bootstrap-fork.ts b/src/bootstrap-fork.ts index a92290a213d..8cc8dcc8dc2 100644 --- a/src/bootstrap-fork.ts +++ b/src/bootstrap-fork.ts @@ -50,7 +50,7 @@ function pipeLoggingToParent(): void { } try { - const res = JSON.stringify(argsArray, function (key, value: unknown) { + const res = JSON.stringify(argsArray, function (_key, value: unknown) { // Objects get special treatment to prevent circles if (isObject(value) || Array.isArray(value)) { @@ -123,7 +123,7 @@ function pipeLoggingToParent(): void { Object.defineProperty(stream, 'write', { set: () => { }, - get: () => (chunk: string | Buffer | Uint8Array, encoding: BufferEncoding | undefined, callback: ((err?: Error | undefined) => void) | undefined) => { + get: () => (chunk: string | Buffer | Uint8Array, encoding: BufferEncoding | undefined, callback: ((err?: Error | null) => void) | undefined) => { buf += chunk.toString(encoding); const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n'); if (eol !== -1) { diff --git a/src/server-main.ts b/src/server-main.ts index 4ffe8fcde12..cabbf5fc940 100644 --- a/src/server-main.ts +++ b/src/server-main.ts @@ -271,7 +271,7 @@ function prompt(question: string): Promise { input: process.stdin, output: process.stdout }); - return new Promise((resolve, reject) => { + return new Promise((resolve) => { rl.question(question + ' ', async function (data) { rl.close(); const str = data.toString().trim().toLowerCase(); diff --git a/src/tsconfig.base.json b/src/tsconfig.base.json index e354b0ed463..8b3a8db9d51 100644 --- a/src/tsconfig.base.json +++ b/src/tsconfig.base.json @@ -7,6 +7,7 @@ "noImplicitReturns": true, "noImplicitOverride": true, "noUnusedLocals": true, + "noUnusedParameters": true, "allowUnreachableCode": false, "strict": true, "exactOptionalPropertyTypes": false, diff --git a/src/vs/base/browser/ui/grid/gridview.ts b/src/vs/base/browser/ui/grid/gridview.ts index 09452c14992..eff2464bec1 100644 --- a/src/vs/base/browser/ui/grid/gridview.ts +++ b/src/vs/base/browser/ui/grid/gridview.ts @@ -414,7 +414,7 @@ class BranchNode implements ISplitView, IDisposable { readonly splitviewProportionalLayout: boolean, size: number = 0, orthogonalSize: number = 0, - edgeSnapping: boolean = false, + _edgeSnapping: boolean = false, childDescriptors?: INodeDescriptor[] ) { this._styles = styles; diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 6603fa22c39..d2c5747871f 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -1320,7 +1320,6 @@ export class AsyncEmitter extends Emitter { const [listener, data] = this._asyncDeliveryQueue.shift()!; const thenables: Promise[] = []; - // eslint-disable-next-line local/code-no-dangerous-type-assertions const event = { ...data, token, diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index cd976d77377..f7d55128222 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isTypedArray, isObject, isUndefinedOrNull } from './types.js'; +import { URI } from './uri.js'; export function deepClone(obj: T): T { if (!obj || typeof obj !== 'object') { @@ -12,6 +13,10 @@ export function deepClone(obj: T): T { if (obj instanceof RegExp) { return obj; } + // Handle URI objects specially + if (obj instanceof URI) { + return obj; + } const result: any = Array.isArray(obj) ? [] : {}; Object.entries(obj).forEach(([key, value]) => { result[key] = value && typeof value === 'object' ? deepClone(value) : value; diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 73a3aa6cd49..1a19a1e9e70 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -473,7 +473,6 @@ class Uri extends URI { } override toJSON(): UriComponents { - // eslint-disable-next-line local/code-no-dangerous-type-assertions const res = { $mid: MarshalledId.Uri }; diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 92bb92c78c6..5304e9bb297 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -554,7 +554,6 @@ export class ChannelClient implements IChannelClient, IDisposable { getChannel(channelName: string): T { const that = this; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { call(command: string, arg?: any, cancellationToken?: CancellationToken) { if (that.isDisposed) { @@ -861,7 +860,6 @@ export class IPCServer implements IChannelServer, I getChannel(channelName: string, routerOrClientFilter: IClientRouter | ((client: Client) => boolean)): T { const that = this; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { let connectionPromise: Promise>; @@ -1012,7 +1010,6 @@ export class IPCClient implements IChannelClient, IChannelSer } export function getDelayedChannel(promise: Promise): T { - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { return promise.then(c => c.call(command, arg, cancellationToken)); @@ -1029,7 +1026,6 @@ export function getDelayedChannel(promise: Promise): T { export function getNextTickChannel(channel: T): T { let didTick = false; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { if (didTick) { diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 2c5ef506bf1..1a88da972f5 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -102,7 +102,6 @@ export class Client implements IChannelClient, IDisposable { getChannel(channelName: string): T { const that = this; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { call(command: string, arg?: any, cancellationToken?: CancellationToken): Promise { return that.requestPromise(channelName, command, arg, cancellationToken); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index c3d2dfe5461..f70e4dd3e4d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -55,7 +55,7 @@ import { ProcessMainService } from '../../platform/process/electron-main/process import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from '../../platform/keyboardLayout/electron-main/keyboardLayoutMainService.js'; import { ILaunchMainService, LaunchMainService } from '../../platform/launch/electron-main/launchMainService.js'; import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from '../../platform/lifecycle/electron-main/lifecycleMainService.js'; -import { ILoggerService, ILogService } from '../../platform/log/common/log.js'; +import { ILoggerService, ILogService, LogLevel } from '../../platform/log/common/log.js'; import { IMenubarMainService, MenubarMainService } from '../../platform/menubar/electron-main/menubarMainService.js'; import { INativeHostMainService, NativeHostMainService } from '../../platform/native/electron-main/nativeHostMainService.js'; import { IProductService } from '../../platform/product/common/productService.js'; @@ -122,17 +122,21 @@ import { NativeMcpDiscoveryHelperService } from '../../platform/mcp/node/nativeM import { IWebContentExtractorService } from '../../platform/webContentExtractor/common/webContentExtractor.js'; import { NativeWebContentExtractorService } from '../../platform/webContentExtractor/electron-main/webContentExtractorService.js'; import ErrorTelemetry from '../../platform/telemetry/electron-main/errorTelemetry.js'; +import { startBuiltinAcpAgent } from '../../platform/acp/electron-main/acpBuiltinAgent.js'; +import { AcpChannel, AcpChannelName } from '../../platform/acp/common/acpIpc.js'; +import { AcpMainService } from '../../platform/acp/electron-main/acpMainService.js'; +import { installDebugFetchLogging } from '../../platform/void/electron-main/llmMessage/sendLLMMessage.impl.js'; +import { IMetricsService } from '../../platform/void/common/metricsService.js'; +import { IVoidUpdateService } from '../../platform/void/common/voidUpdateService.js'; +import { MetricsMainService } from '../../platform/void/electron-main/metricsMainService.js'; +import { VoidMainUpdateService } from '../../platform/void/electron-main/voidUpdateMainService.js'; +import { LLMMessageChannel } from '../../platform/void/electron-main/sendLLMMessageChannel.js'; +import { IRemoteModelsService } from '../../platform/void/common/remoteModelsService.js'; +import { RemoteModelsService } from '../../platform/void/electron-main/remoteModelsService.js'; +import { VoidSCMService } from '../../platform/void/electron-main/voidSCMMainService.js'; +import { IVoidSCMService } from '../../platform/void/common/voidSCMTypes.js'; +import { MCPChannel } from '../../platform/void/electron-main/mcpChannel.js'; -// in theory this is not allowed -// ignore the eslint errors below -import { IMetricsService } from '../../workbench/contrib/void/common/metricsService.js'; -import { IVoidUpdateService } from '../../workbench/contrib/void/common/voidUpdateService.js'; -import { MetricsMainService } from '../../workbench/contrib/void/electron-main/metricsMainService.js'; -import { VoidMainUpdateService } from '../../workbench/contrib/void/electron-main/voidUpdateMainService.js'; -import { LLMMessageChannel } from '../../workbench/contrib/void/electron-main/sendLLMMessageChannel.js'; -import { VoidSCMService } from '../../workbench/contrib/void/electron-main/voidSCMMainService.js'; -import { IVoidSCMService } from '../../workbench/contrib/void/common/voidSCMTypes.js'; -import { MCPChannel } from '../../workbench/contrib/void/electron-main/mcpChannel.js'; /** * The main VS Code application. There will only ever be one instance, * even if the user starts many instances (e.g. from the command line). @@ -608,7 +612,11 @@ export class CodeApplication extends Disposable { // Open Windows await appInstantiationService.invokeFunction(accessor => this.openFirstWindow(accessor, initialProtocolUrls)); - + try { + startBuiltinAcpAgent(this.logService, undefined, appInstantiationService); + } catch (e) { + this.logService.warn('Failed to start built-in ACP Agent', e); + } // Signal phase: after window open this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; @@ -1104,6 +1112,7 @@ export class CodeApplication extends Disposable { // Void main process services (required for services with a channel for comm between browser and electron-main (node)) services.set(IMetricsService, new SyncDescriptor(MetricsMainService, undefined, false)); services.set(IVoidUpdateService, new SyncDescriptor(VoidMainUpdateService, undefined, false)); + services.set(IRemoteModelsService, new SyncDescriptor(RemoteModelsService, undefined, false)); services.set(IVoidSCMService, new SyncDescriptor(VoidSCMService, undefined, false)); // Default Extensions Profile Init @@ -1227,6 +1236,14 @@ export class CodeApplication extends Disposable { const externalTerminalChannel = ProxyChannel.fromService(accessor.get(IExternalTerminalMainService), disposables); mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel); + //ACP + const instantiationService = accessor.get(IInstantiationService); + const acpMainService = instantiationService.createInstance(AcpMainService); + mainProcessElectronServer.registerChannel(AcpChannelName, new AcpChannel(acpMainService)); + Event.once(this.lifecycleMainService.onWillShutdown)(() => { + void acpMainService.disconnect().catch(() => undefined); + }); + // MCP const mcpDiscoveryChannel = ProxyChannel.fromService(accessor.get(INativeMcpDiscoveryHelperService), disposables); mainProcessElectronServer.registerChannel(NativeMcpDiscoveryHelperChannelName, mcpDiscoveryChannel); @@ -1243,15 +1260,26 @@ export class CodeApplication extends Disposable { const voidUpdatesChannel = ProxyChannel.fromService(accessor.get(IVoidUpdateService), disposables); mainProcessElectronServer.registerChannel('void-channel-update', voidUpdatesChannel); - const sendLLMMessageChannel = new LLMMessageChannel(accessor.get(IMetricsService)); + const logService = accessor.get(ILogService); + const lvl = logService.getLevel?.(); + if (lvl === LogLevel.Debug || lvl === LogLevel.Trace) { + installDebugFetchLogging(logService); + } + const sendLLMMessageChannel = new LLMMessageChannel( + accessor.get(IMetricsService), + logService, + ); mainProcessElectronServer.registerChannel('void-channel-llmMessage', sendLLMMessageChannel); + const remoteModelsChannel = ProxyChannel.fromService(accessor.get(IRemoteModelsService), disposables); + mainProcessElectronServer.registerChannel('void-channel-remoteModels', remoteModelsChannel); + // Void added this const voidSCMChannel = ProxyChannel.fromService(accessor.get(IVoidSCMService), disposables); mainProcessElectronServer.registerChannel('void-channel-scm', voidSCMChannel); // Void added this - const mcpChannel = new MCPChannel(); + const mcpChannel = new MCPChannel(logService); mainProcessElectronServer.registerChannel('void-channel-mcp', mcpChannel); // Extension Host Debug Broadcasting diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Bold.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Bold.woff2 new file mode 100644 index 00000000000..023512c051e Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Bold.woff2 differ diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-BoldItalic.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-BoldItalic.woff2 new file mode 100644 index 00000000000..f3e87a35abb Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-BoldItalic.woff2 differ diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Italic.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Italic.woff2 new file mode 100644 index 00000000000..e8eeb4b8e85 Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Italic.woff2 differ diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Medium.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Medium.woff2 new file mode 100644 index 00000000000..484c9e64152 Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Medium.woff2 differ diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-MediumItalic.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-MediumItalic.woff2 new file mode 100644 index 00000000000..e1279949e07 Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-MediumItalic.woff2 differ diff --git a/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Regular.woff2 b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Regular.woff2 new file mode 100644 index 00000000000..8c862e334da Binary files /dev/null and b/src/vs/code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Regular.woff2 differ diff --git a/src/vs/editor/browser/config/editorConfiguration.ts b/src/vs/editor/browser/config/editorConfiguration.ts index 701c940c736..2b2515a6582 100644 --- a/src/vs/editor/browser/config/editorConfiguration.ts +++ b/src/vs/editor/browser/config/editorConfiguration.ts @@ -16,7 +16,7 @@ import { TabFocus } from './tabFocus.js'; import { ComputeOptionsMemory, ConfigurationChangedEvent, EditorOption, editorOptionsRegistry, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, IEnvironmentalOptions } from '../../common/config/editorOptions.js'; import { EditorZoom } from '../../common/config/editorZoom.js'; import { BareFontInfo, FontInfo, IValidatedEditorOptions } from '../../common/config/fontInfo.js'; -import { IDimension } from '../../common/core/dimension.js'; +import { IDimension } from '../../../editor/common/language/core/dimension.js'; import { IEditorConfiguration } from '../../common/config/editorConfiguration.js'; import { AccessibilitySupport, IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js'; import { getWindow, getWindowById } from '../../../base/browser/dom.js'; diff --git a/src/vs/editor/browser/config/elementSizeObserver.ts b/src/vs/editor/browser/config/elementSizeObserver.ts index 303d6377cdf..b92da1a5298 100644 --- a/src/vs/editor/browser/config/elementSizeObserver.ts +++ b/src/vs/editor/browser/config/elementSizeObserver.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../base/common/lifecycle.js'; -import { IDimension } from '../../common/core/dimension.js'; +import { IDimension } from '../../../editor/common/language/core/dimension.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { getWindow, scheduleAtNextAnimationFrame } from '../../../base/browser/dom.js'; diff --git a/src/vs/editor/browser/controller/editContext/clipboardUtils.ts b/src/vs/editor/browser/controller/editContext/clipboardUtils.ts index 6095ad06da7..8c4bcfcdcb1 100644 --- a/src/vs/editor/browser/controller/editContext/clipboardUtils.ts +++ b/src/vs/editor/browser/controller/editContext/clipboardUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { IViewModel } from '../../../common/viewModel.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { isWindows } from '../../../../base/common/platform.js'; import { Mimes } from '../../../../base/common/mime.js'; diff --git a/src/vs/editor/browser/controller/editContext/editContext.ts b/src/vs/editor/browser/controller/editContext/editContext.ts index edcf2be3361..b6d53e7b71c 100644 --- a/src/vs/editor/browser/controller/editContext/editContext.ts +++ b/src/vs/editor/browser/controller/editContext/editContext.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { FastDomNode } from '../../../../base/browser/fastDomNode.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { IEditorAriaOptions } from '../../editorBrowser.js'; import { ViewPart } from '../../view/viewPart.js'; diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts index 9d17f5d9925..2f37537bf10 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -11,7 +11,7 @@ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { EndOfLinePreference, EndOfLineSequence, IModelDeltaDecoration } from '../../../../common/model.js'; +import { EndOfLinePreference, EndOfLineSequence, IModelDeltaDecoration } from '../../../../../editor/common/language/model.js'; import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewDecorationsChangedEvent, ViewFlushedEvent, ViewLinesChangedEvent, ViewLinesDeletedEvent, ViewLinesInsertedEvent, ViewScrollChangedEvent, ViewZonesChangedEvent } from '../../../../common/viewEvents.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import { RestrictedRenderingContext, RenderingContext } from '../../../view/renderingContext.js'; @@ -20,11 +20,11 @@ import { ClipboardEventUtils, ClipboardStoredMetadata, getDataToCopy, InMemoryCl import { AbstractEditContext } from '../editContext.js'; import { editContextAddDisposableListener, FocusTracker, ITypeData } from './nativeEditContextUtils.js'; import { ScreenReaderSupport } from './screenReaderSupport.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { Position } from '../../../../common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { IVisibleRangeProvider } from '../textArea/textAreaEditContext.js'; -import { PositionOffsetTransformer } from '../../../../common/core/positionToOffset.js'; +import { PositionOffsetTransformer } from '../../../../../editor/common/language/core/positionToOffset.js'; import { IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { EditContext } from './editContextFactory.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts index 93a03823220..1a1a5845a4b 100644 --- a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts +++ b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts @@ -10,10 +10,10 @@ import { AccessibilitySupport, IAccessibilityService } from '../../../../../plat import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { FontInfo } from '../../../../common/config/fontInfo.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { EndOfLinePreference } from '../../../../common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { EndOfLinePreference } from '../../../../../editor/common/language/model.js'; import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent } from '../../../../common/viewEvents.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; diff --git a/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts b/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts index 0a222d04817..2267fbb4df9 100644 --- a/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts +++ b/src/vs/editor/browser/controller/editContext/screenReaderUtils.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EndOfLinePreference } from '../../../common/model.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { EditorOption, IComputedEditorOptions } from '../../../common/config/editorOptions.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { AccessibilitySupport } from '../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts index a3f8b75544b..d911c00364d 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContext.ts @@ -17,19 +17,19 @@ import { LineNumbersOverlay } from '../../../viewParts/lineNumbers/lineNumbers.j import { Margin } from '../../../viewParts/margin/margin.js'; import { RenderLineNumbersType, EditorOption, IComputedEditorOptions, EditorOptions } from '../../../../common/config/editorOptions.js'; import { FontInfo } from '../../../../common/config/fontInfo.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ScrollType } from '../../../../common/editorCommon.js'; -import { EndOfLinePreference } from '../../../../common/model.js'; +import { EndOfLinePreference } from '../../../../../editor/common/language/model.js'; import { RenderingContext, RestrictedRenderingContext, HorizontalPosition, LineVisibleRanges } from '../../../view/renderingContext.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../../common/viewEvents.js'; import { AccessibilitySupport } from '../../../../../platform/accessibility/common/accessibility.js'; import { IEditorAriaOptions } from '../../../editorBrowser.js'; import { MOUSE_CURSOR_TEXT_CSS_CLASS_NAME } from '../../../../../base/browser/ui/mouseCursor/mouseCursor.js'; -import { TokenizationRegistry } from '../../../../common/languages.js'; -import { ColorId, ITokenPresentation } from '../../../../common/encodedTokenAttributes.js'; +import { TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { ColorId, ITokenPresentation } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { Color } from '../../../../../base/common/color.js'; import { IME } from '../../../../../base/common/ime.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; @@ -39,7 +39,7 @@ import { ICompositionData, IPasteData, ITextAreaInputHost, TextAreaInput, TextAr import { ariaLabelForScreenReaderContent, ISimpleModel, newlinecount, PagedScreenReaderStrategy } from '../screenReaderUtils.js'; import { ClipboardDataToCopy, getDataToCopy } from '../clipboardUtils.js'; import { _debugComposition, ITypeData, TextAreaState } from './textAreaEditContextState.js'; -import { getMapForWordSeparators, WordCharacterClass } from '../../../../common/core/wordCharacterClassifier.js'; +import { getMapForWordSeparators, WordCharacterClass } from '../../../../../editor/common/language/core/wordCharacterClassifier.js'; export interface IVisibleRangeProvider { visibleRangeForPosition(position: Position): HorizontalPosition | null; diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts index fc2dc0dd5ea..4586ea0c7c1 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts @@ -14,8 +14,8 @@ import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { OperatingSystem } from '../../../../../base/common/platform.js'; import * as strings from '../../../../../base/common/strings.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { ClipboardDataToCopy, ClipboardEventUtils, ClipboardStoredMetadata, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; diff --git a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts index 9a158f6d231..77b08ea5681 100644 --- a/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts +++ b/src/vs/editor/browser/controller/editContext/textArea/textAreaEditContextState.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { commonPrefixLength, commonSuffixLength } from '../../../../../base/common/strings.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ScreenReaderContentState } from '../screenReaderUtils.js'; export const _debugComposition = false; diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index b1e326d835a..85418902bb5 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -12,8 +12,8 @@ import { IMouseTarget, IMouseTargetOutsideEditor, IMouseTargetViewZoneData, Mous import { ClientCoordinates, EditorMouseEvent, EditorMouseEventFactory, GlobalEditorPointerMoveMonitor, createEditorPagePosition, createCoordinatesRelativeToEditor, PageCoordinates } from '../editorDom.js'; import { ViewController } from '../view/viewController.js'; import { EditorZoom } from '../../common/config/editorZoom.js'; -import { Position } from '../../common/core/position.js'; -import { Selection } from '../../common/core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { HorizontalPosition } from '../view/renderingContext.js'; import { ViewContext } from '../../common/viewModel/viewContext.js'; import * as viewEvents from '../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 28b39952393..82f64b8e82c 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -10,15 +10,15 @@ import { PartFingerprint, PartFingerprints } from '../view/viewPart.js'; import { ViewLine } from '../viewParts/viewLines/viewLine.js'; import { IViewCursorRenderData } from '../viewParts/viewCursors/viewCursor.js'; import { EditorLayoutInfo, EditorOption } from '../../common/config/editorOptions.js'; -import { Position } from '../../common/core/position.js'; -import { Range as EditorRange } from '../../common/core/range.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range as EditorRange } from '../../../editor/common/language/core/range.js'; import { HorizontalPosition } from '../view/renderingContext.js'; import { ViewContext } from '../../common/viewModel/viewContext.js'; import { IViewModel } from '../../common/viewModel.js'; -import { CursorColumns } from '../../common/core/cursorColumns.js'; +import { CursorColumns } from '../../../editor/common/language/core/cursorColumns.js'; import * as dom from '../../../base/browser/dom.js'; import { AtomicTabMoveOperations, Direction } from '../../common/cursor/cursorAtomicMoveOperations.js'; -import { PositionAffinity } from '../../common/model.js'; +import { PositionAffinity } from '../../../editor/common/language/model.js'; import { InjectedText } from '../../common/modelLineProjectionData.js'; import { Mutable } from '../../../base/common/types.js'; import { Lazy } from '../../../base/common/lazy.js'; diff --git a/src/vs/editor/browser/coreCommands.ts b/src/vs/editor/browser/coreCommands.ts index 8f1459c1fea..192ab05c696 100644 --- a/src/vs/editor/browser/coreCommands.ts +++ b/src/vs/editor/browser/coreCommands.ts @@ -17,8 +17,8 @@ import { DeleteOperations } from '../common/cursor/cursorDeleteOperations.js'; import { CursorChangeReason } from '../common/cursorEvents.js'; import { CursorMove as CursorMove_, CursorMoveCommands } from '../common/cursor/cursorMoveCommands.js'; import { TypeOperations } from '../common/cursor/cursorTypeOperations.js'; -import { IPosition, Position } from '../common/core/position.js'; -import { Range } from '../common/core/range.js'; +import { IPosition, Position } from '../../editor/common/language/core/position.js'; +import { Range } from '../../editor/common/language/core/range.js'; import { Handler, ScrollType } from '../common/editorCommon.js'; import { EditorContextKeys } from '../common/editorContextKeys.js'; import { VerticalRevealType } from '../common/viewEvents.js'; @@ -28,7 +28,7 @@ import { ServicesAccessor } from '../../platform/instantiation/common/instantiat import { KeybindingWeight, KeybindingsRegistry } from '../../platform/keybinding/common/keybindingsRegistry.js'; import { EditorOption } from '../common/config/editorOptions.js'; import { IViewModel } from '../common/viewModel.js'; -import { ISelection } from '../common/core/selection.js'; +import { ISelection } from '../../editor/common/language/core/selection.js'; import { getActiveElement, isEditableElement } from '../../base/browser/dom.js'; import { EnterOperation } from '../common/cursor/cursorTypeEditOperations.js'; diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index 082c14ea1d3..bdc0c39cf72 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -9,17 +9,17 @@ import { IBoundarySashes } from '../../base/browser/ui/sash/sash.js'; import { Event } from '../../base/common/event.js'; import { IEditorConstructionOptions } from './config/editorConfiguration.js'; import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IDiffEditorOptions, IEditorOptions, OverviewRulerPosition } from '../common/config/editorOptions.js'; -import { IDimension } from '../common/core/dimension.js'; -import { IPosition, Position } from '../common/core/position.js'; -import { IRange, Range } from '../common/core/range.js'; -import { Selection } from '../common/core/selection.js'; -import { IWordAtPosition } from '../common/core/wordHelper.js'; +import { IDimension } from '../../editor/common/language/core/dimension.js'; +import { IPosition, Position } from '../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../editor/common/language/core/range.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; +import { IWordAtPosition } from '../../editor/common/language/core/wordHelper.js'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IDiffComputationResult, ILineChange } from '../common/diff/legacyLinesDiffComputer.js'; import * as editorCommon from '../common/editorCommon.js'; -import { GlyphMarginLane, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from '../common/model.js'; +import { GlyphMarginLane, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from '../../editor/common/language/model.js'; import { InjectedText } from '../common/modelLineProjectionData.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../common/textModelEvents.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../../editor/common/language/textModelEvents.js'; import { IEditorWhitespace, IViewModel } from '../common/viewModel.js'; import { OverviewRulerZone } from '../common/viewModel/overviewZoneManager.js'; import { MenuId } from '../../platform/actions/common/actions.js'; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 3751b26de7d..c2a90f09ebe 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -7,11 +7,11 @@ import * as nls from '../../nls.js'; import { URI } from '../../base/common/uri.js'; import { ICodeEditor, IDiffEditor } from './editorBrowser.js'; import { ICodeEditorService } from './services/codeEditorService.js'; -import { Position } from '../common/core/position.js'; +import { Position } from '../../editor/common/language/core/position.js'; import { IEditorContribution, IDiffEditorContribution } from '../common/editorCommon.js'; -import { ITextModel } from '../common/model.js'; -import { IModelService } from '../common/services/model.js'; -import { ITextModelService } from '../common/services/resolverService.js'; +import { ITextModel } from '../../editor/common/language/model.js'; +import { IModelService } from '../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../editor/common/language/services/resolverService.js'; import { MenuId, MenuRegistry, Action2 } from '../../platform/actions/common/actions.js'; import { CommandsRegistry, ICommandMetadata } from '../../platform/commands/common/commands.js'; import { ContextKeyExpr, IContextKeyService, ContextKeyExpression } from '../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/browser/gpu/atlas/textureAtlas.ts b/src/vs/editor/browser/gpu/atlas/textureAtlas.ts index a4e5865c011..bcc8eb8feb5 100644 --- a/src/vs/editor/browser/gpu/atlas/textureAtlas.ts +++ b/src/vs/editor/browser/gpu/atlas/textureAtlas.ts @@ -11,7 +11,7 @@ import { Disposable, dispose, MutableDisposable, toDisposable } from '../../../. import { NKeyMap } from '../../../../base/common/map.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { GlyphRasterizer } from '../raster/glyphRasterizer.js'; import type { IGlyphRasterizer } from '../raster/raster.js'; import { IdleTaskQueue, type ITaskQueue } from '../taskQueue.js'; diff --git a/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts b/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts index d06871cc24d..82c957a9749 100644 --- a/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts +++ b/src/vs/editor/browser/gpu/raster/glyphRasterizer.ts @@ -6,8 +6,8 @@ import { memoize } from '../../../../base/common/decorators.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { isMacintosh } from '../../../../base/common/platform.js'; -import { StringBuilder } from '../../../common/core/stringBuilder.js'; -import { FontStyle, TokenMetadata } from '../../../common/encodedTokenAttributes.js'; +import { StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; +import { FontStyle, TokenMetadata } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ensureNonNullable } from '../gpuUtils.js'; import { ViewGpuContext } from '../viewGpuContext.js'; import { type IBoundingBox, type IGlyphRasterizer, type IRasterizedGlyph } from './raster.js'; diff --git a/src/vs/editor/browser/gpu/raster/raster.ts b/src/vs/editor/browser/gpu/raster/raster.ts index 32bee48f24d..8f6b2fbda05 100644 --- a/src/vs/editor/browser/gpu/raster/raster.ts +++ b/src/vs/editor/browser/gpu/raster/raster.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; +import type { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; export interface IGlyphRasterizer { /** diff --git a/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts index c41efda089b..e30e230589f 100644 --- a/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts +++ b/src/vs/editor/browser/gpu/renderStrategy/fullFileRenderStrategy.ts @@ -6,8 +6,8 @@ import { getActiveWindow } from '../../../../base/browser/dom.js'; import { Color } from '../../../../base/common/color.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; -import { CursorColumns } from '../../../common/core/cursorColumns.js'; -import type { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { CursorColumns } from '../../../../editor/common/language/core/cursorColumns.js'; +import type { IViewLineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; import { ViewEventType, type ViewConfigurationChangedEvent, type ViewDecorationsChangedEvent, type ViewLineMappingChangedEvent, type ViewLinesChangedEvent, type ViewLinesDeletedEvent, type ViewLinesInsertedEvent, type ViewScrollChangedEvent, type ViewThemeChangedEvent, type ViewTokensChangedEvent, type ViewZonesChangedEvent } from '../../../common/viewEvents.js'; import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import type { InlineDecoration, ViewLineRenderingData } from '../../../common/viewModel.js'; diff --git a/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts b/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts index f35ccc85edc..6e82a7a526f 100644 --- a/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts +++ b/src/vs/editor/browser/gpu/renderStrategy/viewportRenderStrategy.ts @@ -7,8 +7,8 @@ import { getActiveWindow } from '../../../../base/browser/dom.js'; import { Color } from '../../../../base/common/color.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { Emitter } from '../../../../base/common/event.js'; -import { CursorColumns } from '../../../common/core/cursorColumns.js'; -import type { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { CursorColumns } from '../../../../editor/common/language/core/cursorColumns.js'; +import type { IViewLineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; import { type ViewConfigurationChangedEvent, type ViewDecorationsChangedEvent, type ViewLineMappingChangedEvent, type ViewLinesChangedEvent, type ViewLinesDeletedEvent, type ViewLinesInsertedEvent, type ViewScrollChangedEvent, type ViewThemeChangedEvent, type ViewTokensChangedEvent, type ViewZonesChangedEvent } from '../../../common/viewEvents.js'; import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import type { InlineDecoration, ViewLineRenderingData } from '../../../common/viewModel.js'; diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index eb5172693ce..309a6e3c32e 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -7,13 +7,13 @@ import { equalsIfDefined, itemsEquals } from '../../base/common/equals.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; import { IObservable, IObservableWithChange, ITransaction, TransactionImpl, autorun, autorunOpts, derived, derivedOpts, derivedWithSetter, observableFromEvent, observableSignal, observableValue, observableValueOpts } from '../../base/common/observable.js'; import { EditorOption, FindComputedEditorOptionValueById } from '../common/config/editorOptions.js'; -import { LineRange } from '../common/core/lineRange.js'; -import { OffsetRange } from '../common/core/offsetRange.js'; -import { Position } from '../common/core/position.js'; -import { Selection } from '../common/core/selection.js'; +import { LineRange } from '../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../editor/common/language/core/position.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; -import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; -import { IModelContentChangedEvent } from '../common/textModelEvents.js'; +import { IModelDeltaDecoration, ITextModel } from '../../editor/common/language/model.js'; +import { IModelContentChangedEvent } from '../../editor/common/language/textModelEvents.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; import { Point } from './point.js'; diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/browser/rect.ts index e6de59e4fc8..036b161ec55 100644 --- a/src/vs/editor/browser/rect.ts +++ b/src/vs/editor/browser/rect.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { BugIndicatingError } from '../../base/common/errors.js'; -import { OffsetRange } from '../common/core/offsetRange.js'; +import { OffsetRange } from '../../editor/common/language/core/offsetRange.js'; import { Point } from './point.js'; export class Rect { diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 295d5bf1ca1..0646d318648 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -14,7 +14,7 @@ import { URI } from '../../../base/common/uri.js'; import { ICodeEditor, IDiffEditor } from '../editorBrowser.js'; import { ICodeEditorOpenHandler, ICodeEditorService } from './codeEditorService.js'; import { IContentDecorationRenderOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions, isThemeColor } from '../../common/editorCommon.js'; -import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, InjectedTextOptions, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../common/model.js'; +import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, InjectedTextOptions, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../../editor/common/language/model.js'; import { IResourceEditorInput } from '../../../platform/editor/common/editor.js'; import { IColorTheme, IThemeService } from '../../../platform/theme/common/themeService.js'; import { ThemeColor } from '../../../base/common/themables.js'; diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index fa12a2ab99e..644d6cc0e93 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from '../editorBrowser.js'; -import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, IWorkspaceFileEdit, WorkspaceFileEditOptions, IWorkspaceTextEdit } from '../../common/languages.js'; +import { TextEdit, WorkspaceEdit, WorkspaceEditMetadata, IWorkspaceFileEdit, WorkspaceFileEditOptions, IWorkspaceTextEdit } from '../../../editor/common/language/languages.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { IProgress, IProgressStep } from '../../../platform/progress/common/progress.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index 733018faea7..36cd0b13b87 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -6,7 +6,7 @@ import { Event } from '../../../base/common/event.js'; import { ICodeEditor, IDiffEditor } from '../editorBrowser.js'; import { IDecorationRenderOptions } from '../../common/editorCommon.js'; -import { IModelDecorationOptions, ITextModel } from '../../common/model.js'; +import { IModelDecorationOptions, ITextModel } from '../../../editor/common/language/model.js'; import { ITextResourceEditorInput } from '../../../platform/editor/common/editor.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { URI } from '../../../base/common/uri.js'; diff --git a/src/vs/editor/browser/services/editorWorkerService.ts b/src/vs/editor/browser/services/editorWorkerService.ts index f19765d9397..e37725d6a5f 100644 --- a/src/vs/editor/browser/services/editorWorkerService.ts +++ b/src/vs/editor/browser/services/editorWorkerService.ts @@ -8,31 +8,31 @@ import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; import { logOnceWebWorkerWarning, IWebWorkerClient, Proxied } from '../../../base/common/worker/webWorker.js'; import { createWebWorker, IWebWorkerDescriptor } from '../../../base/browser/webWorkerFactory.js'; -import { Position } from '../../common/core/position.js'; -import { IRange, Range } from '../../common/core/range.js'; -import { ITextModel } from '../../common/model.js'; -import * as languages from '../../common/languages.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import * as languages from '../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; -import { EditorWorker } from '../../common/services/editorWebWorker.js'; -import { DiffAlgorithmName, IEditorWorkerService, ILineChange, IUnicodeHighlightsResult } from '../../common/services/editorWorker.js'; -import { IModelService } from '../../common/services/model.js'; -import { ITextResourceConfigurationService } from '../../common/services/textResourceConfiguration.js'; +import { EditorWorker } from '../../../editor/common/language/services/editorWebWorker.js'; +import { DiffAlgorithmName, IEditorWorkerService, ILineChange, IUnicodeHighlightsResult } from '../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ITextResourceConfigurationService } from '../../../editor/common/language/services/textResourceConfiguration.js'; import { isNonEmptyArray } from '../../../base/common/arrays.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; import { canceled, onUnexpectedError } from '../../../base/common/errors.js'; -import { UnicodeHighlighterOptions } from '../../common/services/unicodeTextModelHighlighter.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; +import { UnicodeHighlighterOptions } from '../../../editor/common/language/services/unicodeTextModelHighlighter.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; import { IChange } from '../../common/diff/legacyLinesDiffComputer.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../common/diff/documentDiffProvider.js'; import { ILinesDiffComputerOptions, MovedText } from '../../common/diff/linesDiffComputer.js'; import { DetailedLineRangeMapping, RangeMapping, LineRangeMapping } from '../../common/diff/rangeMapping.js'; -import { LineRange } from '../../common/core/lineRange.js'; -import { SectionHeader, FindSectionHeaderOptions } from '../../common/services/findSectionHeaders.js'; +import { LineRange } from '../../../editor/common/language/core/lineRange.js'; +import { SectionHeader, FindSectionHeaderOptions } from '../../../editor/common/language/services/findSectionHeaders.js'; import { mainWindow } from '../../../base/browser/window.js'; import { WindowIntervalTimer } from '../../../base/browser/dom.js'; -import { WorkerTextModelSyncClient } from '../../common/services/textModelSync/textModelSync.impl.js'; -import { EditorWorkerHost } from '../../common/services/editorWorkerHost.js'; +import { WorkerTextModelSyncClient } from '../../../editor/common/language/services/textModelSync/textModelSync.impl.js'; +import { EditorWorkerHost } from '../../../editor/common/language/services/editorWorkerHost.js'; /** * Stop the worker if it was not needed for 5 min. diff --git a/src/vs/editor/browser/services/markerDecorations.ts b/src/vs/editor/browser/services/markerDecorations.ts index eb519c43fd9..dee5fcf1dd4 100644 --- a/src/vs/editor/browser/services/markerDecorations.ts +++ b/src/vs/editor/browser/services/markerDecorations.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMarkerDecorationsService } from '../../common/services/markerDecorations.js'; +import { IMarkerDecorationsService } from '../../../editor/common/language/services/markerDecorations.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../editorExtensions.js'; import { ICodeEditor } from '../editorBrowser.js'; import { IEditorContribution } from '../../common/editorCommon.js'; diff --git a/src/vs/editor/browser/stableEditorScroll.ts b/src/vs/editor/browser/stableEditorScroll.ts index 66d4d450e8e..97276ff8920 100644 --- a/src/vs/editor/browser/stableEditorScroll.ts +++ b/src/vs/editor/browser/stableEditorScroll.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from './editorBrowser.js'; -import { Position } from '../common/core/position.js'; +import { Position } from '../../editor/common/language/core/position.js'; import { ScrollType } from '../common/editorCommon.js'; export class StableEditorScrollState { diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index 53acaf14f7f..3538fe45141 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -43,11 +43,11 @@ import { ViewZones } from './viewParts/viewZones/viewZones.js'; import { WhitespaceOverlay } from './viewParts/whitespace/whitespace.js'; import { IEditorConfiguration } from '../common/config/editorConfiguration.js'; import { EditorOption } from '../common/config/editorOptions.js'; -import { Position } from '../common/core/position.js'; -import { Range } from '../common/core/range.js'; -import { Selection } from '../common/core/selection.js'; +import { Position } from '../../editor/common/language/core/position.js'; +import { Range } from '../../editor/common/language/core/range.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; import { ScrollType } from '../common/editorCommon.js'; -import { GlyphMarginLane, IGlyphMarginLanesModel } from '../common/model.js'; +import { GlyphMarginLane, IGlyphMarginLanesModel } from '../../editor/common/language/model.js'; import { ViewEventHandler } from '../common/viewEventHandler.js'; import * as viewEvents from '../common/viewEvents.js'; import { ViewportData } from '../common/viewLayout/viewLinesViewportData.js'; diff --git a/src/vs/editor/browser/view/domLineBreaksComputer.ts b/src/vs/editor/browser/view/domLineBreaksComputer.ts index 583d7f9cfa0..4ef9d4b3529 100644 --- a/src/vs/editor/browser/view/domLineBreaksComputer.ts +++ b/src/vs/editor/browser/view/domLineBreaksComputer.ts @@ -10,10 +10,10 @@ import { assertIsDefined } from '../../../base/common/types.js'; import { applyFontInfo } from '../config/domFontInfo.js'; import { WrappingIndent } from '../../common/config/editorOptions.js'; import { FontInfo } from '../../common/config/fontInfo.js'; -import { StringBuilder } from '../../common/core/stringBuilder.js'; -import { InjectedTextOptions } from '../../common/model.js'; +import { StringBuilder } from '../../../editor/common/language/core/stringBuilder.js'; +import { InjectedTextOptions } from '../../../editor/common/language/model.js'; import { ILineBreaksComputer, ILineBreaksComputerFactory, ModelLineProjectionData } from '../../common/modelLineProjectionData.js'; -import { LineInjectedText } from '../../common/textModelEvents.js'; +import { LineInjectedText } from '../../../editor/common/language/textModelEvents.js'; const ttPolicy = createTrustedTypesPolicy('domLineBreaksComputer', { createHTML: value => value }); diff --git a/src/vs/editor/browser/view/renderingContext.ts b/src/vs/editor/browser/view/renderingContext.ts index e5ec4b30eca..89292e17743 100644 --- a/src/vs/editor/browser/view/renderingContext.ts +++ b/src/vs/editor/browser/view/renderingContext.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../../common/core/position.js'; -import { Range } from '../../common/core/range.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { ViewportData } from '../../common/viewLayout/viewLinesViewportData.js'; import { IViewLayout, ViewModelDecoration } from '../../common/viewModel.js'; diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index 5e53d12d2e4..b9a2fc768cf 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -7,8 +7,8 @@ import { IKeyboardEvent } from '../../../base/browser/keyboardEvent.js'; import { CoreNavigationCommands, NavigationCommandRevealType } from '../coreCommands.js'; import { IEditorMouseEvent, IPartialEditorMouseEvent } from '../editorBrowser.js'; import { ViewUserInputEvents } from './viewUserInputEvents.js'; -import { Position } from '../../common/core/position.js'; -import { Selection } from '../../common/core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { IEditorConfiguration } from '../../common/config/editorConfiguration.js'; import { IViewModel } from '../../common/viewModel.js'; import { IMouseWheelEvent } from '../../../base/browser/mouseEvent.js'; diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 4058205be2f..0a663e9d05c 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -7,7 +7,7 @@ import { FastDomNode, createFastDomNode } from '../../../base/browser/fastDomNod import { createTrustedTypesPolicy } from '../../../base/browser/trustedTypes.js'; import { BugIndicatingError } from '../../../base/common/errors.js'; import { EditorOption } from '../../common/config/editorOptions.js'; -import { StringBuilder } from '../../common/core/stringBuilder.js'; +import { StringBuilder } from '../../../editor/common/language/core/stringBuilder.js'; import * as viewEvents from '../../common/viewEvents.js'; import { ViewportData } from '../../common/viewLayout/viewLinesViewportData.js'; diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 6b8e10f341c..1792fb8fcbd 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -8,7 +8,7 @@ import { applyFontInfo } from '../config/domFontInfo.js'; import { DynamicViewOverlay } from './dynamicViewOverlay.js'; import { IVisibleLine, VisibleLinesCollection } from './viewLayer.js'; import { ViewPart } from './viewPart.js'; -import { StringBuilder } from '../../common/core/stringBuilder.js'; +import { StringBuilder } from '../../../editor/common/language/core/stringBuilder.js'; import { RenderingContext, RestrictedRenderingContext } from './renderingContext.js'; import { ViewContext } from '../../common/viewModel/viewContext.js'; import * as viewEvents from '../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/view/viewUserInputEvents.ts b/src/vs/editor/browser/view/viewUserInputEvents.ts index 8a8bf0aa8ff..ca94abcbe27 100644 --- a/src/vs/editor/browser/view/viewUserInputEvents.ts +++ b/src/vs/editor/browser/view/viewUserInputEvents.ts @@ -7,7 +7,7 @@ import { IKeyboardEvent } from '../../../base/browser/keyboardEvent.js'; import { IEditorMouseEvent, IMouseTarget, IMouseTargetViewZoneData, IPartialEditorMouseEvent, MouseTargetType } from '../editorBrowser.js'; import { ICoordinatesConverter } from '../../common/viewModel.js'; import { IMouseWheelEvent } from '../../../base/browser/mouseEvent.js'; -import { Position } from '../../common/core/position.js'; +import { Position } from '../../../editor/common/language/core/position.js'; export interface EventCallback { (event: T): void; diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 6ae696f90dd..2607d7c6c6d 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -12,9 +12,9 @@ import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; -import { PositionAffinity } from '../../../common/model.js'; -import { IPosition, Position } from '../../../common/core/position.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { PositionAffinity } from '../../../../editor/common/language/model.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { IViewModel } from '../../../common/viewModel.js'; /** diff --git a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts index 8d627025769..70c97dfedf9 100644 --- a/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts +++ b/src/vs/editor/browser/viewParts/currentLineHighlight/currentLineHighlight.ts @@ -5,16 +5,16 @@ import './currentLineHighlight.css'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; -import { editorLineHighlight, editorLineHighlightBorder } from '../../../common/core/editorColorRegistry.js'; +import { editorLineHighlight, editorLineHighlightBorder } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; import * as arrays from '../../../../base/common/arrays.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { isHighContrast } from '../../../../platform/theme/common/theme.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; export abstract class AbstractLineHighlightOverlay extends DynamicViewOverlay { private readonly _context: ViewContext; diff --git a/src/vs/editor/browser/viewParts/decorations/decorations.ts b/src/vs/editor/browser/viewParts/decorations/decorations.ts index 6ae2287ccfc..502d75c27a2 100644 --- a/src/vs/editor/browser/viewParts/decorations/decorations.ts +++ b/src/vs/editor/browser/viewParts/decorations/decorations.ts @@ -7,7 +7,7 @@ import './decorations.css'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; import { HorizontalRange, RenderingContext } from '../../view/renderingContext.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewModelDecoration } from '../../../common/viewModel.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; diff --git a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts index c1234141862..d108cb3bbee 100644 --- a/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts +++ b/src/vs/editor/browser/viewParts/glyphMargin/glyphMargin.ts @@ -11,9 +11,9 @@ import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewPart } from '../../view/viewPart.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { GlyphMarginLane } from '../../../common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { GlyphMarginLane } from '../../../../editor/common/language/model.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; diff --git a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts index a140386e6ca..3c947734dda 100644 --- a/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts +++ b/src/vs/editor/browser/viewParts/indentGuides/indentGuides.ts @@ -5,17 +5,17 @@ import './indentGuides.css'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; -import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6, editorIndentGuide1, editorIndentGuide2, editorIndentGuide3, editorIndentGuide4, editorIndentGuide5, editorIndentGuide6, editorActiveIndentGuide1, editorActiveIndentGuide2, editorActiveIndentGuide3, editorActiveIndentGuide4, editorActiveIndentGuide5, editorActiveIndentGuide6 } from '../../../common/core/editorColorRegistry.js'; +import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6, editorIndentGuide1, editorIndentGuide2, editorIndentGuide3, editorIndentGuide4, editorIndentGuide5, editorIndentGuide6, editorActiveIndentGuide1, editorActiveIndentGuide2, editorActiveIndentGuide3, editorActiveIndentGuide4, editorActiveIndentGuide5, editorActiveIndentGuide6 } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; import { EditorOption, InternalGuidesOptions } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { ArrayQueue } from '../../../../base/common/arrays.js'; import { Color } from '../../../../base/common/color.js'; import { isDefined } from '../../../../base/common/types.js'; -import { BracketPairGuidesClassNames } from '../../../common/model/guidesTextModelPart.js'; +import { BracketPairGuidesClassNames } from '../../../../editor/common/language/model/guidesTextModelPart.js'; import { IndentGuide, HorizontalGuidesState } from '../../../common/textModelGuides.js'; /** diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index ee6461fd7f8..ca229e923be 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -7,13 +7,13 @@ import './lineNumbers.css'; import * as platform from '../../../../base/common/platform.js'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; import { RenderLineNumbersType, EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; -import { editorDimmedLineNumber, editorLineNumbers } from '../../../common/core/editorColorRegistry.js'; +import { editorDimmedLineNumber, editorLineNumbers } from '../../../../editor/common/language/core/editorColorRegistry.js'; /** * Renders line numbers to the left of the main view lines content. diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index d57f8a9a28c..fe32dbc1fee 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -14,11 +14,11 @@ import * as strings from '../../../../base/common/strings.js'; import { ILine, RenderedLinesCollection } from '../../view/viewLayer.js'; import { PartFingerprint, PartFingerprints, ViewPart } from '../../view/viewPart.js'; import { RenderMinimap, EditorOption, MINIMAP_GUTTER_WIDTH, EditorLayoutInfoComputer } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; -import { RGBA8 } from '../../../common/core/rgba.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { RGBA8 } from '../../../../editor/common/language/core/rgba.js'; import { ScrollType } from '../../../common/editorCommon.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; -import { ColorId } from '../../../common/encodedTokenAttributes.js'; +import { ColorId } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { MinimapCharRenderer } from './minimapCharRenderer.js'; import { Constants } from './minimapCharSheet.js'; import { MinimapTokensColorTracker } from '../../../common/viewModel/minimapTokensColorTracker.js'; @@ -28,12 +28,12 @@ import { EditorTheme } from '../../../common/editorTheme.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewLineData, ViewModelDecoration } from '../../../common/viewModel.js'; import { minimapSelection, minimapBackground, minimapForegroundOpacity, editorForeground } from '../../../../platform/theme/common/colorRegistry.js'; -import { ModelDecorationMinimapOptions } from '../../../common/model/textModel.js'; -import { Selection } from '../../../common/core/selection.js'; +import { ModelDecorationMinimapOptions } from '../../../../editor/common/language/model/textModel.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { Color } from '../../../../base/common/color.js'; import { GestureEvent, EventType, Gesture } from '../../../../base/browser/touch.js'; import { MinimapCharRendererFactory } from './minimapCharRendererFactory.js'; -import { MinimapPosition, MinimapSectionHeaderStyle, TextModelResolvedOptions } from '../../../common/model.js'; +import { MinimapPosition, MinimapSectionHeaderStyle, TextModelResolvedOptions } from '../../../../editor/common/language/model.js'; import { createSingleCallFunction } from '../../../../base/common/functional.js'; import { LRUCache } from '../../../../base/common/map.js'; import { DEFAULT_FONT_FAMILY } from '../../../../base/browser/fonts.js'; diff --git a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts index 687ab96b521..cf087916309 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimapCharRenderer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RGBA8 } from '../../../common/core/rgba.js'; +import { RGBA8 } from '../../../../editor/common/language/core/rgba.js'; import { Constants, getCharIndex } from './minimapCharSheet.js'; import { toUint8 } from '../../../../base/common/uint.js'; diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index c494f6e2dfb..b89d78e6e64 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -7,10 +7,10 @@ import { FastDomNode, createFastDomNode } from '../../../../base/browser/fastDom import { Color } from '../../../../base/common/color.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ViewPart } from '../../view/viewPart.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; -import { TokenizationRegistry } from '../../../common/languages.js'; -import { editorCursorForeground, editorOverviewRulerBorder, editorOverviewRulerBackground, editorMultiCursorSecondaryForeground, editorMultiCursorPrimaryForeground } from '../../../common/core/editorColorRegistry.js'; +import { TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { editorCursorForeground, editorOverviewRulerBorder, editorOverviewRulerBackground, editorMultiCursorSecondaryForeground, editorMultiCursorPrimaryForeground } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import { EditorTheme } from '../../../common/editorTheme.js'; diff --git a/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts b/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts index af0b20eb9a7..4d1d1088476 100644 --- a/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts +++ b/src/vs/editor/browser/viewParts/rulersGpu/rulersGpu.ts @@ -12,7 +12,7 @@ import type { ViewGpuContext } from '../../gpu/viewGpuContext.js'; import type { IObjectCollectionBufferEntry } from '../../gpu/objectCollectionBuffer.js'; import type { RectangleRenderer, RectangleRendererEntrySpec } from '../../gpu/rectangleRenderer.js'; import { Color } from '../../../../base/common/color.js'; -import { editorRuler } from '../../../common/core/editorColorRegistry.js'; +import { editorRuler } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { autorun, type IReader } from '../../../../base/common/observable.js'; /** diff --git a/src/vs/editor/browser/viewParts/selections/selections.ts b/src/vs/editor/browser/viewParts/selections/selections.ts index a882f80f4f1..569f5ea2ec6 100644 --- a/src/vs/editor/browser/viewParts/selections/selections.ts +++ b/src/vs/editor/browser/viewParts/selections/selections.ts @@ -5,7 +5,7 @@ import './selections.css'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { HorizontalRange, LineVisibleRanges, RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts index b4380373c52..e6d53026023 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursor.ts @@ -8,8 +8,8 @@ import { FastDomNode, createFastDomNode } from '../../../../base/browser/fastDom import * as strings from '../../../../base/common/strings.js'; import { applyFontInfo } from '../../config/domFontInfo.js'; import { TextEditorCursorStyle, EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts index 82c46115a1b..46f05e1386f 100644 --- a/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts +++ b/src/vs/editor/browser/viewParts/viewCursors/viewCursors.ts @@ -9,12 +9,12 @@ import { IntervalTimer, TimeoutTimer } from '../../../../base/common/async.js'; import { ViewPart } from '../../view/viewPart.js'; import { IViewCursorRenderData, ViewCursor, CursorPlurality } from './viewCursor.js'; import { TextEditorCursorBlinkingStyle, TextEditorCursorStyle, EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { editorCursorBackground, editorCursorForeground, editorMultiCursorPrimaryForeground, editorMultiCursorPrimaryBackground, editorMultiCursorSecondaryForeground, editorMultiCursorSecondaryBackground -} from '../../../common/core/editorColorRegistry.js'; +} from '../../../../editor/common/language/core/editorColorRegistry.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts index 16229cd928a..0c530fa42c5 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLine.ts @@ -8,7 +8,7 @@ import { FastDomNode, createFastDomNode } from '../../../../base/browser/fastDom import * as platform from '../../../../base/common/platform.js'; import { IVisibleLine } from '../../view/viewLayer.js'; import { RangeUtil } from './rangeUtil.js'; -import { StringBuilder } from '../../../common/core/stringBuilder.js'; +import { StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; import { FloatHorizontalRange, VisibleRanges } from '../../view/renderingContext.js'; import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js'; import { CharacterMapping, ForeignElementType, RenderLineInput, renderViewLine, LineRange, DomPosition } from '../../../common/viewLayout/viewLineRenderer.js'; diff --git a/src/vs/editor/browser/viewParts/viewLines/viewLines.ts b/src/vs/editor/browser/viewParts/viewLines/viewLines.ts index 78324773a68..a2269afacc8 100644 --- a/src/vs/editor/browser/viewParts/viewLines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/viewLines/viewLines.ts @@ -16,9 +16,9 @@ import { PartFingerprint, PartFingerprints, ViewPart } from '../../view/viewPart import { DomReadingContext } from './domReadingContext.js'; import { ViewLine } from './viewLine.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ScrollType } from '../../../common/editorCommon.js'; import * as viewEvents from '../../../common/viewEvents.js'; import { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; diff --git a/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts b/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts index a6cabd55951..70b3343015b 100644 --- a/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts +++ b/src/vs/editor/browser/viewParts/viewLinesGpu/viewLinesGpu.ts @@ -9,8 +9,8 @@ import { autorun, runOnChange } from '../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import type { ViewportData } from '../../../common/viewLayout/viewLinesViewportData.js'; import type { ViewContext } from '../../../common/viewModel/viewContext.js'; import { TextureAtlasPage } from '../../gpu/atlas/textureAtlasPage.js'; @@ -22,7 +22,7 @@ import { FloatHorizontalRange, HorizontalPosition, HorizontalRange, IViewLines, import { ViewPart } from '../../view/viewPart.js'; import { ViewLineOptions } from '../viewLines/viewLineOptions.js'; import type * as viewEvents from '../../../common/viewEvents.js'; -import { CursorColumns } from '../../../common/core/cursorColumns.js'; +import { CursorColumns } from '../../../../editor/common/language/core/cursorColumns.js'; import { TextureAtlas } from '../../gpu/atlas/textureAtlas.js'; import { createContentSegmenter, type IContentSegmenter } from '../../gpu/contentSegmenter.js'; import { ViewportRenderStrategy } from '../../gpu/renderStrategy/viewportRenderStrategy.js'; diff --git a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts index 4b94e5660d3..2398bb18bc5 100644 --- a/src/vs/editor/browser/viewParts/viewZones/viewZones.ts +++ b/src/vs/editor/browser/viewParts/viewZones/viewZones.ts @@ -7,7 +7,7 @@ import { FastDomNode, createFastDomNode } from '../../../../base/browser/fastDom import { onUnexpectedError } from '../../../../base/common/errors.js'; import { IViewZone, IViewZoneChangeAccessor } from '../../editorBrowser.js'; import { ViewPart } from '../../view/viewPart.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { RenderingContext, RestrictedRenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; diff --git a/src/vs/editor/browser/viewParts/whitespace/whitespace.ts b/src/vs/editor/browser/viewParts/whitespace/whitespace.ts index 56cc4692dc0..40f5551ce6e 100644 --- a/src/vs/editor/browser/viewParts/whitespace/whitespace.ts +++ b/src/vs/editor/browser/viewParts/whitespace/whitespace.ts @@ -5,7 +5,7 @@ import './whitespace.css'; import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { RenderingContext } from '../../view/renderingContext.js'; import { ViewContext } from '../../../common/viewModel/viewContext.js'; import * as viewEvents from '../../../common/viewEvents.js'; @@ -15,8 +15,8 @@ import { IEditorConfiguration } from '../../../common/config/editorConfiguration import * as strings from '../../../../base/common/strings.js'; import { CharCode } from '../../../../base/common/charCode.js'; import { LineRange } from '../../../common/viewLayout/viewLineRenderer.js'; -import { Position } from '../../../common/core/position.js'; -import { editorWhitespaces } from '../../../common/core/editorColorRegistry.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { editorWhitespaces } from '../../../../editor/common/language/core/editorColorRegistry.js'; /** * The whitespace overlay will visual certain whitespace depending on the diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index fc0a05c5ede..1dcad3fa9e1 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -27,24 +27,24 @@ import { ViewUserInputEvents } from '../../view/viewUserInputEvents.js'; import { CodeEditorContributions } from './codeEditorContributions.js'; import { IEditorConfiguration } from '../../../common/config/editorConfiguration.js'; import { ConfigurationChangedEvent, EditorLayoutInfo, EditorOption, FindComputedEditorOptionValueById, IComputedEditorOptions, IEditorOptions, filterValidationDecorations } from '../../../common/config/editorOptions.js'; -import { CursorColumns } from '../../../common/core/cursorColumns.js'; -import { IDimension } from '../../../common/core/dimension.js'; -import { editorUnnecessaryCodeOpacity } from '../../../common/core/editorColorRegistry.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { ISelection, Selection } from '../../../common/core/selection.js'; -import { IWordAtPosition } from '../../../common/core/wordHelper.js'; +import { CursorColumns } from '../../../../editor/common/language/core/cursorColumns.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { editorUnnecessaryCodeOpacity } from '../../../../editor/common/language/core/editorColorRegistry.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../../editor/common/language/core/selection.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; import { WordOperations } from '../../../common/cursor/cursorWordOperations.js'; import { CursorChangeReason, ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from '../../../common/cursorEvents.js'; import { InternalEditorAction } from '../../../common/editorAction.js'; import * as editorCommon from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../common/model.js'; -import { ClassName } from '../../../common/model/intervalTree.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../../../common/textModelEvents.js'; +import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IIdentifiedSingleEditOperation, IModelDecoration, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../../editor/common/language/model.js'; +import { ClassName } from '../../../../editor/common/language/model/intervalTree.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { VerticalRevealType } from '../../../common/viewEvents.js'; import { IEditorWhitespace, IViewModel } from '../../../common/viewModel.js'; import { MonospaceLineBreaksComputerFactory } from '../../../common/viewModel/monospaceLineBreaksComputer.js'; diff --git a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts index 3852374d394..c245a3f15d7 100644 --- a/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget.ts @@ -9,7 +9,7 @@ import { ICodeEditorService } from '../../services/codeEditorService.js'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from './codeEditorWidget.js'; import { ConfigurationChangedEvent, IEditorOptions } from '../../../common/config/editorOptions.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts index 8a1c508df03..7a1b49ec59d 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/accessibleDiffViewer.ts @@ -16,15 +16,15 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; import { applyStyle } from '../utils.js'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../common/core/offsetRange.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { DetailedLineRangeMapping, LineRangeMapping } from '../../../../common/diff/rangeMapping.js'; -import { ILanguageIdCodec } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; -import { ITextModel, TextModelResolvedOptions } from '../../../../common/model.js'; -import { LineTokens } from '../../../../common/tokens/lineTokens.js'; +import { ILanguageIdCodec } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModel, TextModelResolvedOptions } from '../../../../../editor/common/language/model.js'; +import { LineTokens } from '../../../../../editor/common/language/tokens/lineTokens.js'; import { RenderLineInput, renderViewLine2 } from '../../../../common/viewLayout/viewLineRenderer.js'; import { ViewLineRenderingData } from '../../../../common/viewModel.js'; import { localize } from '../../../../../nls.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorDecorations.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorDecorations.ts index 609dd70fcef..70adda58efe 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorDecorations.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorDecorations.ts @@ -13,7 +13,7 @@ import { DiffEditorWidget } from '../diffEditorWidget.js'; import { MovedBlocksLinesFeature } from '../features/movedBlocksLinesFeature.js'; import { diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineAddDecorationBackground, diffLineAddDecorationBackgroundWithIndicator, diffLineDeleteDecorationBackground, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from '../registrations.contribution.js'; import { applyObservableDecorations } from '../utils.js'; -import { IModelDeltaDecoration } from '../../../../common/model.js'; +import { IModelDeltaDecoration } from '../../../../../editor/common/language/model.js'; export class DiffEditorDecorations extends Disposable { constructor( diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts index 638c9fe8bd5..93bc4d72a65 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts @@ -13,7 +13,7 @@ import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../codeEditor/cod import { IDiffCodeEditorWidgetOptions } from '../diffEditorWidget.js'; import { OverviewRulerFeature } from '../features/overviewRulerFeature.js'; import { EditorOptions, IEditorOptions } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { IContentSizeChangedEvent } from '../../../../common/editorCommon.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index 634939aacee..052c7113ef9 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -21,16 +21,16 @@ import { InlineDiffDeletedCodeMargin } from './inlineDiffDeletedCodeMargin.js'; import { LineSource, RenderOptions, renderLines } from './renderLines.js'; import { IObservableViewZone, animatedObservable, joinCombine } from '../../utils.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; -import { Position } from '../../../../../common/core/position.js'; +import { LineRange } from '../../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; import { DetailedLineRangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { ScrollType } from '../../../../../common/editorCommon.js'; -import { BackgroundTokenizationState } from '../../../../../common/tokenizationTextModelPart.js'; +import { BackgroundTokenizationState } from '../../../../../../editor/common/language/tokenizationTextModelPart.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../common/viewModel.js'; import { IClipboardService } from '../../../../../../platform/clipboard/common/clipboardService.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; import { DiffEditorOptions } from '../../diffEditorOptions.js'; -import { Range } from '../../../../../common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; /** * Ensures both editors have the same height by aligning unchanged lines. diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts index fa63055ff08..0f1822f21b0 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/inlineDiffDeletedCodeMargin.ts @@ -14,7 +14,7 @@ import { CodeEditorWidget } from '../../../codeEditor/codeEditorWidget.js'; import { DiffEditorWidget } from '../../diffEditorWidget.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { DetailedLineRangeMapping } from '../../../../../common/diff/rangeMapping.js'; -import { EndOfLineSequence, ITextModel } from '../../../../../common/model.js'; +import { EndOfLineSequence, ITextModel } from '../../../../../../editor/common/language/model.js'; import { localize } from '../../../../../../nls.js'; import { IClipboardService } from '../../../../../../platform/clipboard/common/clipboardService.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts index ac4aee78443..11a2374ebe4 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.ts @@ -8,9 +8,9 @@ import { applyFontInfo } from '../../../../config/domFontInfo.js'; import { ICodeEditor } from '../../../../editorBrowser.js'; import { EditorFontLigatures, EditorOption, FindComputedEditorOptionValueById } from '../../../../../common/config/editorOptions.js'; import { FontInfo } from '../../../../../common/config/fontInfo.js'; -import { StringBuilder } from '../../../../../common/core/stringBuilder.js'; +import { StringBuilder } from '../../../../../../editor/common/language/core/stringBuilder.js'; import { ModelLineProjectionData } from '../../../../../common/modelLineProjectionData.js'; -import { IViewLineTokens, LineTokens } from '../../../../../common/tokens/lineTokens.js'; +import { IViewLineTokens, LineTokens } from '../../../../../../editor/common/language/tokens/lineTokens.js'; import { LineDecoration } from '../../../../../common/viewLayout/lineDecorations.js'; import { RenderLineInput, renderViewLine } from '../../../../../common/viewLayout/viewLineRenderer.js'; import { InlineDecoration, ViewLineRenderingData } from '../../../../../common/viewModel.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts index 056db97f620..e816f7f6ff7 100644 --- a/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts +++ b/src/vs/editor/browser/widget/diffEditor/delegatingEditorImpl.ts @@ -7,12 +7,12 @@ import { Emitter } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { CodeEditorWidget } from '../codeEditor/codeEditorWidget.js'; import { IEditorOptions } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { ISelection, Selection } from '../../../common/core/selection.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../../editor/common/language/core/selection.js'; import { IDiffEditorViewModel, IEditor, IEditorAction, IEditorDecorationsCollection, IEditorModel, IEditorViewState, ScrollType } from '../../../common/editorCommon.js'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from '../../../common/model.js'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; export abstract class DelegatingEditor extends Disposable implements IEditor { private static idCounter = 0; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index 25a79288cc2..d2a94009c69 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -10,15 +10,15 @@ import { IObservable, IReader, ISettableObservable, ITransaction, autorun, autor import { IDiffProviderFactoryService } from './diffProviderFactoryService.js'; import { filterWithPrevious } from './utils.js'; import { readHotReloadableExport } from '../../../../base/common/hotReloadHelpers.js'; -import { ISerializedLineRange, LineRange, LineRangeSet } from '../../../common/core/lineRange.js'; +import { ISerializedLineRange, LineRange, LineRangeSet } from '../../../../editor/common/language/core/lineRange.js'; import { DefaultLinesDiffComputer } from '../../../common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; import { IDocumentDiff } from '../../../common/diff/documentDiffProvider.js'; import { MovedText } from '../../../common/diff/linesDiffComputer.js'; import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; import { IDiffEditorModel, IDiffEditorViewModel } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; -import { TextEditInfo } from '../../../common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; -import { combineTextEditInfos } from '../../../common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { TextEditInfo } from '../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; +import { combineTextEditInfos } from '../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.js'; import { DiffEditorOptions } from './diffEditorOptions.js'; import { optimizeSequenceDiffs } from '../../../common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.js'; import { isDefined } from '../../../../base/common/types.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 046f825dfe8..b6deea96ef6 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -17,15 +17,15 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { IDimension } from '../../../common/core/dimension.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; import { IDiffComputationResult, ILineChange } from '../../../common/diff/legacyLinesDiffComputer.js'; import { LineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; import { EditorType, IDiffEditorModel, IDiffEditorViewModel, IDiffEditorViewState } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { IIdentifiedSingleEditOperation } from '../../../common/model.js'; +import { IIdentifiedSingleEditOperation } from '../../../../editor/common/language/model.js'; import { IEditorConstructionOptions } from '../../config/editorConfiguration.js'; import { ICodeEditor, IDiffEditor, IDiffEditorConstructionOptions } from '../../editorBrowser.js'; import { EditorExtensionsRegistry, IDiffEditorContributionDescription } from '../../editorExtensions.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts index ab087b4ffb6..14deb263cee 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffProviderFactoryService.ts @@ -9,11 +9,11 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { LineRange } from '../../../common/core/lineRange.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from '../../../common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; -import { ITextModel } from '../../../common/model.js'; -import { DiffAlgorithmName, IEditorWorkerService } from '../../../common/services/editorWorker.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; export const IDiffProviderFactoryService = createDecorator('diffProviderFactoryService'); diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index 0d8b665b9e4..ee60fc061f3 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -17,12 +17,12 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { WorkbenchHoverDelegate } from '../../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../common/core/offsetRange.js'; -import { Range } from '../../../../common/core/range.js'; -import { TextEdit } from '../../../../common/core/textEdit.js'; +import { LineRange, LineRangeSet } from '../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextEdit } from '../../../../../editor/common/language/core/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../common/diff/rangeMapping.js'; -import { TextModelText } from '../../../../common/model/textModelText.js'; +import { TextModelText } from '../../../../../editor/common/language/model/textModelText.js'; import { ActionRunnerWithContext } from '../../multiDiffEditor/utils.js'; import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorSash, SashLayout } from '../components/diffEditorSash.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts index e87fe47e055..cdbd7684e9a 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/hideUnchangedRegionsFeature.ts @@ -14,12 +14,12 @@ import { isDefined } from '../../../../../base/common/types.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { CursorChangeReason } from '../../../../common/cursorEvents.js'; -import { SymbolKind, SymbolKinds } from '../../../../common/languages.js'; -import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from '../../../../common/model.js'; +import { SymbolKind, SymbolKinds } from '../../../../../editor/common/language/languages.js'; +import { IModelDecorationOptions, IModelDeltaDecoration, ITextModel } from '../../../../../editor/common/language/model.js'; import { ICodeEditor } from '../../../editorBrowser.js'; import { observableCodeEditor } from '../../../observableCodeEditor.js'; import { DiffEditorEditors } from '../components/diffEditorEditors.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts index a06947da06d..5daa74dc89d 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts @@ -17,8 +17,8 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { PlaceholderViewZone, ViewZoneOverlayWidget, applyStyle, applyViewZones } from '../utils.js'; import { EditorLayoutInfo } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { OffsetRange, OffsetRangeSet } from '../../../../common/core/offsetRange.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange, OffsetRangeSet } from '../../../../../editor/common/language/core/offsetRange.js'; import { MovedText } from '../../../../common/diff/linesDiffComputer.js'; import { localize } from '../../../../../nls.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts index 1db18e1478b..84f36dbc125 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts @@ -15,8 +15,8 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { appendRemoveOnDispose } from '../utils.js'; import { EditorLayoutInfo, EditorOption } from '../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { Position } from '../../../../common/core/position.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { OverviewRulerZone } from '../../../../common/viewModel/overviewZoneManager.js'; import { defaultInsertColor, defaultRemoveColor, diffInserted, diffOverviewRulerInserted, diffOverviewRulerRemoved, diffRemoved } from '../../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts index 2d94b6e0657..6fa9deb2e0a 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts @@ -13,10 +13,10 @@ import { DiffEditorEditors } from '../components/diffEditorEditors.js'; import { DiffEditorOptions } from '../diffEditorOptions.js'; import { DiffEditorViewModel } from '../diffEditorViewModel.js'; import { DiffEditorWidget } from '../diffEditorWidget.js'; -import { LineRange, LineRangeSet } from '../../../../common/core/lineRange.js'; -import { Range } from '../../../../common/core/range.js'; +import { LineRange, LineRangeSet } from '../../../../../editor/common/language/core/lineRange.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { LineRangeMapping, RangeMapping } from '../../../../common/diff/rangeMapping.js'; -import { GlyphMarginLane } from '../../../../common/model.js'; +import { GlyphMarginLane } from '../../../../../editor/common/language/model.js'; import { localize } from '../../../../../nls.js'; const emptyArr: never[] = []; diff --git a/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts b/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts index 2194ddadb7c..5a475179b69 100644 --- a/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts @@ -5,7 +5,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { localize } from '../../../../nls.js'; import { registerColor } from '../../../../platform/theme/common/colorRegistry.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; diff --git a/src/vs/editor/browser/widget/diffEditor/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts index 47faca2f8cf..20f36ecaf09 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils.ts @@ -10,11 +10,11 @@ import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } fr import { IObservable, IObservableWithChange, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from '../../../../base/common/observable.js'; import { ElementSizeObserver } from '../../config/elementSizeObserver.js'; import { ICodeEditor, IOverlayWidget, IViewZone } from '../../editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { DetailedLineRangeMapping } from '../../../common/diff/rangeMapping.js'; -import { IModelDeltaDecoration } from '../../../common/model.js'; -import { TextLength } from '../../../common/core/textLength.js'; +import { IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; +import { TextLength } from '../../../../editor/common/language/core/textLength.js'; export function joinCombine(arr1: readonly T[], arr2: readonly T[], keySelector: (val: T) => number, combine: (v1: T, v2: T) => T): readonly T[] { if (arr1.length === 0) { diff --git a/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts b/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts index bc149ec17ea..87d2d8d5769 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils/editorGutter.ts @@ -7,8 +7,8 @@ import { h, reset } from '../../../../../base/browser/dom.js'; import { Disposable, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, IObservable, IReader, ISettableObservable, observableFromEvent, observableSignal, observableSignalFromEvent, observableValue, transaction } from '../../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../codeEditor/codeEditorWidget.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../common/core/offsetRange.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; export class EditorGutter extends Disposable { private readonly scrollTop = observableFromEvent(this, diff --git a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts index d8f15787616..7cb72038550 100644 --- a/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts +++ b/src/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer.ts @@ -10,7 +10,7 @@ import { IMarkdownString, MarkdownStringTrustedOptions } from '../../../../../ba import { DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js'; import { tokenizeToString } from '../../../../common/languages/textToHtmlTokenizer.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; diff --git a/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts index 48ac35815d4..8d2c3dd4ff6 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts @@ -14,7 +14,7 @@ import { IContextKeyService, type IScopedContextKeyService } from '../../../../p import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; import { observableCodeEditor } from '../../observableCodeEditor.js'; import { DiffEditorWidget } from '../diffEditor/diffEditorWidget.js'; import { DocumentDiffItemViewModel } from './multiDiffEditorViewModel.js'; diff --git a/src/vs/editor/browser/widget/multiDiffEditor/model.ts b/src/vs/editor/browser/widget/multiDiffEditor/model.ts index 32077320010..2aa3d0cfd5c 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/model.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/model.ts @@ -6,7 +6,7 @@ import { Event, IValueWithChangeEvent } from '../../../../base/common/event.js'; import { RefCounted } from '../diffEditor/utils.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ContextKeyValue } from '../../../../platform/contextkey/common/contextkey.js'; export interface IMultiDiffEditorModel { diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts index 49c674d0477..aee24e34fe1 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts @@ -9,9 +9,9 @@ import { URI } from '../../../../base/common/uri.js'; import { ContextKeyValue } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IDiffEditorOptions } from '../../../common/config/editorOptions.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IDiffEditorViewModel } from '../../../common/editorCommon.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { DiffEditorOptions } from '../diffEditor/diffEditorOptions.js'; import { DiffEditorViewModel } from '../diffEditor/diffEditorViewModel.js'; import { RefCounted } from '../diffEditor/utils.js'; diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts index d23ad8c87f6..9074f7b8d2d 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts @@ -10,7 +10,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IDiffEditor } from '../../../common/editorCommon.js'; import { ICodeEditor } from '../../editorBrowser.js'; import { DiffEditorWidget } from '../diffEditor/diffEditorWidget.js'; diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts index ec0bf3cf64c..54cc6b7bbed 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts @@ -17,9 +17,9 @@ import { ContextKeyValue, IContextKeyService } from '../../../../platform/contex import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { IRange } from '../../../common/core/range.js'; -import { ISelection, Selection } from '../../../common/core/selection.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../../editor/common/language/core/selection.js'; import { IDiffEditor } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { ICodeEditor } from '../../editorBrowser.js'; diff --git a/src/vs/editor/common/codecs/baseToken.ts b/src/vs/editor/common/codecs/baseToken.ts index 6430ffb61a5..1c12cd6fa42 100644 --- a/src/vs/editor/common/codecs/baseToken.ts +++ b/src/vs/editor/common/codecs/baseToken.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange, Range } from '../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; /** * Base class for all tokens with a `range` that diff --git a/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts index 3bd72e5bd73..0258cc28039 100644 --- a/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts +++ b/src/vs/editor/common/codecs/linesCodec/linesDecoder.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Line } from './tokens/line.js'; -import { Range } from '../../core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { NewLine } from './tokens/newLine.js'; import { assert } from '../../../../base/common/assert.js'; import { CarriageReturn } from './tokens/carriageReturn.js'; diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts index a509940bc4e..86846172698 100644 --- a/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts +++ b/src/vs/editor/common/codecs/linesCodec/tokens/carriageReturn.ts @@ -5,8 +5,8 @@ import { Line } from './line.js'; import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { VSBuffer } from '../../../../../base/common/buffer.js'; /** diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/line.ts b/src/vs/editor/common/codecs/linesCodec/tokens/line.ts index 6669169967f..bf2a2e02039 100644 --- a/src/vs/editor/common/codecs/linesCodec/tokens/line.ts +++ b/src/vs/editor/common/codecs/linesCodec/tokens/line.ts @@ -5,7 +5,7 @@ import { BaseToken } from '../../baseToken.js'; import { assert } from '../../../../../base/common/assert.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; /** * Token representing a line of text with a `range` which diff --git a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts index fb826b759ca..c272f6b3adb 100644 --- a/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts +++ b/src/vs/editor/common/codecs/linesCodec/tokens/newLine.ts @@ -6,8 +6,8 @@ import { Line } from './line.js'; import { BaseToken } from '../../baseToken.js'; import { VSBuffer } from '../../../../../base/common/buffer.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * A token that represent a `new line` with a `range`. The `range` diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts index df5f3f028ab..5e6ca84e1c3 100644 --- a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { Dash } from '../../simpleCodec/tokens/dash.js'; import { pick } from '../../../../../base/common/arrays.js'; import { assert } from '../../../../../base/common/assert.js'; diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts index f7875d957a2..68350627caf 100644 --- a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { MarkdownToken } from './markdownToken.js'; import { assert } from '../../../../../base/common/assert.js'; diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts index 75e42e31aaf..4ce941b2869 100644 --- a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts @@ -5,7 +5,7 @@ import { BaseToken } from '../../baseToken.js'; import { MarkdownToken } from './markdownToken.js'; -import { IRange, Range } from '../../../core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { assert } from '../../../../../base/common/assert.js'; /** diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts index a4b15718717..d83789bce1b 100644 --- a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts @@ -5,7 +5,7 @@ import { BaseToken } from '../../baseToken.js'; import { MarkdownToken } from './markdownToken.js'; -import { IRange, Range } from '../../../core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { assert } from '../../../../../base/common/assert.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts index 70d264bdd99..33f30444ec1 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts index 16165cf64a7..1a68d696d94 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts index 76e9f0cd2b4..e7bf9510672 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts index ebc0179eeef..68cbf56fc86 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts index 025edf70291..cdd2c5521d8 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts index 35f55dd8a2a..32b88d8956d 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/formFeed.ts @@ -5,8 +5,8 @@ import { BaseToken } from '../../baseToken.js'; import { Line } from '../../linesCodec/tokens/line.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * Token that represent a `form feed` with a `range`. The `range` diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts index ddca12a2279..822209c2f02 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts index d3509824f53..d3f78e82c3f 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../../baseToken.js'; -import { Range } from '../../../core/range.js'; -import { Position } from '../../../core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Line } from '../../linesCodec/tokens/line.js'; /** diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts index 18a5dff4a0a..fee6b7d2637 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/space.ts @@ -5,8 +5,8 @@ import { BaseToken } from '../../baseToken.js'; import { Line } from '../../linesCodec/tokens/line.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * A token that represent a `space` with a `range`. The `range` diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts index c0d775ff8cd..30cb0a85cf2 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts @@ -5,8 +5,8 @@ import { BaseToken } from '../../baseToken.js'; import { Line } from '../../linesCodec/tokens/line.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * A token that represent a `tab` with a `range`. The `range` diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts index c6b87db0e37..bc1741544e0 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/verticalTab.ts @@ -5,8 +5,8 @@ import { BaseToken } from '../../baseToken.js'; import { Line } from '../../linesCodec/tokens/line.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * Token that represent a `vertical tab` with a `range`. The `range` diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts index 2ca5598ac4b..863b22532b5 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts @@ -5,8 +5,8 @@ import { BaseToken } from '../../baseToken.js'; import { Line } from '../../linesCodec/tokens/line.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; /** * A token that represent a word - a set of continuous diff --git a/src/vs/editor/common/commands/replaceCommand.ts b/src/vs/editor/common/commands/replaceCommand.ts index 779dfd9a7b9..80ca0b21acb 100644 --- a/src/vs/editor/common/commands/replaceCommand.ts +++ b/src/vs/editor/common/commands/replaceCommand.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { Selection, SelectionDirection } from '../core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection, SelectionDirection } from '../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; export class ReplaceCommand implements ICommand { diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index aa10151dedc..8954fd91d64 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -5,11 +5,11 @@ import { CharCode } from '../../../base/common/charCode.js'; import * as strings from '../../../base/common/strings.js'; -import { CursorColumns } from '../core/cursorColumns.js'; -import { Range } from '../core/range.js'; -import { Selection, SelectionDirection } from '../core/selection.js'; +import { CursorColumns } from '../../../editor/common/language/core/cursorColumns.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection, SelectionDirection } from '../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { EditorAutoIndentStrategy } from '../config/editorOptions.js'; import { getEnterAction } from '../languages/enterAction.js'; import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js'; diff --git a/src/vs/editor/common/commands/surroundSelectionCommand.ts b/src/vs/editor/common/commands/surroundSelectionCommand.ts index 58ba301fa4d..fc29c2b0f13 100644 --- a/src/vs/editor/common/commands/surroundSelectionCommand.ts +++ b/src/vs/editor/common/commands/surroundSelectionCommand.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../core/range.js'; -import { Position } from '../core/position.js'; -import { Selection } from '../core/selection.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; export class SurroundSelectionCommand implements ICommand { private readonly _range: Selection; diff --git a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts index 23e3f612373..ed2d7785c09 100644 --- a/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts +++ b/src/vs/editor/common/commands/trimTrailingWhitespaceCommand.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from '../../../base/common/strings.js'; -import { EditOperation, ISingleEditOperation } from '../core/editOperation.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { StandardTokenType } from '../encodedTokenAttributes.js'; -import { ITextModel } from '../model.js'; +import { StandardTokenType } from '../../../editor/common/language/encodedTokenAttributes.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; export class TrimTrailingWhitespaceCommand implements ICommand { diff --git a/src/vs/editor/common/config/editorConfiguration.ts b/src/vs/editor/common/config/editorConfiguration.ts index 08f45386aa2..e851aaac4dc 100644 --- a/src/vs/editor/common/config/editorConfiguration.ts +++ b/src/vs/editor/common/config/editorConfiguration.ts @@ -6,7 +6,7 @@ import { Event } from '../../../base/common/event.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { ConfigurationChangedEvent, IComputedEditorOptions, IEditorOptions } from './editorOptions.js'; -import { IDimension } from '../core/dimension.js'; +import { IDimension } from '../../../editor/common/language/core/dimension.js'; import { MenuId } from '../../../platform/actions/common/actions.js'; export interface IEditorConfiguration extends IDisposable { diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 8b8d099149a..3abd6ad698d 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -6,7 +6,7 @@ import type { IJSONSchemaSnippet } from '../../../base/common/jsonSchema.js'; import { diffEditorDefaultOptions } from './diffEditor.js'; import { editorOptionsRegistry } from './editorOptions.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; +import { EDITOR_MODEL_DEFAULTS } from '../../../editor/common/language/core/textModelDefaults.js'; import * as nls from '../../../nls.js'; import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry } from '../../../platform/configuration/common/configurationRegistry.js'; import { Registry } from '../../../platform/registry/common/platform.js'; diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index b0396646102..659d0e4254f 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -11,8 +11,8 @@ import * as platform from '../../../base/common/platform.js'; import { ScrollbarVisibility } from '../../../base/common/scrollable.js'; import { Constants } from '../../../base/common/uint.js'; import { FontInfo } from './fontInfo.js'; -import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; -import { USUAL_WORD_SEPARATORS } from '../core/wordHelper.js'; +import { EDITOR_MODEL_DEFAULTS } from '../../../editor/common/language/core/textModelDefaults.js'; +import { USUAL_WORD_SEPARATORS } from '../../../editor/common/language/core/wordHelper.js'; import * as nls from '../../../nls.js'; import { AccessibilitySupport } from '../../../platform/accessibility/common/accessibility.js'; import { IConfigurationPropertySchema } from '../../../platform/configuration/common/configurationRegistry.js'; diff --git a/src/vs/editor/common/core/characterClassifier.ts b/src/vs/editor/common/core/characterClassifier.ts index 595d08818f1..69dc80d5d6b 100644 --- a/src/vs/editor/common/core/characterClassifier.ts +++ b/src/vs/editor/common/core/characterClassifier.ts @@ -3,84 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { toUint8 } from '../../../base/common/uint.js'; - -/** - * A fast character classifier that uses a compact array for ASCII values. - */ -export class CharacterClassifier { - /** - * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). - */ - protected readonly _asciiMap: Uint8Array; - - /** - * The entire map (sparse array). - */ - protected readonly _map: Map; - - protected readonly _defaultValue: number; - - constructor(_defaultValue: T) { - const defaultValue = toUint8(_defaultValue); - - this._defaultValue = defaultValue; - this._asciiMap = CharacterClassifier._createAsciiMap(defaultValue); - this._map = new Map(); - } - - private static _createAsciiMap(defaultValue: number): Uint8Array { - const asciiMap = new Uint8Array(256); - asciiMap.fill(defaultValue); - return asciiMap; - } - - public set(charCode: number, _value: T): void { - const value = toUint8(_value); - - if (charCode >= 0 && charCode < 256) { - this._asciiMap[charCode] = value; - } else { - this._map.set(charCode, value); - } - } - - public get(charCode: number): T { - if (charCode >= 0 && charCode < 256) { - return this._asciiMap[charCode]; - } else { - return (this._map.get(charCode) || this._defaultValue); - } - } - - public clear() { - this._asciiMap.fill(this._defaultValue); - this._map.clear(); - } -} - -const enum Boolean { - False = 0, - True = 1 -} - -export class CharacterSet { - - private readonly _actual: CharacterClassifier; - - constructor() { - this._actual = new CharacterClassifier(Boolean.False); - } - - public add(charCode: number): void { - this._actual.set(charCode, Boolean.True); - } - - public has(charCode: number): boolean { - return (this._actual.get(charCode) === Boolean.True); - } - - public clear(): void { - return this._actual.clear(); - } -} +export * from '../language/core/characterClassifier.js'; diff --git a/src/vs/editor/common/core/cursorColumns.ts b/src/vs/editor/common/core/cursorColumns.ts index 1d84b8635cc..6a9718b0f8f 100644 --- a/src/vs/editor/common/core/cursorColumns.ts +++ b/src/vs/editor/common/core/cursorColumns.ts @@ -3,147 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; -import * as strings from '../../../base/common/strings.js'; - -/** - * A column in a position is the gap between two adjacent characters. The methods here - * work with a concept called "visible column". A visible column is a very rough approximation - * of the horizontal screen position of a column. For example, using a tab size of 4: - * ```txt - * |||T|ext - * | | | \---- column = 4, visible column = 9 - * | | \------ column = 3, visible column = 8 - * | \------------ column = 2, visible column = 4 - * \------------------ column = 1, visible column = 0 - * ``` - * - * **NOTE**: Visual columns do not work well for RTL text or variable-width fonts or characters. - * - * **NOTE**: These methods work and make sense both on the model and on the view model. - */ -export class CursorColumns { - - private static _nextVisibleColumn(codePoint: number, visibleColumn: number, tabSize: number): number { - if (codePoint === CharCode.Tab) { - return CursorColumns.nextRenderTabStop(visibleColumn, tabSize); - } - if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) { - return visibleColumn + 2; - } - return visibleColumn + 1; - } - - /** - * Returns a visible column from a column. - * @see {@link CursorColumns} - */ - public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number { - const textLen = Math.min(column - 1, lineContent.length); - const text = lineContent.substring(0, textLen); - const iterator = new strings.GraphemeIterator(text); - - let result = 0; - while (!iterator.eol()) { - const codePoint = strings.getNextCodePoint(text, textLen, iterator.offset); - iterator.nextGraphemeLength(); - - result = this._nextVisibleColumn(codePoint, result, tabSize); - } - - return result; - } - - /** - * Returns the value to display as "Col" in the status bar. - * @see {@link CursorColumns} - */ - public static toStatusbarColumn(lineContent: string, column: number, tabSize: number): number { - const text = lineContent.substring(0, Math.min(column - 1, lineContent.length)); - const iterator = new strings.CodePointIterator(text); - - let result = 0; - while (!iterator.eol()) { - const codePoint = iterator.nextCodePoint(); - - if (codePoint === CharCode.Tab) { - result = CursorColumns.nextRenderTabStop(result, tabSize); - } else { - result = result + 1; - } - } - - return result + 1; - } - - /** - * Returns a column from a visible column. - * @see {@link CursorColumns} - */ - public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number { - if (visibleColumn <= 0) { - return 1; - } - - const lineContentLength = lineContent.length; - const iterator = new strings.GraphemeIterator(lineContent); - - let beforeVisibleColumn = 0; - let beforeColumn = 1; - while (!iterator.eol()) { - const codePoint = strings.getNextCodePoint(lineContent, lineContentLength, iterator.offset); - iterator.nextGraphemeLength(); - - const afterVisibleColumn = this._nextVisibleColumn(codePoint, beforeVisibleColumn, tabSize); - const afterColumn = iterator.offset + 1; - - if (afterVisibleColumn >= visibleColumn) { - const beforeDelta = visibleColumn - beforeVisibleColumn; - const afterDelta = afterVisibleColumn - visibleColumn; - if (afterDelta < beforeDelta) { - return afterColumn; - } else { - return beforeColumn; - } - } - - beforeVisibleColumn = afterVisibleColumn; - beforeColumn = afterColumn; - } - - // walked the entire string - return lineContentLength + 1; - } - - /** - * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) - * @see {@link CursorColumns} - */ - public static nextRenderTabStop(visibleColumn: number, tabSize: number): number { - return visibleColumn + tabSize - visibleColumn % tabSize; - } - - /** - * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) - * @see {@link CursorColumns} - */ - public static nextIndentTabStop(visibleColumn: number, indentSize: number): number { - return CursorColumns.nextRenderTabStop(visibleColumn, indentSize); - } - - /** - * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) - * @see {@link CursorColumns} - */ - public static prevRenderTabStop(column: number, tabSize: number): number { - return Math.max(0, column - 1 - (column - 1) % tabSize); - } - - /** - * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) - * @see {@link CursorColumns} - */ - public static prevIndentTabStop(column: number, indentSize: number): number { - return CursorColumns.prevRenderTabStop(column, indentSize); - } -} +export * from '../language/core/cursorColumns.js'; diff --git a/src/vs/editor/common/core/dimension.ts b/src/vs/editor/common/core/dimension.ts index 3381f6c69ad..eb76d821925 100644 --- a/src/vs/editor/common/core/dimension.ts +++ b/src/vs/editor/common/core/dimension.ts @@ -3,7 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export interface IDimension { - width: number; - height: number; -} +export * from '../language/core/dimension.js'; diff --git a/src/vs/editor/common/core/editOperation.ts b/src/vs/editor/common/core/editOperation.ts index 047330dc2ad..9a18dec9ce8 100644 --- a/src/vs/editor/common/core/editOperation.ts +++ b/src/vs/editor/common/core/editOperation.ts @@ -3,58 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from './position.js'; -import { IRange, Range } from './range.js'; - -/** - * A single edit operation, that acts as a simple replace. - * i.e. Replace text at `range` with `text` in model. - */ -export interface ISingleEditOperation { - /** - * The range to replace. This can be empty to emulate a simple insert. - */ - range: IRange; - /** - * The text to replace with. This can be null to emulate a simple delete. - */ - text: string | null; - /** - * This indicates that this operation has "insert" semantics. - * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. - */ - forceMoveMarkers?: boolean; -} - -export class EditOperation { - - public static insert(position: Position, text: string): ISingleEditOperation { - return { - range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), - text: text, - forceMoveMarkers: true - }; - } - - public static delete(range: Range): ISingleEditOperation { - return { - range: range, - text: null - }; - } - - public static replace(range: Range, text: string | null): ISingleEditOperation { - return { - range: range, - text: text - }; - } - - public static replaceMove(range: Range, text: string | null): ISingleEditOperation { - return { - range: range, - text: text, - forceMoveMarkers: true - }; - } -} +export * from '../language/core/editOperation.js'; diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index 15679ff7ba9..d25e95b9432 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -3,109 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from '../../../nls.js'; -import { Color, RGBA } from '../../../base/common/color.js'; -import { activeContrastBorder, editorBackground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder, contrastBorder, editorFindMatchHighlight, editorWarningBackground } from '../../../platform/theme/common/colorRegistry.js'; -import { registerThemingParticipant } from '../../../platform/theme/common/themeService.js'; - -/** - * Definition of the editor colors - */ -export const editorLineHighlight = registerColor('editor.lineHighlightBackground', null, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); -export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hcDark: '#f38518', hcLight: contrastBorder }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); -export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hcDark: null, hcLight: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); -export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.')); -export const editorSymbolHighlight = registerColor('editor.symbolHighlightBackground', { dark: editorFindMatchHighlight, light: editorFindMatchHighlight, hcDark: null, hcLight: null }, nls.localize('symbolHighlight', 'Background color of highlighted symbol, like for go to definition or go next/previous symbol. The color must not be opaque so as not to hide underlying decorations.'), true); -export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlightBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('symbolHighlightBorder', 'Background color of the border around highlighted symbols.')); - -export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hcDark: Color.white, hcLight: '#0F4A85' }, nls.localize('caret', 'Color of the editor cursor.')); -export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); -export const editorMultiCursorPrimaryForeground = registerColor('editorMultiCursor.primary.foreground', editorCursorForeground, nls.localize('editorMultiCursorPrimaryForeground', 'Color of the primary editor cursor when multiple cursors are present.')); -export const editorMultiCursorPrimaryBackground = registerColor('editorMultiCursor.primary.background', editorCursorBackground, nls.localize('editorMultiCursorPrimaryBackground', 'The background color of the primary editor cursor when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); -export const editorMultiCursorSecondaryForeground = registerColor('editorMultiCursor.secondary.foreground', editorCursorForeground, nls.localize('editorMultiCursorSecondaryForeground', 'Color of secondary editor cursors when multiple cursors are present.')); -export const editorMultiCursorSecondaryBackground = registerColor('editorMultiCursor.secondary.background', editorCursorBackground, nls.localize('editorMultiCursorSecondaryBackground', 'The background color of secondary editor cursors when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); -export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); -export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); - -export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', editorWhitespaces, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); -export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', editorWhitespaces, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); - -export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', deprecatedEditorIndentGuides, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); -export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', '#00000000', nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); -export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', '#00000000', nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); -export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', '#00000000', nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); -export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', '#00000000', nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); -export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', '#00000000', nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); - -export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', deprecatedEditorActiveIndentGuides, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); -export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', '#00000000', nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); -export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', '#00000000', nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); -export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', '#00000000', nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); -export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', '#00000000', nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); -export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', '#00000000', nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); - -const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); -export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', deprecatedEditorActiveLineNumber, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); -export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', null, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); - -export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorRuler', 'Color of the editor rulers.')); - -export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#919191', hcDark: '#999999', hcLight: '#292929' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor CodeLens')); - -export const editorBracketMatchBackground = registerColor('editorBracketMatch.background', { dark: '#0064001a', light: '#0064001a', hcDark: '#0064001a', hcLight: '#0000' }, nls.localize('editorBracketMatchBackground', 'Background color behind matching brackets')); -export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes')); - -export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hcDark: '#7f7f7f4d', hcLight: '#666666' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.')); -export const editorOverviewRulerBackground = registerColor('editorOverviewRuler.background', null, nls.localize('editorOverviewRulerBackground', 'Background color of the editor overview ruler.')); - -export const editorGutter = registerColor('editorGutter.background', editorBackground, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); - -export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: contrastBorder }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.')); -export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); - -export const ghostTextBorder = registerColor('editorGhostText.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: Color.fromHex('#292929').transparent(0.8) }, nls.localize('editorGhostTextBorder', 'Border color of ghost text in the editor.')); -export const ghostTextForeground = registerColor('editorGhostText.foreground', { dark: Color.fromHex('#ffffff56'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('editorGhostTextForeground', 'Foreground color of the ghost text in the editor.')); -export const ghostTextBackground = registerColor('editorGhostText.background', null, nls.localize('editorGhostTextBackground', 'Background color of the ghost text in the editor.')); - -const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); -export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', rulerRangeDefault, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); -export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '#B5200D' }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); -export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningBorder, hcLight: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); -export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hcDark: editorInfoBorder, hcLight: editorInfoBorder }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); - -export const editorBracketHighlightingForeground1 = registerColor('editorBracketHighlight.foreground1', { dark: '#FFD700', light: '#0431FAFF', hcDark: '#FFD700', hcLight: '#0431FAFF' }, nls.localize('editorBracketHighlightForeground1', 'Foreground color of brackets (1). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground2 = registerColor('editorBracketHighlight.foreground2', { dark: '#DA70D6', light: '#319331FF', hcDark: '#DA70D6', hcLight: '#319331FF' }, nls.localize('editorBracketHighlightForeground2', 'Foreground color of brackets (2). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground3 = registerColor('editorBracketHighlight.foreground3', { dark: '#179FFF', light: '#7B3814FF', hcDark: '#87CEFA', hcLight: '#7B3814FF' }, nls.localize('editorBracketHighlightForeground3', 'Foreground color of brackets (3). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground4 = registerColor('editorBracketHighlight.foreground4', '#00000000', nls.localize('editorBracketHighlightForeground4', 'Foreground color of brackets (4). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', '#00000000', nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', '#00000000', nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); - -export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '#B5200D' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); - -export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', '#00000000', nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', '#00000000', nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground3 = registerColor('editorBracketPairGuide.background3', '#00000000', nls.localize('editorBracketPairGuide.background3', 'Background color of inactive bracket pair guides (3). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground4 = registerColor('editorBracketPairGuide.background4', '#00000000', nls.localize('editorBracketPairGuide.background4', 'Background color of inactive bracket pair guides (4). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground5 = registerColor('editorBracketPairGuide.background5', '#00000000', nls.localize('editorBracketPairGuide.background5', 'Background color of inactive bracket pair guides (5). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground6 = registerColor('editorBracketPairGuide.background6', '#00000000', nls.localize('editorBracketPairGuide.background6', 'Background color of inactive bracket pair guides (6). Requires enabling bracket pair guides.')); - -export const editorBracketPairGuideActiveBackground1 = registerColor('editorBracketPairGuide.activeBackground1', '#00000000', nls.localize('editorBracketPairGuide.activeBackground1', 'Background color of active bracket pair guides (1). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground2 = registerColor('editorBracketPairGuide.activeBackground2', '#00000000', nls.localize('editorBracketPairGuide.activeBackground2', 'Background color of active bracket pair guides (2). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground3 = registerColor('editorBracketPairGuide.activeBackground3', '#00000000', nls.localize('editorBracketPairGuide.activeBackground3', 'Background color of active bracket pair guides (3). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground4 = registerColor('editorBracketPairGuide.activeBackground4', '#00000000', nls.localize('editorBracketPairGuide.activeBackground4', 'Background color of active bracket pair guides (4). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground5 = registerColor('editorBracketPairGuide.activeBackground5', '#00000000', nls.localize('editorBracketPairGuide.activeBackground5', 'Background color of active bracket pair guides (5). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground6 = registerColor('editorBracketPairGuide.activeBackground6', '#00000000', nls.localize('editorBracketPairGuide.activeBackground6', 'Background color of active bracket pair guides (6). Requires enabling bracket pair guides.')); - -export const editorUnicodeHighlightBorder = registerColor('editorUnicodeHighlight.border', editorWarningForeground, nls.localize('editorUnicodeHighlight.border', 'Border color used to highlight unicode characters.')); -export const editorUnicodeHighlightBackground = registerColor('editorUnicodeHighlight.background', editorWarningBackground, nls.localize('editorUnicodeHighlight.background', 'Background color used to highlight unicode characters.')); - - -// contains all color rules that used to defined in editor/browser/widget/editor.css -registerThemingParticipant((theme, collector) => { - const background = theme.getColor(editorBackground); - const lineHighlight = theme.getColor(editorLineHighlight); - const imeBackground = (lineHighlight && !lineHighlight.isTransparent() ? lineHighlight : background); - if (imeBackground) { - collector.addRule(`.monaco-editor .inputarea.ime-input { background-color: ${imeBackground}; }`); - } -}); +export * from '../language/core/editorColorRegistry.js'; diff --git a/src/vs/editor/common/core/eolCounter.ts b/src/vs/editor/common/core/eolCounter.ts index 42175b1a3f8..91c29872dfa 100644 --- a/src/vs/editor/common/core/eolCounter.ts +++ b/src/vs/editor/common/core/eolCounter.ts @@ -3,49 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; - -export const enum StringEOL { - Unknown = 0, - Invalid = 3, - LF = 1, - CRLF = 2 -} - -export function countEOL(text: string): [number, number, number, StringEOL] { - let eolCount = 0; - let firstLineLength = 0; - let lastLineStart = 0; - let eol: StringEOL = StringEOL.Unknown; - for (let i = 0, len = text.length; i < len; i++) { - const chr = text.charCodeAt(i); - - if (chr === CharCode.CarriageReturn) { - if (eolCount === 0) { - firstLineLength = i; - } - eolCount++; - if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) { - // \r\n... case - eol |= StringEOL.CRLF; - i++; // skip \n - } else { - // \r... case - eol |= StringEOL.Invalid; - } - lastLineStart = i + 1; - } else if (chr === CharCode.LineFeed) { - // \n... case - eol |= StringEOL.LF; - if (eolCount === 0) { - firstLineLength = i; - } - eolCount++; - lastLineStart = i + 1; - } - } - if (eolCount === 0) { - firstLineLength = text.length; - } - return [eolCount, firstLineLength, text.length - lastLineStart, eol]; -} +export * from '../language/core/eolCounter.js'; diff --git a/src/vs/editor/common/core/indentation.ts b/src/vs/editor/common/core/indentation.ts index 03ce99442c8..1eab684e12d 100644 --- a/src/vs/editor/common/core/indentation.ts +++ b/src/vs/editor/common/core/indentation.ts @@ -3,39 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from '../../../base/common/strings.js'; -import { CursorColumns } from './cursorColumns.js'; - -function _normalizeIndentationFromWhitespace(str: string, indentSize: number, insertSpaces: boolean): string { - let spacesCnt = 0; - for (let i = 0; i < str.length; i++) { - if (str.charAt(i) === '\t') { - spacesCnt = CursorColumns.nextIndentTabStop(spacesCnt, indentSize); - } else { - spacesCnt++; - } - } - - let result = ''; - if (!insertSpaces) { - const tabsCnt = Math.floor(spacesCnt / indentSize); - spacesCnt = spacesCnt % indentSize; - for (let i = 0; i < tabsCnt; i++) { - result += '\t'; - } - } - - for (let i = 0; i < spacesCnt; i++) { - result += ' '; - } - - return result; -} - -export function normalizeIndentation(str: string, indentSize: number, insertSpaces: boolean): string { - let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(str); - if (firstNonWhitespaceIndex === -1) { - firstNonWhitespaceIndex = str.length; - } - return _normalizeIndentationFromWhitespace(str.substring(0, firstNonWhitespaceIndex), indentSize, insertSpaces) + str.substring(firstNonWhitespaceIndex); -} +export * from '../language/core/indentation.js'; diff --git a/src/vs/editor/common/core/lineEdit.ts b/src/vs/editor/common/core/lineEdit.ts index 507bd7b0f33..45233f4214f 100644 --- a/src/vs/editor/common/core/lineEdit.ts +++ b/src/vs/editor/common/core/lineEdit.ts @@ -3,378 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, groupAdjacentBy, numberComparator } from '../../../base/common/arrays.js'; -import { assert, checkAdjacentItems } from '../../../base/common/assert.js'; -import { splitLines } from '../../../base/common/strings.js'; -import { LineRange } from './lineRange.js'; -import { OffsetEdit, SingleOffsetEdit } from './offsetEdit.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; -import { AbstractText, SingleTextEdit, TextEdit } from './textEdit.js'; - - -export class LineEdit { - public static readonly empty = new LineEdit([]); - - public static deserialize(data: SerializedLineEdit): LineEdit { - return new LineEdit(data.map(e => SingleLineEdit.deserialize(e))); - } - - public static fromEdit(edit: OffsetEdit, initialValue: AbstractText): LineEdit { - const textEdit = TextEdit.fromOffsetEdit(edit, initialValue); - return LineEdit.fromTextEdit(textEdit, initialValue); - } - - public static fromTextEdit(edit: TextEdit, initialValue: AbstractText): LineEdit { - const edits = edit.edits; - - const result: SingleLineEdit[] = []; - - const currentEdits: SingleTextEdit[] = []; - for (let i = 0; i < edits.length; i++) { - const edit = edits[i]; - const nextEditRange = i + 1 < edits.length ? edits[i + 1] : undefined; - currentEdits.push(edit); - if (nextEditRange && nextEditRange.range.startLineNumber === edit.range.endLineNumber) { - continue; - } - - const singleEdit = SingleTextEdit.joinEdits(currentEdits, initialValue); - currentEdits.length = 0; - - const singleLineEdit = SingleLineEdit.fromSingleTextEdit(singleEdit, initialValue); - result.push(singleLineEdit); - } - - return new LineEdit(result); - } - - public static createFromUnsorted(edits: readonly SingleLineEdit[]): LineEdit { - const result = edits.slice(); - result.sort(compareBy(i => i.lineRange.startLineNumber, numberComparator)); - return new LineEdit(result); - } - - constructor( - /** - * Have to be sorted by start line number and non-intersecting. - */ - public readonly edits: readonly SingleLineEdit[] - ) { - assert(checkAdjacentItems(edits, (i1, i2) => i1.lineRange.endLineNumberExclusive <= i2.lineRange.startLineNumber)); - } - - public toEdit(initialValue: AbstractText): OffsetEdit { - const edits: SingleOffsetEdit[] = []; - for (const edit of this.edits) { - const singleEdit = edit.toSingleEdit(initialValue); - edits.push(singleEdit); - } - return new OffsetEdit(edits); - } - - public toString(): string { - return this.edits.map(e => e.toString()).join(','); - } - - public serialize(): SerializedLineEdit { - return this.edits.map(e => e.serialize()); - } - - public getNewLineRanges(): LineRange[] { - const ranges: LineRange[] = []; - let offset = 0; - for (const e of this.edits) { - ranges.push(LineRange.ofLength(e.lineRange.startLineNumber + offset, e.newLines.length),); - offset += e.newLines.length - e.lineRange.length; - } - return ranges; - } - - public mapLineNumber(lineNumber: number): number { - let lineDelta = 0; - for (const e of this.edits) { - if (e.lineRange.endLineNumberExclusive > lineNumber) { - break; - } - - lineDelta += e.newLines.length - e.lineRange.length; - } - return lineNumber + lineDelta; - } - - public mapLineRange(lineRange: LineRange): LineRange { - return new LineRange( - this.mapLineNumber(lineRange.startLineNumber), - this.mapLineNumber(lineRange.endLineNumberExclusive), - ); - } - - public rebase(base: LineEdit): LineEdit { - return new LineEdit( - this.edits.map(e => new SingleLineEdit(base.mapLineRange(e.lineRange), e.newLines)), - ); - } - - public humanReadablePatch(originalLines: string[]): string { - const result: string[] = []; - - function pushLine(originalLineNumber: number, modifiedLineNumber: number, kind: 'unmodified' | 'deleted' | 'added', content: string | undefined) { - const specialChar = (kind === 'unmodified' ? ' ' : (kind === 'deleted' ? '-' : '+')); - - if (content === undefined) { - content = '[[[[[ WARNING: LINE DOES NOT EXIST ]]]]]'; - } - - const origLn = originalLineNumber === -1 ? ' ' : originalLineNumber.toString().padStart(3, ' '); - const modLn = modifiedLineNumber === -1 ? ' ' : modifiedLineNumber.toString().padStart(3, ' '); - - result.push(`${specialChar} ${origLn} ${modLn} ${content}`); - } - - function pushSeperator() { - result.push('---'); - } - - let lineDelta = 0; - let first = true; - - for (const edits of groupAdjacentBy(this.edits, (e1, e2) => e1.lineRange.distanceToRange(e2.lineRange) <= 5)) { - if (!first) { - pushSeperator(); - } else { - first = false; - } - - let lastLineNumber = edits[0].lineRange.startLineNumber - 2; - - for (const edit of edits) { - for (let i = Math.max(1, lastLineNumber); i < edit.lineRange.startLineNumber; i++) { - pushLine(i, i + lineDelta, 'unmodified', originalLines[i - 1]); - } - - const range = edit.lineRange; - const newLines = edit.newLines; - for (const replaceLineNumber of range.mapToLineArray(n => n)) { - const line = originalLines[replaceLineNumber - 1]; - pushLine(replaceLineNumber, -1, 'deleted', line); - } - for (let i = 0; i < newLines.length; i++) { - const line = newLines[i]; - pushLine(-1, range.startLineNumber + lineDelta + i, 'added', line); - } - - lastLineNumber = range.endLineNumberExclusive; - - lineDelta += edit.newLines.length - edit.lineRange.length; - } - - for (let i = lastLineNumber; i <= Math.min(lastLineNumber + 2, originalLines.length); i++) { - pushLine(i, i + lineDelta, 'unmodified', originalLines[i - 1]); - } - } - - return result.join('\n'); - } - - public apply(lines: string[]): string[] { - const result: string[] = []; - - let currentLineIndex = 0; - - for (const edit of this.edits) { - while (currentLineIndex < edit.lineRange.startLineNumber - 1) { - result.push(lines[currentLineIndex]); - currentLineIndex++; - } - - for (const newLine of edit.newLines) { - result.push(newLine); - } - - currentLineIndex = edit.lineRange.endLineNumberExclusive - 1; - } - - while (currentLineIndex < lines.length) { - result.push(lines[currentLineIndex]); - currentLineIndex++; - } - - return result; - } - - public toSingleEdit() { - - } -} - -export class SingleLineEdit { - public static deserialize(e: SerializedSingleLineEdit): SingleLineEdit { - return new SingleLineEdit( - LineRange.ofLength(e[0], e[1] - e[0]), - e[2], - ); - } - - public static fromSingleTextEdit(edit: SingleTextEdit, initialValue: AbstractText): SingleLineEdit { - // 1: ab[cde - // 2: fghijk - // 3: lmn]opq - - // replaced with - - // 1n: 123 - // 2n: 456 - // 3n: 789 - - // simple solution: replace [1..4) with [1n..4n) - - const newLines = splitLines(edit.text); - let startLineNumber = edit.range.startLineNumber; - const survivingFirstLineText = initialValue.getValueOfRange(Range.fromPositions( - new Position(edit.range.startLineNumber, 1), - edit.range.getStartPosition() - )); - newLines[0] = survivingFirstLineText + newLines[0]; - - let endLineNumberEx = edit.range.endLineNumber + 1; - const editEndLineNumberMaxColumn = initialValue.getTransformer().getLineLength(edit.range.endLineNumber) + 1; - const survivingEndLineText = initialValue.getValueOfRange(Range.fromPositions( - edit.range.getEndPosition(), - new Position(edit.range.endLineNumber, editEndLineNumberMaxColumn) - )); - newLines[newLines.length - 1] = newLines[newLines.length - 1] + survivingEndLineText; - - // Replacing [startLineNumber, endLineNumberEx) with newLines would be correct, however it might not be minimal. - - const startBeforeNewLine = edit.range.startColumn === initialValue.getTransformer().getLineLength(edit.range.startLineNumber) + 1; - const endAfterNewLine = edit.range.endColumn === 1; - - if (startBeforeNewLine && newLines[0].length === survivingFirstLineText.length) { - // the replacement would not delete any text on the first line - startLineNumber++; - newLines.shift(); - } - - if (newLines.length > 0 && startLineNumber < endLineNumberEx && endAfterNewLine && newLines[newLines.length - 1].length === survivingEndLineText.length) { - // the replacement would not delete any text on the last line - endLineNumberEx--; - newLines.pop(); - } - - return new SingleLineEdit(new LineRange(startLineNumber, endLineNumberEx), newLines); - } - - constructor( - public readonly lineRange: LineRange, - public readonly newLines: readonly string[], - ) { } - - public toSingleTextEdit(initialValue: AbstractText): SingleTextEdit { - if (this.newLines.length === 0) { - // Deletion - const textLen = initialValue.getTransformer().textLength; - if (this.lineRange.endLineNumberExclusive === textLen.lineCount + 2) { - let startPos: Position; - if (this.lineRange.startLineNumber > 1) { - const startLineNumber = this.lineRange.startLineNumber - 1; - const startColumn = initialValue.getTransformer().getLineLength(startLineNumber) + 1; - startPos = new Position(startLineNumber, startColumn); - } else { - // Delete everything. - // In terms of lines, this would end up with 0 lines. - // However, a string has always 1 line (which can be empty). - startPos = new Position(1, 1); - } - - const lastPosition = textLen.addToPosition(new Position(1, 1)); - return new SingleTextEdit(Range.fromPositions(startPos, lastPosition), ''); - } else { - return new SingleTextEdit(new Range(this.lineRange.startLineNumber, 1, this.lineRange.endLineNumberExclusive, 1), ''); - } - - } else if (this.lineRange.isEmpty) { - // Insertion - - let endLineNumber: number; - let column: number; - let text: string; - const insertionLine = this.lineRange.startLineNumber; - if (insertionLine === initialValue.getTransformer().textLength.lineCount + 2) { - endLineNumber = insertionLine - 1; - column = initialValue.getTransformer().getLineLength(endLineNumber) + 1; - text = this.newLines.map(l => '\n' + l).join(''); - } else { - endLineNumber = insertionLine; - column = 1; - text = this.newLines.map(l => l + '\n').join(''); - } - return new SingleTextEdit(Range.fromPositions(new Position(endLineNumber, column)), text); - } else { - const endLineNumber = this.lineRange.endLineNumberExclusive - 1; - const endLineNumberMaxColumn = initialValue.getTransformer().getLineLength(endLineNumber) + 1; - const range = new Range( - this.lineRange.startLineNumber, - 1, - endLineNumber, - endLineNumberMaxColumn - ); - // Don't add \n to the last line. This is because we subtract one from lineRange.endLineNumberExclusive for endLineNumber. - const text = this.newLines.join('\n'); - return new SingleTextEdit(range, text); - } - } - - public toSingleEdit(initialValue: AbstractText): SingleOffsetEdit { - const textEdit = this.toSingleTextEdit(initialValue); - const range = initialValue.getTransformer().getOffsetRange(textEdit.range); - return new SingleOffsetEdit(range, textEdit.text); - } - - public toString(): string { - return `${this.lineRange}->${JSON.stringify(this.newLines)}`; - } - - public serialize(): SerializedSingleLineEdit { - return [ - this.lineRange.startLineNumber, - this.lineRange.endLineNumberExclusive, - this.newLines, - ]; - } - - public removeCommonSuffixPrefixLines(initialValue: AbstractText): SingleLineEdit { - let startLineNumber = this.lineRange.startLineNumber; - let endLineNumberEx = this.lineRange.endLineNumberExclusive; - - let trimStartCount = 0; - while ( - startLineNumber < endLineNumberEx && trimStartCount < this.newLines.length - && this.newLines[trimStartCount] === initialValue.getLineAt(startLineNumber) - ) { - startLineNumber++; - trimStartCount++; - } - - let trimEndCount = 0; - while ( - startLineNumber < endLineNumberEx && trimEndCount + trimStartCount < this.newLines.length - && this.newLines[this.newLines.length - 1 - trimEndCount] === initialValue.getLineAt(endLineNumberEx - 1) - ) { - endLineNumberEx--; - trimEndCount++; - } - - if (trimStartCount === 0 && trimEndCount === 0) { - return this; - } - return new SingleLineEdit(new LineRange(startLineNumber, endLineNumberEx), this.newLines.slice(trimStartCount, this.newLines.length - trimEndCount)); - } - - public toLineEdit(): LineEdit { - return new LineEdit([this]); - } -} - -export type SerializedLineEdit = SerializedSingleLineEdit[]; -export type SerializedSingleLineEdit = [startLineNumber: number, endLineNumber: number, newLines: readonly string[]]; +export * from '../language/core/lineEdit.js'; diff --git a/src/vs/editor/common/core/lineRange.ts b/src/vs/editor/common/core/lineRange.ts index b4d7a39da19..d629cf84da5 100644 --- a/src/vs/editor/common/core/lineRange.ts +++ b/src/vs/editor/common/core/lineRange.ts @@ -3,409 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { OffsetRange } from './offsetRange.js'; -import { Range } from './range.js'; -import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from '../../../base/common/arraysFind.js'; - -/** - * A range of lines (1-based). - */ -export class LineRange { - public static fromRange(range: Range): LineRange { - return new LineRange(range.startLineNumber, range.endLineNumber); - } - - public static fromRangeInclusive(range: Range): LineRange { - return new LineRange(range.startLineNumber, range.endLineNumber + 1); - } - - public static subtract(a: LineRange, b: LineRange | undefined): LineRange[] { - if (!b) { - return [a]; - } - if (a.startLineNumber < b.startLineNumber && b.endLineNumberExclusive < a.endLineNumberExclusive) { - return [ - new LineRange(a.startLineNumber, b.startLineNumber), - new LineRange(b.endLineNumberExclusive, a.endLineNumberExclusive) - ]; - } else if (b.startLineNumber <= a.startLineNumber && a.endLineNumberExclusive <= b.endLineNumberExclusive) { - return []; - } else if (b.endLineNumberExclusive < a.endLineNumberExclusive) { - return [new LineRange(Math.max(b.endLineNumberExclusive, a.startLineNumber), a.endLineNumberExclusive)]; - } else { - return [new LineRange(a.startLineNumber, Math.min(b.startLineNumber, a.endLineNumberExclusive))]; - } - } - - /** - * @param lineRanges An array of sorted line ranges. - */ - public static joinMany(lineRanges: readonly (readonly LineRange[])[]): readonly LineRange[] { - if (lineRanges.length === 0) { - return []; - } - let result = new LineRangeSet(lineRanges[0].slice()); - for (let i = 1; i < lineRanges.length; i++) { - result = result.getUnion(new LineRangeSet(lineRanges[i].slice())); - } - return result.ranges; - } - - public static join(lineRanges: LineRange[]): LineRange { - if (lineRanges.length === 0) { - throw new BugIndicatingError('lineRanges cannot be empty'); - } - let startLineNumber = lineRanges[0].startLineNumber; - let endLineNumberExclusive = lineRanges[0].endLineNumberExclusive; - for (let i = 1; i < lineRanges.length; i++) { - startLineNumber = Math.min(startLineNumber, lineRanges[i].startLineNumber); - endLineNumberExclusive = Math.max(endLineNumberExclusive, lineRanges[i].endLineNumberExclusive); - } - return new LineRange(startLineNumber, endLineNumberExclusive); - } - - public static ofLength(startLineNumber: number, length: number): LineRange { - return new LineRange(startLineNumber, startLineNumber + length); - } - - /** - * @internal - */ - public static deserialize(lineRange: ISerializedLineRange): LineRange { - return new LineRange(lineRange[0], lineRange[1]); - } - - /** - * The start line number. - */ - public readonly startLineNumber: number; - - /** - * The end line number (exclusive). - */ - public readonly endLineNumberExclusive: number; - - constructor( - startLineNumber: number, - endLineNumberExclusive: number, - ) { - if (startLineNumber > endLineNumberExclusive) { - throw new BugIndicatingError(`startLineNumber ${startLineNumber} cannot be after endLineNumberExclusive ${endLineNumberExclusive}`); - } - this.startLineNumber = startLineNumber; - this.endLineNumberExclusive = endLineNumberExclusive; - } - - /** - * Indicates if this line range contains the given line number. - */ - public contains(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - - /** - * Indicates if this line range is empty. - */ - get isEmpty(): boolean { - return this.startLineNumber === this.endLineNumberExclusive; - } - - /** - * Moves this line range by the given offset of line numbers. - */ - public delta(offset: number): LineRange { - return new LineRange(this.startLineNumber + offset, this.endLineNumberExclusive + offset); - } - - public deltaLength(offset: number): LineRange { - return new LineRange(this.startLineNumber, this.endLineNumberExclusive + offset); - } - - /** - * The number of lines this line range spans. - */ - public get length(): number { - return this.endLineNumberExclusive - this.startLineNumber; - } - - /** - * Creates a line range that combines this and the given line range. - */ - public join(other: LineRange): LineRange { - return new LineRange( - Math.min(this.startLineNumber, other.startLineNumber), - Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - ); - } - - public toString(): string { - return `[${this.startLineNumber},${this.endLineNumberExclusive})`; - } - - /** - * The resulting range is empty if the ranges do not intersect, but touch. - * If the ranges don't even touch, the result is undefined. - */ - public intersect(other: LineRange): LineRange | undefined { - const startLineNumber = Math.max(this.startLineNumber, other.startLineNumber); - const endLineNumberExclusive = Math.min(this.endLineNumberExclusive, other.endLineNumberExclusive); - if (startLineNumber <= endLineNumberExclusive) { - return new LineRange(startLineNumber, endLineNumberExclusive); - } - return undefined; - } - - public intersectsStrict(other: LineRange): boolean { - return this.startLineNumber < other.endLineNumberExclusive && other.startLineNumber < this.endLineNumberExclusive; - } - - public overlapOrTouch(other: LineRange): boolean { - return this.startLineNumber <= other.endLineNumberExclusive && other.startLineNumber <= this.endLineNumberExclusive; - } - - public equals(b: LineRange): boolean { - return this.startLineNumber === b.startLineNumber && this.endLineNumberExclusive === b.endLineNumberExclusive; - } - - public toInclusiveRange(): Range | null { - if (this.isEmpty) { - return null; - } - return new Range(this.startLineNumber, 1, this.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER); - } - - /** - * @deprecated Using this function is discouraged because it might lead to bugs: The end position is not guaranteed to be a valid position! - */ - public toExclusiveRange(): Range { - return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); - } - - public mapToLineArray(f: (lineNumber: number) => T): T[] { - const result: T[] = []; - for (let lineNumber = this.startLineNumber; lineNumber < this.endLineNumberExclusive; lineNumber++) { - result.push(f(lineNumber)); - } - return result; - } - - public forEach(f: (lineNumber: number) => void): void { - for (let lineNumber = this.startLineNumber; lineNumber < this.endLineNumberExclusive; lineNumber++) { - f(lineNumber); - } - } - - /** - * @internal - */ - public serialize(): ISerializedLineRange { - return [this.startLineNumber, this.endLineNumberExclusive]; - } - - public includes(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - - /** - * Converts this 1-based line range to a 0-based offset range (subtracts 1!). - * @internal - */ - public toOffsetRange(): OffsetRange { - return new OffsetRange(this.startLineNumber - 1, this.endLineNumberExclusive - 1); - } - - public distanceToRange(other: LineRange): number { - if (this.endLineNumberExclusive <= other.startLineNumber) { - return other.startLineNumber - this.endLineNumberExclusive; - } - if (other.endLineNumberExclusive <= this.startLineNumber) { - return this.startLineNumber - other.endLineNumberExclusive; - } - return 0; - } - - public distanceToLine(lineNumber: number): number { - if (this.contains(lineNumber)) { - return 0; - } - if (lineNumber < this.startLineNumber) { - return this.startLineNumber - lineNumber; - } - return lineNumber - this.endLineNumberExclusive; - } - - public addMargin(marginTop: number, marginBottom: number): LineRange { - return new LineRange( - this.startLineNumber - marginTop, - this.endLineNumberExclusive + marginBottom - ); - } -} - -export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number]; - - -export class LineRangeSet { - constructor( - /** - * Sorted by start line number. - * No two line ranges are touching or intersecting. - */ - private readonly _normalizedRanges: LineRange[] = [] - ) { - } - - get ranges(): readonly LineRange[] { - return this._normalizedRanges; - } - - addRange(range: LineRange): void { - if (range.length === 0) { - return; - } - - // Idea: Find joinRange such that: - // replaceRange = _normalizedRanges.replaceRange(joinRange, range.joinAll(joinRange.map(idx => this._normalizedRanges[idx]))) - - // idx of first element that touches range or that is after range - const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); - // idx of element after { last element that touches range or that is before range } - const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; - - if (joinRangeStartIdx === joinRangeEndIdxExclusive) { - // If there is no element that touches range, then joinRangeStartIdx === joinRangeEndIdxExclusive and that value is the index of the element after range - this._normalizedRanges.splice(joinRangeStartIdx, 0, range); - } else if (joinRangeStartIdx === joinRangeEndIdxExclusive - 1) { - // Else, there is an element that touches range and in this case it is both the first and last element. Thus we can replace it - const joinRange = this._normalizedRanges[joinRangeStartIdx]; - this._normalizedRanges[joinRangeStartIdx] = joinRange.join(range); - } else { - // First and last element are different - we need to replace the entire range - const joinRange = this._normalizedRanges[joinRangeStartIdx].join(this._normalizedRanges[joinRangeEndIdxExclusive - 1]).join(range); - this._normalizedRanges.splice(joinRangeStartIdx, joinRangeEndIdxExclusive - joinRangeStartIdx, joinRange); - } - } - - contains(lineNumber: number): boolean { - const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber <= lineNumber); - return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > lineNumber; - } - - intersects(range: LineRange): boolean { - const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber < range.endLineNumberExclusive); - return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > range.startLineNumber; - } - - getUnion(other: LineRangeSet): LineRangeSet { - if (this._normalizedRanges.length === 0) { - return other; - } - if (other._normalizedRanges.length === 0) { - return this; - } - - const result: LineRange[] = []; - let i1 = 0; - let i2 = 0; - let current: LineRange | null = null; - while (i1 < this._normalizedRanges.length || i2 < other._normalizedRanges.length) { - let next: LineRange | null = null; - if (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { - const lineRange1 = this._normalizedRanges[i1]; - const lineRange2 = other._normalizedRanges[i2]; - if (lineRange1.startLineNumber < lineRange2.startLineNumber) { - next = lineRange1; - i1++; - } else { - next = lineRange2; - i2++; - } - } else if (i1 < this._normalizedRanges.length) { - next = this._normalizedRanges[i1]; - i1++; - } else { - next = other._normalizedRanges[i2]; - i2++; - } - - if (current === null) { - current = next; - } else { - if (current.endLineNumberExclusive >= next.startLineNumber) { - // merge - current = new LineRange(current.startLineNumber, Math.max(current.endLineNumberExclusive, next.endLineNumberExclusive)); - } else { - // push - result.push(current); - current = next; - } - } - } - if (current !== null) { - result.push(current); - } - return new LineRangeSet(result); - } - - /** - * Subtracts all ranges in this set from `range` and returns the result. - */ - subtractFrom(range: LineRange): LineRangeSet { - // idx of first element that touches range or that is after range - const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); - // idx of element after { last element that touches range or that is before range } - const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; - - if (joinRangeStartIdx === joinRangeEndIdxExclusive) { - return new LineRangeSet([range]); - } - - const result: LineRange[] = []; - let startLineNumber = range.startLineNumber; - for (let i = joinRangeStartIdx; i < joinRangeEndIdxExclusive; i++) { - const r = this._normalizedRanges[i]; - if (r.startLineNumber > startLineNumber) { - result.push(new LineRange(startLineNumber, r.startLineNumber)); - } - startLineNumber = r.endLineNumberExclusive; - } - if (startLineNumber < range.endLineNumberExclusive) { - result.push(new LineRange(startLineNumber, range.endLineNumberExclusive)); - } - - return new LineRangeSet(result); - } - - toString() { - return this._normalizedRanges.map(r => r.toString()).join(', '); - } - - getIntersection(other: LineRangeSet): LineRangeSet { - const result: LineRange[] = []; - - let i1 = 0; - let i2 = 0; - while (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { - const r1 = this._normalizedRanges[i1]; - const r2 = other._normalizedRanges[i2]; - - const i = r1.intersect(r2); - if (i && !i.isEmpty) { - result.push(i); - } - - if (r1.endLineNumberExclusive < r2.endLineNumberExclusive) { - i1++; - } else { - i2++; - } - } - - return new LineRangeSet(result); - } - - getWithDelta(value: number): LineRangeSet { - return new LineRangeSet(this._normalizedRanges.map(r => r.delta(value))); - } -} +export * from '../language/core/lineRange.js'; diff --git a/src/vs/editor/common/core/offsetEdit.ts b/src/vs/editor/common/core/offsetEdit.ts index f35eb12d5b4..09572a5c17e 100644 --- a/src/vs/editor/common/core/offsetEdit.ts +++ b/src/vs/editor/common/core/offsetEdit.ts @@ -3,421 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { OffsetRange } from './offsetRange.js'; - -/** - * Describes an edit to a (0-based) string. - * Use `TextEdit` to describe edits for a 1-based line/column text. -*/ -export class OffsetEdit { - public static readonly empty = new OffsetEdit([]); - - public static fromJson(data: IOffsetEdit): OffsetEdit { - return new OffsetEdit(data.map(SingleOffsetEdit.fromJson)); - } - - public static replace( - range: OffsetRange, - newText: string, - ): OffsetEdit { - return new OffsetEdit([new SingleOffsetEdit(range, newText)]); - } - - public static insert( - offset: number, - insertText: string, - ): OffsetEdit { - return OffsetEdit.replace(OffsetRange.emptyAt(offset), insertText); - } - - constructor( - public readonly edits: readonly SingleOffsetEdit[], - ) { - let lastEndEx = -1; - for (const edit of edits) { - if (!(edit.replaceRange.start >= lastEndEx)) { - throw new BugIndicatingError(`Edits must be disjoint and sorted. Found ${edit} after ${lastEndEx}`); - } - lastEndEx = edit.replaceRange.endExclusive; - } - } - - normalize(): OffsetEdit { - const edits: SingleOffsetEdit[] = []; - let lastEdit: SingleOffsetEdit | undefined; - for (const edit of this.edits) { - if (edit.newText.length === 0 && edit.replaceRange.length === 0) { - continue; - } - if (lastEdit && lastEdit.replaceRange.endExclusive === edit.replaceRange.start) { - lastEdit = new SingleOffsetEdit( - lastEdit.replaceRange.join(edit.replaceRange), - lastEdit.newText + edit.newText, - ); - } else { - if (lastEdit) { - edits.push(lastEdit); - } - lastEdit = edit; - } - } - if (lastEdit) { - edits.push(lastEdit); - } - return new OffsetEdit(edits); - } - - toString() { - const edits = this.edits.map(e => e.toString()).join(', '); - return `[${edits}]`; - } - - apply(str: string): string { - const resultText: string[] = []; - let pos = 0; - for (const edit of this.edits) { - resultText.push(str.substring(pos, edit.replaceRange.start)); - resultText.push(edit.newText); - pos = edit.replaceRange.endExclusive; - } - resultText.push(str.substring(pos)); - return resultText.join(''); - } - - compose(other: OffsetEdit): OffsetEdit { - return joinEdits(this, other); - } - - /** - * Creates an edit that reverts this edit. - */ - inverse(originalStr: string): OffsetEdit { - const edits: SingleOffsetEdit[] = []; - let offset = 0; - for (const e of this.edits) { - edits.push(new SingleOffsetEdit( - OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newText.length), - originalStr.substring(e.replaceRange.start, e.replaceRange.endExclusive), - )); - offset += e.newText.length - e.replaceRange.length; - } - return new OffsetEdit(edits); - } - - getNewTextRanges(): OffsetRange[] { - const ranges: OffsetRange[] = []; - let offset = 0; - for (const e of this.edits) { - ranges.push(OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newText.length),); - offset += e.newText.length - e.replaceRange.length; - } - return ranges; - } - - get isEmpty(): boolean { - return this.edits.length === 0; - } - - /** - * Consider `t1 := text o base` and `t2 := text o this`. - * We are interested in `tm := tryMerge(t1, t2, base: text)`. - * For that, we compute `tm' := t1 o base o this.rebase(base)` - * such that `tm' === tm`. - */ - tryRebase(base: OffsetEdit): OffsetEdit; - tryRebase(base: OffsetEdit, noOverlap: true): OffsetEdit | undefined; - tryRebase(base: OffsetEdit, noOverlap?: true): OffsetEdit | undefined { - const newEdits: SingleOffsetEdit[] = []; - - let baseIdx = 0; - let ourIdx = 0; - let offset = 0; - - while (ourIdx < this.edits.length || baseIdx < base.edits.length) { - // take the edit that starts first - const baseEdit = base.edits[baseIdx]; - const ourEdit = this.edits[ourIdx]; - - if (!ourEdit) { - // We processed all our edits - break; - } else if (!baseEdit) { - // no more edits from base - newEdits.push(new SingleOffsetEdit( - ourEdit.replaceRange.delta(offset), - ourEdit.newText, - )); - ourIdx++; - } else if (ourEdit.replaceRange.intersectsOrTouches(baseEdit.replaceRange)) { - ourIdx++; // Don't take our edit, as it is conflicting -> skip - if (noOverlap) { - return undefined; - } - } else if (ourEdit.replaceRange.start < baseEdit.replaceRange.start) { - // Our edit starts first - newEdits.push(new SingleOffsetEdit( - ourEdit.replaceRange.delta(offset), - ourEdit.newText, - )); - ourIdx++; - } else { - baseIdx++; - offset += baseEdit.newText.length - baseEdit.replaceRange.length; - } - } - - return new OffsetEdit(newEdits); - } - - applyToOffset(originalOffset: number): number { - let accumulatedDelta = 0; - for (const edit of this.edits) { - if (edit.replaceRange.start <= originalOffset) { - if (originalOffset < edit.replaceRange.endExclusive) { - // the offset is in the replaced range - return edit.replaceRange.start + accumulatedDelta; - } - accumulatedDelta += edit.newText.length - edit.replaceRange.length; - } else { - break; - } - } - return originalOffset + accumulatedDelta; - } - - applyToOffsetRange(originalRange: OffsetRange): OffsetRange { - return new OffsetRange( - this.applyToOffset(originalRange.start), - this.applyToOffset(originalRange.endExclusive) - ); - } - - applyInverseToOffset(postEditsOffset: number): number { - let accumulatedDelta = 0; - for (const edit of this.edits) { - const editLength = edit.newText.length; - if (edit.replaceRange.start <= postEditsOffset - accumulatedDelta) { - if (postEditsOffset - accumulatedDelta < edit.replaceRange.start + editLength) { - // the offset is in the replaced range - return edit.replaceRange.start; - } - accumulatedDelta += editLength - edit.replaceRange.length; - } else { - break; - } - } - return postEditsOffset - accumulatedDelta; - } - - equals(other: OffsetEdit): boolean { - if (this.edits.length !== other.edits.length) { - return false; - } - for (let i = 0; i < this.edits.length; i++) { - if (!this.edits[i].equals(other.edits[i])) { - return false; - } - - } - return true; - } -} - -export type IOffsetEdit = ISingleOffsetEdit[]; - -export interface ISingleOffsetEdit { - txt: string; - pos: number; - len: number; -} - -export class SingleOffsetEdit { - public static fromJson(data: ISingleOffsetEdit): SingleOffsetEdit { - return new SingleOffsetEdit(OffsetRange.ofStartAndLength(data.pos, data.len), data.txt); - } - - public static insert(offset: number, text: string): SingleOffsetEdit { - return new SingleOffsetEdit(OffsetRange.emptyAt(offset), text); - } - - public static replace(range: OffsetRange, text: string): SingleOffsetEdit { - return new SingleOffsetEdit(range, text); - } - - constructor( - public readonly replaceRange: OffsetRange, - public readonly newText: string, - ) { } - - toString(): string { - return `${this.replaceRange} -> "${this.newText}"`; - } - - get isEmpty() { - return this.newText.length === 0 && this.replaceRange.length === 0; - } - - apply(str: string): string { - return str.substring(0, this.replaceRange.start) + this.newText + str.substring(this.replaceRange.endExclusive); - } - - getRangeAfterApply(): OffsetRange { - return new OffsetRange(this.replaceRange.start, this.replaceRange.start + this.newText.length); - } - - equals(other: SingleOffsetEdit): boolean { - return this.replaceRange.equals(other.replaceRange) && this.newText === other.newText; - } -} - -/** - * Invariant: - * ``` - * edits2.apply(edits1.apply(str)) = join(edits1, edits2).apply(str) - * ``` - */ -function joinEdits(edits1: OffsetEdit, edits2: OffsetEdit): OffsetEdit { - edits1 = edits1.normalize(); - edits2 = edits2.normalize(); - - if (edits1.isEmpty) { return edits2; } - if (edits2.isEmpty) { return edits1; } - - const edit1Queue = [...edits1.edits]; - const result: SingleOffsetEdit[] = []; - - let edit1ToEdit2 = 0; - - for (const edit2 of edits2.edits) { - // Copy over edit1 unmodified until it touches edit2. - while (true) { - const edit1 = edit1Queue[0]!; - if (!edit1 || edit1.replaceRange.start + edit1ToEdit2 + edit1.newText.length >= edit2.replaceRange.start) { - break; - } - edit1Queue.shift(); - - result.push(edit1); - edit1ToEdit2 += edit1.newText.length - edit1.replaceRange.length; - } - - const firstEdit1ToEdit2 = edit1ToEdit2; - let firstIntersecting: SingleOffsetEdit | undefined; // or touching - let lastIntersecting: SingleOffsetEdit | undefined; // or touching - - while (true) { - const edit1 = edit1Queue[0]; - if (!edit1 || edit1.replaceRange.start + edit1ToEdit2 > edit2.replaceRange.endExclusive) { - break; - } - // else we intersect, because the new end of edit1 is after or equal to our start - - if (!firstIntersecting) { - firstIntersecting = edit1; - } - lastIntersecting = edit1; - edit1Queue.shift(); - - edit1ToEdit2 += edit1.newText.length - edit1.replaceRange.length; - } - - if (!firstIntersecting) { - result.push(new SingleOffsetEdit(edit2.replaceRange.delta(-edit1ToEdit2), edit2.newText)); - } else { - let prefix = ''; - const prefixLength = edit2.replaceRange.start - (firstIntersecting.replaceRange.start + firstEdit1ToEdit2); - if (prefixLength > 0) { - prefix = firstIntersecting.newText.slice(0, prefixLength); - } - const suffixLength = (lastIntersecting!.replaceRange.endExclusive + edit1ToEdit2) - edit2.replaceRange.endExclusive; - if (suffixLength > 0) { - const e = new SingleOffsetEdit(OffsetRange.ofStartAndLength(lastIntersecting!.replaceRange.endExclusive, 0), lastIntersecting!.newText.slice(-suffixLength)); - edit1Queue.unshift(e); - edit1ToEdit2 -= e.newText.length - e.replaceRange.length; - } - const newText = prefix + edit2.newText; - - const newReplaceRange = new OffsetRange( - Math.min(firstIntersecting.replaceRange.start, edit2.replaceRange.start - firstEdit1ToEdit2), - edit2.replaceRange.endExclusive - edit1ToEdit2 - ); - result.push(new SingleOffsetEdit(newReplaceRange, newText)); - } - } - - while (true) { - const item = edit1Queue.shift(); - if (!item) { break; } - result.push(item); - } - - return new OffsetEdit(result).normalize(); -} - -export function applyEditsToRanges(sortedRanges: OffsetRange[], edits: OffsetEdit): OffsetRange[] { - sortedRanges = sortedRanges.slice(); - - // treat edits as deletion of the replace range and then as insertion that extends the first range - const result: OffsetRange[] = []; - - let offset = 0; - - for (const e of edits.edits) { - while (true) { - // ranges before the current edit - const r = sortedRanges[0]; - if (!r || r.endExclusive >= e.replaceRange.start) { - break; - } - sortedRanges.shift(); - result.push(r.delta(offset)); - } - - const intersecting: OffsetRange[] = []; - while (true) { - const r = sortedRanges[0]; - if (!r || !r.intersectsOrTouches(e.replaceRange)) { - break; - } - sortedRanges.shift(); - intersecting.push(r); - } - - for (let i = intersecting.length - 1; i >= 0; i--) { - let r = intersecting[i]; - - const overlap = r.intersect(e.replaceRange)!.length; - r = r.deltaEnd(-overlap + (i === 0 ? e.newText.length : 0)); - - const rangeAheadOfReplaceRange = r.start - e.replaceRange.start; - if (rangeAheadOfReplaceRange > 0) { - r = r.delta(-rangeAheadOfReplaceRange); - } - - if (i !== 0) { - r = r.delta(e.newText.length); - } - - // We already took our offset into account. - // Because we add r back to the queue (which then adds offset again), - // we have to remove it here. - r = r.delta(-(e.newText.length - e.replaceRange.length)); - - sortedRanges.unshift(r); - } - - offset += e.newText.length - e.replaceRange.length; - } - - while (true) { - const r = sortedRanges[0]; - if (!r) { - break; - } - sortedRanges.shift(); - result.push(r.delta(offset)); - } - - return result; -} +export * from '../language/core/offsetEdit.js'; diff --git a/src/vs/editor/common/core/offsetRange.ts b/src/vs/editor/common/core/offsetRange.ts index 7e1aa41b29e..8fb3ddeaade 100644 --- a/src/vs/editor/common/core/offsetRange.ts +++ b/src/vs/editor/common/core/offsetRange.ts @@ -3,256 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../base/common/errors.js'; - -export interface IOffsetRange { - readonly start: number; - readonly endExclusive: number; -} - -/** - * A range of offsets (0-based). -*/ -export class OffsetRange implements IOffsetRange { - public static fromTo(start: number, endExclusive: number): OffsetRange { - return new OffsetRange(start, endExclusive); - } - - public static addRange(range: OffsetRange, sortedRanges: OffsetRange[]): void { - let i = 0; - while (i < sortedRanges.length && sortedRanges[i].endExclusive < range.start) { - i++; - } - let j = i; - while (j < sortedRanges.length && sortedRanges[j].start <= range.endExclusive) { - j++; - } - if (i === j) { - sortedRanges.splice(i, 0, range); - } else { - const start = Math.min(range.start, sortedRanges[i].start); - const end = Math.max(range.endExclusive, sortedRanges[j - 1].endExclusive); - sortedRanges.splice(i, j - i, new OffsetRange(start, end)); - } - } - - public static tryCreate(start: number, endExclusive: number): OffsetRange | undefined { - if (start > endExclusive) { - return undefined; - } - return new OffsetRange(start, endExclusive); - } - - public static ofLength(length: number): OffsetRange { - return new OffsetRange(0, length); - } - - public static ofStartAndLength(start: number, length: number): OffsetRange { - return new OffsetRange(start, start + length); - } - - public static emptyAt(offset: number): OffsetRange { - return new OffsetRange(offset, offset); - } - - constructor(public readonly start: number, public readonly endExclusive: number) { - if (start > endExclusive) { - throw new BugIndicatingError(`Invalid range: ${this.toString()}`); - } - } - - get isEmpty(): boolean { - return this.start === this.endExclusive; - } - - public delta(offset: number): OffsetRange { - return new OffsetRange(this.start + offset, this.endExclusive + offset); - } - - public deltaStart(offset: number): OffsetRange { - return new OffsetRange(this.start + offset, this.endExclusive); - } - - public deltaEnd(offset: number): OffsetRange { - return new OffsetRange(this.start, this.endExclusive + offset); - } - - public get length(): number { - return this.endExclusive - this.start; - } - - public toString() { - return `[${this.start}, ${this.endExclusive})`; - } - - public equals(other: OffsetRange): boolean { - return this.start === other.start && this.endExclusive === other.endExclusive; - } - - public containsRange(other: OffsetRange): boolean { - return this.start <= other.start && other.endExclusive <= this.endExclusive; - } - - public contains(offset: number): boolean { - return this.start <= offset && offset < this.endExclusive; - } - - /** - * for all numbers n: range1.contains(n) or range2.contains(n) => range1.join(range2).contains(n) - * The joined range is the smallest range that contains both ranges. - */ - public join(other: OffsetRange): OffsetRange { - return new OffsetRange(Math.min(this.start, other.start), Math.max(this.endExclusive, other.endExclusive)); - } - - /** - * for all numbers n: range1.contains(n) and range2.contains(n) <=> range1.intersect(range2).contains(n) - * - * The resulting range is empty if the ranges do not intersect, but touch. - * If the ranges don't even touch, the result is undefined. - */ - public intersect(other: OffsetRange): OffsetRange | undefined { - const start = Math.max(this.start, other.start); - const end = Math.min(this.endExclusive, other.endExclusive); - if (start <= end) { - return new OffsetRange(start, end); - } - return undefined; - } - - public intersectionLength(range: OffsetRange): number { - const start = Math.max(this.start, range.start); - const end = Math.min(this.endExclusive, range.endExclusive); - return Math.max(0, end - start); - } - - public intersects(other: OffsetRange): boolean { - const start = Math.max(this.start, other.start); - const end = Math.min(this.endExclusive, other.endExclusive); - return start < end; - } - - public intersectsOrTouches(other: OffsetRange): boolean { - const start = Math.max(this.start, other.start); - const end = Math.min(this.endExclusive, other.endExclusive); - return start <= end; - } - - public isBefore(other: OffsetRange): boolean { - return this.endExclusive <= other.start; - } - - public isAfter(other: OffsetRange): boolean { - return this.start >= other.endExclusive; - } - - public slice(arr: T[]): T[] { - return arr.slice(this.start, this.endExclusive); - } - - public substring(str: string): string { - return str.substring(this.start, this.endExclusive); - } - - /** - * Returns the given value if it is contained in this instance, otherwise the closest value that is contained. - * The range must not be empty. - */ - public clip(value: number): number { - if (this.isEmpty) { - throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); - } - return Math.max(this.start, Math.min(this.endExclusive - 1, value)); - } - - /** - * Returns `r := value + k * length` such that `r` is contained in this range. - * The range must not be empty. - * - * E.g. `[5, 10).clipCyclic(10) === 5`, `[5, 10).clipCyclic(11) === 6` and `[5, 10).clipCyclic(4) === 9`. - */ - public clipCyclic(value: number): number { - if (this.isEmpty) { - throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); - } - if (value < this.start) { - return this.endExclusive - ((this.start - value) % this.length); - } - if (value >= this.endExclusive) { - return this.start + ((value - this.start) % this.length); - } - return value; - } - - public map(f: (offset: number) => T): T[] { - const result: T[] = []; - for (let i = this.start; i < this.endExclusive; i++) { - result.push(f(i)); - } - return result; - } - - public forEach(f: (offset: number) => void): void { - for (let i = this.start; i < this.endExclusive; i++) { - f(i); - } - } -} - -export class OffsetRangeSet { - private readonly _sortedRanges: OffsetRange[] = []; - - public addRange(range: OffsetRange): void { - let i = 0; - while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive < range.start) { - i++; - } - let j = i; - while (j < this._sortedRanges.length && this._sortedRanges[j].start <= range.endExclusive) { - j++; - } - if (i === j) { - this._sortedRanges.splice(i, 0, range); - } else { - const start = Math.min(range.start, this._sortedRanges[i].start); - const end = Math.max(range.endExclusive, this._sortedRanges[j - 1].endExclusive); - this._sortedRanges.splice(i, j - i, new OffsetRange(start, end)); - } - } - - public toString(): string { - return this._sortedRanges.map(r => r.toString()).join(', '); - } - - /** - * Returns of there is a value that is contained in this instance and the given range. - */ - public intersectsStrict(other: OffsetRange): boolean { - // TODO use binary search - let i = 0; - while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive <= other.start) { - i++; - } - return i < this._sortedRanges.length && this._sortedRanges[i].start < other.endExclusive; - } - - public intersectWithRange(other: OffsetRange): OffsetRangeSet { - // TODO use binary search + slice - const result = new OffsetRangeSet(); - for (const range of this._sortedRanges) { - const intersection = range.intersect(other); - if (intersection) { - result.addRange(intersection); - } - } - return result; - } - - public intersectWithRangeLength(other: OffsetRange): number { - return this.intersectWithRange(other).length; - } - - public get length(): number { - return this._sortedRanges.reduce((prev, cur) => prev + cur.length, 0); - } -} +export * from '../language/core/offsetRange.js'; diff --git a/src/vs/editor/common/core/position.ts b/src/vs/editor/common/core/position.ts index e3033e953f9..75cf8c32c14 100644 --- a/src/vs/editor/common/core/position.ts +++ b/src/vs/editor/common/core/position.ts @@ -3,182 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * A position in the editor. This interface is suitable for serialization. - */ -export interface IPosition { - /** - * line number (starts at 1) - */ - readonly lineNumber: number; - /** - * column (the first character in a line is between column 1 and column 2) - */ - readonly column: number; -} - -/** - * A position in the editor. - */ -export class Position { - /** - * line number (starts at 1) - */ - public readonly lineNumber: number; - /** - * column (the first character in a line is between column 1 and column 2) - */ - public readonly column: number; - - constructor(lineNumber: number, column: number) { - this.lineNumber = lineNumber; - this.column = column; - } - - /** - * Create a new position from this position. - * - * @param newLineNumber new line number - * @param newColumn new column - */ - with(newLineNumber: number = this.lineNumber, newColumn: number = this.column): Position { - if (newLineNumber === this.lineNumber && newColumn === this.column) { - return this; - } else { - return new Position(newLineNumber, newColumn); - } - } - - /** - * Derive a new position from this position. - * - * @param deltaLineNumber line number delta - * @param deltaColumn column delta - */ - delta(deltaLineNumber: number = 0, deltaColumn: number = 0): Position { - return this.with(Math.max(1, this.lineNumber + deltaLineNumber), Math.max(1, this.column + deltaColumn)); - } - - /** - * Test if this position equals other position - */ - public equals(other: IPosition): boolean { - return Position.equals(this, other); - } - - /** - * Test if position `a` equals position `b` - */ - public static equals(a: IPosition | null, b: IPosition | null): boolean { - if (!a && !b) { - return true; - } - return ( - !!a && - !!b && - a.lineNumber === b.lineNumber && - a.column === b.column - ); - } - - /** - * Test if this position is before other position. - * If the two positions are equal, the result will be false. - */ - public isBefore(other: IPosition): boolean { - return Position.isBefore(this, other); - } - - /** - * Test if position `a` is before position `b`. - * If the two positions are equal, the result will be false. - */ - public static isBefore(a: IPosition, b: IPosition): boolean { - if (a.lineNumber < b.lineNumber) { - return true; - } - if (b.lineNumber < a.lineNumber) { - return false; - } - return a.column < b.column; - } - - /** - * Test if this position is before other position. - * If the two positions are equal, the result will be true. - */ - public isBeforeOrEqual(other: IPosition): boolean { - return Position.isBeforeOrEqual(this, other); - } - - /** - * Test if position `a` is before position `b`. - * If the two positions are equal, the result will be true. - */ - public static isBeforeOrEqual(a: IPosition, b: IPosition): boolean { - if (a.lineNumber < b.lineNumber) { - return true; - } - if (b.lineNumber < a.lineNumber) { - return false; - } - return a.column <= b.column; - } - - /** - * A function that compares positions, useful for sorting - */ - public static compare(a: IPosition, b: IPosition): number { - const aLineNumber = a.lineNumber | 0; - const bLineNumber = b.lineNumber | 0; - - if (aLineNumber === bLineNumber) { - const aColumn = a.column | 0; - const bColumn = b.column | 0; - return aColumn - bColumn; - } - - return aLineNumber - bLineNumber; - } - - /** - * Clone this position. - */ - public clone(): Position { - return new Position(this.lineNumber, this.column); - } - - /** - * Convert to a human-readable representation. - */ - public toString(): string { - return '(' + this.lineNumber + ',' + this.column + ')'; - } - - // --- - - /** - * Create a `Position` from an `IPosition`. - */ - public static lift(pos: IPosition): Position { - return new Position(pos.lineNumber, pos.column); - } - - /** - * Test if `obj` is an `IPosition`. - */ - public static isIPosition(obj: any): obj is IPosition { - return ( - obj - && (typeof obj.lineNumber === 'number') - && (typeof obj.column === 'number') - ); - } - - public toJSON(): IPosition { - return { - lineNumber: this.lineNumber, - column: this.column - }; - } -} +export * from '../language/core/position.js'; diff --git a/src/vs/editor/common/core/positionToOffset.ts b/src/vs/editor/common/core/positionToOffset.ts index 3548c8d0e89..26bcdf3d69b 100644 --- a/src/vs/editor/common/core/positionToOffset.ts +++ b/src/vs/editor/common/core/positionToOffset.ts @@ -3,89 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLastIdxMonotonous } from '../../../base/common/arraysFind.js'; -import { OffsetRange } from './offsetRange.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; -import { TextLength } from './textLength.js'; - -export class PositionOffsetTransformer { - private readonly lineStartOffsetByLineIdx: number[]; - private readonly lineEndOffsetByLineIdx: number[]; - - constructor(public readonly text: string) { - this.lineStartOffsetByLineIdx = []; - this.lineEndOffsetByLineIdx = []; - - this.lineStartOffsetByLineIdx.push(0); - for (let i = 0; i < text.length; i++) { - if (text.charAt(i) === '\n') { - this.lineStartOffsetByLineIdx.push(i + 1); - if (i > 0 && text.charAt(i - 1) === '\r') { - this.lineEndOffsetByLineIdx.push(i - 1); - } else { - this.lineEndOffsetByLineIdx.push(i); - } - } - } - this.lineEndOffsetByLineIdx.push(text.length); - } - - getOffset(position: Position): number { - const valPos = this._validatePosition(position); - return this.lineStartOffsetByLineIdx[valPos.lineNumber - 1] + valPos.column - 1; - } - - private _validatePosition(position: Position): Position { - if (position.lineNumber < 1) { - return new Position(1, 1); - } - const lineCount = this.textLength.lineCount + 1; - if (position.lineNumber > lineCount) { - const lineLength = this.getLineLength(lineCount); - return new Position(lineCount, lineLength + 1); - } - if (position.column < 1) { - return new Position(position.lineNumber, 1); - } - const lineLength = this.getLineLength(position.lineNumber); - if (position.column - 1 > lineLength) { - return new Position(position.lineNumber, lineLength + 1); - } - return position; - } - - getOffsetRange(range: Range): OffsetRange { - return new OffsetRange( - this.getOffset(range.getStartPosition()), - this.getOffset(range.getEndPosition()) - ); - } - - getPosition(offset: number): Position { - const idx = findLastIdxMonotonous(this.lineStartOffsetByLineIdx, i => i <= offset); - const lineNumber = idx + 1; - const column = offset - this.lineStartOffsetByLineIdx[idx] + 1; - return new Position(lineNumber, column); - } - - getRange(offsetRange: OffsetRange): Range { - return Range.fromPositions( - this.getPosition(offsetRange.start), - this.getPosition(offsetRange.endExclusive) - ); - } - - getTextLength(offsetRange: OffsetRange): TextLength { - return TextLength.ofRange(this.getRange(offsetRange)); - } - - get textLength(): TextLength { - const lineIdx = this.lineStartOffsetByLineIdx.length - 1; - return new TextLength(lineIdx, this.text.length - this.lineStartOffsetByLineIdx[lineIdx]); - } - - getLineLength(lineNumber: number): number { - return this.lineEndOffsetByLineIdx[lineNumber - 1] - this.lineStartOffsetByLineIdx[lineNumber - 1]; - } -} +export * from '../language/core/positionToOffset.js'; diff --git a/src/vs/editor/common/core/range.ts b/src/vs/editor/common/core/range.ts index 81658a4aeb5..7d94979d215 100644 --- a/src/vs/editor/common/core/range.ts +++ b/src/vs/editor/common/core/range.ts @@ -3,515 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IPosition, Position } from './position.js'; - -/** - * A range in the editor. This interface is suitable for serialization. - */ -export interface IRange { - /** - * Line number on which the range starts (starts at 1). - */ - readonly startLineNumber: number; - /** - * Column on which the range starts in line `startLineNumber` (starts at 1). - */ - readonly startColumn: number; - /** - * Line number on which the range ends. - */ - readonly endLineNumber: number; - /** - * Column on which the range ends in line `endLineNumber`. - */ - readonly endColumn: number; -} - -/** - * A range in the editor. (startLineNumber,startColumn) is <= (endLineNumber,endColumn) - */ -export class Range { - - /** - * Line number on which the range starts (starts at 1). - */ - public readonly startLineNumber: number; - /** - * Column on which the range starts in line `startLineNumber` (starts at 1). - */ - public readonly startColumn: number; - /** - * Line number on which the range ends. - */ - public readonly endLineNumber: number; - /** - * Column on which the range ends in line `endLineNumber`. - */ - public readonly endColumn: number; - - constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) { - if ((startLineNumber > endLineNumber) || (startLineNumber === endLineNumber && startColumn > endColumn)) { - this.startLineNumber = endLineNumber; - this.startColumn = endColumn; - this.endLineNumber = startLineNumber; - this.endColumn = startColumn; - } else { - this.startLineNumber = startLineNumber; - this.startColumn = startColumn; - this.endLineNumber = endLineNumber; - this.endColumn = endColumn; - } - } - - /** - * Test if this range is empty. - */ - public isEmpty(): boolean { - return Range.isEmpty(this); - } - - /** - * Test if `range` is empty. - */ - public static isEmpty(range: IRange): boolean { - return (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn); - } - - /** - * Test if position is in this range. If the position is at the edges, will return true. - */ - public containsPosition(position: IPosition): boolean { - return Range.containsPosition(this, position); - } - - /** - * Test if `position` is in `range`. If the position is at the edges, will return true. - */ - public static containsPosition(range: IRange, position: IPosition): boolean { - if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { - return false; - } - if (position.lineNumber === range.startLineNumber && position.column < range.startColumn) { - return false; - } - if (position.lineNumber === range.endLineNumber && position.column > range.endColumn) { - return false; - } - return true; - } - - /** - * Test if `position` is in `range`. If the position is at the edges, will return false. - * @internal - */ - public static strictContainsPosition(range: IRange, position: IPosition): boolean { - if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { - return false; - } - if (position.lineNumber === range.startLineNumber && position.column <= range.startColumn) { - return false; - } - if (position.lineNumber === range.endLineNumber && position.column >= range.endColumn) { - return false; - } - return true; - } - - /** - * Test if range is in this range. If the range is equal to this range, will return true. - */ - public containsRange(range: IRange): boolean { - return Range.containsRange(this, range); - } - - /** - * Test if `otherRange` is in `range`. If the ranges are equal, will return true. - */ - public static containsRange(range: IRange, otherRange: IRange): boolean { - if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) { - return false; - } - if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) { - return false; - } - if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn < range.startColumn) { - return false; - } - if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn > range.endColumn) { - return false; - } - return true; - } - - /** - * Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true. - */ - public strictContainsRange(range: IRange): boolean { - return Range.strictContainsRange(this, range); - } - - /** - * Test if `otherRange` is strictly in `range` (must start after, and end before). If the ranges are equal, will return false. - */ - public static strictContainsRange(range: IRange, otherRange: IRange): boolean { - if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) { - return false; - } - if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) { - return false; - } - if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn <= range.startColumn) { - return false; - } - if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn >= range.endColumn) { - return false; - } - return true; - } - - /** - * A reunion of the two ranges. - * The smallest position will be used as the start point, and the largest one as the end point. - */ - public plusRange(range: IRange): Range { - return Range.plusRange(this, range); - } - - /** - * A reunion of the two ranges. - * The smallest position will be used as the start point, and the largest one as the end point. - */ - public static plusRange(a: IRange, b: IRange): Range { - let startLineNumber: number; - let startColumn: number; - let endLineNumber: number; - let endColumn: number; - - if (b.startLineNumber < a.startLineNumber) { - startLineNumber = b.startLineNumber; - startColumn = b.startColumn; - } else if (b.startLineNumber === a.startLineNumber) { - startLineNumber = b.startLineNumber; - startColumn = Math.min(b.startColumn, a.startColumn); - } else { - startLineNumber = a.startLineNumber; - startColumn = a.startColumn; - } - - if (b.endLineNumber > a.endLineNumber) { - endLineNumber = b.endLineNumber; - endColumn = b.endColumn; - } else if (b.endLineNumber === a.endLineNumber) { - endLineNumber = b.endLineNumber; - endColumn = Math.max(b.endColumn, a.endColumn); - } else { - endLineNumber = a.endLineNumber; - endColumn = a.endColumn; - } - - return new Range(startLineNumber, startColumn, endLineNumber, endColumn); - } - - /** - * A intersection of the two ranges. - */ - public intersectRanges(range: IRange): Range | null { - return Range.intersectRanges(this, range); - } - - /** - * A intersection of the two ranges. - */ - public static intersectRanges(a: IRange, b: IRange): Range | null { - let resultStartLineNumber = a.startLineNumber; - let resultStartColumn = a.startColumn; - let resultEndLineNumber = a.endLineNumber; - let resultEndColumn = a.endColumn; - const otherStartLineNumber = b.startLineNumber; - const otherStartColumn = b.startColumn; - const otherEndLineNumber = b.endLineNumber; - const otherEndColumn = b.endColumn; - - if (resultStartLineNumber < otherStartLineNumber) { - resultStartLineNumber = otherStartLineNumber; - resultStartColumn = otherStartColumn; - } else if (resultStartLineNumber === otherStartLineNumber) { - resultStartColumn = Math.max(resultStartColumn, otherStartColumn); - } - - if (resultEndLineNumber > otherEndLineNumber) { - resultEndLineNumber = otherEndLineNumber; - resultEndColumn = otherEndColumn; - } else if (resultEndLineNumber === otherEndLineNumber) { - resultEndColumn = Math.min(resultEndColumn, otherEndColumn); - } - - // Check if selection is now empty - if (resultStartLineNumber > resultEndLineNumber) { - return null; - } - if (resultStartLineNumber === resultEndLineNumber && resultStartColumn > resultEndColumn) { - return null; - } - return new Range(resultStartLineNumber, resultStartColumn, resultEndLineNumber, resultEndColumn); - } - - /** - * Test if this range equals other. - */ - public equalsRange(other: IRange | null | undefined): boolean { - return Range.equalsRange(this, other); - } - - /** - * Test if range `a` equals `b`. - */ - public static equalsRange(a: IRange | null | undefined, b: IRange | null | undefined): boolean { - if (!a && !b) { - return true; - } - return ( - !!a && - !!b && - a.startLineNumber === b.startLineNumber && - a.startColumn === b.startColumn && - a.endLineNumber === b.endLineNumber && - a.endColumn === b.endColumn - ); - } - - /** - * Return the end position (which will be after or equal to the start position) - */ - public getEndPosition(): Position { - return Range.getEndPosition(this); - } - - /** - * Return the end position (which will be after or equal to the start position) - */ - public static getEndPosition(range: IRange): Position { - return new Position(range.endLineNumber, range.endColumn); - } - - /** - * Return the start position (which will be before or equal to the end position) - */ - public getStartPosition(): Position { - return Range.getStartPosition(this); - } - - /** - * Return the start position (which will be before or equal to the end position) - */ - public static getStartPosition(range: IRange): Position { - return new Position(range.startLineNumber, range.startColumn); - } - - /** - * Transform to a user presentable string representation. - */ - public toString(): string { - return '[' + this.startLineNumber + ',' + this.startColumn + ' -> ' + this.endLineNumber + ',' + this.endColumn + ']'; - } - - /** - * Create a new range using this range's start position, and using endLineNumber and endColumn as the end position. - */ - public setEndPosition(endLineNumber: number, endColumn: number): Range { - return new Range(this.startLineNumber, this.startColumn, endLineNumber, endColumn); - } - - /** - * Create a new range using this range's end position, and using startLineNumber and startColumn as the start position. - */ - public setStartPosition(startLineNumber: number, startColumn: number): Range { - return new Range(startLineNumber, startColumn, this.endLineNumber, this.endColumn); - } - - /** - * Create a new empty range using this range's start position. - */ - public collapseToStart(): Range { - return Range.collapseToStart(this); - } - - /** - * Create a new empty range using this range's start position. - */ - public static collapseToStart(range: IRange): Range { - return new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn); - } - - /** - * Create a new empty range using this range's end position. - */ - public collapseToEnd(): Range { - return Range.collapseToEnd(this); - } - - /** - * Create a new empty range using this range's end position. - */ - public static collapseToEnd(range: IRange): Range { - return new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn); - } - - /** - * Moves the range by the given amount of lines. - */ - public delta(lineCount: number): Range { - return new Range(this.startLineNumber + lineCount, this.startColumn, this.endLineNumber + lineCount, this.endColumn); - } - - public isSingleLine(): boolean { - return this.startLineNumber === this.endLineNumber; - } - - // --- - - public static fromPositions(start: IPosition, end: IPosition = start): Range { - return new Range(start.lineNumber, start.column, end.lineNumber, end.column); - } - - /** - * Create a `Range` from an `IRange`. - */ - public static lift(range: undefined | null): null; - public static lift(range: IRange): Range; - public static lift(range: IRange | undefined | null): Range | null; - public static lift(range: IRange | undefined | null): Range | null { - if (!range) { - return null; - } - return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); - } - - /** - * Test if `obj` is an `IRange`. - */ - public static isIRange(obj: any): obj is IRange { - return ( - obj - && (typeof obj.startLineNumber === 'number') - && (typeof obj.startColumn === 'number') - && (typeof obj.endLineNumber === 'number') - && (typeof obj.endColumn === 'number') - ); - } - - /** - * Test if the two ranges are touching in any way. - */ - public static areIntersectingOrTouching(a: IRange, b: IRange): boolean { - // Check if `a` is before `b` - if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn < b.startColumn)) { - return false; - } - - // Check if `b` is before `a` - if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn < a.startColumn)) { - return false; - } - - // These ranges must intersect - return true; - } - - /** - * Test if the two ranges are intersecting. If the ranges are touching it returns true. - */ - public static areIntersecting(a: IRange, b: IRange): boolean { - // Check if `a` is before `b` - if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn <= b.startColumn)) { - return false; - } - - // Check if `b` is before `a` - if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn <= a.startColumn)) { - return false; - } - - // These ranges must intersect - return true; - } - - /** - * Test if the two ranges are intersecting, but not touching at all. - */ - public static areOnlyIntersecting(a: IRange, b: IRange): boolean { - // Check if `a` is before `b` - if (a.endLineNumber < (b.startLineNumber - 1) || (a.endLineNumber === b.startLineNumber && a.endColumn < (b.startColumn - 1))) { - return false; - } - - // Check if `b` is before `a` - if (b.endLineNumber < (a.startLineNumber - 1) || (b.endLineNumber === a.startLineNumber && b.endColumn < (a.startColumn - 1))) { - return false; - } - - // These ranges must intersect - return true; - } - - /** - * A function that compares ranges, useful for sorting ranges - * It will first compare ranges on the startPosition and then on the endPosition - */ - public static compareRangesUsingStarts(a: IRange | null | undefined, b: IRange | null | undefined): number { - if (a && b) { - const aStartLineNumber = a.startLineNumber | 0; - const bStartLineNumber = b.startLineNumber | 0; - - if (aStartLineNumber === bStartLineNumber) { - const aStartColumn = a.startColumn | 0; - const bStartColumn = b.startColumn | 0; - - if (aStartColumn === bStartColumn) { - const aEndLineNumber = a.endLineNumber | 0; - const bEndLineNumber = b.endLineNumber | 0; - - if (aEndLineNumber === bEndLineNumber) { - const aEndColumn = a.endColumn | 0; - const bEndColumn = b.endColumn | 0; - return aEndColumn - bEndColumn; - } - return aEndLineNumber - bEndLineNumber; - } - return aStartColumn - bStartColumn; - } - return aStartLineNumber - bStartLineNumber; - } - const aExists = (a ? 1 : 0); - const bExists = (b ? 1 : 0); - return aExists - bExists; - } - - /** - * A function that compares ranges, useful for sorting ranges - * It will first compare ranges on the endPosition and then on the startPosition - */ - public static compareRangesUsingEnds(a: IRange, b: IRange): number { - if (a.endLineNumber === b.endLineNumber) { - if (a.endColumn === b.endColumn) { - if (a.startLineNumber === b.startLineNumber) { - return a.startColumn - b.startColumn; - } - return a.startLineNumber - b.startLineNumber; - } - return a.endColumn - b.endColumn; - } - return a.endLineNumber - b.endLineNumber; - } - - /** - * Test if the range spans multiple lines. - */ - public static spansMultipleLines(range: IRange): boolean { - return range.endLineNumber > range.startLineNumber; - } - - public toJSON(): IRange { - return this; - } -} +export * from '../language/core/range.js'; diff --git a/src/vs/editor/common/core/rangeMapping.ts b/src/vs/editor/common/core/rangeMapping.ts index 8d838cc1f75..958007afaa1 100644 --- a/src/vs/editor/common/core/rangeMapping.ts +++ b/src/vs/editor/common/core/rangeMapping.ts @@ -3,71 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLastMonotonous } from '../../../base/common/arraysFind.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; -import { TextLength } from './textLength.js'; - -/** - * Represents a list of mappings of ranges from one document to another. - */ -export class RangeMapping { - constructor(public readonly mappings: readonly SingleRangeMapping[]) { - } - - mapPosition(position: Position): PositionOrRange { - const mapping = findLastMonotonous(this.mappings, m => m.original.getStartPosition().isBeforeOrEqual(position)); - if (!mapping) { - return PositionOrRange.position(position); - } - if (mapping.original.containsPosition(position)) { - return PositionOrRange.range(mapping.modified); - } - const l = TextLength.betweenPositions(mapping.original.getEndPosition(), position); - return PositionOrRange.position(l.addToPosition(mapping.modified.getEndPosition())); - } - - mapRange(range: Range): Range { - const start = this.mapPosition(range.getStartPosition()); - const end = this.mapPosition(range.getEndPosition()); - return Range.fromPositions( - start.range?.getStartPosition() ?? start.position!, - end.range?.getEndPosition() ?? end.position!, - ); - } - - reverse(): RangeMapping { - return new RangeMapping(this.mappings.map(mapping => mapping.reverse())); - } -} - -export class SingleRangeMapping { - constructor( - public readonly original: Range, - public readonly modified: Range, - ) { - } - - reverse(): SingleRangeMapping { - return new SingleRangeMapping(this.modified, this.original); - } - - toString() { - return `${this.original.toString()} -> ${this.modified.toString()}`; - } -} - -export class PositionOrRange { - public static position(position: Position): PositionOrRange { - return new PositionOrRange(position, undefined); - } - - public static range(range: Range): PositionOrRange { - return new PositionOrRange(undefined, range); - } - - private constructor( - public readonly position: Position | undefined, - public readonly range: Range | undefined, - ) { } -} +export * from '../language/core/rangeMapping.js'; diff --git a/src/vs/editor/common/core/rgba.ts b/src/vs/editor/common/core/rgba.ts index 006c7c7048e..af5374ab775 100644 --- a/src/vs/editor/common/core/rgba.ts +++ b/src/vs/editor/common/core/rgba.ts @@ -3,55 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * A very VM friendly rgba datastructure. - * Please don't touch unless you take a look at the IR. - */ -export class RGBA8 { - _rgba8Brand: void = undefined; - - static readonly Empty = new RGBA8(0, 0, 0, 0); - - /** - * Red: integer in [0-255] - */ - public readonly r: number; - /** - * Green: integer in [0-255] - */ - public readonly g: number; - /** - * Blue: integer in [0-255] - */ - public readonly b: number; - /** - * Alpha: integer in [0-255] - */ - public readonly a: number; - - constructor(r: number, g: number, b: number, a: number) { - this.r = RGBA8._clamp(r); - this.g = RGBA8._clamp(g); - this.b = RGBA8._clamp(b); - this.a = RGBA8._clamp(a); - } - - public equals(other: RGBA8): boolean { - return ( - this.r === other.r - && this.g === other.g - && this.b === other.b - && this.a === other.a - ); - } - - public static _clamp(c: number): number { - if (c < 0) { - return 0; - } - if (c > 255) { - return 255; - } - return c | 0; - } -} +export * from '../language/core/rgba.js'; diff --git a/src/vs/editor/common/core/selection.ts b/src/vs/editor/common/core/selection.ts index c436bb61ef6..e221fa21fb9 100644 --- a/src/vs/editor/common/core/selection.ts +++ b/src/vs/editor/common/core/selection.ts @@ -3,218 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IPosition, Position } from './position.js'; -import { Range } from './range.js'; - -/** - * A selection in the editor. - * The selection is a range that has an orientation. - */ -export interface ISelection { - /** - * The line number on which the selection has started. - */ - readonly selectionStartLineNumber: number; - /** - * The column on `selectionStartLineNumber` where the selection has started. - */ - readonly selectionStartColumn: number; - /** - * The line number on which the selection has ended. - */ - readonly positionLineNumber: number; - /** - * The column on `positionLineNumber` where the selection has ended. - */ - readonly positionColumn: number; -} - -/** - * The direction of a selection. - */ -export const enum SelectionDirection { - /** - * The selection starts above where it ends. - */ - LTR, - /** - * The selection starts below where it ends. - */ - RTL -} - -/** - * A selection in the editor. - * The selection is a range that has an orientation. - */ -export class Selection extends Range { - /** - * The line number on which the selection has started. - */ - public readonly selectionStartLineNumber: number; - /** - * The column on `selectionStartLineNumber` where the selection has started. - */ - public readonly selectionStartColumn: number; - /** - * The line number on which the selection has ended. - */ - public readonly positionLineNumber: number; - /** - * The column on `positionLineNumber` where the selection has ended. - */ - public readonly positionColumn: number; - - constructor(selectionStartLineNumber: number, selectionStartColumn: number, positionLineNumber: number, positionColumn: number) { - super(selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn); - this.selectionStartLineNumber = selectionStartLineNumber; - this.selectionStartColumn = selectionStartColumn; - this.positionLineNumber = positionLineNumber; - this.positionColumn = positionColumn; - } - - /** - * Transform to a human-readable representation. - */ - public override toString(): string { - return '[' + this.selectionStartLineNumber + ',' + this.selectionStartColumn + ' -> ' + this.positionLineNumber + ',' + this.positionColumn + ']'; - } - - /** - * Test if equals other selection. - */ - public equalsSelection(other: ISelection): boolean { - return ( - Selection.selectionsEqual(this, other) - ); - } - - /** - * Test if the two selections are equal. - */ - public static selectionsEqual(a: ISelection, b: ISelection): boolean { - return ( - a.selectionStartLineNumber === b.selectionStartLineNumber && - a.selectionStartColumn === b.selectionStartColumn && - a.positionLineNumber === b.positionLineNumber && - a.positionColumn === b.positionColumn - ); - } - - /** - * Get directions (LTR or RTL). - */ - public getDirection(): SelectionDirection { - if (this.selectionStartLineNumber === this.startLineNumber && this.selectionStartColumn === this.startColumn) { - return SelectionDirection.LTR; - } - return SelectionDirection.RTL; - } - - /** - * Create a new selection with a different `positionLineNumber` and `positionColumn`. - */ - public override setEndPosition(endLineNumber: number, endColumn: number): Selection { - if (this.getDirection() === SelectionDirection.LTR) { - return new Selection(this.startLineNumber, this.startColumn, endLineNumber, endColumn); - } - return new Selection(endLineNumber, endColumn, this.startLineNumber, this.startColumn); - } - - /** - * Get the position at `positionLineNumber` and `positionColumn`. - */ - public getPosition(): Position { - return new Position(this.positionLineNumber, this.positionColumn); - } - - /** - * Get the position at the start of the selection. - */ - public getSelectionStart(): Position { - return new Position(this.selectionStartLineNumber, this.selectionStartColumn); - } - - /** - * Create a new selection with a different `selectionStartLineNumber` and `selectionStartColumn`. - */ - public override setStartPosition(startLineNumber: number, startColumn: number): Selection { - if (this.getDirection() === SelectionDirection.LTR) { - return new Selection(startLineNumber, startColumn, this.endLineNumber, this.endColumn); - } - return new Selection(this.endLineNumber, this.endColumn, startLineNumber, startColumn); - } - - // ---- - - /** - * Create a `Selection` from one or two positions - */ - public static override fromPositions(start: IPosition, end: IPosition = start): Selection { - return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); - } - - /** - * Creates a `Selection` from a range, given a direction. - */ - public static fromRange(range: Range, direction: SelectionDirection): Selection { - if (direction === SelectionDirection.LTR) { - return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); - } else { - return new Selection(range.endLineNumber, range.endColumn, range.startLineNumber, range.startColumn); - } - } - - /** - * Create a `Selection` from an `ISelection`. - */ - public static liftSelection(sel: ISelection): Selection { - return new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); - } - - /** - * `a` equals `b`. - */ - public static selectionsArrEqual(a: ISelection[], b: ISelection[]): boolean { - if (a && !b || !a && b) { - return false; - } - if (!a && !b) { - return true; - } - if (a.length !== b.length) { - return false; - } - for (let i = 0, len = a.length; i < len; i++) { - if (!this.selectionsEqual(a[i], b[i])) { - return false; - } - } - return true; - } - - /** - * Test if `obj` is an `ISelection`. - */ - public static isISelection(obj: any): obj is ISelection { - return ( - obj - && (typeof obj.selectionStartLineNumber === 'number') - && (typeof obj.selectionStartColumn === 'number') - && (typeof obj.positionLineNumber === 'number') - && (typeof obj.positionColumn === 'number') - ); - } - - /** - * Create with a direction. - */ - public static createWithDirection(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, direction: SelectionDirection): Selection { - - if (direction === SelectionDirection.LTR) { - return new Selection(startLineNumber, startColumn, endLineNumber, endColumn); - } - - return new Selection(endLineNumber, endColumn, startLineNumber, startColumn); - } -} +export * from '../language/core/selection.js'; diff --git a/src/vs/editor/common/core/stringBuilder.ts b/src/vs/editor/common/core/stringBuilder.ts index 4231fba27fa..ee0175db52c 100644 --- a/src/vs/editor/common/core/stringBuilder.ts +++ b/src/vs/editor/common/core/stringBuilder.ts @@ -3,144 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as strings from '../../../base/common/strings.js'; -import * as platform from '../../../base/common/platform.js'; -import * as buffer from '../../../base/common/buffer.js'; - -let _utf16LE_TextDecoder: TextDecoder | null; -function getUTF16LE_TextDecoder(): TextDecoder { - if (!_utf16LE_TextDecoder) { - _utf16LE_TextDecoder = new TextDecoder('UTF-16LE'); - } - return _utf16LE_TextDecoder; -} - -let _utf16BE_TextDecoder: TextDecoder | null; -function getUTF16BE_TextDecoder(): TextDecoder { - if (!_utf16BE_TextDecoder) { - _utf16BE_TextDecoder = new TextDecoder('UTF-16BE'); - } - return _utf16BE_TextDecoder; -} - -let _platformTextDecoder: TextDecoder | null; -export function getPlatformTextDecoder(): TextDecoder { - if (!_platformTextDecoder) { - _platformTextDecoder = platform.isLittleEndian() ? getUTF16LE_TextDecoder() : getUTF16BE_TextDecoder(); - } - return _platformTextDecoder; -} - -export function decodeUTF16LE(source: Uint8Array, offset: number, len: number): string { - const view = new Uint16Array(source.buffer, offset, len); - if (len > 0 && (view[0] === 0xFEFF || view[0] === 0xFFFE)) { - // UTF16 sometimes starts with a BOM https://de.wikipedia.org/wiki/Byte_Order_Mark - // It looks like TextDecoder.decode will eat up a leading BOM (0xFEFF or 0xFFFE) - // We don't want that behavior because we know the string is UTF16LE and the BOM should be maintained - // So we use the manual decoder - return compatDecodeUTF16LE(source, offset, len); - } - return getUTF16LE_TextDecoder().decode(view); -} - -function compatDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string { - const result: string[] = []; - let resultLen = 0; - for (let i = 0; i < len; i++) { - const charCode = buffer.readUInt16LE(source, offset); offset += 2; - result[resultLen++] = String.fromCharCode(charCode); - } - return result.join(''); -} - -export class StringBuilder { - - private readonly _capacity: number; - private readonly _buffer: Uint16Array; - - private _completedStrings: string[] | null; - private _bufferLength: number; - - constructor(capacity: number) { - this._capacity = capacity | 0; - this._buffer = new Uint16Array(this._capacity); - - this._completedStrings = null; - this._bufferLength = 0; - } - - public reset(): void { - this._completedStrings = null; - this._bufferLength = 0; - } - - public build(): string { - if (this._completedStrings !== null) { - this._flushBuffer(); - return this._completedStrings.join(''); - } - return this._buildBuffer(); - } - - private _buildBuffer(): string { - if (this._bufferLength === 0) { - return ''; - } - - const view = new Uint16Array(this._buffer.buffer, 0, this._bufferLength); - return getPlatformTextDecoder().decode(view); - } - - private _flushBuffer(): void { - const bufferString = this._buildBuffer(); - this._bufferLength = 0; - - if (this._completedStrings === null) { - this._completedStrings = [bufferString]; - } else { - this._completedStrings[this._completedStrings.length] = bufferString; - } - } - - /** - * Append a char code (<2^16) - */ - public appendCharCode(charCode: number): void { - const remainingSpace = this._capacity - this._bufferLength; - - if (remainingSpace <= 1) { - if (remainingSpace === 0 || strings.isHighSurrogate(charCode)) { - this._flushBuffer(); - } - } - - this._buffer[this._bufferLength++] = charCode; - } - - /** - * Append an ASCII char code (<2^8) - */ - public appendASCIICharCode(charCode: number): void { - if (this._bufferLength === this._capacity) { - // buffer is full - this._flushBuffer(); - } - this._buffer[this._bufferLength++] = charCode; - } - - public appendString(str: string): void { - const strLen = str.length; - - if (this._bufferLength + strLen >= this._capacity) { - // This string does not fit in the remaining buffer space - - this._flushBuffer(); - this._completedStrings![this._completedStrings!.length] = str; - return; - } - - for (let i = 0; i < strLen; i++) { - this._buffer[this._bufferLength++] = str.charCodeAt(i); - } - } -} +export * from '../language/core/stringBuilder.js'; diff --git a/src/vs/editor/common/core/textChange.ts b/src/vs/editor/common/core/textChange.ts index 43925f18126..3fb13d4f64b 100644 --- a/src/vs/editor/common/core/textChange.ts +++ b/src/vs/editor/common/core/textChange.ts @@ -3,344 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as buffer from '../../../base/common/buffer.js'; -import { decodeUTF16LE } from './stringBuilder.js'; - -function escapeNewLine(str: string): string { - return ( - str - .replace(/\n/g, '\\n') - .replace(/\r/g, '\\r') - ); -} - -export class TextChange { - - public get oldLength(): number { - return this.oldText.length; - } - - public get oldEnd(): number { - return this.oldPosition + this.oldText.length; - } - - public get newLength(): number { - return this.newText.length; - } - - public get newEnd(): number { - return this.newPosition + this.newText.length; - } - - constructor( - public readonly oldPosition: number, - public readonly oldText: string, - public readonly newPosition: number, - public readonly newText: string - ) { } - - public toString(): string { - if (this.oldText.length === 0) { - return `(insert@${this.oldPosition} "${escapeNewLine(this.newText)}")`; - } - if (this.newText.length === 0) { - return `(delete@${this.oldPosition} "${escapeNewLine(this.oldText)}")`; - } - return `(replace@${this.oldPosition} "${escapeNewLine(this.oldText)}" with "${escapeNewLine(this.newText)}")`; - } - - private static _writeStringSize(str: string): number { - return ( - 4 + 2 * str.length - ); - } - - private static _writeString(b: Uint8Array, str: string, offset: number): number { - const len = str.length; - buffer.writeUInt32BE(b, len, offset); offset += 4; - for (let i = 0; i < len; i++) { - buffer.writeUInt16LE(b, str.charCodeAt(i), offset); offset += 2; - } - return offset; - } - - private static _readString(b: Uint8Array, offset: number): string { - const len = buffer.readUInt32BE(b, offset); offset += 4; - return decodeUTF16LE(b, offset, len); - } - - public writeSize(): number { - return ( - + 4 // oldPosition - + 4 // newPosition - + TextChange._writeStringSize(this.oldText) - + TextChange._writeStringSize(this.newText) - ); - } - - public write(b: Uint8Array, offset: number): number { - buffer.writeUInt32BE(b, this.oldPosition, offset); offset += 4; - buffer.writeUInt32BE(b, this.newPosition, offset); offset += 4; - offset = TextChange._writeString(b, this.oldText, offset); - offset = TextChange._writeString(b, this.newText, offset); - return offset; - } - - public static read(b: Uint8Array, offset: number, dest: TextChange[]): number { - const oldPosition = buffer.readUInt32BE(b, offset); offset += 4; - const newPosition = buffer.readUInt32BE(b, offset); offset += 4; - const oldText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(oldText); - const newText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(newText); - dest.push(new TextChange(oldPosition, oldText, newPosition, newText)); - return offset; - } -} - -export function compressConsecutiveTextChanges(prevEdits: TextChange[] | null, currEdits: TextChange[]): TextChange[] { - if (prevEdits === null || prevEdits.length === 0) { - return currEdits; - } - const compressor = new TextChangeCompressor(prevEdits, currEdits); - return compressor.compress(); -} - -class TextChangeCompressor { - - private _prevEdits: TextChange[]; - private _currEdits: TextChange[]; - - private _result: TextChange[]; - private _resultLen: number; - - private _prevLen: number; - private _prevDeltaOffset: number; - - private _currLen: number; - private _currDeltaOffset: number; - - constructor(prevEdits: TextChange[], currEdits: TextChange[]) { - this._prevEdits = prevEdits; - this._currEdits = currEdits; - - this._result = []; - this._resultLen = 0; - - this._prevLen = this._prevEdits.length; - this._prevDeltaOffset = 0; - - this._currLen = this._currEdits.length; - this._currDeltaOffset = 0; - } - - public compress(): TextChange[] { - let prevIndex = 0; - let currIndex = 0; - - let prevEdit = this._getPrev(prevIndex); - let currEdit = this._getCurr(currIndex); - - while (prevIndex < this._prevLen || currIndex < this._currLen) { - - if (prevEdit === null) { - this._acceptCurr(currEdit!); - currEdit = this._getCurr(++currIndex); - continue; - } - - if (currEdit === null) { - this._acceptPrev(prevEdit); - prevEdit = this._getPrev(++prevIndex); - continue; - } - - if (currEdit.oldEnd <= prevEdit.newPosition) { - this._acceptCurr(currEdit); - currEdit = this._getCurr(++currIndex); - continue; - } - - if (prevEdit.newEnd <= currEdit.oldPosition) { - this._acceptPrev(prevEdit); - prevEdit = this._getPrev(++prevIndex); - continue; - } - - if (currEdit.oldPosition < prevEdit.newPosition) { - const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newPosition - currEdit.oldPosition); - this._acceptCurr(e1); - currEdit = e2; - continue; - } - - if (prevEdit.newPosition < currEdit.oldPosition) { - const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldPosition - prevEdit.newPosition); - this._acceptPrev(e1); - prevEdit = e2; - continue; - } - - // At this point, currEdit.oldPosition === prevEdit.newPosition - - let mergePrev: TextChange; - let mergeCurr: TextChange; - - if (currEdit.oldEnd === prevEdit.newEnd) { - mergePrev = prevEdit; - mergeCurr = currEdit; - prevEdit = this._getPrev(++prevIndex); - currEdit = this._getCurr(++currIndex); - } else if (currEdit.oldEnd < prevEdit.newEnd) { - const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldLength); - mergePrev = e1; - mergeCurr = currEdit; - prevEdit = e2; - currEdit = this._getCurr(++currIndex); - } else { - const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newLength); - mergePrev = prevEdit; - mergeCurr = e1; - prevEdit = this._getPrev(++prevIndex); - currEdit = e2; - } - - this._result[this._resultLen++] = new TextChange( - mergePrev.oldPosition, - mergePrev.oldText, - mergeCurr.newPosition, - mergeCurr.newText - ); - this._prevDeltaOffset += mergePrev.newLength - mergePrev.oldLength; - this._currDeltaOffset += mergeCurr.newLength - mergeCurr.oldLength; - } - - const merged = TextChangeCompressor._merge(this._result); - const cleaned = TextChangeCompressor._removeNoOps(merged); - return cleaned; - } - - private _acceptCurr(currEdit: TextChange): void { - this._result[this._resultLen++] = TextChangeCompressor._rebaseCurr(this._prevDeltaOffset, currEdit); - this._currDeltaOffset += currEdit.newLength - currEdit.oldLength; - } - - private _getCurr(currIndex: number): TextChange | null { - return (currIndex < this._currLen ? this._currEdits[currIndex] : null); - } - - private _acceptPrev(prevEdit: TextChange): void { - this._result[this._resultLen++] = TextChangeCompressor._rebasePrev(this._currDeltaOffset, prevEdit); - this._prevDeltaOffset += prevEdit.newLength - prevEdit.oldLength; - } - - private _getPrev(prevIndex: number): TextChange | null { - return (prevIndex < this._prevLen ? this._prevEdits[prevIndex] : null); - } - - private static _rebaseCurr(prevDeltaOffset: number, currEdit: TextChange): TextChange { - return new TextChange( - currEdit.oldPosition - prevDeltaOffset, - currEdit.oldText, - currEdit.newPosition, - currEdit.newText - ); - } - - private static _rebasePrev(currDeltaOffset: number, prevEdit: TextChange): TextChange { - return new TextChange( - prevEdit.oldPosition, - prevEdit.oldText, - prevEdit.newPosition + currDeltaOffset, - prevEdit.newText - ); - } - - private static _splitPrev(edit: TextChange, offset: number): [TextChange, TextChange] { - const preText = edit.newText.substr(0, offset); - const postText = edit.newText.substr(offset); - - return [ - new TextChange( - edit.oldPosition, - edit.oldText, - edit.newPosition, - preText - ), - new TextChange( - edit.oldEnd, - '', - edit.newPosition + offset, - postText - ) - ]; - } - - private static _splitCurr(edit: TextChange, offset: number): [TextChange, TextChange] { - const preText = edit.oldText.substr(0, offset); - const postText = edit.oldText.substr(offset); - - return [ - new TextChange( - edit.oldPosition, - preText, - edit.newPosition, - edit.newText - ), - new TextChange( - edit.oldPosition + offset, - postText, - edit.newEnd, - '' - ) - ]; - } - - private static _merge(edits: TextChange[]): TextChange[] { - if (edits.length === 0) { - return edits; - } - - const result: TextChange[] = []; - let resultLen = 0; - - let prev = edits[0]; - for (let i = 1; i < edits.length; i++) { - const curr = edits[i]; - - if (prev.oldEnd === curr.oldPosition) { - // Merge into `prev` - prev = new TextChange( - prev.oldPosition, - prev.oldText + curr.oldText, - prev.newPosition, - prev.newText + curr.newText - ); - } else { - result[resultLen++] = prev; - prev = curr; - } - } - result[resultLen++] = prev; - - return result; - } - - private static _removeNoOps(edits: TextChange[]): TextChange[] { - if (edits.length === 0) { - return edits; - } - - const result: TextChange[] = []; - let resultLen = 0; - - for (let i = 0; i < edits.length; i++) { - const edit = edits[i]; - - if (edit.oldText === edit.newText) { - continue; - } - result[resultLen++] = edit; - } - - return result; - } -} +export * from '../language/core/textChange.js'; diff --git a/src/vs/editor/common/core/textEdit.ts b/src/vs/editor/common/core/textEdit.ts index 83baa3bff43..415a158b136 100644 --- a/src/vs/editor/common/core/textEdit.ts +++ b/src/vs/editor/common/core/textEdit.ts @@ -3,398 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { equals } from '../../../base/common/arrays.js'; -import { assert, assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../base/common/strings.js'; -import { ISingleEditOperation } from './editOperation.js'; -import { LineRange } from './lineRange.js'; -import { OffsetEdit } from './offsetEdit.js'; -import { Position } from './position.js'; -import { PositionOffsetTransformer } from './positionToOffset.js'; -import { Range } from './range.js'; -import { TextLength } from './textLength.js'; - -export class TextEdit { - public static fromOffsetEdit(edit: OffsetEdit, initialState: AbstractText): TextEdit { - const edits = edit.edits.map(e => new SingleTextEdit(initialState.getTransformer().getRange(e.replaceRange), e.newText)); - return new TextEdit(edits); - } - - public static single(originalRange: Range, newText: string): TextEdit { - return new TextEdit([new SingleTextEdit(originalRange, newText)]); - } - - public static insert(position: Position, newText: string): TextEdit { - return new TextEdit([new SingleTextEdit(Range.fromPositions(position, position), newText)]); - } - - constructor(public readonly edits: readonly SingleTextEdit[]) { - assertFn(() => checkAdjacentItems(edits, (a, b) => a.range.getEndPosition().isBeforeOrEqual(b.range.getStartPosition()))); - } - - /** - * Joins touching edits and removes empty edits. - */ - normalize(): TextEdit { - const edits: SingleTextEdit[] = []; - for (const edit of this.edits) { - if (edits.length > 0 && edits[edits.length - 1].range.getEndPosition().equals(edit.range.getStartPosition())) { - const last = edits[edits.length - 1]; - edits[edits.length - 1] = new SingleTextEdit(last.range.plusRange(edit.range), last.text + edit.text); - } else if (!edit.isEmpty) { - edits.push(edit); - } - } - return new TextEdit(edits); - } - - mapPosition(position: Position): Position | Range { - let lineDelta = 0; - let curLine = 0; - let columnDeltaInCurLine = 0; - - for (const edit of this.edits) { - const start = edit.range.getStartPosition(); - - if (position.isBeforeOrEqual(start)) { - break; - } - - const end = edit.range.getEndPosition(); - const len = TextLength.ofText(edit.text); - if (position.isBefore(end)) { - const startPos = new Position(start.lineNumber + lineDelta, start.column + (start.lineNumber + lineDelta === curLine ? columnDeltaInCurLine : 0)); - const endPos = len.addToPosition(startPos); - return rangeFromPositions(startPos, endPos); - } - - if (start.lineNumber + lineDelta !== curLine) { - columnDeltaInCurLine = 0; - } - - lineDelta += len.lineCount - (edit.range.endLineNumber - edit.range.startLineNumber); - - if (len.lineCount === 0) { - if (end.lineNumber !== start.lineNumber) { - columnDeltaInCurLine += len.columnCount - (end.column - 1); - } else { - columnDeltaInCurLine += len.columnCount - (end.column - start.column); - } - } else { - columnDeltaInCurLine = len.columnCount; - } - curLine = end.lineNumber + lineDelta; - } - - return new Position(position.lineNumber + lineDelta, position.column + (position.lineNumber + lineDelta === curLine ? columnDeltaInCurLine : 0)); - } - - mapRange(range: Range): Range { - function getStart(p: Position | Range) { - return p instanceof Position ? p : p.getStartPosition(); - } - - function getEnd(p: Position | Range) { - return p instanceof Position ? p : p.getEndPosition(); - } - - const start = getStart(this.mapPosition(range.getStartPosition())); - const end = getEnd(this.mapPosition(range.getEndPosition())); - - return rangeFromPositions(start, end); - } - - // TODO: `doc` is not needed for this! - inverseMapPosition(positionAfterEdit: Position, doc: AbstractText): Position | Range { - const reversed = this.inverse(doc); - return reversed.mapPosition(positionAfterEdit); - } - - inverseMapRange(range: Range, doc: AbstractText): Range { - const reversed = this.inverse(doc); - return reversed.mapRange(range); - } - - apply(text: AbstractText): string { - let result = ''; - let lastEditEnd = new Position(1, 1); - for (const edit of this.edits) { - const editRange = edit.range; - const editStart = editRange.getStartPosition(); - const editEnd = editRange.getEndPosition(); - - const r = rangeFromPositions(lastEditEnd, editStart); - if (!r.isEmpty()) { - result += text.getValueOfRange(r); - } - result += edit.text; - lastEditEnd = editEnd; - } - const r = rangeFromPositions(lastEditEnd, text.endPositionExclusive); - if (!r.isEmpty()) { - result += text.getValueOfRange(r); - } - return result; - } - - applyToString(str: string): string { - const strText = new StringText(str); - return this.apply(strText); - } - - inverse(doc: AbstractText): TextEdit { - const ranges = this.getNewRanges(); - return new TextEdit(this.edits.map((e, idx) => new SingleTextEdit(ranges[idx], doc.getValueOfRange(e.range)))); - } - - getNewRanges(): Range[] { - const newRanges: Range[] = []; - let previousEditEndLineNumber = 0; - let lineOffset = 0; - let columnOffset = 0; - for (const edit of this.edits) { - const textLength = TextLength.ofText(edit.text); - const newRangeStart = Position.lift({ - lineNumber: edit.range.startLineNumber + lineOffset, - column: edit.range.startColumn + (edit.range.startLineNumber === previousEditEndLineNumber ? columnOffset : 0) - }); - const newRange = textLength.createRange(newRangeStart); - newRanges.push(newRange); - lineOffset = newRange.endLineNumber - edit.range.endLineNumber; - columnOffset = newRange.endColumn - edit.range.endColumn; - previousEditEndLineNumber = edit.range.endLineNumber; - } - return newRanges; - } - - toSingle(text: AbstractText) { - if (this.edits.length === 0) { throw new BugIndicatingError(); } - if (this.edits.length === 1) { return this.edits[0]; } - - const startPos = this.edits[0].range.getStartPosition(); - const endPos = this.edits[this.edits.length - 1].range.getEndPosition(); - - let newText = ''; - - for (let i = 0; i < this.edits.length; i++) { - const curEdit = this.edits[i]; - newText += curEdit.text; - if (i < this.edits.length - 1) { - const nextEdit = this.edits[i + 1]; - const gapRange = Range.fromPositions(curEdit.range.getEndPosition(), nextEdit.range.getStartPosition()); - const gapText = text.getValueOfRange(gapRange); - newText += gapText; - } - } - return new SingleTextEdit(Range.fromPositions(startPos, endPos), newText); - } - - equals(other: TextEdit): boolean { - return equals(this.edits, other.edits, (a, b) => a.equals(b)); - } -} - -export class SingleTextEdit { - public static joinEdits(edits: SingleTextEdit[], initialValue: AbstractText): SingleTextEdit { - if (edits.length === 0) { throw new BugIndicatingError(); } - if (edits.length === 1) { return edits[0]; } - - const startPos = edits[0].range.getStartPosition(); - const endPos = edits[edits.length - 1].range.getEndPosition(); - - let newText = ''; - - for (let i = 0; i < edits.length; i++) { - const curEdit = edits[i]; - newText += curEdit.text; - if (i < edits.length - 1) { - const nextEdit = edits[i + 1]; - const gapRange = Range.fromPositions(curEdit.range.getEndPosition(), nextEdit.range.getStartPosition()); - const gapText = initialValue.getValueOfRange(gapRange); - newText += gapText; - } - } - return new SingleTextEdit(Range.fromPositions(startPos, endPos), newText); - } - - constructor( - public readonly range: Range, - public readonly text: string, - ) { - } - - get isEmpty(): boolean { - return this.range.isEmpty() && this.text.length === 0; - } - - static equals(first: SingleTextEdit, second: SingleTextEdit) { - return first.range.equalsRange(second.range) && first.text === second.text; - } - - public toSingleEditOperation(): ISingleEditOperation { - return { - range: this.range, - text: this.text, - }; - } - - public toEdit(): TextEdit { - return new TextEdit([this]); - } - - public equals(other: SingleTextEdit): boolean { - return SingleTextEdit.equals(this, other); - } - - public extendToCoverRange(range: Range, initialValue: AbstractText): SingleTextEdit { - if (this.range.containsRange(range)) { return this; } - - const newRange = this.range.plusRange(range); - const textBefore = initialValue.getValueOfRange(Range.fromPositions(newRange.getStartPosition(), this.range.getStartPosition())); - const textAfter = initialValue.getValueOfRange(Range.fromPositions(this.range.getEndPosition(), newRange.getEndPosition())); - const newText = textBefore + this.text + textAfter; - return new SingleTextEdit(newRange, newText); - } - - public extendToFullLine(initialValue: AbstractText): SingleTextEdit { - const newRange = new Range( - this.range.startLineNumber, - 1, - this.range.endLineNumber, - initialValue.getTransformer().getLineLength(this.range.endLineNumber) + 1 - ); - return this.extendToCoverRange(newRange, initialValue); - } - - public removeCommonPrefix(text: AbstractText): SingleTextEdit { - const normalizedOriginalText = text.getValueOfRange(this.range).replaceAll('\r\n', '\n'); - const normalizedModifiedText = this.text.replaceAll('\r\n', '\n'); - - const commonPrefixLen = commonPrefixLength(normalizedOriginalText, normalizedModifiedText); - const start = TextLength.ofText(normalizedOriginalText.substring(0, commonPrefixLen)) - .addToPosition(this.range.getStartPosition()); - - const newText = normalizedModifiedText.substring(commonPrefixLen); - const range = Range.fromPositions(start, this.range.getEndPosition()); - return new SingleTextEdit(range, newText); - } - - public isEffectiveDeletion(text: AbstractText): boolean { - let newText = this.text.replaceAll('\r\n', '\n'); - let existingText = text.getValueOfRange(this.range).replaceAll('\r\n', '\n'); - const l = commonPrefixLength(newText, existingText); - newText = newText.substring(l); - existingText = existingText.substring(l); - const r = commonSuffixLength(newText, existingText); - newText = newText.substring(0, newText.length - r); - existingText = existingText.substring(0, existingText.length - r); - - return newText === ''; - } -} - -function rangeFromPositions(start: Position, end: Position): Range { - if (start.lineNumber === end.lineNumber && start.column === Number.MAX_SAFE_INTEGER) { - return Range.fromPositions(end, end); - } else if (!start.isBeforeOrEqual(end)) { - throw new BugIndicatingError('start must be before end'); - } - return new Range(start.lineNumber, start.column, end.lineNumber, end.column); -} - -export abstract class AbstractText { - abstract getValueOfRange(range: Range): string; - abstract readonly length: TextLength; - - get endPositionExclusive(): Position { - return this.length.addToPosition(new Position(1, 1)); - } - - get lineRange(): LineRange { - return this.length.toLineRange(); - } - - getValue(): string { - return this.getValueOfRange(this.length.toRange()); - } - - getLineLength(lineNumber: number): number { - return this.getValueOfRange(new Range(lineNumber, 1, lineNumber, Number.MAX_SAFE_INTEGER)).length; - } - - private _transformer: PositionOffsetTransformer | undefined = undefined; - - getTransformer(): PositionOffsetTransformer { - if (!this._transformer) { - this._transformer = new PositionOffsetTransformer(this.getValue()); - } - return this._transformer; - } - - getLineAt(lineNumber: number): string { - return this.getValueOfRange(new Range(lineNumber, 1, lineNumber, Number.MAX_SAFE_INTEGER)); - } - - getLines(): string[] { - const value = this.getValue(); - return splitLines(value); - } -} - -export class LineBasedText extends AbstractText { - constructor( - private readonly _getLineContent: (lineNumber: number) => string, - private readonly _lineCount: number, - ) { - assert(_lineCount >= 1); - - super(); - } - - override getValueOfRange(range: Range): string { - if (range.startLineNumber === range.endLineNumber) { - return this._getLineContent(range.startLineNumber).substring(range.startColumn - 1, range.endColumn - 1); - } - let result = this._getLineContent(range.startLineNumber).substring(range.startColumn - 1); - for (let i = range.startLineNumber + 1; i < range.endLineNumber; i++) { - result += '\n' + this._getLineContent(i); - } - result += '\n' + this._getLineContent(range.endLineNumber).substring(0, range.endColumn - 1); - return result; - } - - override getLineLength(lineNumber: number): number { - return this._getLineContent(lineNumber).length; - } - - get length(): TextLength { - const lastLine = this._getLineContent(this._lineCount); - return new TextLength(this._lineCount - 1, lastLine.length); - } -} - -export class ArrayText extends LineBasedText { - constructor(lines: string[]) { - super( - lineNumber => lines[lineNumber - 1], - lines.length - ); - } -} - -export class StringText extends AbstractText { - private readonly _t = new PositionOffsetTransformer(this.value); - - constructor(public readonly value: string) { - super(); - } - - getValueOfRange(range: Range): string { - return this._t.getOffsetRange(range).substring(this.value); - } - - get length(): TextLength { - return this._t.textLength; - } -} +export * from '../language/core/textEdit.js'; diff --git a/src/vs/editor/common/core/textLength.ts b/src/vs/editor/common/core/textLength.ts index a1f2d8e35c8..202c1a7591f 100644 --- a/src/vs/editor/common/core/textLength.ts +++ b/src/vs/editor/common/core/textLength.ts @@ -2,138 +2,5 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineRange } from './lineRange.js'; -import { Position } from './position.js'; -import { Range } from './range.js'; -/** - * Represents a non-negative length of text in terms of line and column count. -*/ -export class TextLength { - public static zero = new TextLength(0, 0); - - public static lengthDiffNonNegative(start: TextLength, end: TextLength): TextLength { - if (end.isLessThan(start)) { - return TextLength.zero; - } - if (start.lineCount === end.lineCount) { - return new TextLength(0, end.columnCount - start.columnCount); - } else { - return new TextLength(end.lineCount - start.lineCount, end.columnCount); - } - } - - public static betweenPositions(position1: Position, position2: Position): TextLength { - if (position1.lineNumber === position2.lineNumber) { - return new TextLength(0, position2.column - position1.column); - } else { - return new TextLength(position2.lineNumber - position1.lineNumber, position2.column - 1); - } - } - - public static fromPosition(pos: Position): TextLength { - return new TextLength(pos.lineNumber - 1, pos.column - 1); - } - - public static ofRange(range: Range) { - return TextLength.betweenPositions(range.getStartPosition(), range.getEndPosition()); - } - - public static ofText(text: string): TextLength { - let line = 0; - let column = 0; - for (const c of text) { - if (c === '\n') { - line++; - column = 0; - } else { - column++; - } - } - return new TextLength(line, column); - } - - constructor( - public readonly lineCount: number, - public readonly columnCount: number - ) { } - - public isZero() { - return this.lineCount === 0 && this.columnCount === 0; - } - - public isLessThan(other: TextLength): boolean { - if (this.lineCount !== other.lineCount) { - return this.lineCount < other.lineCount; - } - return this.columnCount < other.columnCount; - } - - public isGreaterThan(other: TextLength): boolean { - if (this.lineCount !== other.lineCount) { - return this.lineCount > other.lineCount; - } - return this.columnCount > other.columnCount; - } - - public isGreaterThanOrEqualTo(other: TextLength): boolean { - if (this.lineCount !== other.lineCount) { - return this.lineCount > other.lineCount; - } - return this.columnCount >= other.columnCount; - } - - public equals(other: TextLength): boolean { - return this.lineCount === other.lineCount && this.columnCount === other.columnCount; - } - - public compare(other: TextLength): number { - if (this.lineCount !== other.lineCount) { - return this.lineCount - other.lineCount; - } - return this.columnCount - other.columnCount; - } - - public add(other: TextLength): TextLength { - if (other.lineCount === 0) { - return new TextLength(this.lineCount, this.columnCount + other.columnCount); - } else { - return new TextLength(this.lineCount + other.lineCount, other.columnCount); - } - } - - public createRange(startPosition: Position): Range { - if (this.lineCount === 0) { - return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column + this.columnCount); - } else { - return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber + this.lineCount, this.columnCount + 1); - } - } - - public toRange(): Range { - return new Range(1, 1, this.lineCount + 1, this.columnCount + 1); - } - - public toLineRange(): LineRange { - return LineRange.ofLength(1, this.lineCount + 1); - } - - public addToPosition(position: Position): Position { - if (this.lineCount === 0) { - return new Position(position.lineNumber, position.column + this.columnCount); - } else { - return new Position(position.lineNumber + this.lineCount, this.columnCount + 1); - } - } - - public addToRange(range: Range): Range { - return Range.fromPositions( - this.addToPosition(range.getStartPosition()), - this.addToPosition(range.getEndPosition()) - ); - } - - toString() { - return `${this.lineCount},${this.columnCount}`; - } -} +export * from '../language/core/textLength.js'; diff --git a/src/vs/editor/common/core/textModelDefaults.ts b/src/vs/editor/common/core/textModelDefaults.ts index cb0150baacf..a9cf51faee3 100644 --- a/src/vs/editor/common/core/textModelDefaults.ts +++ b/src/vs/editor/common/core/textModelDefaults.ts @@ -3,15 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export const EDITOR_MODEL_DEFAULTS = { - tabSize: 4, - indentSize: 4, - insertSpaces: true, - detectIndentation: true, - trimAutoWhitespace: true, - largeFileOptimizations: true, - bracketPairColorizationOptions: { - enabled: true, - independentColorPoolPerBracketType: false, - }, -}; +export * from '../language/core/textModelDefaults.js'; diff --git a/src/vs/editor/common/core/wordCharacterClassifier.ts b/src/vs/editor/common/core/wordCharacterClassifier.ts index 4ebacdd0087..d46134c8702 100644 --- a/src/vs/editor/common/core/wordCharacterClassifier.ts +++ b/src/vs/editor/common/core/wordCharacterClassifier.ts @@ -3,109 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; -import { safeIntl } from '../../../base/common/date.js'; -import { LRUCache } from '../../../base/common/map.js'; -import { CharacterClassifier } from './characterClassifier.js'; - -export const enum WordCharacterClass { - Regular = 0, - Whitespace = 1, - WordSeparator = 2 -} - -export class WordCharacterClassifier extends CharacterClassifier { - - public readonly intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]; - private readonly _segmenter: Intl.Segmenter | null = null; - private _cachedLine: string | null = null; - private _cachedSegments: IntlWordSegmentData[] = []; - - constructor(wordSeparators: string, intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]) { - super(WordCharacterClass.Regular); - this.intlSegmenterLocales = intlSegmenterLocales; - if (this.intlSegmenterLocales.length > 0) { - this._segmenter = safeIntl.Segmenter(this.intlSegmenterLocales, { granularity: 'word' }); - } else { - this._segmenter = null; - } - - for (let i = 0, len = wordSeparators.length; i < len; i++) { - this.set(wordSeparators.charCodeAt(i), WordCharacterClass.WordSeparator); - } - - this.set(CharCode.Space, WordCharacterClass.Whitespace); - this.set(CharCode.Tab, WordCharacterClass.Whitespace); - } - - public findPrevIntlWordBeforeOrAtOffset(line: string, offset: number): IntlWordSegmentData | null { - let candidate: IntlWordSegmentData | null = null; - for (const segment of this._getIntlSegmenterWordsOnLine(line)) { - if (segment.index > offset) { - break; - } - candidate = segment; - } - return candidate; - } - - public findNextIntlWordAtOrAfterOffset(lineContent: string, offset: number): IntlWordSegmentData | null { - for (const segment of this._getIntlSegmenterWordsOnLine(lineContent)) { - if (segment.index < offset) { - continue; - } - return segment; - } - return null; - } - - private _getIntlSegmenterWordsOnLine(line: string): IntlWordSegmentData[] { - if (!this._segmenter) { - return []; - } - - // Check if the line has changed from the previous call - if (this._cachedLine === line) { - return this._cachedSegments; - } - - // Update the cache with the new line - this._cachedLine = line; - this._cachedSegments = this._filterWordSegments(this._segmenter.segment(line)); - - return this._cachedSegments; - } - - private _filterWordSegments(segments: Intl.Segments): IntlWordSegmentData[] { - const result: IntlWordSegmentData[] = []; - for (const segment of segments) { - if (this._isWordLike(segment)) { - result.push(segment); - } - } - return result; - } - - private _isWordLike(segment: Intl.SegmentData): segment is IntlWordSegmentData { - if (segment.isWordLike) { - return true; - } - return false; - } -} - -export interface IntlWordSegmentData extends Intl.SegmentData { - isWordLike: true; -} - -const wordClassifierCache = new LRUCache(10); - -export function getMapForWordSeparators(wordSeparators: string, intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]): WordCharacterClassifier { - const key = `${wordSeparators}/${intlSegmenterLocales.join(',')}`; - let result = wordClassifierCache.get(key)!; - if (!result) { - result = new WordCharacterClassifier(wordSeparators, intlSegmenterLocales); - wordClassifierCache.set(key, result); - } - return result; -} +export * from '../language/core/wordCharacterClassifier.js'; diff --git a/src/vs/editor/common/core/wordHelper.ts b/src/vs/editor/common/core/wordHelper.ts index 32838f92011..071280bbc08 100644 --- a/src/vs/editor/common/core/wordHelper.ts +++ b/src/vs/editor/common/core/wordHelper.ts @@ -3,173 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Iterable } from '../../../base/common/iterator.js'; -import { toDisposable } from '../../../base/common/lifecycle.js'; -import { LinkedList } from '../../../base/common/linkedList.js'; - -export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; - -/** - * Word inside a model. - */ -export interface IWordAtPosition { - /** - * The word. - */ - readonly word: string; - /** - * The column where the word starts. - */ - readonly startColumn: number; - /** - * The column where the word ends. - */ - readonly endColumn: number; -} - -/** - * Create a word definition regular expression based on default word separators. - * Optionally provide allowed separators that should be included in words. - * - * The default would look like this: - * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g - */ -function createWordRegExp(allowInWords: string = ''): RegExp { - let source = '(-?\\d*\\.\\d\\w*)|([^'; - for (const sep of USUAL_WORD_SEPARATORS) { - if (allowInWords.indexOf(sep) >= 0) { - continue; - } - source += '\\' + sep; - } - source += '\\s]+)'; - return new RegExp(source, 'g'); -} - -// catches numbers (including floating numbers) in the first group, and alphanum in the second -export const DEFAULT_WORD_REGEXP = createWordRegExp(); - -export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegExp { - let result: RegExp = DEFAULT_WORD_REGEXP; - - if (wordDefinition && (wordDefinition instanceof RegExp)) { - if (!wordDefinition.global) { - let flags = 'g'; - if (wordDefinition.ignoreCase) { - flags += 'i'; - } - if (wordDefinition.multiline) { - flags += 'm'; - } - if (wordDefinition.unicode) { - flags += 'u'; - } - result = new RegExp(wordDefinition.source, flags); - } else { - result = wordDefinition; - } - } - - result.lastIndex = 0; - - return result; -} - - -export interface IGetWordAtTextConfig { - maxLen: number; - windowSize: number; - timeBudget: number; -} - - -const _defaultConfig = new LinkedList(); -_defaultConfig.unshift({ - maxLen: 1000, - windowSize: 15, - timeBudget: 150 -}); - -export function setDefaultGetWordAtTextConfig(value: IGetWordAtTextConfig) { - const rm = _defaultConfig.unshift(value); - return toDisposable(rm); -} - -export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config?: IGetWordAtTextConfig): IWordAtPosition | null { - // Ensure the regex has the 'g' flag, otherwise this will loop forever - wordDefinition = ensureValidWordDefinition(wordDefinition); - - if (!config) { - config = Iterable.first(_defaultConfig)!; - } - - if (text.length > config.maxLen) { - // don't throw strings that long at the regexp - // but use a sub-string in which a word must occur - let start = column - config.maxLen / 2; - if (start < 0) { - start = 0; - } else { - textOffset += start; - } - text = text.substring(start, column + config.maxLen / 2); - return getWordAtText(column, wordDefinition, text, textOffset, config); - } - - const t1 = Date.now(); - const pos = column - 1 - textOffset; - - let prevRegexIndex = -1; - let match: RegExpExecArray | null = null; - - for (let i = 1; ; i++) { - // check time budget - if (Date.now() - t1 >= config.timeBudget) { - break; - } - - // reset the index at which the regexp should start matching, also know where it - // should stop so that subsequent search don't repeat previous searches - const regexIndex = pos - config.windowSize * i; - wordDefinition.lastIndex = Math.max(0, regexIndex); - const thisMatch = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex); - - if (!thisMatch && match) { - // stop: we have something - break; - } - - match = thisMatch; - - // stop: searched at start - if (regexIndex <= 0) { - break; - } - prevRegexIndex = regexIndex; - } - - if (match) { - const result = { - word: match[0], - startColumn: textOffset + 1 + match.index, - endColumn: textOffset + 1 + match.index + match[0].length - }; - wordDefinition.lastIndex = 0; - return result; - } - - return null; -} - -function _findRegexMatchEnclosingPosition(wordDefinition: RegExp, text: string, pos: number, stopPos: number): RegExpExecArray | null { - let match: RegExpExecArray | null; - while (match = wordDefinition.exec(text)) { - const matchIndex = match.index || 0; - if (matchIndex <= pos && wordDefinition.lastIndex >= pos) { - return match; - } else if (stopPos > 0 && matchIndex > stopPos) { - return null; - } - } - return null; -} +export * from '../language/core/wordHelper.js'; diff --git a/src/vs/editor/common/cursor/cursor.ts b/src/vs/editor/common/cursor/cursor.ts index a276dfb3548..c2b0d62003b 100644 --- a/src/vs/editor/common/cursor/cursor.ts +++ b/src/vs/editor/common/cursor/cursor.ts @@ -12,12 +12,12 @@ import { DeleteOperations } from './cursorDeleteOperations.js'; import { CursorChangeReason } from '../cursorEvents.js'; import { CompositionOutcome, TypeOperations } from './cursorTypeOperations.js'; import { BaseTypeWithAutoClosingCommand } from './cursorTypeEditOperations.js'; -import { Position } from '../core/position.js'; -import { Range, IRange } from '../core/range.js'; -import { ISelection, Selection, SelectionDirection } from '../core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range, IRange } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection, SelectionDirection } from '../../../editor/common/language/core/selection.js'; import * as editorCommon from '../editorCommon.js'; -import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from '../model.js'; -import { RawContentChangedType, ModelInjectedTextChangedEvent, InternalModelContentChangeEvent } from '../textModelEvents.js'; +import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, ICursorStateComputer, IIdentifiedSingleEditOperation, IValidEditOperation } from '../../../editor/common/language/model.js'; +import { RawContentChangedType, ModelInjectedTextChangedEvent, InternalModelContentChangeEvent } from '../../../editor/common/language/textModelEvents.js'; import { VerticalRevealType, ViewCursorStateChangedEvent, ViewRevealRangeRequestEvent } from '../viewEvents.js'; import { dispose, Disposable } from '../../../base/common/lifecycle.js'; import { ICoordinatesConverter } from '../viewModel.js'; diff --git a/src/vs/editor/common/cursor/cursorAtomicMoveOperations.ts b/src/vs/editor/common/cursor/cursorAtomicMoveOperations.ts index 6b2c9cd1f87..87cfdde8ec6 100644 --- a/src/vs/editor/common/cursor/cursorAtomicMoveOperations.ts +++ b/src/vs/editor/common/cursor/cursorAtomicMoveOperations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../base/common/charCode.js'; -import { CursorColumns } from '../core/cursorColumns.js'; +import { CursorColumns } from '../../../editor/common/language/core/cursorColumns.js'; export const enum Direction { Left, diff --git a/src/vs/editor/common/cursor/cursorCollection.ts b/src/vs/editor/common/cursor/cursorCollection.ts index ae1f4b6ca15..2df9a2a0c06 100644 --- a/src/vs/editor/common/cursor/cursorCollection.ts +++ b/src/vs/editor/common/cursor/cursorCollection.ts @@ -8,9 +8,9 @@ import { findLastMax, findFirstMin } from '../../../base/common/arraysFind.js'; import { CursorState, PartialCursorState } from '../cursorCommon.js'; import { CursorContext } from './cursorContext.js'; import { Cursor } from './oneCursor.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { ISelection, Selection } from '../core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../editor/common/language/core/selection.js'; export class CursorCollection { diff --git a/src/vs/editor/common/cursor/cursorColumnSelection.ts b/src/vs/editor/common/cursor/cursorColumnSelection.ts index eef55621902..0f72c0a482f 100644 --- a/src/vs/editor/common/cursor/cursorColumnSelection.ts +++ b/src/vs/editor/common/cursor/cursorColumnSelection.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { CursorConfiguration, ICursorSimpleModel, SingleCursorState, IColumnSelectData, SelectionStartKind } from '../cursorCommon.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; export class ColumnSelection { diff --git a/src/vs/editor/common/cursor/cursorContext.ts b/src/vs/editor/common/cursor/cursorContext.ts index 30c25a6d626..eff37773796 100644 --- a/src/vs/editor/common/cursor/cursorContext.ts +++ b/src/vs/editor/common/cursor/cursorContext.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { ICoordinatesConverter } from '../viewModel.js'; import { CursorConfiguration, ICursorSimpleModel } from '../cursorCommon.js'; diff --git a/src/vs/editor/common/cursor/cursorDeleteOperations.ts b/src/vs/editor/common/cursor/cursorDeleteOperations.ts index acf11b2d3ba..cfb0c94b31c 100644 --- a/src/vs/editor/common/cursor/cursorDeleteOperations.ts +++ b/src/vs/editor/common/cursor/cursorDeleteOperations.ts @@ -7,13 +7,13 @@ import * as strings from '../../../base/common/strings.js'; import { ReplaceCommand } from '../commands/replaceCommand.js'; import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from '../config/editorOptions.js'; import { CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from '../cursorCommon.js'; -import { CursorColumns } from '../core/cursorColumns.js'; +import { CursorColumns } from '../../../editor/common/language/core/cursorColumns.js'; import { MoveOperations } from './cursorMoveOperations.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { ICommand } from '../editorCommon.js'; import { StandardAutoClosingPairConditional } from '../languages/languageConfiguration.js'; -import { Position } from '../core/position.js'; +import { Position } from '../../../editor/common/language/core/position.js'; export class DeleteOperations { diff --git a/src/vs/editor/common/cursor/cursorMoveCommands.ts b/src/vs/editor/common/cursor/cursorMoveCommands.ts index 46e2192ff3e..99e7612e103 100644 --- a/src/vs/editor/common/cursor/cursorMoveCommands.ts +++ b/src/vs/editor/common/cursor/cursorMoveCommands.ts @@ -7,8 +7,8 @@ import * as types from '../../../base/common/types.js'; import { CursorState, ICursorSimpleModel, PartialCursorState, SelectionStartKind, SingleCursorState } from '../cursorCommon.js'; import { MoveOperations } from './cursorMoveOperations.js'; import { WordOperations } from './cursorWordOperations.js'; -import { IPosition, Position } from '../core/position.js'; -import { Range } from '../core/range.js'; +import { IPosition, Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { ICommandMetadata } from '../../../platform/commands/common/commands.js'; import { IViewModel } from '../viewModel.js'; diff --git a/src/vs/editor/common/cursor/cursorMoveOperations.ts b/src/vs/editor/common/cursor/cursorMoveOperations.ts index ebfde76738d..14cdcebc1c1 100644 --- a/src/vs/editor/common/cursor/cursorMoveOperations.ts +++ b/src/vs/editor/common/cursor/cursorMoveOperations.ts @@ -5,12 +5,12 @@ import * as strings from '../../../base/common/strings.js'; import { Constants } from '../../../base/common/uint.js'; -import { CursorColumns } from '../core/cursorColumns.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; +import { CursorColumns } from '../../../editor/common/language/core/cursorColumns.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { AtomicTabMoveOperations, Direction } from './cursorAtomicMoveOperations.js'; import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from '../cursorCommon.js'; -import { PositionAffinity } from '../model.js'; +import { PositionAffinity } from '../../../editor/common/language/model.js'; export class CursorPosition { _cursorPositionBrand: void = undefined; diff --git a/src/vs/editor/common/cursor/cursorTypeEditOperations.ts b/src/vs/editor/common/cursor/cursorTypeEditOperations.ts index e250bb74431..7614da10b8a 100644 --- a/src/vs/editor/common/cursor/cursorTypeEditOperations.ts +++ b/src/vs/editor/common/cursor/cursorTypeEditOperations.ts @@ -10,12 +10,12 @@ import { ReplaceCommand, ReplaceCommandWithOffsetCursorState, ReplaceCommandWith import { ShiftCommand } from '../commands/shiftCommand.js'; import { SurroundSelectionCommand } from '../commands/surroundSelectionCommand.js'; import { CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from '../cursorCommon.js'; -import { WordCharacterClass, getMapForWordSeparators } from '../core/wordCharacterClassifier.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; -import { Position } from '../core/position.js'; +import { WordCharacterClass, getMapForWordSeparators } from '../../../editor/common/language/core/wordCharacterClassifier.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { EnterAction, IndentAction, StandardAutoClosingPairConditional } from '../languages/languageConfiguration.js'; import { getIndentationAtPosition } from '../languages/languageConfigurationRegistry.js'; import { IElectricAction } from '../languages/supports/electricCharacter.js'; diff --git a/src/vs/editor/common/cursor/cursorTypeOperations.ts b/src/vs/editor/common/cursor/cursorTypeOperations.ts index b6d1a559cd0..f38cb5815e6 100644 --- a/src/vs/editor/common/cursor/cursorTypeOperations.ts +++ b/src/vs/editor/common/cursor/cursorTypeOperations.ts @@ -6,11 +6,11 @@ import { ShiftCommand } from '../commands/shiftCommand.js'; import { CompositionSurroundSelectionCommand } from '../commands/surroundSelectionCommand.js'; import { CursorConfiguration, EditOperationResult, EditOperationType, ICursorSimpleModel, isQuote } from '../cursorCommon.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; -import { Position } from '../core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; +import { Position } from '../../../editor/common/language/core/position.js'; import { ICommand } from '../editorCommon.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { AutoClosingOpenCharTypeOperation, AutoClosingOvertypeOperation, AutoClosingOvertypeWithInterceptorsOperation, AutoIndentOperation, CompositionOperation, CompositionEndOvertypeOperation, EnterOperation, InterceptorElectricCharOperation, PasteOperation, shiftIndent, shouldSurroundChar, SimpleCharacterTypeOperation, SurroundSelectionOperation, TabOperation, TypeWithoutInterceptorsOperation, unshiftIndent } from './cursorTypeEditOperations.js'; export class TypeOperations { diff --git a/src/vs/editor/common/cursor/cursorWordOperations.ts b/src/vs/editor/common/cursor/cursorWordOperations.ts index 3e1d066587d..797b5f16252 100644 --- a/src/vs/editor/common/cursor/cursorWordOperations.ts +++ b/src/vs/editor/common/cursor/cursorWordOperations.ts @@ -8,12 +8,12 @@ import * as strings from '../../../base/common/strings.js'; import { EditorAutoClosingEditStrategy, EditorAutoClosingStrategy } from '../config/editorOptions.js'; import { CursorConfiguration, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from '../cursorCommon.js'; import { DeleteOperations } from './cursorDeleteOperations.js'; -import { WordCharacterClass, WordCharacterClassifier, IntlWordSegmentData, getMapForWordSeparators } from '../core/wordCharacterClassifier.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; -import { ITextModel } from '../model.js'; -import { IWordAtPosition } from '../core/wordHelper.js'; +import { WordCharacterClass, WordCharacterClassifier, IntlWordSegmentData, getMapForWordSeparators } from '../../../editor/common/language/core/wordCharacterClassifier.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { IWordAtPosition } from '../../../editor/common/language/core/wordHelper.js'; import { AutoClosingPairs } from '../languages/languageConfiguration.js'; interface IFindWordResult { diff --git a/src/vs/editor/common/cursor/oneCursor.ts b/src/vs/editor/common/cursor/oneCursor.ts index 17bdb4e69e0..2cd66d3d6e1 100644 --- a/src/vs/editor/common/cursor/oneCursor.ts +++ b/src/vs/editor/common/cursor/oneCursor.ts @@ -5,10 +5,10 @@ import { CursorState, ICursorSimpleModel, SelectionStartKind, SingleCursorState } from '../cursorCommon.js'; import { CursorContext } from './cursorContext.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; -import { PositionAffinity, TrackedRangeStickiness } from '../model.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; +import { PositionAffinity, TrackedRangeStickiness } from '../../../editor/common/language/model.js'; /** * Represents a single cursor. diff --git a/src/vs/editor/common/cursorCommon.ts b/src/vs/editor/common/cursorCommon.ts index 2e1c78484fc..4187ed73975 100644 --- a/src/vs/editor/common/cursorCommon.ts +++ b/src/vs/editor/common/cursorCommon.ts @@ -4,19 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { ConfigurationChangedEvent, EditorAutoClosingEditStrategy, EditorAutoClosingStrategy, EditorAutoIndentStrategy, EditorAutoSurroundStrategy, EditorOption } from './config/editorOptions.js'; -import { LineTokens } from './tokens/lineTokens.js'; -import { Position } from './core/position.js'; -import { Range } from './core/range.js'; -import { ISelection, Selection } from './core/selection.js'; +import { LineTokens } from '../../editor/common/language/tokens/lineTokens.js'; +import { Position } from '../../editor/common/language/core/position.js'; +import { Range } from '../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../editor/common/language/core/selection.js'; import { ICommand } from './editorCommon.js'; import { IEditorConfiguration } from './config/editorConfiguration.js'; -import { PositionAffinity, TextModelResolvedOptions } from './model.js'; +import { PositionAffinity, TextModelResolvedOptions } from '../../editor/common/language/model.js'; import { AutoClosingPairs } from './languages/languageConfiguration.js'; import { ILanguageConfigurationService } from './languages/languageConfigurationRegistry.js'; import { createScopedLineTokens } from './languages/supports.js'; import { IElectricAction } from './languages/supports/electricCharacter.js'; -import { CursorColumns } from './core/cursorColumns.js'; -import { normalizeIndentation } from './core/indentation.js'; +import { CursorColumns } from '../../editor/common/language/core/cursorColumns.js'; +import { normalizeIndentation } from '../../editor/common/language/core/indentation.js'; import { InputMode } from './inputMode.js'; export interface IColumnSelectData { diff --git a/src/vs/editor/common/cursorEvents.ts b/src/vs/editor/common/cursorEvents.ts index e3b58c3126f..3a015d61dae 100644 --- a/src/vs/editor/common/cursorEvents.ts +++ b/src/vs/editor/common/cursorEvents.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from './core/position.js'; -import { Selection } from './core/selection.js'; +import { Position } from '../../editor/common/language/core/position.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; /** * Describes the reason the cursor has changed its position. diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts index 0d0eae6d7e1..d9a597fe485 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts @@ -5,7 +5,7 @@ import { forEachAdjacent } from '../../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; -import { OffsetRange } from '../../../core/offsetRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; /** * Represents a synchronous diff algorithm. Should be executed in a worker. diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts index 565560c5cf4..7dfc4744236 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OffsetRange } from '../../../core/offsetRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; import { IDiffAlgorithm, SequenceDiff, ISequence, ITimeout, InfiniteTimeout, DiffAlgorithmResult } from './diffAlgorithm.js'; import { Array2D } from '../utils.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts index b496d002de8..c16cf42d5ce 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OffsetRange } from '../../../core/offsetRange.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; import { DiffAlgorithmResult, IDiffAlgorithm, ISequence, ITimeout, InfiniteTimeout, SequenceDiff } from './diffAlgorithm.js'; /** diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts index 523486d84f1..38d6969d615 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts @@ -8,11 +8,11 @@ import { DetailedLineRangeMapping, LineRangeMapping } from '../rangeMapping.js'; import { pushMany, compareBy, numberComparator, reverseOrder } from '../../../../base/common/arrays.js'; import { MonotonousArray, findLastMonotonous } from '../../../../base/common/arraysFind.js'; import { SetMap } from '../../../../base/common/map.js'; -import { LineRange, LineRangeSet } from '../../core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../../../editor/common/language/core/lineRange.js'; import { LinesSliceCharSequence } from './linesSliceCharSequence.js'; import { LineRangeFragment, isSpace } from './utils.js'; import { MyersDiffAlgorithm } from './algorithms/myersDiffAlgorithm.js'; -import { Range } from '../../core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; export function computeMovedLines( changes: DetailedLineRangeMapping[], diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index 884cd838698..f1b4c4e258f 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -5,11 +5,11 @@ import { equals } from '../../../../base/common/arrays.js'; import { assertFn } from '../../../../base/common/assert.js'; -import { LineRange } from '../../core/lineRange.js'; -import { OffsetRange } from '../../core/offsetRange.js'; -import { Position } from '../../core/position.js'; -import { Range } from '../../core/range.js'; -import { ArrayText } from '../../core/textEdit.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ArrayText } from '../../../../editor/common/language/core/textEdit.js'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from '../linesDiffComputer.js'; import { DetailedLineRangeMapping, LineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../rangeMapping.js'; import { DateTimeout, InfiniteTimeout, ITimeout, SequenceDiff } from './algorithms/diffAlgorithm.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts index 20de8dd1ab0..6610947a291 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/heuristicSequenceOptimizations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { forEachWithNeighbors } from '../../../../base/common/arrays.js'; -import { OffsetRange } from '../../core/offsetRange.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; import { ISequence, OffsetPair, SequenceDiff } from './algorithms/diffAlgorithm.js'; import { LineSequence } from './lineSequence.js'; import { LinesSliceCharSequence } from './linesSliceCharSequence.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts index 998459b21e3..a99978174c5 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/lineSequence.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../../base/common/charCode.js'; -import { OffsetRange } from '../../core/offsetRange.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; import { ISequence } from './algorithms/diffAlgorithm.js'; export class LineSequence implements ISequence { diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts index 25b4a6127f8..c90f01a90b6 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts @@ -5,9 +5,9 @@ import { findLastIdxMonotonous, findLastMonotonous, findFirstMonotonous } from '../../../../base/common/arraysFind.js'; import { CharCode } from '../../../../base/common/charCode.js'; -import { OffsetRange } from '../../core/offsetRange.js'; -import { Position } from '../../core/position.js'; -import { Range } from '../../core/range.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ISequence } from './algorithms/diffAlgorithm.js'; import { isSpace } from './utils.js'; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts index cc5e19a2c93..6067cd51355 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/utils.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../../base/common/charCode.js'; -import { LineRange } from '../../core/lineRange.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; import { DetailedLineRangeMapping } from '../rangeMapping.js'; export class Array2D { diff --git a/src/vs/editor/common/diff/documentDiffProvider.ts b/src/vs/editor/common/diff/documentDiffProvider.ts index 6f6f06a9d73..410a8ed6b16 100644 --- a/src/vs/editor/common/diff/documentDiffProvider.ts +++ b/src/vs/editor/common/diff/documentDiffProvider.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { Event } from '../../../base/common/event.js'; import { MovedText } from './linesDiffComputer.js'; import { DetailedLineRangeMapping } from './rangeMapping.js'; -import { ITextModel } from '../model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; /** * A document diff provider computes the diff between two text models. diff --git a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts index f3129ac9a7f..3961a9d6051 100644 --- a/src/vs/editor/common/diff/legacyLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/legacyLinesDiffComputer.ts @@ -8,9 +8,9 @@ import { IDiffChange, ISequence, LcsDiff, IDiffResult } from '../../../base/comm import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff } from './linesDiffComputer.js'; import { RangeMapping, DetailedLineRangeMapping } from './rangeMapping.js'; import * as strings from '../../../base/common/strings.js'; -import { Range } from '../core/range.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; -import { LineRange } from '../core/lineRange.js'; +import { LineRange } from '../../../editor/common/language/core/lineRange.js'; const MINIMUM_MATCHING_CHARACTER_LENGTH = 3; diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index f6565bdc4e2..8658b09b695 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -6,10 +6,10 @@ import { groupAdjacentBy } from '../../../base/common/arrays.js'; import { assertFn, checkAdjacentItems } from '../../../base/common/assert.js'; import { BugIndicatingError } from '../../../base/common/errors.js'; -import { LineRange } from '../core/lineRange.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { AbstractText, SingleTextEdit, TextEdit } from '../core/textEdit.js'; +import { LineRange } from '../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { AbstractText, SingleTextEdit, TextEdit } from '../../../editor/common/language/core/textEdit.js'; import { IChange } from './legacyLinesDiffComputer.js'; /** diff --git a/src/vs/editor/common/editorCommon.ts b/src/vs/editor/common/editorCommon.ts index e25edb4fb50..c987e6cd9f9 100644 --- a/src/vs/editor/common/editorCommon.ts +++ b/src/vs/editor/common/editorCommon.ts @@ -9,12 +9,12 @@ import { IDisposable } from '../../base/common/lifecycle.js'; import { ThemeColor } from '../../base/common/themables.js'; import { URI, UriComponents } from '../../base/common/uri.js'; import { IEditorOptions } from './config/editorOptions.js'; -import { IDimension } from './core/dimension.js'; -import { IPosition, Position } from './core/position.js'; -import { IRange, Range } from './core/range.js'; -import { ISelection, Selection } from './core/selection.js'; -import { IModelDecoration, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane, TrackedRangeStickiness } from './model.js'; -import { IModelDecorationsChangedEvent } from './textModelEvents.js'; +import { IDimension } from '../../editor/common/language/core/dimension.js'; +import { IPosition, Position } from '../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../editor/common/language/core/selection.js'; +import { IModelDecoration, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, IValidEditOperation, OverviewRulerLane, TrackedRangeStickiness } from '../../editor/common/language/model.js'; +import { IModelDecorationsChangedEvent } from '../../editor/common/language/textModelEvents.js'; import { ICommandMetadata } from '../../platform/commands/common/commands.js'; /** diff --git a/src/vs/editor/common/encodedTokenAttributes.ts b/src/vs/editor/common/encodedTokenAttributes.ts index 22ee1417e55..53264185231 100644 --- a/src/vs/editor/common/encodedTokenAttributes.ts +++ b/src/vs/editor/common/encodedTokenAttributes.ts @@ -3,191 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/** - * Open ended enum at runtime - */ -export const enum LanguageId { - Null = 0, - PlainText = 1 -} - -/** - * A font style. Values are 2^x such that a bit mask can be used. - */ -export const enum FontStyle { - NotSet = -1, - None = 0, - Italic = 1, - Bold = 2, - Underline = 4, - Strikethrough = 8, -} - -/** - * Open ended enum at runtime - */ -export const enum ColorId { - None = 0, - DefaultForeground = 1, - DefaultBackground = 2 -} - -/** - * A standard token type. - */ -export const enum StandardTokenType { - Other = 0, - Comment = 1, - String = 2, - RegEx = 3 -} - -/** - * Helpers to manage the "collapsed" metadata of an entire StackElement stack. - * The following assumptions have been made: - * - languageId < 256 => needs 8 bits - * - unique color count < 512 => needs 9 bits - * - * The binary format is: - * - ------------------------------------------- - * 3322 2222 2222 1111 1111 1100 0000 0000 - * 1098 7654 3210 9876 5432 1098 7654 3210 - * - ------------------------------------------- - * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx - * bbbb bbbb ffff ffff fFFF FBTT LLLL LLLL - * - ------------------------------------------- - * - L = LanguageId (8 bits) - * - T = StandardTokenType (2 bits) - * - B = Balanced bracket (1 bit) - * - F = FontStyle (4 bits) - * - f = foreground color (9 bits) - * - b = background color (8 bits) - * - */ -export const enum MetadataConsts { - LANGUAGEID_MASK /* */ = 0b00000000_00000000_00000000_11111111, - TOKEN_TYPE_MASK /* */ = 0b00000000_00000000_00000011_00000000, - BALANCED_BRACKETS_MASK /* */ = 0b00000000_00000000_00000100_00000000, - FONT_STYLE_MASK /* */ = 0b00000000_00000000_01111000_00000000, - FOREGROUND_MASK /* */ = 0b00000000_11111111_10000000_00000000, - BACKGROUND_MASK /* */ = 0b11111111_00000000_00000000_00000000, - - ITALIC_MASK /* */ = 0b00000000_00000000_00001000_00000000, - BOLD_MASK /* */ = 0b00000000_00000000_00010000_00000000, - UNDERLINE_MASK /* */ = 0b00000000_00000000_00100000_00000000, - STRIKETHROUGH_MASK /* */ = 0b00000000_00000000_01000000_00000000, - - // Semantic tokens cannot set the language id, so we can - // use the first 8 bits for control purposes - SEMANTIC_USE_ITALIC /* */ = 0b00000000_00000000_00000000_00000001, - SEMANTIC_USE_BOLD /* */ = 0b00000000_00000000_00000000_00000010, - SEMANTIC_USE_UNDERLINE /* */ = 0b00000000_00000000_00000000_00000100, - SEMANTIC_USE_STRIKETHROUGH /* */ = 0b00000000_00000000_00000000_00001000, - SEMANTIC_USE_FOREGROUND /* */ = 0b00000000_00000000_00000000_00010000, - SEMANTIC_USE_BACKGROUND /* */ = 0b00000000_00000000_00000000_00100000, - - LANGUAGEID_OFFSET = 0, - TOKEN_TYPE_OFFSET = 8, - BALANCED_BRACKETS_OFFSET = 10, - FONT_STYLE_OFFSET = 11, - FOREGROUND_OFFSET = 15, - BACKGROUND_OFFSET = 24 -} - -/** - */ -export class TokenMetadata { - - public static getLanguageId(metadata: number): LanguageId { - return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET; - } - - public static getTokenType(metadata: number): StandardTokenType { - return (metadata & MetadataConsts.TOKEN_TYPE_MASK) >>> MetadataConsts.TOKEN_TYPE_OFFSET; - } - - public static containsBalancedBrackets(metadata: number): boolean { - return (metadata & MetadataConsts.BALANCED_BRACKETS_MASK) !== 0; - } - - public static getFontStyle(metadata: number): FontStyle { - return (metadata & MetadataConsts.FONT_STYLE_MASK) >>> MetadataConsts.FONT_STYLE_OFFSET; - } - - public static getForeground(metadata: number): ColorId { - return (metadata & MetadataConsts.FOREGROUND_MASK) >>> MetadataConsts.FOREGROUND_OFFSET; - } - - public static getBackground(metadata: number): ColorId { - return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET; - } - - public static getClassNameFromMetadata(metadata: number): string { - const foreground = this.getForeground(metadata); - let className = 'mtk' + foreground; - - const fontStyle = this.getFontStyle(metadata); - if (fontStyle & FontStyle.Italic) { - className += ' mtki'; - } - if (fontStyle & FontStyle.Bold) { - className += ' mtkb'; - } - if (fontStyle & FontStyle.Underline) { - className += ' mtku'; - } - if (fontStyle & FontStyle.Strikethrough) { - className += ' mtks'; - } - - return className; - } - - public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string { - const foreground = this.getForeground(metadata); - const fontStyle = this.getFontStyle(metadata); - - let result = `color: ${colorMap[foreground]};`; - if (fontStyle & FontStyle.Italic) { - result += 'font-style: italic;'; - } - if (fontStyle & FontStyle.Bold) { - result += 'font-weight: bold;'; - } - let textDecoration = ''; - if (fontStyle & FontStyle.Underline) { - textDecoration += ' underline'; - } - if (fontStyle & FontStyle.Strikethrough) { - textDecoration += ' line-through'; - } - if (textDecoration) { - result += `text-decoration:${textDecoration};`; - - } - return result; - } - - public static getPresentationFromMetadata(metadata: number): ITokenPresentation { - const foreground = this.getForeground(metadata); - const fontStyle = this.getFontStyle(metadata); - - return { - foreground: foreground, - italic: Boolean(fontStyle & FontStyle.Italic), - bold: Boolean(fontStyle & FontStyle.Bold), - underline: Boolean(fontStyle & FontStyle.Underline), - strikethrough: Boolean(fontStyle & FontStyle.Strikethrough), - }; - } -} - -/** - */ -export interface ITokenPresentation { - foreground: ColorId; - italic: boolean; - bold: boolean; - underline: boolean; - strikethrough: boolean; -} +export * from './language/encodedTokenAttributes.js'; diff --git a/src/vs/editor/common/language/core/characterClassifier.ts b/src/vs/editor/common/language/core/characterClassifier.ts new file mode 100644 index 00000000000..ab1721eee8a --- /dev/null +++ b/src/vs/editor/common/language/core/characterClassifier.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { toUint8 } from '../../../../base/common/uint.js'; + +/** + * A fast character classifier that uses a compact array for ASCII values. + */ +export class CharacterClassifier { + /** + * Maintain a compact (fully initialized ASCII map for quickly classifying ASCII characters - used more often in code). + */ + protected readonly _asciiMap: Uint8Array; + + /** + * The entire map (sparse array). + */ + protected readonly _map: Map; + + protected readonly _defaultValue: number; + + constructor(_defaultValue: T) { + const defaultValue = toUint8(_defaultValue); + + this._defaultValue = defaultValue; + this._asciiMap = CharacterClassifier._createAsciiMap(defaultValue); + this._map = new Map(); + } + + private static _createAsciiMap(defaultValue: number): Uint8Array { + const asciiMap = new Uint8Array(256); + asciiMap.fill(defaultValue); + return asciiMap; + } + + public set(charCode: number, _value: T): void { + const value = toUint8(_value); + + if (charCode >= 0 && charCode < 256) { + this._asciiMap[charCode] = value; + } else { + this._map.set(charCode, value); + } + } + + public get(charCode: number): T { + if (charCode >= 0 && charCode < 256) { + return this._asciiMap[charCode]; + } else { + return (this._map.get(charCode) || this._defaultValue); + } + } + + public clear() { + this._asciiMap.fill(this._defaultValue); + this._map.clear(); + } +} + +const enum Boolean { + False = 0, + True = 1 +} + +export class CharacterSet { + + private readonly _actual: CharacterClassifier; + + constructor() { + this._actual = new CharacterClassifier(Boolean.False); + } + + public add(charCode: number): void { + this._actual.set(charCode, Boolean.True); + } + + public has(charCode: number): boolean { + return (this._actual.get(charCode) === Boolean.True); + } + + public clear(): void { + return this._actual.clear(); + } +} diff --git a/src/vs/editor/common/language/core/cursorColumns.ts b/src/vs/editor/common/language/core/cursorColumns.ts new file mode 100644 index 00000000000..2308017983b --- /dev/null +++ b/src/vs/editor/common/language/core/cursorColumns.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from '../../../../base/common/charCode.js'; +import * as strings from '../../../../base/common/strings.js'; + +/** + * A column in a position is the gap between two adjacent characters. The methods here + * work with a concept called "visible column". A visible column is a very rough approximation + * of the horizontal screen position of a column. For example, using a tab size of 4: + * ```txt + * |||T|ext + * | | | \---- column = 4, visible column = 9 + * | | \------ column = 3, visible column = 8 + * | \------------ column = 2, visible column = 4 + * \------------------ column = 1, visible column = 0 + * ``` + * + * **NOTE**: Visual columns do not work well for RTL text or variable-width fonts or characters. + * + * **NOTE**: These methods work and make sense both on the model and on the view model. + */ +export class CursorColumns { + + private static _nextVisibleColumn(codePoint: number, visibleColumn: number, tabSize: number): number { + if (codePoint === CharCode.Tab) { + return CursorColumns.nextRenderTabStop(visibleColumn, tabSize); + } + if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) { + return visibleColumn + 2; + } + return visibleColumn + 1; + } + + /** + * Returns a visible column from a column. + * @see {@link CursorColumns} + */ + public static visibleColumnFromColumn(lineContent: string, column: number, tabSize: number): number { + const textLen = Math.min(column - 1, lineContent.length); + const text = lineContent.substring(0, textLen); + const iterator = new strings.GraphemeIterator(text); + + let result = 0; + while (!iterator.eol()) { + const codePoint = strings.getNextCodePoint(text, textLen, iterator.offset); + iterator.nextGraphemeLength(); + + result = this._nextVisibleColumn(codePoint, result, tabSize); + } + + return result; + } + + /** + * Returns the value to display as "Col" in the status bar. + * @see {@link CursorColumns} + */ + public static toStatusbarColumn(lineContent: string, column: number, tabSize: number): number { + const text = lineContent.substring(0, Math.min(column - 1, lineContent.length)); + const iterator = new strings.CodePointIterator(text); + + let result = 0; + while (!iterator.eol()) { + const codePoint = iterator.nextCodePoint(); + + if (codePoint === CharCode.Tab) { + result = CursorColumns.nextRenderTabStop(result, tabSize); + } else { + result = result + 1; + } + } + + return result + 1; + } + + /** + * Returns a column from a visible column. + * @see {@link CursorColumns} + */ + public static columnFromVisibleColumn(lineContent: string, visibleColumn: number, tabSize: number): number { + if (visibleColumn <= 0) { + return 1; + } + + const lineContentLength = lineContent.length; + const iterator = new strings.GraphemeIterator(lineContent); + + let beforeVisibleColumn = 0; + let beforeColumn = 1; + while (!iterator.eol()) { + const codePoint = strings.getNextCodePoint(lineContent, lineContentLength, iterator.offset); + iterator.nextGraphemeLength(); + + const afterVisibleColumn = this._nextVisibleColumn(codePoint, beforeVisibleColumn, tabSize); + const afterColumn = iterator.offset + 1; + + if (afterVisibleColumn >= visibleColumn) { + const beforeDelta = visibleColumn - beforeVisibleColumn; + const afterDelta = afterVisibleColumn - visibleColumn; + if (afterDelta < beforeDelta) { + return afterColumn; + } else { + return beforeColumn; + } + } + + beforeVisibleColumn = afterVisibleColumn; + beforeColumn = afterColumn; + } + + // walked the entire string + return lineContentLength + 1; + } + + /** + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) + * @see {@link CursorColumns} + */ + public static nextRenderTabStop(visibleColumn: number, tabSize: number): number { + return visibleColumn + tabSize - visibleColumn % tabSize; + } + + /** + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) + * @see {@link CursorColumns} + */ + public static nextIndentTabStop(visibleColumn: number, indentSize: number): number { + return CursorColumns.nextRenderTabStop(visibleColumn, indentSize); + } + + /** + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) + * @see {@link CursorColumns} + */ + public static prevRenderTabStop(column: number, tabSize: number): number { + return Math.max(0, column - 1 - (column - 1) % tabSize); + } + + /** + * ATTENTION: This works with 0-based columns (as opposed to the regular 1-based columns) + * @see {@link CursorColumns} + */ + public static prevIndentTabStop(column: number, indentSize: number): number { + return CursorColumns.prevRenderTabStop(column, indentSize); + } +} diff --git a/src/vs/editor/common/language/core/dimension.ts b/src/vs/editor/common/language/core/dimension.ts new file mode 100644 index 00000000000..3381f6c69ad --- /dev/null +++ b/src/vs/editor/common/language/core/dimension.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IDimension { + width: number; + height: number; +} diff --git a/src/vs/editor/common/language/core/editOperation.ts b/src/vs/editor/common/language/core/editOperation.ts new file mode 100644 index 00000000000..047330dc2ad --- /dev/null +++ b/src/vs/editor/common/language/core/editOperation.ts @@ -0,0 +1,60 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Position } from './position.js'; +import { IRange, Range } from './range.js'; + +/** + * A single edit operation, that acts as a simple replace. + * i.e. Replace text at `range` with `text` in model. + */ +export interface ISingleEditOperation { + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: IRange; + /** + * The text to replace with. This can be null to emulate a simple delete. + */ + text: string | null; + /** + * This indicates that this operation has "insert" semantics. + * i.e. forceMoveMarkers = true => if `range` is collapsed, all markers at the position will be moved. + */ + forceMoveMarkers?: boolean; +} + +export class EditOperation { + + public static insert(position: Position, text: string): ISingleEditOperation { + return { + range: new Range(position.lineNumber, position.column, position.lineNumber, position.column), + text: text, + forceMoveMarkers: true + }; + } + + public static delete(range: Range): ISingleEditOperation { + return { + range: range, + text: null + }; + } + + public static replace(range: Range, text: string | null): ISingleEditOperation { + return { + range: range, + text: text + }; + } + + public static replaceMove(range: Range, text: string | null): ISingleEditOperation { + return { + range: range, + text: text, + forceMoveMarkers: true + }; + } +} diff --git a/src/vs/editor/common/language/core/editorColorRegistry.ts b/src/vs/editor/common/language/core/editorColorRegistry.ts new file mode 100644 index 00000000000..06b0fcd1a9d --- /dev/null +++ b/src/vs/editor/common/language/core/editorColorRegistry.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from '../../../../nls.js'; +import { Color, RGBA } from '../../../../base/common/color.js'; +import { activeContrastBorder, editorBackground, registerColor, editorWarningForeground, editorInfoForeground, editorWarningBorder, editorInfoBorder, contrastBorder, editorFindMatchHighlight, editorWarningBackground } from '../../../../platform/theme/common/colorRegistry.js'; +import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; + +/** + * Definition of the editor colors + */ +export const editorLineHighlight = registerColor('editor.lineHighlightBackground', null, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); +export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hcDark: '#f38518', hcLight: contrastBorder }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); +export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hcDark: null, hcLight: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); +export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.')); +export const editorSymbolHighlight = registerColor('editor.symbolHighlightBackground', { dark: editorFindMatchHighlight, light: editorFindMatchHighlight, hcDark: null, hcLight: null }, nls.localize('symbolHighlight', 'Background color of highlighted symbol, like for go to definition or go next/previous symbol. The color must not be opaque so as not to hide underlying decorations.'), true); +export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlightBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('symbolHighlightBorder', 'Background color of the border around highlighted symbols.')); + +export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hcDark: Color.white, hcLight: '#0F4A85' }, nls.localize('caret', 'Color of the editor cursor.')); +export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorMultiCursorPrimaryForeground = registerColor('editorMultiCursor.primary.foreground', editorCursorForeground, nls.localize('editorMultiCursorPrimaryForeground', 'Color of the primary editor cursor when multiple cursors are present.')); +export const editorMultiCursorPrimaryBackground = registerColor('editorMultiCursor.primary.background', editorCursorBackground, nls.localize('editorMultiCursorPrimaryBackground', 'The background color of the primary editor cursor when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorMultiCursorSecondaryForeground = registerColor('editorMultiCursor.secondary.foreground', editorCursorForeground, nls.localize('editorMultiCursorSecondaryForeground', 'Color of secondary editor cursors when multiple cursors are present.')); +export const editorMultiCursorSecondaryBackground = registerColor('editorMultiCursor.secondary.background', editorCursorBackground, nls.localize('editorMultiCursorSecondaryBackground', 'The background color of secondary editor cursors when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); +export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); + +export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', editorWhitespaces, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); +export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', editorWhitespaces, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); + +export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', deprecatedEditorIndentGuides, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); +export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', '#00000000', nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); +export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', '#00000000', nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); +export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', '#00000000', nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); +export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', '#00000000', nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); +export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', '#00000000', nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); + +export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', deprecatedEditorActiveIndentGuides, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); +export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', '#00000000', nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); +export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', '#00000000', nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); +export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', '#00000000', nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); +export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', '#00000000', nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); +export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', '#00000000', nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); + +const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); +export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', deprecatedEditorActiveLineNumber, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); +export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', null, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); + +export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorRuler', 'Color of the editor rulers.')); + +export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#919191', hcDark: '#999999', hcLight: '#292929' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor CodeLens')); + +export const editorBracketMatchBackground = registerColor('editorBracketMatch.background', { dark: '#0064001a', light: '#0064001a', hcDark: '#0064001a', hcLight: '#0000' }, nls.localize('editorBracketMatchBackground', 'Background color behind matching brackets')); +export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes')); + +export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hcDark: '#7f7f7f4d', hcLight: '#666666' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.')); +export const editorOverviewRulerBackground = registerColor('editorOverviewRuler.background', null, nls.localize('editorOverviewRulerBackground', 'Background color of the editor overview ruler.')); + +export const editorGutter = registerColor('editorGutter.background', editorBackground, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); + +export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: contrastBorder }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.')); +export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); + +export const ghostTextBorder = registerColor('editorGhostText.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: Color.fromHex('#292929').transparent(0.8) }, nls.localize('editorGhostTextBorder', 'Border color of ghost text in the editor.')); +export const ghostTextForeground = registerColor('editorGhostText.foreground', { dark: Color.fromHex('#ffffff56'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('editorGhostTextForeground', 'Foreground color of the ghost text in the editor.')); +export const ghostTextBackground = registerColor('editorGhostText.background', null, nls.localize('editorGhostTextBackground', 'Background color of the ghost text in the editor.')); + +const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); +export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', rulerRangeDefault, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '#B5200D' }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); +export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningBorder, hcLight: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); +export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hcDark: editorInfoBorder, hcLight: editorInfoBorder }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); + +export const editorBracketHighlightingForeground1 = registerColor('editorBracketHighlight.foreground1', { dark: '#FFD700', light: '#0431FAFF', hcDark: '#FFD700', hcLight: '#0431FAFF' }, nls.localize('editorBracketHighlightForeground1', 'Foreground color of brackets (1). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground2 = registerColor('editorBracketHighlight.foreground2', { dark: '#DA70D6', light: '#319331FF', hcDark: '#DA70D6', hcLight: '#319331FF' }, nls.localize('editorBracketHighlightForeground2', 'Foreground color of brackets (2). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground3 = registerColor('editorBracketHighlight.foreground3', { dark: '#179FFF', light: '#7B3814FF', hcDark: '#87CEFA', hcLight: '#7B3814FF' }, nls.localize('editorBracketHighlightForeground3', 'Foreground color of brackets (3). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground4 = registerColor('editorBracketHighlight.foreground4', '#00000000', nls.localize('editorBracketHighlightForeground4', 'Foreground color of brackets (4). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', '#00000000', nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', '#00000000', nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); + +export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '#B5200D' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); + +export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', '#00000000', nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', '#00000000', nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground3 = registerColor('editorBracketPairGuide.background3', '#00000000', nls.localize('editorBracketPairGuide.background3', 'Background color of inactive bracket pair guides (3). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground4 = registerColor('editorBracketPairGuide.background4', '#00000000', nls.localize('editorBracketPairGuide.background4', 'Background color of inactive bracket pair guides (4). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground5 = registerColor('editorBracketPairGuide.background5', '#00000000', nls.localize('editorBracketPairGuide.background5', 'Background color of inactive bracket pair guides (5). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground6 = registerColor('editorBracketPairGuide.background6', '#00000000', nls.localize('editorBracketPairGuide.background6', 'Background color of inactive bracket pair guides (6). Requires enabling bracket pair guides.')); + +export const editorBracketPairGuideActiveBackground1 = registerColor('editorBracketPairGuide.activeBackground1', '#00000000', nls.localize('editorBracketPairGuide.activeBackground1', 'Background color of active bracket pair guides (1). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground2 = registerColor('editorBracketPairGuide.activeBackground2', '#00000000', nls.localize('editorBracketPairGuide.activeBackground2', 'Background color of active bracket pair guides (2). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground3 = registerColor('editorBracketPairGuide.activeBackground3', '#00000000', nls.localize('editorBracketPairGuide.activeBackground3', 'Background color of active bracket pair guides (3). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground4 = registerColor('editorBracketPairGuide.activeBackground4', '#00000000', nls.localize('editorBracketPairGuide.activeBackground4', 'Background color of active bracket pair guides (4). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground5 = registerColor('editorBracketPairGuide.activeBackground5', '#00000000', nls.localize('editorBracketPairGuide.activeBackground5', 'Background color of active bracket pair guides (5). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground6 = registerColor('editorBracketPairGuide.activeBackground6', '#00000000', nls.localize('editorBracketPairGuide.activeBackground6', 'Background color of active bracket pair guides (6). Requires enabling bracket pair guides.')); + +export const editorUnicodeHighlightBorder = registerColor('editorUnicodeHighlight.border', editorWarningForeground, nls.localize('editorUnicodeHighlight.border', 'Border color used to highlight unicode characters.')); +export const editorUnicodeHighlightBackground = registerColor('editorUnicodeHighlight.background', editorWarningBackground, nls.localize('editorUnicodeHighlight.background', 'Background color used to highlight unicode characters.')); + + +// contains all color rules that used to defined in editor/browser/widget/editor.css +registerThemingParticipant((theme, collector) => { + const background = theme.getColor(editorBackground); + const lineHighlight = theme.getColor(editorLineHighlight); + const imeBackground = (lineHighlight && !lineHighlight.isTransparent() ? lineHighlight : background); + if (imeBackground) { + collector.addRule(`.monaco-editor .inputarea.ime-input { background-color: ${imeBackground}; }`); + } +}); diff --git a/src/vs/editor/common/language/core/eolCounter.ts b/src/vs/editor/common/language/core/eolCounter.ts new file mode 100644 index 00000000000..f37989cb442 --- /dev/null +++ b/src/vs/editor/common/language/core/eolCounter.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from '../../../../base/common/charCode.js'; + +export const enum StringEOL { + Unknown = 0, + Invalid = 3, + LF = 1, + CRLF = 2 +} + +export function countEOL(text: string): [number, number, number, StringEOL] { + let eolCount = 0; + let firstLineLength = 0; + let lastLineStart = 0; + let eol: StringEOL = StringEOL.Unknown; + for (let i = 0, len = text.length; i < len; i++) { + const chr = text.charCodeAt(i); + + if (chr === CharCode.CarriageReturn) { + if (eolCount === 0) { + firstLineLength = i; + } + eolCount++; + if (i + 1 < len && text.charCodeAt(i + 1) === CharCode.LineFeed) { + // \r\n... case + eol |= StringEOL.CRLF; + i++; // skip \n + } else { + // \r... case + eol |= StringEOL.Invalid; + } + lastLineStart = i + 1; + } else if (chr === CharCode.LineFeed) { + // \n... case + eol |= StringEOL.LF; + if (eolCount === 0) { + firstLineLength = i; + } + eolCount++; + lastLineStart = i + 1; + } + } + if (eolCount === 0) { + firstLineLength = text.length; + } + return [eolCount, firstLineLength, text.length - lastLineStart, eol]; +} diff --git a/src/vs/editor/common/language/core/indentation.ts b/src/vs/editor/common/language/core/indentation.ts new file mode 100644 index 00000000000..d68865fdfc8 --- /dev/null +++ b/src/vs/editor/common/language/core/indentation.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as strings from '../../../../base/common/strings.js'; +import { CursorColumns } from './cursorColumns.js'; + +function _normalizeIndentationFromWhitespace(str: string, indentSize: number, insertSpaces: boolean): string { + let spacesCnt = 0; + for (let i = 0; i < str.length; i++) { + if (str.charAt(i) === '\t') { + spacesCnt = CursorColumns.nextIndentTabStop(spacesCnt, indentSize); + } else { + spacesCnt++; + } + } + + let result = ''; + if (!insertSpaces) { + const tabsCnt = Math.floor(spacesCnt / indentSize); + spacesCnt = spacesCnt % indentSize; + for (let i = 0; i < tabsCnt; i++) { + result += '\t'; + } + } + + for (let i = 0; i < spacesCnt; i++) { + result += ' '; + } + + return result; +} + +export function normalizeIndentation(str: string, indentSize: number, insertSpaces: boolean): string { + let firstNonWhitespaceIndex = strings.firstNonWhitespaceIndex(str); + if (firstNonWhitespaceIndex === -1) { + firstNonWhitespaceIndex = str.length; + } + return _normalizeIndentationFromWhitespace(str.substring(0, firstNonWhitespaceIndex), indentSize, insertSpaces) + str.substring(firstNonWhitespaceIndex); +} diff --git a/src/vs/editor/common/language/core/lineEdit.ts b/src/vs/editor/common/language/core/lineEdit.ts new file mode 100644 index 00000000000..7f3517fb969 --- /dev/null +++ b/src/vs/editor/common/language/core/lineEdit.ts @@ -0,0 +1,380 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { compareBy, groupAdjacentBy, numberComparator } from '../../../../base/common/arrays.js'; +import { assert, checkAdjacentItems } from '../../../../base/common/assert.js'; +import { splitLines } from '../../../../base/common/strings.js'; +import { LineRange } from './lineRange.js'; +import { OffsetEdit, SingleOffsetEdit } from './offsetEdit.js'; +import { Position } from './position.js'; +import { Range } from './range.js'; +import { AbstractText, SingleTextEdit, TextEdit } from './textEdit.js'; + + +export class LineEdit { + public static readonly empty = new LineEdit([]); + + public static deserialize(data: SerializedLineEdit): LineEdit { + return new LineEdit(data.map(e => SingleLineEdit.deserialize(e))); + } + + public static fromEdit(edit: OffsetEdit, initialValue: AbstractText): LineEdit { + const textEdit = TextEdit.fromOffsetEdit(edit, initialValue); + return LineEdit.fromTextEdit(textEdit, initialValue); + } + + public static fromTextEdit(edit: TextEdit, initialValue: AbstractText): LineEdit { + const edits = edit.edits; + + const result: SingleLineEdit[] = []; + + const currentEdits: SingleTextEdit[] = []; + for (let i = 0; i < edits.length; i++) { + const edit = edits[i]; + const nextEditRange = i + 1 < edits.length ? edits[i + 1] : undefined; + currentEdits.push(edit); + if (nextEditRange && nextEditRange.range.startLineNumber === edit.range.endLineNumber) { + continue; + } + + const singleEdit = SingleTextEdit.joinEdits(currentEdits, initialValue); + currentEdits.length = 0; + + const singleLineEdit = SingleLineEdit.fromSingleTextEdit(singleEdit, initialValue); + result.push(singleLineEdit); + } + + return new LineEdit(result); + } + + public static createFromUnsorted(edits: readonly SingleLineEdit[]): LineEdit { + const result = edits.slice(); + result.sort(compareBy(i => i.lineRange.startLineNumber, numberComparator)); + return new LineEdit(result); + } + + constructor( + /** + * Have to be sorted by start line number and non-intersecting. + */ + public readonly edits: readonly SingleLineEdit[] + ) { + assert(checkAdjacentItems(edits, (i1, i2) => i1.lineRange.endLineNumberExclusive <= i2.lineRange.startLineNumber)); + } + + public toEdit(initialValue: AbstractText): OffsetEdit { + const edits: SingleOffsetEdit[] = []; + for (const edit of this.edits) { + const singleEdit = edit.toSingleEdit(initialValue); + edits.push(singleEdit); + } + return new OffsetEdit(edits); + } + + public toString(): string { + return this.edits.map(e => e.toString()).join(','); + } + + public serialize(): SerializedLineEdit { + return this.edits.map(e => e.serialize()); + } + + public getNewLineRanges(): LineRange[] { + const ranges: LineRange[] = []; + let offset = 0; + for (const e of this.edits) { + ranges.push(LineRange.ofLength(e.lineRange.startLineNumber + offset, e.newLines.length),); + offset += e.newLines.length - e.lineRange.length; + } + return ranges; + } + + public mapLineNumber(lineNumber: number): number { + let lineDelta = 0; + for (const e of this.edits) { + if (e.lineRange.endLineNumberExclusive > lineNumber) { + break; + } + + lineDelta += e.newLines.length - e.lineRange.length; + } + return lineNumber + lineDelta; + } + + public mapLineRange(lineRange: LineRange): LineRange { + return new LineRange( + this.mapLineNumber(lineRange.startLineNumber), + this.mapLineNumber(lineRange.endLineNumberExclusive), + ); + } + + public rebase(base: LineEdit): LineEdit { + return new LineEdit( + this.edits.map(e => new SingleLineEdit(base.mapLineRange(e.lineRange), e.newLines)), + ); + } + + public humanReadablePatch(originalLines: string[]): string { + const result: string[] = []; + + function pushLine(originalLineNumber: number, modifiedLineNumber: number, kind: 'unmodified' | 'deleted' | 'added', content: string | undefined) { + const specialChar = (kind === 'unmodified' ? ' ' : (kind === 'deleted' ? '-' : '+')); + + if (content === undefined) { + content = '[[[[[ WARNING: LINE DOES NOT EXIST ]]]]]'; + } + + const origLn = originalLineNumber === -1 ? ' ' : originalLineNumber.toString().padStart(3, ' '); + const modLn = modifiedLineNumber === -1 ? ' ' : modifiedLineNumber.toString().padStart(3, ' '); + + result.push(`${specialChar} ${origLn} ${modLn} ${content}`); + } + + function pushSeperator() { + result.push('---'); + } + + let lineDelta = 0; + let first = true; + + for (const edits of groupAdjacentBy(this.edits, (e1, e2) => e1.lineRange.distanceToRange(e2.lineRange) <= 5)) { + if (!first) { + pushSeperator(); + } else { + first = false; + } + + let lastLineNumber = edits[0].lineRange.startLineNumber - 2; + + for (const edit of edits) { + for (let i = Math.max(1, lastLineNumber); i < edit.lineRange.startLineNumber; i++) { + pushLine(i, i + lineDelta, 'unmodified', originalLines[i - 1]); + } + + const range = edit.lineRange; + const newLines = edit.newLines; + for (const replaceLineNumber of range.mapToLineArray(n => n)) { + const line = originalLines[replaceLineNumber - 1]; + pushLine(replaceLineNumber, -1, 'deleted', line); + } + for (let i = 0; i < newLines.length; i++) { + const line = newLines[i]; + pushLine(-1, range.startLineNumber + lineDelta + i, 'added', line); + } + + lastLineNumber = range.endLineNumberExclusive; + + lineDelta += edit.newLines.length - edit.lineRange.length; + } + + for (let i = lastLineNumber; i <= Math.min(lastLineNumber + 2, originalLines.length); i++) { + pushLine(i, i + lineDelta, 'unmodified', originalLines[i - 1]); + } + } + + return result.join('\n'); + } + + public apply(lines: string[]): string[] { + const result: string[] = []; + + let currentLineIndex = 0; + + for (const edit of this.edits) { + while (currentLineIndex < edit.lineRange.startLineNumber - 1) { + result.push(lines[currentLineIndex]); + currentLineIndex++; + } + + for (const newLine of edit.newLines) { + result.push(newLine); + } + + currentLineIndex = edit.lineRange.endLineNumberExclusive - 1; + } + + while (currentLineIndex < lines.length) { + result.push(lines[currentLineIndex]); + currentLineIndex++; + } + + return result; + } + + public toSingleEdit() { + + } +} + +export class SingleLineEdit { + public static deserialize(e: SerializedSingleLineEdit): SingleLineEdit { + return new SingleLineEdit( + LineRange.ofLength(e[0], e[1] - e[0]), + e[2], + ); + } + + public static fromSingleTextEdit(edit: SingleTextEdit, initialValue: AbstractText): SingleLineEdit { + // 1: ab[cde + // 2: fghijk + // 3: lmn]opq + + // replaced with + + // 1n: 123 + // 2n: 456 + // 3n: 789 + + // simple solution: replace [1..4) with [1n..4n) + + const newLines = splitLines(edit.text); + let startLineNumber = edit.range.startLineNumber; + const survivingFirstLineText = initialValue.getValueOfRange(Range.fromPositions( + new Position(edit.range.startLineNumber, 1), + edit.range.getStartPosition() + )); + newLines[0] = survivingFirstLineText + newLines[0]; + + let endLineNumberEx = edit.range.endLineNumber + 1; + const editEndLineNumberMaxColumn = initialValue.getTransformer().getLineLength(edit.range.endLineNumber) + 1; + const survivingEndLineText = initialValue.getValueOfRange(Range.fromPositions( + edit.range.getEndPosition(), + new Position(edit.range.endLineNumber, editEndLineNumberMaxColumn) + )); + newLines[newLines.length - 1] = newLines[newLines.length - 1] + survivingEndLineText; + + // Replacing [startLineNumber, endLineNumberEx) with newLines would be correct, however it might not be minimal. + + const startBeforeNewLine = edit.range.startColumn === initialValue.getTransformer().getLineLength(edit.range.startLineNumber) + 1; + const endAfterNewLine = edit.range.endColumn === 1; + + if (startBeforeNewLine && newLines[0].length === survivingFirstLineText.length) { + // the replacement would not delete any text on the first line + startLineNumber++; + newLines.shift(); + } + + if (newLines.length > 0 && startLineNumber < endLineNumberEx && endAfterNewLine && newLines[newLines.length - 1].length === survivingEndLineText.length) { + // the replacement would not delete any text on the last line + endLineNumberEx--; + newLines.pop(); + } + + return new SingleLineEdit(new LineRange(startLineNumber, endLineNumberEx), newLines); + } + + constructor( + public readonly lineRange: LineRange, + public readonly newLines: readonly string[], + ) { } + + public toSingleTextEdit(initialValue: AbstractText): SingleTextEdit { + if (this.newLines.length === 0) { + // Deletion + const textLen = initialValue.getTransformer().textLength; + if (this.lineRange.endLineNumberExclusive === textLen.lineCount + 2) { + let startPos: Position; + if (this.lineRange.startLineNumber > 1) { + const startLineNumber = this.lineRange.startLineNumber - 1; + const startColumn = initialValue.getTransformer().getLineLength(startLineNumber) + 1; + startPos = new Position(startLineNumber, startColumn); + } else { + // Delete everything. + // In terms of lines, this would end up with 0 lines. + // However, a string has always 1 line (which can be empty). + startPos = new Position(1, 1); + } + + const lastPosition = textLen.addToPosition(new Position(1, 1)); + return new SingleTextEdit(Range.fromPositions(startPos, lastPosition), ''); + } else { + return new SingleTextEdit(new Range(this.lineRange.startLineNumber, 1, this.lineRange.endLineNumberExclusive, 1), ''); + } + + } else if (this.lineRange.isEmpty) { + // Insertion + + let endLineNumber: number; + let column: number; + let text: string; + const insertionLine = this.lineRange.startLineNumber; + if (insertionLine === initialValue.getTransformer().textLength.lineCount + 2) { + endLineNumber = insertionLine - 1; + column = initialValue.getTransformer().getLineLength(endLineNumber) + 1; + text = this.newLines.map(l => '\n' + l).join(''); + } else { + endLineNumber = insertionLine; + column = 1; + text = this.newLines.map(l => l + '\n').join(''); + } + return new SingleTextEdit(Range.fromPositions(new Position(endLineNumber, column)), text); + } else { + const endLineNumber = this.lineRange.endLineNumberExclusive - 1; + const endLineNumberMaxColumn = initialValue.getTransformer().getLineLength(endLineNumber) + 1; + const range = new Range( + this.lineRange.startLineNumber, + 1, + endLineNumber, + endLineNumberMaxColumn + ); + // Don't add \n to the last line. This is because we subtract one from lineRange.endLineNumberExclusive for endLineNumber. + const text = this.newLines.join('\n'); + return new SingleTextEdit(range, text); + } + } + + public toSingleEdit(initialValue: AbstractText): SingleOffsetEdit { + const textEdit = this.toSingleTextEdit(initialValue); + const range = initialValue.getTransformer().getOffsetRange(textEdit.range); + return new SingleOffsetEdit(range, textEdit.text); + } + + public toString(): string { + return `${this.lineRange}->${JSON.stringify(this.newLines)}`; + } + + public serialize(): SerializedSingleLineEdit { + return [ + this.lineRange.startLineNumber, + this.lineRange.endLineNumberExclusive, + this.newLines, + ]; + } + + public removeCommonSuffixPrefixLines(initialValue: AbstractText): SingleLineEdit { + let startLineNumber = this.lineRange.startLineNumber; + let endLineNumberEx = this.lineRange.endLineNumberExclusive; + + let trimStartCount = 0; + while ( + startLineNumber < endLineNumberEx && trimStartCount < this.newLines.length + && this.newLines[trimStartCount] === initialValue.getLineAt(startLineNumber) + ) { + startLineNumber++; + trimStartCount++; + } + + let trimEndCount = 0; + while ( + startLineNumber < endLineNumberEx && trimEndCount + trimStartCount < this.newLines.length + && this.newLines[this.newLines.length - 1 - trimEndCount] === initialValue.getLineAt(endLineNumberEx - 1) + ) { + endLineNumberEx--; + trimEndCount++; + } + + if (trimStartCount === 0 && trimEndCount === 0) { + return this; + } + return new SingleLineEdit(new LineRange(startLineNumber, endLineNumberEx), this.newLines.slice(trimStartCount, this.newLines.length - trimEndCount)); + } + + public toLineEdit(): LineEdit { + return new LineEdit([this]); + } +} + +export type SerializedLineEdit = SerializedSingleLineEdit[]; +export type SerializedSingleLineEdit = [startLineNumber: number, endLineNumber: number, newLines: readonly string[]]; diff --git a/src/vs/editor/common/language/core/lineRange.ts b/src/vs/editor/common/language/core/lineRange.ts new file mode 100644 index 00000000000..694a5c7c186 --- /dev/null +++ b/src/vs/editor/common/language/core/lineRange.ts @@ -0,0 +1,411 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from './offsetRange.js'; +import { Range } from './range.js'; +import { findFirstIdxMonotonousOrArrLen, findLastIdxMonotonous, findLastMonotonous } from '../../../../base/common/arraysFind.js'; + +/** + * A range of lines (1-based). + */ +export class LineRange { + public static fromRange(range: Range): LineRange { + return new LineRange(range.startLineNumber, range.endLineNumber); + } + + public static fromRangeInclusive(range: Range): LineRange { + return new LineRange(range.startLineNumber, range.endLineNumber + 1); + } + + public static subtract(a: LineRange, b: LineRange | undefined): LineRange[] { + if (!b) { + return [a]; + } + if (a.startLineNumber < b.startLineNumber && b.endLineNumberExclusive < a.endLineNumberExclusive) { + return [ + new LineRange(a.startLineNumber, b.startLineNumber), + new LineRange(b.endLineNumberExclusive, a.endLineNumberExclusive) + ]; + } else if (b.startLineNumber <= a.startLineNumber && a.endLineNumberExclusive <= b.endLineNumberExclusive) { + return []; + } else if (b.endLineNumberExclusive < a.endLineNumberExclusive) { + return [new LineRange(Math.max(b.endLineNumberExclusive, a.startLineNumber), a.endLineNumberExclusive)]; + } else { + return [new LineRange(a.startLineNumber, Math.min(b.startLineNumber, a.endLineNumberExclusive))]; + } + } + + /** + * @param lineRanges An array of sorted line ranges. + */ + public static joinMany(lineRanges: readonly (readonly LineRange[])[]): readonly LineRange[] { + if (lineRanges.length === 0) { + return []; + } + let result = new LineRangeSet(lineRanges[0].slice()); + for (let i = 1; i < lineRanges.length; i++) { + result = result.getUnion(new LineRangeSet(lineRanges[i].slice())); + } + return result.ranges; + } + + public static join(lineRanges: LineRange[]): LineRange { + if (lineRanges.length === 0) { + throw new BugIndicatingError('lineRanges cannot be empty'); + } + let startLineNumber = lineRanges[0].startLineNumber; + let endLineNumberExclusive = lineRanges[0].endLineNumberExclusive; + for (let i = 1; i < lineRanges.length; i++) { + startLineNumber = Math.min(startLineNumber, lineRanges[i].startLineNumber); + endLineNumberExclusive = Math.max(endLineNumberExclusive, lineRanges[i].endLineNumberExclusive); + } + return new LineRange(startLineNumber, endLineNumberExclusive); + } + + public static ofLength(startLineNumber: number, length: number): LineRange { + return new LineRange(startLineNumber, startLineNumber + length); + } + + /** + * @internal + */ + public static deserialize(lineRange: ISerializedLineRange): LineRange { + return new LineRange(lineRange[0], lineRange[1]); + } + + /** + * The start line number. + */ + public readonly startLineNumber: number; + + /** + * The end line number (exclusive). + */ + public readonly endLineNumberExclusive: number; + + constructor( + startLineNumber: number, + endLineNumberExclusive: number, + ) { + if (startLineNumber > endLineNumberExclusive) { + throw new BugIndicatingError(`startLineNumber ${startLineNumber} cannot be after endLineNumberExclusive ${endLineNumberExclusive}`); + } + this.startLineNumber = startLineNumber; + this.endLineNumberExclusive = endLineNumberExclusive; + } + + /** + * Indicates if this line range contains the given line number. + */ + public contains(lineNumber: number): boolean { + return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; + } + + /** + * Indicates if this line range is empty. + */ + get isEmpty(): boolean { + return this.startLineNumber === this.endLineNumberExclusive; + } + + /** + * Moves this line range by the given offset of line numbers. + */ + public delta(offset: number): LineRange { + return new LineRange(this.startLineNumber + offset, this.endLineNumberExclusive + offset); + } + + public deltaLength(offset: number): LineRange { + return new LineRange(this.startLineNumber, this.endLineNumberExclusive + offset); + } + + /** + * The number of lines this line range spans. + */ + public get length(): number { + return this.endLineNumberExclusive - this.startLineNumber; + } + + /** + * Creates a line range that combines this and the given line range. + */ + public join(other: LineRange): LineRange { + return new LineRange( + Math.min(this.startLineNumber, other.startLineNumber), + Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) + ); + } + + public toString(): string { + return `[${this.startLineNumber},${this.endLineNumberExclusive})`; + } + + /** + * The resulting range is empty if the ranges do not intersect, but touch. + * If the ranges don't even touch, the result is undefined. + */ + public intersect(other: LineRange): LineRange | undefined { + const startLineNumber = Math.max(this.startLineNumber, other.startLineNumber); + const endLineNumberExclusive = Math.min(this.endLineNumberExclusive, other.endLineNumberExclusive); + if (startLineNumber <= endLineNumberExclusive) { + return new LineRange(startLineNumber, endLineNumberExclusive); + } + return undefined; + } + + public intersectsStrict(other: LineRange): boolean { + return this.startLineNumber < other.endLineNumberExclusive && other.startLineNumber < this.endLineNumberExclusive; + } + + public overlapOrTouch(other: LineRange): boolean { + return this.startLineNumber <= other.endLineNumberExclusive && other.startLineNumber <= this.endLineNumberExclusive; + } + + public equals(b: LineRange): boolean { + return this.startLineNumber === b.startLineNumber && this.endLineNumberExclusive === b.endLineNumberExclusive; + } + + public toInclusiveRange(): Range | null { + if (this.isEmpty) { + return null; + } + return new Range(this.startLineNumber, 1, this.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER); + } + + /** + * @deprecated Using this function is discouraged because it might lead to bugs: The end position is not guaranteed to be a valid position! + */ + public toExclusiveRange(): Range { + return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); + } + + public mapToLineArray(f: (lineNumber: number) => T): T[] { + const result: T[] = []; + for (let lineNumber = this.startLineNumber; lineNumber < this.endLineNumberExclusive; lineNumber++) { + result.push(f(lineNumber)); + } + return result; + } + + public forEach(f: (lineNumber: number) => void): void { + for (let lineNumber = this.startLineNumber; lineNumber < this.endLineNumberExclusive; lineNumber++) { + f(lineNumber); + } + } + + /** + * @internal + */ + public serialize(): ISerializedLineRange { + return [this.startLineNumber, this.endLineNumberExclusive]; + } + + public includes(lineNumber: number): boolean { + return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; + } + + /** + * Converts this 1-based line range to a 0-based offset range (subtracts 1!). + * @internal + */ + public toOffsetRange(): OffsetRange { + return new OffsetRange(this.startLineNumber - 1, this.endLineNumberExclusive - 1); + } + + public distanceToRange(other: LineRange): number { + if (this.endLineNumberExclusive <= other.startLineNumber) { + return other.startLineNumber - this.endLineNumberExclusive; + } + if (other.endLineNumberExclusive <= this.startLineNumber) { + return this.startLineNumber - other.endLineNumberExclusive; + } + return 0; + } + + public distanceToLine(lineNumber: number): number { + if (this.contains(lineNumber)) { + return 0; + } + if (lineNumber < this.startLineNumber) { + return this.startLineNumber - lineNumber; + } + return lineNumber - this.endLineNumberExclusive; + } + + public addMargin(marginTop: number, marginBottom: number): LineRange { + return new LineRange( + this.startLineNumber - marginTop, + this.endLineNumberExclusive + marginBottom + ); + } +} + +export type ISerializedLineRange = [startLineNumber: number, endLineNumberExclusive: number]; + + +export class LineRangeSet { + constructor( + /** + * Sorted by start line number. + * No two line ranges are touching or intersecting. + */ + private readonly _normalizedRanges: LineRange[] = [] + ) { + } + + get ranges(): readonly LineRange[] { + return this._normalizedRanges; + } + + addRange(range: LineRange): void { + if (range.length === 0) { + return; + } + + // Idea: Find joinRange such that: + // replaceRange = _normalizedRanges.replaceRange(joinRange, range.joinAll(joinRange.map(idx => this._normalizedRanges[idx]))) + + // idx of first element that touches range or that is after range + const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + // If there is no element that touches range, then joinRangeStartIdx === joinRangeEndIdxExclusive and that value is the index of the element after range + this._normalizedRanges.splice(joinRangeStartIdx, 0, range); + } else if (joinRangeStartIdx === joinRangeEndIdxExclusive - 1) { + // Else, there is an element that touches range and in this case it is both the first and last element. Thus we can replace it + const joinRange = this._normalizedRanges[joinRangeStartIdx]; + this._normalizedRanges[joinRangeStartIdx] = joinRange.join(range); + } else { + // First and last element are different - we need to replace the entire range + const joinRange = this._normalizedRanges[joinRangeStartIdx].join(this._normalizedRanges[joinRangeEndIdxExclusive - 1]).join(range); + this._normalizedRanges.splice(joinRangeStartIdx, joinRangeEndIdxExclusive - joinRangeStartIdx, joinRange); + } + } + + contains(lineNumber: number): boolean { + const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber <= lineNumber); + return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > lineNumber; + } + + intersects(range: LineRange): boolean { + const rangeThatStartsBeforeEnd = findLastMonotonous(this._normalizedRanges, r => r.startLineNumber < range.endLineNumberExclusive); + return !!rangeThatStartsBeforeEnd && rangeThatStartsBeforeEnd.endLineNumberExclusive > range.startLineNumber; + } + + getUnion(other: LineRangeSet): LineRangeSet { + if (this._normalizedRanges.length === 0) { + return other; + } + if (other._normalizedRanges.length === 0) { + return this; + } + + const result: LineRange[] = []; + let i1 = 0; + let i2 = 0; + let current: LineRange | null = null; + while (i1 < this._normalizedRanges.length || i2 < other._normalizedRanges.length) { + let next: LineRange | null = null; + if (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { + const lineRange1 = this._normalizedRanges[i1]; + const lineRange2 = other._normalizedRanges[i2]; + if (lineRange1.startLineNumber < lineRange2.startLineNumber) { + next = lineRange1; + i1++; + } else { + next = lineRange2; + i2++; + } + } else if (i1 < this._normalizedRanges.length) { + next = this._normalizedRanges[i1]; + i1++; + } else { + next = other._normalizedRanges[i2]; + i2++; + } + + if (current === null) { + current = next; + } else { + if (current.endLineNumberExclusive >= next.startLineNumber) { + // merge + current = new LineRange(current.startLineNumber, Math.max(current.endLineNumberExclusive, next.endLineNumberExclusive)); + } else { + // push + result.push(current); + current = next; + } + } + } + if (current !== null) { + result.push(current); + } + return new LineRangeSet(result); + } + + /** + * Subtracts all ranges in this set from `range` and returns the result. + */ + subtractFrom(range: LineRange): LineRangeSet { + // idx of first element that touches range or that is after range + const joinRangeStartIdx = findFirstIdxMonotonousOrArrLen(this._normalizedRanges, r => r.endLineNumberExclusive >= range.startLineNumber); + // idx of element after { last element that touches range or that is before range } + const joinRangeEndIdxExclusive = findLastIdxMonotonous(this._normalizedRanges, r => r.startLineNumber <= range.endLineNumberExclusive) + 1; + + if (joinRangeStartIdx === joinRangeEndIdxExclusive) { + return new LineRangeSet([range]); + } + + const result: LineRange[] = []; + let startLineNumber = range.startLineNumber; + for (let i = joinRangeStartIdx; i < joinRangeEndIdxExclusive; i++) { + const r = this._normalizedRanges[i]; + if (r.startLineNumber > startLineNumber) { + result.push(new LineRange(startLineNumber, r.startLineNumber)); + } + startLineNumber = r.endLineNumberExclusive; + } + if (startLineNumber < range.endLineNumberExclusive) { + result.push(new LineRange(startLineNumber, range.endLineNumberExclusive)); + } + + return new LineRangeSet(result); + } + + toString() { + return this._normalizedRanges.map(r => r.toString()).join(', '); + } + + getIntersection(other: LineRangeSet): LineRangeSet { + const result: LineRange[] = []; + + let i1 = 0; + let i2 = 0; + while (i1 < this._normalizedRanges.length && i2 < other._normalizedRanges.length) { + const r1 = this._normalizedRanges[i1]; + const r2 = other._normalizedRanges[i2]; + + const i = r1.intersect(r2); + if (i && !i.isEmpty) { + result.push(i); + } + + if (r1.endLineNumberExclusive < r2.endLineNumberExclusive) { + i1++; + } else { + i2++; + } + } + + return new LineRangeSet(result); + } + + getWithDelta(value: number): LineRangeSet { + return new LineRangeSet(this._normalizedRanges.map(r => r.delta(value))); + } +} diff --git a/src/vs/editor/common/language/core/offsetEdit.ts b/src/vs/editor/common/language/core/offsetEdit.ts new file mode 100644 index 00000000000..120750957d3 --- /dev/null +++ b/src/vs/editor/common/language/core/offsetEdit.ts @@ -0,0 +1,423 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { OffsetRange } from './offsetRange.js'; + +/** + * Describes an edit to a (0-based) string. + * Use `TextEdit` to describe edits for a 1-based line/column text. +*/ +export class OffsetEdit { + public static readonly empty = new OffsetEdit([]); + + public static fromJson(data: IOffsetEdit): OffsetEdit { + return new OffsetEdit(data.map(SingleOffsetEdit.fromJson)); + } + + public static replace( + range: OffsetRange, + newText: string, + ): OffsetEdit { + return new OffsetEdit([new SingleOffsetEdit(range, newText)]); + } + + public static insert( + offset: number, + insertText: string, + ): OffsetEdit { + return OffsetEdit.replace(OffsetRange.emptyAt(offset), insertText); + } + + constructor( + public readonly edits: readonly SingleOffsetEdit[], + ) { + let lastEndEx = -1; + for (const edit of edits) { + if (!(edit.replaceRange.start >= lastEndEx)) { + throw new BugIndicatingError(`Edits must be disjoint and sorted. Found ${edit} after ${lastEndEx}`); + } + lastEndEx = edit.replaceRange.endExclusive; + } + } + + normalize(): OffsetEdit { + const edits: SingleOffsetEdit[] = []; + let lastEdit: SingleOffsetEdit | undefined; + for (const edit of this.edits) { + if (edit.newText.length === 0 && edit.replaceRange.length === 0) { + continue; + } + if (lastEdit && lastEdit.replaceRange.endExclusive === edit.replaceRange.start) { + lastEdit = new SingleOffsetEdit( + lastEdit.replaceRange.join(edit.replaceRange), + lastEdit.newText + edit.newText, + ); + } else { + if (lastEdit) { + edits.push(lastEdit); + } + lastEdit = edit; + } + } + if (lastEdit) { + edits.push(lastEdit); + } + return new OffsetEdit(edits); + } + + toString() { + const edits = this.edits.map(e => e.toString()).join(', '); + return `[${edits}]`; + } + + apply(str: string): string { + const resultText: string[] = []; + let pos = 0; + for (const edit of this.edits) { + resultText.push(str.substring(pos, edit.replaceRange.start)); + resultText.push(edit.newText); + pos = edit.replaceRange.endExclusive; + } + resultText.push(str.substring(pos)); + return resultText.join(''); + } + + compose(other: OffsetEdit): OffsetEdit { + return joinEdits(this, other); + } + + /** + * Creates an edit that reverts this edit. + */ + inverse(originalStr: string): OffsetEdit { + const edits: SingleOffsetEdit[] = []; + let offset = 0; + for (const e of this.edits) { + edits.push(new SingleOffsetEdit( + OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newText.length), + originalStr.substring(e.replaceRange.start, e.replaceRange.endExclusive), + )); + offset += e.newText.length - e.replaceRange.length; + } + return new OffsetEdit(edits); + } + + getNewTextRanges(): OffsetRange[] { + const ranges: OffsetRange[] = []; + let offset = 0; + for (const e of this.edits) { + ranges.push(OffsetRange.ofStartAndLength(e.replaceRange.start + offset, e.newText.length),); + offset += e.newText.length - e.replaceRange.length; + } + return ranges; + } + + get isEmpty(): boolean { + return this.edits.length === 0; + } + + /** + * Consider `t1 := text o base` and `t2 := text o this`. + * We are interested in `tm := tryMerge(t1, t2, base: text)`. + * For that, we compute `tm' := t1 o base o this.rebase(base)` + * such that `tm' === tm`. + */ + tryRebase(base: OffsetEdit): OffsetEdit; + tryRebase(base: OffsetEdit, noOverlap: true): OffsetEdit | undefined; + tryRebase(base: OffsetEdit, noOverlap?: true): OffsetEdit | undefined { + const newEdits: SingleOffsetEdit[] = []; + + let baseIdx = 0; + let ourIdx = 0; + let offset = 0; + + while (ourIdx < this.edits.length || baseIdx < base.edits.length) { + // take the edit that starts first + const baseEdit = base.edits[baseIdx]; + const ourEdit = this.edits[ourIdx]; + + if (!ourEdit) { + // We processed all our edits + break; + } else if (!baseEdit) { + // no more edits from base + newEdits.push(new SingleOffsetEdit( + ourEdit.replaceRange.delta(offset), + ourEdit.newText, + )); + ourIdx++; + } else if (ourEdit.replaceRange.intersectsOrTouches(baseEdit.replaceRange)) { + ourIdx++; // Don't take our edit, as it is conflicting -> skip + if (noOverlap) { + return undefined; + } + } else if (ourEdit.replaceRange.start < baseEdit.replaceRange.start) { + // Our edit starts first + newEdits.push(new SingleOffsetEdit( + ourEdit.replaceRange.delta(offset), + ourEdit.newText, + )); + ourIdx++; + } else { + baseIdx++; + offset += baseEdit.newText.length - baseEdit.replaceRange.length; + } + } + + return new OffsetEdit(newEdits); + } + + applyToOffset(originalOffset: number): number { + let accumulatedDelta = 0; + for (const edit of this.edits) { + if (edit.replaceRange.start <= originalOffset) { + if (originalOffset < edit.replaceRange.endExclusive) { + // the offset is in the replaced range + return edit.replaceRange.start + accumulatedDelta; + } + accumulatedDelta += edit.newText.length - edit.replaceRange.length; + } else { + break; + } + } + return originalOffset + accumulatedDelta; + } + + applyToOffsetRange(originalRange: OffsetRange): OffsetRange { + return new OffsetRange( + this.applyToOffset(originalRange.start), + this.applyToOffset(originalRange.endExclusive) + ); + } + + applyInverseToOffset(postEditsOffset: number): number { + let accumulatedDelta = 0; + for (const edit of this.edits) { + const editLength = edit.newText.length; + if (edit.replaceRange.start <= postEditsOffset - accumulatedDelta) { + if (postEditsOffset - accumulatedDelta < edit.replaceRange.start + editLength) { + // the offset is in the replaced range + return edit.replaceRange.start; + } + accumulatedDelta += editLength - edit.replaceRange.length; + } else { + break; + } + } + return postEditsOffset - accumulatedDelta; + } + + equals(other: OffsetEdit): boolean { + if (this.edits.length !== other.edits.length) { + return false; + } + for (let i = 0; i < this.edits.length; i++) { + if (!this.edits[i].equals(other.edits[i])) { + return false; + } + + } + return true; + } +} + +export type IOffsetEdit = ISingleOffsetEdit[]; + +export interface ISingleOffsetEdit { + txt: string; + pos: number; + len: number; +} + +export class SingleOffsetEdit { + public static fromJson(data: ISingleOffsetEdit): SingleOffsetEdit { + return new SingleOffsetEdit(OffsetRange.ofStartAndLength(data.pos, data.len), data.txt); + } + + public static insert(offset: number, text: string): SingleOffsetEdit { + return new SingleOffsetEdit(OffsetRange.emptyAt(offset), text); + } + + public static replace(range: OffsetRange, text: string): SingleOffsetEdit { + return new SingleOffsetEdit(range, text); + } + + constructor( + public readonly replaceRange: OffsetRange, + public readonly newText: string, + ) { } + + toString(): string { + return `${this.replaceRange} -> "${this.newText}"`; + } + + get isEmpty() { + return this.newText.length === 0 && this.replaceRange.length === 0; + } + + apply(str: string): string { + return str.substring(0, this.replaceRange.start) + this.newText + str.substring(this.replaceRange.endExclusive); + } + + getRangeAfterApply(): OffsetRange { + return new OffsetRange(this.replaceRange.start, this.replaceRange.start + this.newText.length); + } + + equals(other: SingleOffsetEdit): boolean { + return this.replaceRange.equals(other.replaceRange) && this.newText === other.newText; + } +} + +/** + * Invariant: + * ``` + * edits2.apply(edits1.apply(str)) = join(edits1, edits2).apply(str) + * ``` + */ +function joinEdits(edits1: OffsetEdit, edits2: OffsetEdit): OffsetEdit { + edits1 = edits1.normalize(); + edits2 = edits2.normalize(); + + if (edits1.isEmpty) { return edits2; } + if (edits2.isEmpty) { return edits1; } + + const edit1Queue = [...edits1.edits]; + const result: SingleOffsetEdit[] = []; + + let edit1ToEdit2 = 0; + + for (const edit2 of edits2.edits) { + // Copy over edit1 unmodified until it touches edit2. + while (true) { + const edit1 = edit1Queue[0]!; + if (!edit1 || edit1.replaceRange.start + edit1ToEdit2 + edit1.newText.length >= edit2.replaceRange.start) { + break; + } + edit1Queue.shift(); + + result.push(edit1); + edit1ToEdit2 += edit1.newText.length - edit1.replaceRange.length; + } + + const firstEdit1ToEdit2 = edit1ToEdit2; + let firstIntersecting: SingleOffsetEdit | undefined; // or touching + let lastIntersecting: SingleOffsetEdit | undefined; // or touching + + while (true) { + const edit1 = edit1Queue[0]; + if (!edit1 || edit1.replaceRange.start + edit1ToEdit2 > edit2.replaceRange.endExclusive) { + break; + } + // else we intersect, because the new end of edit1 is after or equal to our start + + if (!firstIntersecting) { + firstIntersecting = edit1; + } + lastIntersecting = edit1; + edit1Queue.shift(); + + edit1ToEdit2 += edit1.newText.length - edit1.replaceRange.length; + } + + if (!firstIntersecting) { + result.push(new SingleOffsetEdit(edit2.replaceRange.delta(-edit1ToEdit2), edit2.newText)); + } else { + let prefix = ''; + const prefixLength = edit2.replaceRange.start - (firstIntersecting.replaceRange.start + firstEdit1ToEdit2); + if (prefixLength > 0) { + prefix = firstIntersecting.newText.slice(0, prefixLength); + } + const suffixLength = (lastIntersecting!.replaceRange.endExclusive + edit1ToEdit2) - edit2.replaceRange.endExclusive; + if (suffixLength > 0) { + const e = new SingleOffsetEdit(OffsetRange.ofStartAndLength(lastIntersecting!.replaceRange.endExclusive, 0), lastIntersecting!.newText.slice(-suffixLength)); + edit1Queue.unshift(e); + edit1ToEdit2 -= e.newText.length - e.replaceRange.length; + } + const newText = prefix + edit2.newText; + + const newReplaceRange = new OffsetRange( + Math.min(firstIntersecting.replaceRange.start, edit2.replaceRange.start - firstEdit1ToEdit2), + edit2.replaceRange.endExclusive - edit1ToEdit2 + ); + result.push(new SingleOffsetEdit(newReplaceRange, newText)); + } + } + + while (true) { + const item = edit1Queue.shift(); + if (!item) { break; } + result.push(item); + } + + return new OffsetEdit(result).normalize(); +} + +export function applyEditsToRanges(sortedRanges: OffsetRange[], edits: OffsetEdit): OffsetRange[] { + sortedRanges = sortedRanges.slice(); + + // treat edits as deletion of the replace range and then as insertion that extends the first range + const result: OffsetRange[] = []; + + let offset = 0; + + for (const e of edits.edits) { + while (true) { + // ranges before the current edit + const r = sortedRanges[0]; + if (!r || r.endExclusive >= e.replaceRange.start) { + break; + } + sortedRanges.shift(); + result.push(r.delta(offset)); + } + + const intersecting: OffsetRange[] = []; + while (true) { + const r = sortedRanges[0]; + if (!r || !r.intersectsOrTouches(e.replaceRange)) { + break; + } + sortedRanges.shift(); + intersecting.push(r); + } + + for (let i = intersecting.length - 1; i >= 0; i--) { + let r = intersecting[i]; + + const overlap = r.intersect(e.replaceRange)!.length; + r = r.deltaEnd(-overlap + (i === 0 ? e.newText.length : 0)); + + const rangeAheadOfReplaceRange = r.start - e.replaceRange.start; + if (rangeAheadOfReplaceRange > 0) { + r = r.delta(-rangeAheadOfReplaceRange); + } + + if (i !== 0) { + r = r.delta(e.newText.length); + } + + // We already took our offset into account. + // Because we add r back to the queue (which then adds offset again), + // we have to remove it here. + r = r.delta(-(e.newText.length - e.replaceRange.length)); + + sortedRanges.unshift(r); + } + + offset += e.newText.length - e.replaceRange.length; + } + + while (true) { + const r = sortedRanges[0]; + if (!r) { + break; + } + sortedRanges.shift(); + result.push(r.delta(offset)); + } + + return result; +} diff --git a/src/vs/editor/common/language/core/offsetRange.ts b/src/vs/editor/common/language/core/offsetRange.ts new file mode 100644 index 00000000000..9e2bb57647c --- /dev/null +++ b/src/vs/editor/common/language/core/offsetRange.ts @@ -0,0 +1,258 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BugIndicatingError } from '../../../../base/common/errors.js'; + +export interface IOffsetRange { + readonly start: number; + readonly endExclusive: number; +} + +/** + * A range of offsets (0-based). +*/ +export class OffsetRange implements IOffsetRange { + public static fromTo(start: number, endExclusive: number): OffsetRange { + return new OffsetRange(start, endExclusive); + } + + public static addRange(range: OffsetRange, sortedRanges: OffsetRange[]): void { + let i = 0; + while (i < sortedRanges.length && sortedRanges[i].endExclusive < range.start) { + i++; + } + let j = i; + while (j < sortedRanges.length && sortedRanges[j].start <= range.endExclusive) { + j++; + } + if (i === j) { + sortedRanges.splice(i, 0, range); + } else { + const start = Math.min(range.start, sortedRanges[i].start); + const end = Math.max(range.endExclusive, sortedRanges[j - 1].endExclusive); + sortedRanges.splice(i, j - i, new OffsetRange(start, end)); + } + } + + public static tryCreate(start: number, endExclusive: number): OffsetRange | undefined { + if (start > endExclusive) { + return undefined; + } + return new OffsetRange(start, endExclusive); + } + + public static ofLength(length: number): OffsetRange { + return new OffsetRange(0, length); + } + + public static ofStartAndLength(start: number, length: number): OffsetRange { + return new OffsetRange(start, start + length); + } + + public static emptyAt(offset: number): OffsetRange { + return new OffsetRange(offset, offset); + } + + constructor(public readonly start: number, public readonly endExclusive: number) { + if (start > endExclusive) { + throw new BugIndicatingError(`Invalid range: ${this.toString()}`); + } + } + + get isEmpty(): boolean { + return this.start === this.endExclusive; + } + + public delta(offset: number): OffsetRange { + return new OffsetRange(this.start + offset, this.endExclusive + offset); + } + + public deltaStart(offset: number): OffsetRange { + return new OffsetRange(this.start + offset, this.endExclusive); + } + + public deltaEnd(offset: number): OffsetRange { + return new OffsetRange(this.start, this.endExclusive + offset); + } + + public get length(): number { + return this.endExclusive - this.start; + } + + public toString() { + return `[${this.start}, ${this.endExclusive})`; + } + + public equals(other: OffsetRange): boolean { + return this.start === other.start && this.endExclusive === other.endExclusive; + } + + public containsRange(other: OffsetRange): boolean { + return this.start <= other.start && other.endExclusive <= this.endExclusive; + } + + public contains(offset: number): boolean { + return this.start <= offset && offset < this.endExclusive; + } + + /** + * for all numbers n: range1.contains(n) or range2.contains(n) => range1.join(range2).contains(n) + * The joined range is the smallest range that contains both ranges. + */ + public join(other: OffsetRange): OffsetRange { + return new OffsetRange(Math.min(this.start, other.start), Math.max(this.endExclusive, other.endExclusive)); + } + + /** + * for all numbers n: range1.contains(n) and range2.contains(n) <=> range1.intersect(range2).contains(n) + * + * The resulting range is empty if the ranges do not intersect, but touch. + * If the ranges don't even touch, the result is undefined. + */ + public intersect(other: OffsetRange): OffsetRange | undefined { + const start = Math.max(this.start, other.start); + const end = Math.min(this.endExclusive, other.endExclusive); + if (start <= end) { + return new OffsetRange(start, end); + } + return undefined; + } + + public intersectionLength(range: OffsetRange): number { + const start = Math.max(this.start, range.start); + const end = Math.min(this.endExclusive, range.endExclusive); + return Math.max(0, end - start); + } + + public intersects(other: OffsetRange): boolean { + const start = Math.max(this.start, other.start); + const end = Math.min(this.endExclusive, other.endExclusive); + return start < end; + } + + public intersectsOrTouches(other: OffsetRange): boolean { + const start = Math.max(this.start, other.start); + const end = Math.min(this.endExclusive, other.endExclusive); + return start <= end; + } + + public isBefore(other: OffsetRange): boolean { + return this.endExclusive <= other.start; + } + + public isAfter(other: OffsetRange): boolean { + return this.start >= other.endExclusive; + } + + public slice(arr: T[]): T[] { + return arr.slice(this.start, this.endExclusive); + } + + public substring(str: string): string { + return str.substring(this.start, this.endExclusive); + } + + /** + * Returns the given value if it is contained in this instance, otherwise the closest value that is contained. + * The range must not be empty. + */ + public clip(value: number): number { + if (this.isEmpty) { + throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); + } + return Math.max(this.start, Math.min(this.endExclusive - 1, value)); + } + + /** + * Returns `r := value + k * length` such that `r` is contained in this range. + * The range must not be empty. + * + * E.g. `[5, 10).clipCyclic(10) === 5`, `[5, 10).clipCyclic(11) === 6` and `[5, 10).clipCyclic(4) === 9`. + */ + public clipCyclic(value: number): number { + if (this.isEmpty) { + throw new BugIndicatingError(`Invalid clipping range: ${this.toString()}`); + } + if (value < this.start) { + return this.endExclusive - ((this.start - value) % this.length); + } + if (value >= this.endExclusive) { + return this.start + ((value - this.start) % this.length); + } + return value; + } + + public map(f: (offset: number) => T): T[] { + const result: T[] = []; + for (let i = this.start; i < this.endExclusive; i++) { + result.push(f(i)); + } + return result; + } + + public forEach(f: (offset: number) => void): void { + for (let i = this.start; i < this.endExclusive; i++) { + f(i); + } + } +} + +export class OffsetRangeSet { + private readonly _sortedRanges: OffsetRange[] = []; + + public addRange(range: OffsetRange): void { + let i = 0; + while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive < range.start) { + i++; + } + let j = i; + while (j < this._sortedRanges.length && this._sortedRanges[j].start <= range.endExclusive) { + j++; + } + if (i === j) { + this._sortedRanges.splice(i, 0, range); + } else { + const start = Math.min(range.start, this._sortedRanges[i].start); + const end = Math.max(range.endExclusive, this._sortedRanges[j - 1].endExclusive); + this._sortedRanges.splice(i, j - i, new OffsetRange(start, end)); + } + } + + public toString(): string { + return this._sortedRanges.map(r => r.toString()).join(', '); + } + + /** + * Returns of there is a value that is contained in this instance and the given range. + */ + public intersectsStrict(other: OffsetRange): boolean { + // TODO use binary search + let i = 0; + while (i < this._sortedRanges.length && this._sortedRanges[i].endExclusive <= other.start) { + i++; + } + return i < this._sortedRanges.length && this._sortedRanges[i].start < other.endExclusive; + } + + public intersectWithRange(other: OffsetRange): OffsetRangeSet { + // TODO use binary search + slice + const result = new OffsetRangeSet(); + for (const range of this._sortedRanges) { + const intersection = range.intersect(other); + if (intersection) { + result.addRange(intersection); + } + } + return result; + } + + public intersectWithRangeLength(other: OffsetRange): number { + return this.intersectWithRange(other).length; + } + + public get length(): number { + return this._sortedRanges.reduce((prev, cur) => prev + cur.length, 0); + } +} diff --git a/src/vs/editor/common/language/core/position.ts b/src/vs/editor/common/language/core/position.ts new file mode 100644 index 00000000000..e3033e953f9 --- /dev/null +++ b/src/vs/editor/common/language/core/position.ts @@ -0,0 +1,184 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * A position in the editor. This interface is suitable for serialization. + */ +export interface IPosition { + /** + * line number (starts at 1) + */ + readonly lineNumber: number; + /** + * column (the first character in a line is between column 1 and column 2) + */ + readonly column: number; +} + +/** + * A position in the editor. + */ +export class Position { + /** + * line number (starts at 1) + */ + public readonly lineNumber: number; + /** + * column (the first character in a line is between column 1 and column 2) + */ + public readonly column: number; + + constructor(lineNumber: number, column: number) { + this.lineNumber = lineNumber; + this.column = column; + } + + /** + * Create a new position from this position. + * + * @param newLineNumber new line number + * @param newColumn new column + */ + with(newLineNumber: number = this.lineNumber, newColumn: number = this.column): Position { + if (newLineNumber === this.lineNumber && newColumn === this.column) { + return this; + } else { + return new Position(newLineNumber, newColumn); + } + } + + /** + * Derive a new position from this position. + * + * @param deltaLineNumber line number delta + * @param deltaColumn column delta + */ + delta(deltaLineNumber: number = 0, deltaColumn: number = 0): Position { + return this.with(Math.max(1, this.lineNumber + deltaLineNumber), Math.max(1, this.column + deltaColumn)); + } + + /** + * Test if this position equals other position + */ + public equals(other: IPosition): boolean { + return Position.equals(this, other); + } + + /** + * Test if position `a` equals position `b` + */ + public static equals(a: IPosition | null, b: IPosition | null): boolean { + if (!a && !b) { + return true; + } + return ( + !!a && + !!b && + a.lineNumber === b.lineNumber && + a.column === b.column + ); + } + + /** + * Test if this position is before other position. + * If the two positions are equal, the result will be false. + */ + public isBefore(other: IPosition): boolean { + return Position.isBefore(this, other); + } + + /** + * Test if position `a` is before position `b`. + * If the two positions are equal, the result will be false. + */ + public static isBefore(a: IPosition, b: IPosition): boolean { + if (a.lineNumber < b.lineNumber) { + return true; + } + if (b.lineNumber < a.lineNumber) { + return false; + } + return a.column < b.column; + } + + /** + * Test if this position is before other position. + * If the two positions are equal, the result will be true. + */ + public isBeforeOrEqual(other: IPosition): boolean { + return Position.isBeforeOrEqual(this, other); + } + + /** + * Test if position `a` is before position `b`. + * If the two positions are equal, the result will be true. + */ + public static isBeforeOrEqual(a: IPosition, b: IPosition): boolean { + if (a.lineNumber < b.lineNumber) { + return true; + } + if (b.lineNumber < a.lineNumber) { + return false; + } + return a.column <= b.column; + } + + /** + * A function that compares positions, useful for sorting + */ + public static compare(a: IPosition, b: IPosition): number { + const aLineNumber = a.lineNumber | 0; + const bLineNumber = b.lineNumber | 0; + + if (aLineNumber === bLineNumber) { + const aColumn = a.column | 0; + const bColumn = b.column | 0; + return aColumn - bColumn; + } + + return aLineNumber - bLineNumber; + } + + /** + * Clone this position. + */ + public clone(): Position { + return new Position(this.lineNumber, this.column); + } + + /** + * Convert to a human-readable representation. + */ + public toString(): string { + return '(' + this.lineNumber + ',' + this.column + ')'; + } + + // --- + + /** + * Create a `Position` from an `IPosition`. + */ + public static lift(pos: IPosition): Position { + return new Position(pos.lineNumber, pos.column); + } + + /** + * Test if `obj` is an `IPosition`. + */ + public static isIPosition(obj: any): obj is IPosition { + return ( + obj + && (typeof obj.lineNumber === 'number') + && (typeof obj.column === 'number') + ); + } + + public toJSON(): IPosition { + return { + lineNumber: this.lineNumber, + column: this.column + }; + } +} diff --git a/src/vs/editor/common/language/core/positionToOffset.ts b/src/vs/editor/common/language/core/positionToOffset.ts new file mode 100644 index 00000000000..d38bd0f811c --- /dev/null +++ b/src/vs/editor/common/language/core/positionToOffset.ts @@ -0,0 +1,91 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { findLastIdxMonotonous } from '../../../../base/common/arraysFind.js'; +import { OffsetRange } from './offsetRange.js'; +import { Position } from './position.js'; +import { Range } from './range.js'; +import { TextLength } from './textLength.js'; + +export class PositionOffsetTransformer { + private readonly lineStartOffsetByLineIdx: number[]; + private readonly lineEndOffsetByLineIdx: number[]; + + constructor(public readonly text: string) { + this.lineStartOffsetByLineIdx = []; + this.lineEndOffsetByLineIdx = []; + + this.lineStartOffsetByLineIdx.push(0); + for (let i = 0; i < text.length; i++) { + if (text.charAt(i) === '\n') { + this.lineStartOffsetByLineIdx.push(i + 1); + if (i > 0 && text.charAt(i - 1) === '\r') { + this.lineEndOffsetByLineIdx.push(i - 1); + } else { + this.lineEndOffsetByLineIdx.push(i); + } + } + } + this.lineEndOffsetByLineIdx.push(text.length); + } + + getOffset(position: Position): number { + const valPos = this._validatePosition(position); + return this.lineStartOffsetByLineIdx[valPos.lineNumber - 1] + valPos.column - 1; + } + + private _validatePosition(position: Position): Position { + if (position.lineNumber < 1) { + return new Position(1, 1); + } + const lineCount = this.textLength.lineCount + 1; + if (position.lineNumber > lineCount) { + const lineLength = this.getLineLength(lineCount); + return new Position(lineCount, lineLength + 1); + } + if (position.column < 1) { + return new Position(position.lineNumber, 1); + } + const lineLength = this.getLineLength(position.lineNumber); + if (position.column - 1 > lineLength) { + return new Position(position.lineNumber, lineLength + 1); + } + return position; + } + + getOffsetRange(range: Range): OffsetRange { + return new OffsetRange( + this.getOffset(range.getStartPosition()), + this.getOffset(range.getEndPosition()) + ); + } + + getPosition(offset: number): Position { + const idx = findLastIdxMonotonous(this.lineStartOffsetByLineIdx, i => i <= offset); + const lineNumber = idx + 1; + const column = offset - this.lineStartOffsetByLineIdx[idx] + 1; + return new Position(lineNumber, column); + } + + getRange(offsetRange: OffsetRange): Range { + return Range.fromPositions( + this.getPosition(offsetRange.start), + this.getPosition(offsetRange.endExclusive) + ); + } + + getTextLength(offsetRange: OffsetRange): TextLength { + return TextLength.ofRange(this.getRange(offsetRange)); + } + + get textLength(): TextLength { + const lineIdx = this.lineStartOffsetByLineIdx.length - 1; + return new TextLength(lineIdx, this.text.length - this.lineStartOffsetByLineIdx[lineIdx]); + } + + getLineLength(lineNumber: number): number { + return this.lineEndOffsetByLineIdx[lineNumber - 1] - this.lineStartOffsetByLineIdx[lineNumber - 1]; + } +} diff --git a/src/vs/editor/common/language/core/range.ts b/src/vs/editor/common/language/core/range.ts new file mode 100644 index 00000000000..81658a4aeb5 --- /dev/null +++ b/src/vs/editor/common/language/core/range.ts @@ -0,0 +1,517 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPosition, Position } from './position.js'; + +/** + * A range in the editor. This interface is suitable for serialization. + */ +export interface IRange { + /** + * Line number on which the range starts (starts at 1). + */ + readonly startLineNumber: number; + /** + * Column on which the range starts in line `startLineNumber` (starts at 1). + */ + readonly startColumn: number; + /** + * Line number on which the range ends. + */ + readonly endLineNumber: number; + /** + * Column on which the range ends in line `endLineNumber`. + */ + readonly endColumn: number; +} + +/** + * A range in the editor. (startLineNumber,startColumn) is <= (endLineNumber,endColumn) + */ +export class Range { + + /** + * Line number on which the range starts (starts at 1). + */ + public readonly startLineNumber: number; + /** + * Column on which the range starts in line `startLineNumber` (starts at 1). + */ + public readonly startColumn: number; + /** + * Line number on which the range ends. + */ + public readonly endLineNumber: number; + /** + * Column on which the range ends in line `endLineNumber`. + */ + public readonly endColumn: number; + + constructor(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number) { + if ((startLineNumber > endLineNumber) || (startLineNumber === endLineNumber && startColumn > endColumn)) { + this.startLineNumber = endLineNumber; + this.startColumn = endColumn; + this.endLineNumber = startLineNumber; + this.endColumn = startColumn; + } else { + this.startLineNumber = startLineNumber; + this.startColumn = startColumn; + this.endLineNumber = endLineNumber; + this.endColumn = endColumn; + } + } + + /** + * Test if this range is empty. + */ + public isEmpty(): boolean { + return Range.isEmpty(this); + } + + /** + * Test if `range` is empty. + */ + public static isEmpty(range: IRange): boolean { + return (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn); + } + + /** + * Test if position is in this range. If the position is at the edges, will return true. + */ + public containsPosition(position: IPosition): boolean { + return Range.containsPosition(this, position); + } + + /** + * Test if `position` is in `range`. If the position is at the edges, will return true. + */ + public static containsPosition(range: IRange, position: IPosition): boolean { + if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { + return false; + } + if (position.lineNumber === range.startLineNumber && position.column < range.startColumn) { + return false; + } + if (position.lineNumber === range.endLineNumber && position.column > range.endColumn) { + return false; + } + return true; + } + + /** + * Test if `position` is in `range`. If the position is at the edges, will return false. + * @internal + */ + public static strictContainsPosition(range: IRange, position: IPosition): boolean { + if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { + return false; + } + if (position.lineNumber === range.startLineNumber && position.column <= range.startColumn) { + return false; + } + if (position.lineNumber === range.endLineNumber && position.column >= range.endColumn) { + return false; + } + return true; + } + + /** + * Test if range is in this range. If the range is equal to this range, will return true. + */ + public containsRange(range: IRange): boolean { + return Range.containsRange(this, range); + } + + /** + * Test if `otherRange` is in `range`. If the ranges are equal, will return true. + */ + public static containsRange(range: IRange, otherRange: IRange): boolean { + if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) { + return false; + } + if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) { + return false; + } + if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn < range.startColumn) { + return false; + } + if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn > range.endColumn) { + return false; + } + return true; + } + + /** + * Test if `range` is strictly in this range. `range` must start after and end before this range for the result to be true. + */ + public strictContainsRange(range: IRange): boolean { + return Range.strictContainsRange(this, range); + } + + /** + * Test if `otherRange` is strictly in `range` (must start after, and end before). If the ranges are equal, will return false. + */ + public static strictContainsRange(range: IRange, otherRange: IRange): boolean { + if (otherRange.startLineNumber < range.startLineNumber || otherRange.endLineNumber < range.startLineNumber) { + return false; + } + if (otherRange.startLineNumber > range.endLineNumber || otherRange.endLineNumber > range.endLineNumber) { + return false; + } + if (otherRange.startLineNumber === range.startLineNumber && otherRange.startColumn <= range.startColumn) { + return false; + } + if (otherRange.endLineNumber === range.endLineNumber && otherRange.endColumn >= range.endColumn) { + return false; + } + return true; + } + + /** + * A reunion of the two ranges. + * The smallest position will be used as the start point, and the largest one as the end point. + */ + public plusRange(range: IRange): Range { + return Range.plusRange(this, range); + } + + /** + * A reunion of the two ranges. + * The smallest position will be used as the start point, and the largest one as the end point. + */ + public static plusRange(a: IRange, b: IRange): Range { + let startLineNumber: number; + let startColumn: number; + let endLineNumber: number; + let endColumn: number; + + if (b.startLineNumber < a.startLineNumber) { + startLineNumber = b.startLineNumber; + startColumn = b.startColumn; + } else if (b.startLineNumber === a.startLineNumber) { + startLineNumber = b.startLineNumber; + startColumn = Math.min(b.startColumn, a.startColumn); + } else { + startLineNumber = a.startLineNumber; + startColumn = a.startColumn; + } + + if (b.endLineNumber > a.endLineNumber) { + endLineNumber = b.endLineNumber; + endColumn = b.endColumn; + } else if (b.endLineNumber === a.endLineNumber) { + endLineNumber = b.endLineNumber; + endColumn = Math.max(b.endColumn, a.endColumn); + } else { + endLineNumber = a.endLineNumber; + endColumn = a.endColumn; + } + + return new Range(startLineNumber, startColumn, endLineNumber, endColumn); + } + + /** + * A intersection of the two ranges. + */ + public intersectRanges(range: IRange): Range | null { + return Range.intersectRanges(this, range); + } + + /** + * A intersection of the two ranges. + */ + public static intersectRanges(a: IRange, b: IRange): Range | null { + let resultStartLineNumber = a.startLineNumber; + let resultStartColumn = a.startColumn; + let resultEndLineNumber = a.endLineNumber; + let resultEndColumn = a.endColumn; + const otherStartLineNumber = b.startLineNumber; + const otherStartColumn = b.startColumn; + const otherEndLineNumber = b.endLineNumber; + const otherEndColumn = b.endColumn; + + if (resultStartLineNumber < otherStartLineNumber) { + resultStartLineNumber = otherStartLineNumber; + resultStartColumn = otherStartColumn; + } else if (resultStartLineNumber === otherStartLineNumber) { + resultStartColumn = Math.max(resultStartColumn, otherStartColumn); + } + + if (resultEndLineNumber > otherEndLineNumber) { + resultEndLineNumber = otherEndLineNumber; + resultEndColumn = otherEndColumn; + } else if (resultEndLineNumber === otherEndLineNumber) { + resultEndColumn = Math.min(resultEndColumn, otherEndColumn); + } + + // Check if selection is now empty + if (resultStartLineNumber > resultEndLineNumber) { + return null; + } + if (resultStartLineNumber === resultEndLineNumber && resultStartColumn > resultEndColumn) { + return null; + } + return new Range(resultStartLineNumber, resultStartColumn, resultEndLineNumber, resultEndColumn); + } + + /** + * Test if this range equals other. + */ + public equalsRange(other: IRange | null | undefined): boolean { + return Range.equalsRange(this, other); + } + + /** + * Test if range `a` equals `b`. + */ + public static equalsRange(a: IRange | null | undefined, b: IRange | null | undefined): boolean { + if (!a && !b) { + return true; + } + return ( + !!a && + !!b && + a.startLineNumber === b.startLineNumber && + a.startColumn === b.startColumn && + a.endLineNumber === b.endLineNumber && + a.endColumn === b.endColumn + ); + } + + /** + * Return the end position (which will be after or equal to the start position) + */ + public getEndPosition(): Position { + return Range.getEndPosition(this); + } + + /** + * Return the end position (which will be after or equal to the start position) + */ + public static getEndPosition(range: IRange): Position { + return new Position(range.endLineNumber, range.endColumn); + } + + /** + * Return the start position (which will be before or equal to the end position) + */ + public getStartPosition(): Position { + return Range.getStartPosition(this); + } + + /** + * Return the start position (which will be before or equal to the end position) + */ + public static getStartPosition(range: IRange): Position { + return new Position(range.startLineNumber, range.startColumn); + } + + /** + * Transform to a user presentable string representation. + */ + public toString(): string { + return '[' + this.startLineNumber + ',' + this.startColumn + ' -> ' + this.endLineNumber + ',' + this.endColumn + ']'; + } + + /** + * Create a new range using this range's start position, and using endLineNumber and endColumn as the end position. + */ + public setEndPosition(endLineNumber: number, endColumn: number): Range { + return new Range(this.startLineNumber, this.startColumn, endLineNumber, endColumn); + } + + /** + * Create a new range using this range's end position, and using startLineNumber and startColumn as the start position. + */ + public setStartPosition(startLineNumber: number, startColumn: number): Range { + return new Range(startLineNumber, startColumn, this.endLineNumber, this.endColumn); + } + + /** + * Create a new empty range using this range's start position. + */ + public collapseToStart(): Range { + return Range.collapseToStart(this); + } + + /** + * Create a new empty range using this range's start position. + */ + public static collapseToStart(range: IRange): Range { + return new Range(range.startLineNumber, range.startColumn, range.startLineNumber, range.startColumn); + } + + /** + * Create a new empty range using this range's end position. + */ + public collapseToEnd(): Range { + return Range.collapseToEnd(this); + } + + /** + * Create a new empty range using this range's end position. + */ + public static collapseToEnd(range: IRange): Range { + return new Range(range.endLineNumber, range.endColumn, range.endLineNumber, range.endColumn); + } + + /** + * Moves the range by the given amount of lines. + */ + public delta(lineCount: number): Range { + return new Range(this.startLineNumber + lineCount, this.startColumn, this.endLineNumber + lineCount, this.endColumn); + } + + public isSingleLine(): boolean { + return this.startLineNumber === this.endLineNumber; + } + + // --- + + public static fromPositions(start: IPosition, end: IPosition = start): Range { + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); + } + + /** + * Create a `Range` from an `IRange`. + */ + public static lift(range: undefined | null): null; + public static lift(range: IRange): Range; + public static lift(range: IRange | undefined | null): Range | null; + public static lift(range: IRange | undefined | null): Range | null { + if (!range) { + return null; + } + return new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + } + + /** + * Test if `obj` is an `IRange`. + */ + public static isIRange(obj: any): obj is IRange { + return ( + obj + && (typeof obj.startLineNumber === 'number') + && (typeof obj.startColumn === 'number') + && (typeof obj.endLineNumber === 'number') + && (typeof obj.endColumn === 'number') + ); + } + + /** + * Test if the two ranges are touching in any way. + */ + public static areIntersectingOrTouching(a: IRange, b: IRange): boolean { + // Check if `a` is before `b` + if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn < b.startColumn)) { + return false; + } + + // Check if `b` is before `a` + if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn < a.startColumn)) { + return false; + } + + // These ranges must intersect + return true; + } + + /** + * Test if the two ranges are intersecting. If the ranges are touching it returns true. + */ + public static areIntersecting(a: IRange, b: IRange): boolean { + // Check if `a` is before `b` + if (a.endLineNumber < b.startLineNumber || (a.endLineNumber === b.startLineNumber && a.endColumn <= b.startColumn)) { + return false; + } + + // Check if `b` is before `a` + if (b.endLineNumber < a.startLineNumber || (b.endLineNumber === a.startLineNumber && b.endColumn <= a.startColumn)) { + return false; + } + + // These ranges must intersect + return true; + } + + /** + * Test if the two ranges are intersecting, but not touching at all. + */ + public static areOnlyIntersecting(a: IRange, b: IRange): boolean { + // Check if `a` is before `b` + if (a.endLineNumber < (b.startLineNumber - 1) || (a.endLineNumber === b.startLineNumber && a.endColumn < (b.startColumn - 1))) { + return false; + } + + // Check if `b` is before `a` + if (b.endLineNumber < (a.startLineNumber - 1) || (b.endLineNumber === a.startLineNumber && b.endColumn < (a.startColumn - 1))) { + return false; + } + + // These ranges must intersect + return true; + } + + /** + * A function that compares ranges, useful for sorting ranges + * It will first compare ranges on the startPosition and then on the endPosition + */ + public static compareRangesUsingStarts(a: IRange | null | undefined, b: IRange | null | undefined): number { + if (a && b) { + const aStartLineNumber = a.startLineNumber | 0; + const bStartLineNumber = b.startLineNumber | 0; + + if (aStartLineNumber === bStartLineNumber) { + const aStartColumn = a.startColumn | 0; + const bStartColumn = b.startColumn | 0; + + if (aStartColumn === bStartColumn) { + const aEndLineNumber = a.endLineNumber | 0; + const bEndLineNumber = b.endLineNumber | 0; + + if (aEndLineNumber === bEndLineNumber) { + const aEndColumn = a.endColumn | 0; + const bEndColumn = b.endColumn | 0; + return aEndColumn - bEndColumn; + } + return aEndLineNumber - bEndLineNumber; + } + return aStartColumn - bStartColumn; + } + return aStartLineNumber - bStartLineNumber; + } + const aExists = (a ? 1 : 0); + const bExists = (b ? 1 : 0); + return aExists - bExists; + } + + /** + * A function that compares ranges, useful for sorting ranges + * It will first compare ranges on the endPosition and then on the startPosition + */ + public static compareRangesUsingEnds(a: IRange, b: IRange): number { + if (a.endLineNumber === b.endLineNumber) { + if (a.endColumn === b.endColumn) { + if (a.startLineNumber === b.startLineNumber) { + return a.startColumn - b.startColumn; + } + return a.startLineNumber - b.startLineNumber; + } + return a.endColumn - b.endColumn; + } + return a.endLineNumber - b.endLineNumber; + } + + /** + * Test if the range spans multiple lines. + */ + public static spansMultipleLines(range: IRange): boolean { + return range.endLineNumber > range.startLineNumber; + } + + public toJSON(): IRange { + return this; + } +} diff --git a/src/vs/editor/common/language/core/rangeMapping.ts b/src/vs/editor/common/language/core/rangeMapping.ts new file mode 100644 index 00000000000..79563cf00cd --- /dev/null +++ b/src/vs/editor/common/language/core/rangeMapping.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { findLastMonotonous } from '../../../../base/common/arraysFind.js'; +import { Position } from './position.js'; +import { Range } from './range.js'; +import { TextLength } from './textLength.js'; + +/** + * Represents a list of mappings of ranges from one document to another. + */ +export class RangeMapping { + constructor(public readonly mappings: readonly SingleRangeMapping[]) { + } + + mapPosition(position: Position): PositionOrRange { + const mapping = findLastMonotonous(this.mappings, m => m.original.getStartPosition().isBeforeOrEqual(position)); + if (!mapping) { + return PositionOrRange.position(position); + } + if (mapping.original.containsPosition(position)) { + return PositionOrRange.range(mapping.modified); + } + const l = TextLength.betweenPositions(mapping.original.getEndPosition(), position); + return PositionOrRange.position(l.addToPosition(mapping.modified.getEndPosition())); + } + + mapRange(range: Range): Range { + const start = this.mapPosition(range.getStartPosition()); + const end = this.mapPosition(range.getEndPosition()); + return Range.fromPositions( + start.range?.getStartPosition() ?? start.position!, + end.range?.getEndPosition() ?? end.position!, + ); + } + + reverse(): RangeMapping { + return new RangeMapping(this.mappings.map(mapping => mapping.reverse())); + } +} + +export class SingleRangeMapping { + constructor( + public readonly original: Range, + public readonly modified: Range, + ) { + } + + reverse(): SingleRangeMapping { + return new SingleRangeMapping(this.modified, this.original); + } + + toString() { + return `${this.original.toString()} -> ${this.modified.toString()}`; + } +} + +export class PositionOrRange { + public static position(position: Position): PositionOrRange { + return new PositionOrRange(position, undefined); + } + + public static range(range: Range): PositionOrRange { + return new PositionOrRange(undefined, range); + } + + private constructor( + public readonly position: Position | undefined, + public readonly range: Range | undefined, + ) { } +} diff --git a/src/vs/editor/common/language/core/rgba.ts b/src/vs/editor/common/language/core/rgba.ts new file mode 100644 index 00000000000..006c7c7048e --- /dev/null +++ b/src/vs/editor/common/language/core/rgba.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * A very VM friendly rgba datastructure. + * Please don't touch unless you take a look at the IR. + */ +export class RGBA8 { + _rgba8Brand: void = undefined; + + static readonly Empty = new RGBA8(0, 0, 0, 0); + + /** + * Red: integer in [0-255] + */ + public readonly r: number; + /** + * Green: integer in [0-255] + */ + public readonly g: number; + /** + * Blue: integer in [0-255] + */ + public readonly b: number; + /** + * Alpha: integer in [0-255] + */ + public readonly a: number; + + constructor(r: number, g: number, b: number, a: number) { + this.r = RGBA8._clamp(r); + this.g = RGBA8._clamp(g); + this.b = RGBA8._clamp(b); + this.a = RGBA8._clamp(a); + } + + public equals(other: RGBA8): boolean { + return ( + this.r === other.r + && this.g === other.g + && this.b === other.b + && this.a === other.a + ); + } + + public static _clamp(c: number): number { + if (c < 0) { + return 0; + } + if (c > 255) { + return 255; + } + return c | 0; + } +} diff --git a/src/vs/editor/common/language/core/selection.ts b/src/vs/editor/common/language/core/selection.ts new file mode 100644 index 00000000000..c436bb61ef6 --- /dev/null +++ b/src/vs/editor/common/language/core/selection.ts @@ -0,0 +1,220 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPosition, Position } from './position.js'; +import { Range } from './range.js'; + +/** + * A selection in the editor. + * The selection is a range that has an orientation. + */ +export interface ISelection { + /** + * The line number on which the selection has started. + */ + readonly selectionStartLineNumber: number; + /** + * The column on `selectionStartLineNumber` where the selection has started. + */ + readonly selectionStartColumn: number; + /** + * The line number on which the selection has ended. + */ + readonly positionLineNumber: number; + /** + * The column on `positionLineNumber` where the selection has ended. + */ + readonly positionColumn: number; +} + +/** + * The direction of a selection. + */ +export const enum SelectionDirection { + /** + * The selection starts above where it ends. + */ + LTR, + /** + * The selection starts below where it ends. + */ + RTL +} + +/** + * A selection in the editor. + * The selection is a range that has an orientation. + */ +export class Selection extends Range { + /** + * The line number on which the selection has started. + */ + public readonly selectionStartLineNumber: number; + /** + * The column on `selectionStartLineNumber` where the selection has started. + */ + public readonly selectionStartColumn: number; + /** + * The line number on which the selection has ended. + */ + public readonly positionLineNumber: number; + /** + * The column on `positionLineNumber` where the selection has ended. + */ + public readonly positionColumn: number; + + constructor(selectionStartLineNumber: number, selectionStartColumn: number, positionLineNumber: number, positionColumn: number) { + super(selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn); + this.selectionStartLineNumber = selectionStartLineNumber; + this.selectionStartColumn = selectionStartColumn; + this.positionLineNumber = positionLineNumber; + this.positionColumn = positionColumn; + } + + /** + * Transform to a human-readable representation. + */ + public override toString(): string { + return '[' + this.selectionStartLineNumber + ',' + this.selectionStartColumn + ' -> ' + this.positionLineNumber + ',' + this.positionColumn + ']'; + } + + /** + * Test if equals other selection. + */ + public equalsSelection(other: ISelection): boolean { + return ( + Selection.selectionsEqual(this, other) + ); + } + + /** + * Test if the two selections are equal. + */ + public static selectionsEqual(a: ISelection, b: ISelection): boolean { + return ( + a.selectionStartLineNumber === b.selectionStartLineNumber && + a.selectionStartColumn === b.selectionStartColumn && + a.positionLineNumber === b.positionLineNumber && + a.positionColumn === b.positionColumn + ); + } + + /** + * Get directions (LTR or RTL). + */ + public getDirection(): SelectionDirection { + if (this.selectionStartLineNumber === this.startLineNumber && this.selectionStartColumn === this.startColumn) { + return SelectionDirection.LTR; + } + return SelectionDirection.RTL; + } + + /** + * Create a new selection with a different `positionLineNumber` and `positionColumn`. + */ + public override setEndPosition(endLineNumber: number, endColumn: number): Selection { + if (this.getDirection() === SelectionDirection.LTR) { + return new Selection(this.startLineNumber, this.startColumn, endLineNumber, endColumn); + } + return new Selection(endLineNumber, endColumn, this.startLineNumber, this.startColumn); + } + + /** + * Get the position at `positionLineNumber` and `positionColumn`. + */ + public getPosition(): Position { + return new Position(this.positionLineNumber, this.positionColumn); + } + + /** + * Get the position at the start of the selection. + */ + public getSelectionStart(): Position { + return new Position(this.selectionStartLineNumber, this.selectionStartColumn); + } + + /** + * Create a new selection with a different `selectionStartLineNumber` and `selectionStartColumn`. + */ + public override setStartPosition(startLineNumber: number, startColumn: number): Selection { + if (this.getDirection() === SelectionDirection.LTR) { + return new Selection(startLineNumber, startColumn, this.endLineNumber, this.endColumn); + } + return new Selection(this.endLineNumber, this.endColumn, startLineNumber, startColumn); + } + + // ---- + + /** + * Create a `Selection` from one or two positions + */ + public static override fromPositions(start: IPosition, end: IPosition = start): Selection { + return new Selection(start.lineNumber, start.column, end.lineNumber, end.column); + } + + /** + * Creates a `Selection` from a range, given a direction. + */ + public static fromRange(range: Range, direction: SelectionDirection): Selection { + if (direction === SelectionDirection.LTR) { + return new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn); + } else { + return new Selection(range.endLineNumber, range.endColumn, range.startLineNumber, range.startColumn); + } + } + + /** + * Create a `Selection` from an `ISelection`. + */ + public static liftSelection(sel: ISelection): Selection { + return new Selection(sel.selectionStartLineNumber, sel.selectionStartColumn, sel.positionLineNumber, sel.positionColumn); + } + + /** + * `a` equals `b`. + */ + public static selectionsArrEqual(a: ISelection[], b: ISelection[]): boolean { + if (a && !b || !a && b) { + return false; + } + if (!a && !b) { + return true; + } + if (a.length !== b.length) { + return false; + } + for (let i = 0, len = a.length; i < len; i++) { + if (!this.selectionsEqual(a[i], b[i])) { + return false; + } + } + return true; + } + + /** + * Test if `obj` is an `ISelection`. + */ + public static isISelection(obj: any): obj is ISelection { + return ( + obj + && (typeof obj.selectionStartLineNumber === 'number') + && (typeof obj.selectionStartColumn === 'number') + && (typeof obj.positionLineNumber === 'number') + && (typeof obj.positionColumn === 'number') + ); + } + + /** + * Create with a direction. + */ + public static createWithDirection(startLineNumber: number, startColumn: number, endLineNumber: number, endColumn: number, direction: SelectionDirection): Selection { + + if (direction === SelectionDirection.LTR) { + return new Selection(startLineNumber, startColumn, endLineNumber, endColumn); + } + + return new Selection(endLineNumber, endColumn, startLineNumber, startColumn); + } +} diff --git a/src/vs/editor/common/language/core/stringBuilder.ts b/src/vs/editor/common/language/core/stringBuilder.ts new file mode 100644 index 00000000000..d12d8fd8114 --- /dev/null +++ b/src/vs/editor/common/language/core/stringBuilder.ts @@ -0,0 +1,146 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as strings from '../../../../base/common/strings.js'; +import * as platform from '../../../../base/common/platform.js'; +import * as buffer from '../../../../base/common/buffer.js'; + +let _utf16LE_TextDecoder: TextDecoder | null; +function getUTF16LE_TextDecoder(): TextDecoder { + if (!_utf16LE_TextDecoder) { + _utf16LE_TextDecoder = new TextDecoder('UTF-16LE'); + } + return _utf16LE_TextDecoder; +} + +let _utf16BE_TextDecoder: TextDecoder | null; +function getUTF16BE_TextDecoder(): TextDecoder { + if (!_utf16BE_TextDecoder) { + _utf16BE_TextDecoder = new TextDecoder('UTF-16BE'); + } + return _utf16BE_TextDecoder; +} + +let _platformTextDecoder: TextDecoder | null; +export function getPlatformTextDecoder(): TextDecoder { + if (!_platformTextDecoder) { + _platformTextDecoder = platform.isLittleEndian() ? getUTF16LE_TextDecoder() : getUTF16BE_TextDecoder(); + } + return _platformTextDecoder; +} + +export function decodeUTF16LE(source: Uint8Array, offset: number, len: number): string { + const view = new Uint16Array(source.buffer, offset, len); + if (len > 0 && (view[0] === 0xFEFF || view[0] === 0xFFFE)) { + // UTF16 sometimes starts with a BOM https://de.wikipedia.org/wiki/Byte_Order_Mark + // It looks like TextDecoder.decode will eat up a leading BOM (0xFEFF or 0xFFFE) + // We don't want that behavior because we know the string is UTF16LE and the BOM should be maintained + // So we use the manual decoder + return compatDecodeUTF16LE(source, offset, len); + } + return getUTF16LE_TextDecoder().decode(view); +} + +function compatDecodeUTF16LE(source: Uint8Array, offset: number, len: number): string { + const result: string[] = []; + let resultLen = 0; + for (let i = 0; i < len; i++) { + const charCode = buffer.readUInt16LE(source, offset); offset += 2; + result[resultLen++] = String.fromCharCode(charCode); + } + return result.join(''); +} + +export class StringBuilder { + + private readonly _capacity: number; + private readonly _buffer: Uint16Array; + + private _completedStrings: string[] | null; + private _bufferLength: number; + + constructor(capacity: number) { + this._capacity = capacity | 0; + this._buffer = new Uint16Array(this._capacity); + + this._completedStrings = null; + this._bufferLength = 0; + } + + public reset(): void { + this._completedStrings = null; + this._bufferLength = 0; + } + + public build(): string { + if (this._completedStrings !== null) { + this._flushBuffer(); + return this._completedStrings.join(''); + } + return this._buildBuffer(); + } + + private _buildBuffer(): string { + if (this._bufferLength === 0) { + return ''; + } + + const view = new Uint16Array(this._buffer.buffer, 0, this._bufferLength); + return getPlatformTextDecoder().decode(view); + } + + private _flushBuffer(): void { + const bufferString = this._buildBuffer(); + this._bufferLength = 0; + + if (this._completedStrings === null) { + this._completedStrings = [bufferString]; + } else { + this._completedStrings[this._completedStrings.length] = bufferString; + } + } + + /** + * Append a char code (<2^16) + */ + public appendCharCode(charCode: number): void { + const remainingSpace = this._capacity - this._bufferLength; + + if (remainingSpace <= 1) { + if (remainingSpace === 0 || strings.isHighSurrogate(charCode)) { + this._flushBuffer(); + } + } + + this._buffer[this._bufferLength++] = charCode; + } + + /** + * Append an ASCII char code (<2^8) + */ + public appendASCIICharCode(charCode: number): void { + if (this._bufferLength === this._capacity) { + // buffer is full + this._flushBuffer(); + } + this._buffer[this._bufferLength++] = charCode; + } + + public appendString(str: string): void { + const strLen = str.length; + + if (this._bufferLength + strLen >= this._capacity) { + // This string does not fit in the remaining buffer space + + this._flushBuffer(); + this._completedStrings![this._completedStrings!.length] = str; + return; + } + + for (let i = 0; i < strLen; i++) { + this._buffer[this._bufferLength++] = str.charCodeAt(i); + } + } +} diff --git a/src/vs/editor/common/language/core/textChange.ts b/src/vs/editor/common/language/core/textChange.ts new file mode 100644 index 00000000000..7e93e5972b1 --- /dev/null +++ b/src/vs/editor/common/language/core/textChange.ts @@ -0,0 +1,346 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as buffer from '../../../../base/common/buffer.js'; +import { decodeUTF16LE } from './stringBuilder.js'; + +function escapeNewLine(str: string): string { + return ( + str + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + ); +} + +export class TextChange { + + public get oldLength(): number { + return this.oldText.length; + } + + public get oldEnd(): number { + return this.oldPosition + this.oldText.length; + } + + public get newLength(): number { + return this.newText.length; + } + + public get newEnd(): number { + return this.newPosition + this.newText.length; + } + + constructor( + public readonly oldPosition: number, + public readonly oldText: string, + public readonly newPosition: number, + public readonly newText: string + ) { } + + public toString(): string { + if (this.oldText.length === 0) { + return `(insert@${this.oldPosition} "${escapeNewLine(this.newText)}")`; + } + if (this.newText.length === 0) { + return `(delete@${this.oldPosition} "${escapeNewLine(this.oldText)}")`; + } + return `(replace@${this.oldPosition} "${escapeNewLine(this.oldText)}" with "${escapeNewLine(this.newText)}")`; + } + + private static _writeStringSize(str: string): number { + return ( + 4 + 2 * str.length + ); + } + + private static _writeString(b: Uint8Array, str: string, offset: number): number { + const len = str.length; + buffer.writeUInt32BE(b, len, offset); offset += 4; + for (let i = 0; i < len; i++) { + buffer.writeUInt16LE(b, str.charCodeAt(i), offset); offset += 2; + } + return offset; + } + + private static _readString(b: Uint8Array, offset: number): string { + const len = buffer.readUInt32BE(b, offset); offset += 4; + return decodeUTF16LE(b, offset, len); + } + + public writeSize(): number { + return ( + + 4 // oldPosition + + 4 // newPosition + + TextChange._writeStringSize(this.oldText) + + TextChange._writeStringSize(this.newText) + ); + } + + public write(b: Uint8Array, offset: number): number { + buffer.writeUInt32BE(b, this.oldPosition, offset); offset += 4; + buffer.writeUInt32BE(b, this.newPosition, offset); offset += 4; + offset = TextChange._writeString(b, this.oldText, offset); + offset = TextChange._writeString(b, this.newText, offset); + return offset; + } + + public static read(b: Uint8Array, offset: number, dest: TextChange[]): number { + const oldPosition = buffer.readUInt32BE(b, offset); offset += 4; + const newPosition = buffer.readUInt32BE(b, offset); offset += 4; + const oldText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(oldText); + const newText = TextChange._readString(b, offset); offset += TextChange._writeStringSize(newText); + dest.push(new TextChange(oldPosition, oldText, newPosition, newText)); + return offset; + } +} + +export function compressConsecutiveTextChanges(prevEdits: TextChange[] | null, currEdits: TextChange[]): TextChange[] { + if (prevEdits === null || prevEdits.length === 0) { + return currEdits; + } + const compressor = new TextChangeCompressor(prevEdits, currEdits); + return compressor.compress(); +} + +class TextChangeCompressor { + + private _prevEdits: TextChange[]; + private _currEdits: TextChange[]; + + private _result: TextChange[]; + private _resultLen: number; + + private _prevLen: number; + private _prevDeltaOffset: number; + + private _currLen: number; + private _currDeltaOffset: number; + + constructor(prevEdits: TextChange[], currEdits: TextChange[]) { + this._prevEdits = prevEdits; + this._currEdits = currEdits; + + this._result = []; + this._resultLen = 0; + + this._prevLen = this._prevEdits.length; + this._prevDeltaOffset = 0; + + this._currLen = this._currEdits.length; + this._currDeltaOffset = 0; + } + + public compress(): TextChange[] { + let prevIndex = 0; + let currIndex = 0; + + let prevEdit = this._getPrev(prevIndex); + let currEdit = this._getCurr(currIndex); + + while (prevIndex < this._prevLen || currIndex < this._currLen) { + + if (prevEdit === null) { + this._acceptCurr(currEdit!); + currEdit = this._getCurr(++currIndex); + continue; + } + + if (currEdit === null) { + this._acceptPrev(prevEdit); + prevEdit = this._getPrev(++prevIndex); + continue; + } + + if (currEdit.oldEnd <= prevEdit.newPosition) { + this._acceptCurr(currEdit); + currEdit = this._getCurr(++currIndex); + continue; + } + + if (prevEdit.newEnd <= currEdit.oldPosition) { + this._acceptPrev(prevEdit); + prevEdit = this._getPrev(++prevIndex); + continue; + } + + if (currEdit.oldPosition < prevEdit.newPosition) { + const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newPosition - currEdit.oldPosition); + this._acceptCurr(e1); + currEdit = e2; + continue; + } + + if (prevEdit.newPosition < currEdit.oldPosition) { + const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldPosition - prevEdit.newPosition); + this._acceptPrev(e1); + prevEdit = e2; + continue; + } + + // At this point, currEdit.oldPosition === prevEdit.newPosition + + let mergePrev: TextChange; + let mergeCurr: TextChange; + + if (currEdit.oldEnd === prevEdit.newEnd) { + mergePrev = prevEdit; + mergeCurr = currEdit; + prevEdit = this._getPrev(++prevIndex); + currEdit = this._getCurr(++currIndex); + } else if (currEdit.oldEnd < prevEdit.newEnd) { + const [e1, e2] = TextChangeCompressor._splitPrev(prevEdit, currEdit.oldLength); + mergePrev = e1; + mergeCurr = currEdit; + prevEdit = e2; + currEdit = this._getCurr(++currIndex); + } else { + const [e1, e2] = TextChangeCompressor._splitCurr(currEdit, prevEdit.newLength); + mergePrev = prevEdit; + mergeCurr = e1; + prevEdit = this._getPrev(++prevIndex); + currEdit = e2; + } + + this._result[this._resultLen++] = new TextChange( + mergePrev.oldPosition, + mergePrev.oldText, + mergeCurr.newPosition, + mergeCurr.newText + ); + this._prevDeltaOffset += mergePrev.newLength - mergePrev.oldLength; + this._currDeltaOffset += mergeCurr.newLength - mergeCurr.oldLength; + } + + const merged = TextChangeCompressor._merge(this._result); + const cleaned = TextChangeCompressor._removeNoOps(merged); + return cleaned; + } + + private _acceptCurr(currEdit: TextChange): void { + this._result[this._resultLen++] = TextChangeCompressor._rebaseCurr(this._prevDeltaOffset, currEdit); + this._currDeltaOffset += currEdit.newLength - currEdit.oldLength; + } + + private _getCurr(currIndex: number): TextChange | null { + return (currIndex < this._currLen ? this._currEdits[currIndex] : null); + } + + private _acceptPrev(prevEdit: TextChange): void { + this._result[this._resultLen++] = TextChangeCompressor._rebasePrev(this._currDeltaOffset, prevEdit); + this._prevDeltaOffset += prevEdit.newLength - prevEdit.oldLength; + } + + private _getPrev(prevIndex: number): TextChange | null { + return (prevIndex < this._prevLen ? this._prevEdits[prevIndex] : null); + } + + private static _rebaseCurr(prevDeltaOffset: number, currEdit: TextChange): TextChange { + return new TextChange( + currEdit.oldPosition - prevDeltaOffset, + currEdit.oldText, + currEdit.newPosition, + currEdit.newText + ); + } + + private static _rebasePrev(currDeltaOffset: number, prevEdit: TextChange): TextChange { + return new TextChange( + prevEdit.oldPosition, + prevEdit.oldText, + prevEdit.newPosition + currDeltaOffset, + prevEdit.newText + ); + } + + private static _splitPrev(edit: TextChange, offset: number): [TextChange, TextChange] { + const preText = edit.newText.substr(0, offset); + const postText = edit.newText.substr(offset); + + return [ + new TextChange( + edit.oldPosition, + edit.oldText, + edit.newPosition, + preText + ), + new TextChange( + edit.oldEnd, + '', + edit.newPosition + offset, + postText + ) + ]; + } + + private static _splitCurr(edit: TextChange, offset: number): [TextChange, TextChange] { + const preText = edit.oldText.substr(0, offset); + const postText = edit.oldText.substr(offset); + + return [ + new TextChange( + edit.oldPosition, + preText, + edit.newPosition, + edit.newText + ), + new TextChange( + edit.oldPosition + offset, + postText, + edit.newEnd, + '' + ) + ]; + } + + private static _merge(edits: TextChange[]): TextChange[] { + if (edits.length === 0) { + return edits; + } + + const result: TextChange[] = []; + let resultLen = 0; + + let prev = edits[0]; + for (let i = 1; i < edits.length; i++) { + const curr = edits[i]; + + if (prev.oldEnd === curr.oldPosition) { + // Merge into `prev` + prev = new TextChange( + prev.oldPosition, + prev.oldText + curr.oldText, + prev.newPosition, + prev.newText + curr.newText + ); + } else { + result[resultLen++] = prev; + prev = curr; + } + } + result[resultLen++] = prev; + + return result; + } + + private static _removeNoOps(edits: TextChange[]): TextChange[] { + if (edits.length === 0) { + return edits; + } + + const result: TextChange[] = []; + let resultLen = 0; + + for (let i = 0; i < edits.length; i++) { + const edit = edits[i]; + + if (edit.oldText === edit.newText) { + continue; + } + result[resultLen++] = edit; + } + + return result; + } +} diff --git a/src/vs/editor/common/language/core/textEdit.ts b/src/vs/editor/common/language/core/textEdit.ts new file mode 100644 index 00000000000..95cd06c45a4 --- /dev/null +++ b/src/vs/editor/common/language/core/textEdit.ts @@ -0,0 +1,400 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals } from '../../../../base/common/arrays.js'; +import { assert, assertFn, checkAdjacentItems } from '../../../../base/common/assert.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../../base/common/strings.js'; +import { ISingleEditOperation } from './editOperation.js'; +import { LineRange } from './lineRange.js'; +import { OffsetEdit } from './offsetEdit.js'; +import { Position } from './position.js'; +import { PositionOffsetTransformer } from './positionToOffset.js'; +import { Range } from './range.js'; +import { TextLength } from './textLength.js'; + +export class TextEdit { + public static fromOffsetEdit(edit: OffsetEdit, initialState: AbstractText): TextEdit { + const edits = edit.edits.map(e => new SingleTextEdit(initialState.getTransformer().getRange(e.replaceRange), e.newText)); + return new TextEdit(edits); + } + + public static single(originalRange: Range, newText: string): TextEdit { + return new TextEdit([new SingleTextEdit(originalRange, newText)]); + } + + public static insert(position: Position, newText: string): TextEdit { + return new TextEdit([new SingleTextEdit(Range.fromPositions(position, position), newText)]); + } + + constructor(public readonly edits: readonly SingleTextEdit[]) { + assertFn(() => checkAdjacentItems(edits, (a, b) => a.range.getEndPosition().isBeforeOrEqual(b.range.getStartPosition()))); + } + + /** + * Joins touching edits and removes empty edits. + */ + normalize(): TextEdit { + const edits: SingleTextEdit[] = []; + for (const edit of this.edits) { + if (edits.length > 0 && edits[edits.length - 1].range.getEndPosition().equals(edit.range.getStartPosition())) { + const last = edits[edits.length - 1]; + edits[edits.length - 1] = new SingleTextEdit(last.range.plusRange(edit.range), last.text + edit.text); + } else if (!edit.isEmpty) { + edits.push(edit); + } + } + return new TextEdit(edits); + } + + mapPosition(position: Position): Position | Range { + let lineDelta = 0; + let curLine = 0; + let columnDeltaInCurLine = 0; + + for (const edit of this.edits) { + const start = edit.range.getStartPosition(); + + if (position.isBeforeOrEqual(start)) { + break; + } + + const end = edit.range.getEndPosition(); + const len = TextLength.ofText(edit.text); + if (position.isBefore(end)) { + const startPos = new Position(start.lineNumber + lineDelta, start.column + (start.lineNumber + lineDelta === curLine ? columnDeltaInCurLine : 0)); + const endPos = len.addToPosition(startPos); + return rangeFromPositions(startPos, endPos); + } + + if (start.lineNumber + lineDelta !== curLine) { + columnDeltaInCurLine = 0; + } + + lineDelta += len.lineCount - (edit.range.endLineNumber - edit.range.startLineNumber); + + if (len.lineCount === 0) { + if (end.lineNumber !== start.lineNumber) { + columnDeltaInCurLine += len.columnCount - (end.column - 1); + } else { + columnDeltaInCurLine += len.columnCount - (end.column - start.column); + } + } else { + columnDeltaInCurLine = len.columnCount; + } + curLine = end.lineNumber + lineDelta; + } + + return new Position(position.lineNumber + lineDelta, position.column + (position.lineNumber + lineDelta === curLine ? columnDeltaInCurLine : 0)); + } + + mapRange(range: Range): Range { + function getStart(p: Position | Range) { + return p instanceof Position ? p : p.getStartPosition(); + } + + function getEnd(p: Position | Range) { + return p instanceof Position ? p : p.getEndPosition(); + } + + const start = getStart(this.mapPosition(range.getStartPosition())); + const end = getEnd(this.mapPosition(range.getEndPosition())); + + return rangeFromPositions(start, end); + } + + // TODO: `doc` is not needed for this! + inverseMapPosition(positionAfterEdit: Position, doc: AbstractText): Position | Range { + const reversed = this.inverse(doc); + return reversed.mapPosition(positionAfterEdit); + } + + inverseMapRange(range: Range, doc: AbstractText): Range { + const reversed = this.inverse(doc); + return reversed.mapRange(range); + } + + apply(text: AbstractText): string { + let result = ''; + let lastEditEnd = new Position(1, 1); + for (const edit of this.edits) { + const editRange = edit.range; + const editStart = editRange.getStartPosition(); + const editEnd = editRange.getEndPosition(); + + const r = rangeFromPositions(lastEditEnd, editStart); + if (!r.isEmpty()) { + result += text.getValueOfRange(r); + } + result += edit.text; + lastEditEnd = editEnd; + } + const r = rangeFromPositions(lastEditEnd, text.endPositionExclusive); + if (!r.isEmpty()) { + result += text.getValueOfRange(r); + } + return result; + } + + applyToString(str: string): string { + const strText = new StringText(str); + return this.apply(strText); + } + + inverse(doc: AbstractText): TextEdit { + const ranges = this.getNewRanges(); + return new TextEdit(this.edits.map((e, idx) => new SingleTextEdit(ranges[idx], doc.getValueOfRange(e.range)))); + } + + getNewRanges(): Range[] { + const newRanges: Range[] = []; + let previousEditEndLineNumber = 0; + let lineOffset = 0; + let columnOffset = 0; + for (const edit of this.edits) { + const textLength = TextLength.ofText(edit.text); + const newRangeStart = Position.lift({ + lineNumber: edit.range.startLineNumber + lineOffset, + column: edit.range.startColumn + (edit.range.startLineNumber === previousEditEndLineNumber ? columnOffset : 0) + }); + const newRange = textLength.createRange(newRangeStart); + newRanges.push(newRange); + lineOffset = newRange.endLineNumber - edit.range.endLineNumber; + columnOffset = newRange.endColumn - edit.range.endColumn; + previousEditEndLineNumber = edit.range.endLineNumber; + } + return newRanges; + } + + toSingle(text: AbstractText) { + if (this.edits.length === 0) { throw new BugIndicatingError(); } + if (this.edits.length === 1) { return this.edits[0]; } + + const startPos = this.edits[0].range.getStartPosition(); + const endPos = this.edits[this.edits.length - 1].range.getEndPosition(); + + let newText = ''; + + for (let i = 0; i < this.edits.length; i++) { + const curEdit = this.edits[i]; + newText += curEdit.text; + if (i < this.edits.length - 1) { + const nextEdit = this.edits[i + 1]; + const gapRange = Range.fromPositions(curEdit.range.getEndPosition(), nextEdit.range.getStartPosition()); + const gapText = text.getValueOfRange(gapRange); + newText += gapText; + } + } + return new SingleTextEdit(Range.fromPositions(startPos, endPos), newText); + } + + equals(other: TextEdit): boolean { + return equals(this.edits, other.edits, (a, b) => a.equals(b)); + } +} + +export class SingleTextEdit { + public static joinEdits(edits: SingleTextEdit[], initialValue: AbstractText): SingleTextEdit { + if (edits.length === 0) { throw new BugIndicatingError(); } + if (edits.length === 1) { return edits[0]; } + + const startPos = edits[0].range.getStartPosition(); + const endPos = edits[edits.length - 1].range.getEndPosition(); + + let newText = ''; + + for (let i = 0; i < edits.length; i++) { + const curEdit = edits[i]; + newText += curEdit.text; + if (i < edits.length - 1) { + const nextEdit = edits[i + 1]; + const gapRange = Range.fromPositions(curEdit.range.getEndPosition(), nextEdit.range.getStartPosition()); + const gapText = initialValue.getValueOfRange(gapRange); + newText += gapText; + } + } + return new SingleTextEdit(Range.fromPositions(startPos, endPos), newText); + } + + constructor( + public readonly range: Range, + public readonly text: string, + ) { + } + + get isEmpty(): boolean { + return this.range.isEmpty() && this.text.length === 0; + } + + static equals(first: SingleTextEdit, second: SingleTextEdit) { + return first.range.equalsRange(second.range) && first.text === second.text; + } + + public toSingleEditOperation(): ISingleEditOperation { + return { + range: this.range, + text: this.text, + }; + } + + public toEdit(): TextEdit { + return new TextEdit([this]); + } + + public equals(other: SingleTextEdit): boolean { + return SingleTextEdit.equals(this, other); + } + + public extendToCoverRange(range: Range, initialValue: AbstractText): SingleTextEdit { + if (this.range.containsRange(range)) { return this; } + + const newRange = this.range.plusRange(range); + const textBefore = initialValue.getValueOfRange(Range.fromPositions(newRange.getStartPosition(), this.range.getStartPosition())); + const textAfter = initialValue.getValueOfRange(Range.fromPositions(this.range.getEndPosition(), newRange.getEndPosition())); + const newText = textBefore + this.text + textAfter; + return new SingleTextEdit(newRange, newText); + } + + public extendToFullLine(initialValue: AbstractText): SingleTextEdit { + const newRange = new Range( + this.range.startLineNumber, + 1, + this.range.endLineNumber, + initialValue.getTransformer().getLineLength(this.range.endLineNumber) + 1 + ); + return this.extendToCoverRange(newRange, initialValue); + } + + public removeCommonPrefix(text: AbstractText): SingleTextEdit { + const normalizedOriginalText = text.getValueOfRange(this.range).replaceAll('\r\n', '\n'); + const normalizedModifiedText = this.text.replaceAll('\r\n', '\n'); + + const commonPrefixLen = commonPrefixLength(normalizedOriginalText, normalizedModifiedText); + const start = TextLength.ofText(normalizedOriginalText.substring(0, commonPrefixLen)) + .addToPosition(this.range.getStartPosition()); + + const newText = normalizedModifiedText.substring(commonPrefixLen); + const range = Range.fromPositions(start, this.range.getEndPosition()); + return new SingleTextEdit(range, newText); + } + + public isEffectiveDeletion(text: AbstractText): boolean { + let newText = this.text.replaceAll('\r\n', '\n'); + let existingText = text.getValueOfRange(this.range).replaceAll('\r\n', '\n'); + const l = commonPrefixLength(newText, existingText); + newText = newText.substring(l); + existingText = existingText.substring(l); + const r = commonSuffixLength(newText, existingText); + newText = newText.substring(0, newText.length - r); + existingText = existingText.substring(0, existingText.length - r); + + return newText === ''; + } +} + +function rangeFromPositions(start: Position, end: Position): Range { + if (start.lineNumber === end.lineNumber && start.column === Number.MAX_SAFE_INTEGER) { + return Range.fromPositions(end, end); + } else if (!start.isBeforeOrEqual(end)) { + throw new BugIndicatingError('start must be before end'); + } + return new Range(start.lineNumber, start.column, end.lineNumber, end.column); +} + +export abstract class AbstractText { + abstract getValueOfRange(range: Range): string; + abstract readonly length: TextLength; + + get endPositionExclusive(): Position { + return this.length.addToPosition(new Position(1, 1)); + } + + get lineRange(): LineRange { + return this.length.toLineRange(); + } + + getValue(): string { + return this.getValueOfRange(this.length.toRange()); + } + + getLineLength(lineNumber: number): number { + return this.getValueOfRange(new Range(lineNumber, 1, lineNumber, Number.MAX_SAFE_INTEGER)).length; + } + + private _transformer: PositionOffsetTransformer | undefined = undefined; + + getTransformer(): PositionOffsetTransformer { + if (!this._transformer) { + this._transformer = new PositionOffsetTransformer(this.getValue()); + } + return this._transformer; + } + + getLineAt(lineNumber: number): string { + return this.getValueOfRange(new Range(lineNumber, 1, lineNumber, Number.MAX_SAFE_INTEGER)); + } + + getLines(): string[] { + const value = this.getValue(); + return splitLines(value); + } +} + +export class LineBasedText extends AbstractText { + constructor( + private readonly _getLineContent: (lineNumber: number) => string, + private readonly _lineCount: number, + ) { + assert(_lineCount >= 1); + + super(); + } + + override getValueOfRange(range: Range): string { + if (range.startLineNumber === range.endLineNumber) { + return this._getLineContent(range.startLineNumber).substring(range.startColumn - 1, range.endColumn - 1); + } + let result = this._getLineContent(range.startLineNumber).substring(range.startColumn - 1); + for (let i = range.startLineNumber + 1; i < range.endLineNumber; i++) { + result += '\n' + this._getLineContent(i); + } + result += '\n' + this._getLineContent(range.endLineNumber).substring(0, range.endColumn - 1); + return result; + } + + override getLineLength(lineNumber: number): number { + return this._getLineContent(lineNumber).length; + } + + get length(): TextLength { + const lastLine = this._getLineContent(this._lineCount); + return new TextLength(this._lineCount - 1, lastLine.length); + } +} + +export class ArrayText extends LineBasedText { + constructor(lines: string[]) { + super( + lineNumber => lines[lineNumber - 1], + lines.length + ); + } +} + +export class StringText extends AbstractText { + private readonly _t = new PositionOffsetTransformer(this.value); + + constructor(public readonly value: string) { + super(); + } + + getValueOfRange(range: Range): string { + return this._t.getOffsetRange(range).substring(this.value); + } + + get length(): TextLength { + return this._t.textLength; + } +} diff --git a/src/vs/editor/common/language/core/textLength.ts b/src/vs/editor/common/language/core/textLength.ts new file mode 100644 index 00000000000..a1f2d8e35c8 --- /dev/null +++ b/src/vs/editor/common/language/core/textLength.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { LineRange } from './lineRange.js'; +import { Position } from './position.js'; +import { Range } from './range.js'; + +/** + * Represents a non-negative length of text in terms of line and column count. +*/ +export class TextLength { + public static zero = new TextLength(0, 0); + + public static lengthDiffNonNegative(start: TextLength, end: TextLength): TextLength { + if (end.isLessThan(start)) { + return TextLength.zero; + } + if (start.lineCount === end.lineCount) { + return new TextLength(0, end.columnCount - start.columnCount); + } else { + return new TextLength(end.lineCount - start.lineCount, end.columnCount); + } + } + + public static betweenPositions(position1: Position, position2: Position): TextLength { + if (position1.lineNumber === position2.lineNumber) { + return new TextLength(0, position2.column - position1.column); + } else { + return new TextLength(position2.lineNumber - position1.lineNumber, position2.column - 1); + } + } + + public static fromPosition(pos: Position): TextLength { + return new TextLength(pos.lineNumber - 1, pos.column - 1); + } + + public static ofRange(range: Range) { + return TextLength.betweenPositions(range.getStartPosition(), range.getEndPosition()); + } + + public static ofText(text: string): TextLength { + let line = 0; + let column = 0; + for (const c of text) { + if (c === '\n') { + line++; + column = 0; + } else { + column++; + } + } + return new TextLength(line, column); + } + + constructor( + public readonly lineCount: number, + public readonly columnCount: number + ) { } + + public isZero() { + return this.lineCount === 0 && this.columnCount === 0; + } + + public isLessThan(other: TextLength): boolean { + if (this.lineCount !== other.lineCount) { + return this.lineCount < other.lineCount; + } + return this.columnCount < other.columnCount; + } + + public isGreaterThan(other: TextLength): boolean { + if (this.lineCount !== other.lineCount) { + return this.lineCount > other.lineCount; + } + return this.columnCount > other.columnCount; + } + + public isGreaterThanOrEqualTo(other: TextLength): boolean { + if (this.lineCount !== other.lineCount) { + return this.lineCount > other.lineCount; + } + return this.columnCount >= other.columnCount; + } + + public equals(other: TextLength): boolean { + return this.lineCount === other.lineCount && this.columnCount === other.columnCount; + } + + public compare(other: TextLength): number { + if (this.lineCount !== other.lineCount) { + return this.lineCount - other.lineCount; + } + return this.columnCount - other.columnCount; + } + + public add(other: TextLength): TextLength { + if (other.lineCount === 0) { + return new TextLength(this.lineCount, this.columnCount + other.columnCount); + } else { + return new TextLength(this.lineCount + other.lineCount, other.columnCount); + } + } + + public createRange(startPosition: Position): Range { + if (this.lineCount === 0) { + return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column + this.columnCount); + } else { + return new Range(startPosition.lineNumber, startPosition.column, startPosition.lineNumber + this.lineCount, this.columnCount + 1); + } + } + + public toRange(): Range { + return new Range(1, 1, this.lineCount + 1, this.columnCount + 1); + } + + public toLineRange(): LineRange { + return LineRange.ofLength(1, this.lineCount + 1); + } + + public addToPosition(position: Position): Position { + if (this.lineCount === 0) { + return new Position(position.lineNumber, position.column + this.columnCount); + } else { + return new Position(position.lineNumber + this.lineCount, this.columnCount + 1); + } + } + + public addToRange(range: Range): Range { + return Range.fromPositions( + this.addToPosition(range.getStartPosition()), + this.addToPosition(range.getEndPosition()) + ); + } + + toString() { + return `${this.lineCount},${this.columnCount}`; + } +} diff --git a/src/vs/editor/common/language/core/textModelDefaults.ts b/src/vs/editor/common/language/core/textModelDefaults.ts new file mode 100644 index 00000000000..cb0150baacf --- /dev/null +++ b/src/vs/editor/common/language/core/textModelDefaults.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const EDITOR_MODEL_DEFAULTS = { + tabSize: 4, + indentSize: 4, + insertSpaces: true, + detectIndentation: true, + trimAutoWhitespace: true, + largeFileOptimizations: true, + bracketPairColorizationOptions: { + enabled: true, + independentColorPoolPerBracketType: false, + }, +}; diff --git a/src/vs/editor/common/language/core/wordCharacterClassifier.ts b/src/vs/editor/common/language/core/wordCharacterClassifier.ts new file mode 100644 index 00000000000..c4d8f14147a --- /dev/null +++ b/src/vs/editor/common/language/core/wordCharacterClassifier.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from '../../../../base/common/charCode.js'; +import { safeIntl } from '../../../../base/common/date.js'; +import { LRUCache } from '../../../../base/common/map.js'; +import { CharacterClassifier } from './characterClassifier.js'; + +export const enum WordCharacterClass { + Regular = 0, + Whitespace = 1, + WordSeparator = 2 +} + +export class WordCharacterClassifier extends CharacterClassifier { + + public readonly intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]; + private readonly _segmenter: Intl.Segmenter | null = null; + private _cachedLine: string | null = null; + private _cachedSegments: IntlWordSegmentData[] = []; + + constructor(wordSeparators: string, intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]) { + super(WordCharacterClass.Regular); + this.intlSegmenterLocales = intlSegmenterLocales; + if (this.intlSegmenterLocales.length > 0) { + this._segmenter = safeIntl.Segmenter(this.intlSegmenterLocales, { granularity: 'word' }); + } else { + this._segmenter = null; + } + + for (let i = 0, len = wordSeparators.length; i < len; i++) { + this.set(wordSeparators.charCodeAt(i), WordCharacterClass.WordSeparator); + } + + this.set(CharCode.Space, WordCharacterClass.Whitespace); + this.set(CharCode.Tab, WordCharacterClass.Whitespace); + } + + public findPrevIntlWordBeforeOrAtOffset(line: string, offset: number): IntlWordSegmentData | null { + let candidate: IntlWordSegmentData | null = null; + for (const segment of this._getIntlSegmenterWordsOnLine(line)) { + if (segment.index > offset) { + break; + } + candidate = segment; + } + return candidate; + } + + public findNextIntlWordAtOrAfterOffset(lineContent: string, offset: number): IntlWordSegmentData | null { + for (const segment of this._getIntlSegmenterWordsOnLine(lineContent)) { + if (segment.index < offset) { + continue; + } + return segment; + } + return null; + } + + private _getIntlSegmenterWordsOnLine(line: string): IntlWordSegmentData[] { + if (!this._segmenter) { + return []; + } + + // Check if the line has changed from the previous call + if (this._cachedLine === line) { + return this._cachedSegments; + } + + // Update the cache with the new line + this._cachedLine = line; + this._cachedSegments = this._filterWordSegments(this._segmenter.segment(line)); + + return this._cachedSegments; + } + + private _filterWordSegments(segments: Intl.Segments): IntlWordSegmentData[] { + const result: IntlWordSegmentData[] = []; + for (const segment of segments) { + if (this._isWordLike(segment)) { + result.push(segment); + } + } + return result; + } + + private _isWordLike(segment: Intl.SegmentData): segment is IntlWordSegmentData { + if (segment.isWordLike) { + return true; + } + return false; + } +} + +export interface IntlWordSegmentData extends Intl.SegmentData { + isWordLike: true; +} + +const wordClassifierCache = new LRUCache(10); + +export function getMapForWordSeparators(wordSeparators: string, intlSegmenterLocales: Intl.UnicodeBCP47LocaleIdentifier[]): WordCharacterClassifier { + const key = `${wordSeparators}/${intlSegmenterLocales.join(',')}`; + let result = wordClassifierCache.get(key)!; + if (!result) { + result = new WordCharacterClassifier(wordSeparators, intlSegmenterLocales); + wordClassifierCache.set(key, result); + } + return result; +} diff --git a/src/vs/editor/common/language/core/wordHelper.ts b/src/vs/editor/common/language/core/wordHelper.ts new file mode 100644 index 00000000000..896a0900c78 --- /dev/null +++ b/src/vs/editor/common/language/core/wordHelper.ts @@ -0,0 +1,175 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Iterable } from '../../../../base/common/iterator.js'; +import { toDisposable } from '../../../../base/common/lifecycle.js'; +import { LinkedList } from '../../../../base/common/linkedList.js'; + +export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; + +/** + * Word inside a model. + */ +export interface IWordAtPosition { + /** + * The word. + */ + readonly word: string; + /** + * The column where the word starts. + */ + readonly startColumn: number; + /** + * The column where the word ends. + */ + readonly endColumn: number; +} + +/** + * Create a word definition regular expression based on default word separators. + * Optionally provide allowed separators that should be included in words. + * + * The default would look like this: + * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g + */ +function createWordRegExp(allowInWords: string = ''): RegExp { + let source = '(-?\\d*\\.\\d\\w*)|([^'; + for (const sep of USUAL_WORD_SEPARATORS) { + if (allowInWords.indexOf(sep) >= 0) { + continue; + } + source += '\\' + sep; + } + source += '\\s]+)'; + return new RegExp(source, 'g'); +} + +// catches numbers (including floating numbers) in the first group, and alphanum in the second +export const DEFAULT_WORD_REGEXP = createWordRegExp(); + +export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegExp { + let result: RegExp = DEFAULT_WORD_REGEXP; + + if (wordDefinition && (wordDefinition instanceof RegExp)) { + if (!wordDefinition.global) { + let flags = 'g'; + if (wordDefinition.ignoreCase) { + flags += 'i'; + } + if (wordDefinition.multiline) { + flags += 'm'; + } + if (wordDefinition.unicode) { + flags += 'u'; + } + result = new RegExp(wordDefinition.source, flags); + } else { + result = wordDefinition; + } + } + + result.lastIndex = 0; + + return result; +} + + +export interface IGetWordAtTextConfig { + maxLen: number; + windowSize: number; + timeBudget: number; +} + + +const _defaultConfig = new LinkedList(); +_defaultConfig.unshift({ + maxLen: 1000, + windowSize: 15, + timeBudget: 150 +}); + +export function setDefaultGetWordAtTextConfig(value: IGetWordAtTextConfig) { + const rm = _defaultConfig.unshift(value); + return toDisposable(rm); +} + +export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number, config?: IGetWordAtTextConfig): IWordAtPosition | null { + // Ensure the regex has the 'g' flag, otherwise this will loop forever + wordDefinition = ensureValidWordDefinition(wordDefinition); + + if (!config) { + config = Iterable.first(_defaultConfig)!; + } + + if (text.length > config.maxLen) { + // don't throw strings that long at the regexp + // but use a sub-string in which a word must occur + let start = column - config.maxLen / 2; + if (start < 0) { + start = 0; + } else { + textOffset += start; + } + text = text.substring(start, column + config.maxLen / 2); + return getWordAtText(column, wordDefinition, text, textOffset, config); + } + + const t1 = Date.now(); + const pos = column - 1 - textOffset; + + let prevRegexIndex = -1; + let match: RegExpExecArray | null = null; + + for (let i = 1; ; i++) { + // check time budget + if (Date.now() - t1 >= config.timeBudget) { + break; + } + + // reset the index at which the regexp should start matching, also know where it + // should stop so that subsequent search don't repeat previous searches + const regexIndex = pos - config.windowSize * i; + wordDefinition.lastIndex = Math.max(0, regexIndex); + const thisMatch = _findRegexMatchEnclosingPosition(wordDefinition, text, pos, prevRegexIndex); + + if (!thisMatch && match) { + // stop: we have something + break; + } + + match = thisMatch; + + // stop: searched at start + if (regexIndex <= 0) { + break; + } + prevRegexIndex = regexIndex; + } + + if (match) { + const result = { + word: match[0], + startColumn: textOffset + 1 + match.index, + endColumn: textOffset + 1 + match.index + match[0].length + }; + wordDefinition.lastIndex = 0; + return result; + } + + return null; +} + +function _findRegexMatchEnclosingPosition(wordDefinition: RegExp, text: string, pos: number, stopPos: number): RegExpExecArray | null { + let match: RegExpExecArray | null; + while (match = wordDefinition.exec(text)) { + const matchIndex = match.index || 0; + if (matchIndex <= pos && wordDefinition.lastIndex >= pos) { + return match; + } else if (stopPos > 0 && matchIndex > stopPos) { + return null; + } + } + return null; +} diff --git a/src/vs/editor/common/language/encodedTokenAttributes.ts b/src/vs/editor/common/language/encodedTokenAttributes.ts new file mode 100644 index 00000000000..22ee1417e55 --- /dev/null +++ b/src/vs/editor/common/language/encodedTokenAttributes.ts @@ -0,0 +1,193 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Open ended enum at runtime + */ +export const enum LanguageId { + Null = 0, + PlainText = 1 +} + +/** + * A font style. Values are 2^x such that a bit mask can be used. + */ +export const enum FontStyle { + NotSet = -1, + None = 0, + Italic = 1, + Bold = 2, + Underline = 4, + Strikethrough = 8, +} + +/** + * Open ended enum at runtime + */ +export const enum ColorId { + None = 0, + DefaultForeground = 1, + DefaultBackground = 2 +} + +/** + * A standard token type. + */ +export const enum StandardTokenType { + Other = 0, + Comment = 1, + String = 2, + RegEx = 3 +} + +/** + * Helpers to manage the "collapsed" metadata of an entire StackElement stack. + * The following assumptions have been made: + * - languageId < 256 => needs 8 bits + * - unique color count < 512 => needs 9 bits + * + * The binary format is: + * - ------------------------------------------- + * 3322 2222 2222 1111 1111 1100 0000 0000 + * 1098 7654 3210 9876 5432 1098 7654 3210 + * - ------------------------------------------- + * xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx + * bbbb bbbb ffff ffff fFFF FBTT LLLL LLLL + * - ------------------------------------------- + * - L = LanguageId (8 bits) + * - T = StandardTokenType (2 bits) + * - B = Balanced bracket (1 bit) + * - F = FontStyle (4 bits) + * - f = foreground color (9 bits) + * - b = background color (8 bits) + * + */ +export const enum MetadataConsts { + LANGUAGEID_MASK /* */ = 0b00000000_00000000_00000000_11111111, + TOKEN_TYPE_MASK /* */ = 0b00000000_00000000_00000011_00000000, + BALANCED_BRACKETS_MASK /* */ = 0b00000000_00000000_00000100_00000000, + FONT_STYLE_MASK /* */ = 0b00000000_00000000_01111000_00000000, + FOREGROUND_MASK /* */ = 0b00000000_11111111_10000000_00000000, + BACKGROUND_MASK /* */ = 0b11111111_00000000_00000000_00000000, + + ITALIC_MASK /* */ = 0b00000000_00000000_00001000_00000000, + BOLD_MASK /* */ = 0b00000000_00000000_00010000_00000000, + UNDERLINE_MASK /* */ = 0b00000000_00000000_00100000_00000000, + STRIKETHROUGH_MASK /* */ = 0b00000000_00000000_01000000_00000000, + + // Semantic tokens cannot set the language id, so we can + // use the first 8 bits for control purposes + SEMANTIC_USE_ITALIC /* */ = 0b00000000_00000000_00000000_00000001, + SEMANTIC_USE_BOLD /* */ = 0b00000000_00000000_00000000_00000010, + SEMANTIC_USE_UNDERLINE /* */ = 0b00000000_00000000_00000000_00000100, + SEMANTIC_USE_STRIKETHROUGH /* */ = 0b00000000_00000000_00000000_00001000, + SEMANTIC_USE_FOREGROUND /* */ = 0b00000000_00000000_00000000_00010000, + SEMANTIC_USE_BACKGROUND /* */ = 0b00000000_00000000_00000000_00100000, + + LANGUAGEID_OFFSET = 0, + TOKEN_TYPE_OFFSET = 8, + BALANCED_BRACKETS_OFFSET = 10, + FONT_STYLE_OFFSET = 11, + FOREGROUND_OFFSET = 15, + BACKGROUND_OFFSET = 24 +} + +/** + */ +export class TokenMetadata { + + public static getLanguageId(metadata: number): LanguageId { + return (metadata & MetadataConsts.LANGUAGEID_MASK) >>> MetadataConsts.LANGUAGEID_OFFSET; + } + + public static getTokenType(metadata: number): StandardTokenType { + return (metadata & MetadataConsts.TOKEN_TYPE_MASK) >>> MetadataConsts.TOKEN_TYPE_OFFSET; + } + + public static containsBalancedBrackets(metadata: number): boolean { + return (metadata & MetadataConsts.BALANCED_BRACKETS_MASK) !== 0; + } + + public static getFontStyle(metadata: number): FontStyle { + return (metadata & MetadataConsts.FONT_STYLE_MASK) >>> MetadataConsts.FONT_STYLE_OFFSET; + } + + public static getForeground(metadata: number): ColorId { + return (metadata & MetadataConsts.FOREGROUND_MASK) >>> MetadataConsts.FOREGROUND_OFFSET; + } + + public static getBackground(metadata: number): ColorId { + return (metadata & MetadataConsts.BACKGROUND_MASK) >>> MetadataConsts.BACKGROUND_OFFSET; + } + + public static getClassNameFromMetadata(metadata: number): string { + const foreground = this.getForeground(metadata); + let className = 'mtk' + foreground; + + const fontStyle = this.getFontStyle(metadata); + if (fontStyle & FontStyle.Italic) { + className += ' mtki'; + } + if (fontStyle & FontStyle.Bold) { + className += ' mtkb'; + } + if (fontStyle & FontStyle.Underline) { + className += ' mtku'; + } + if (fontStyle & FontStyle.Strikethrough) { + className += ' mtks'; + } + + return className; + } + + public static getInlineStyleFromMetadata(metadata: number, colorMap: string[]): string { + const foreground = this.getForeground(metadata); + const fontStyle = this.getFontStyle(metadata); + + let result = `color: ${colorMap[foreground]};`; + if (fontStyle & FontStyle.Italic) { + result += 'font-style: italic;'; + } + if (fontStyle & FontStyle.Bold) { + result += 'font-weight: bold;'; + } + let textDecoration = ''; + if (fontStyle & FontStyle.Underline) { + textDecoration += ' underline'; + } + if (fontStyle & FontStyle.Strikethrough) { + textDecoration += ' line-through'; + } + if (textDecoration) { + result += `text-decoration:${textDecoration};`; + + } + return result; + } + + public static getPresentationFromMetadata(metadata: number): ITokenPresentation { + const foreground = this.getForeground(metadata); + const fontStyle = this.getFontStyle(metadata); + + return { + foreground: foreground, + italic: Boolean(fontStyle & FontStyle.Italic), + bold: Boolean(fontStyle & FontStyle.Bold), + underline: Boolean(fontStyle & FontStyle.Underline), + strikethrough: Boolean(fontStyle & FontStyle.Strikethrough), + }; + } +} + +/** + */ +export interface ITokenPresentation { + foreground: ColorId; + italic: boolean; + bold: boolean; + underline: boolean; + strikethrough: boolean; +} diff --git a/src/vs/editor/common/language/language.ts b/src/vs/editor/common/language/language.ts new file mode 100644 index 00000000000..5c16c73dc12 --- /dev/null +++ b/src/vs/editor/common/language/language.ts @@ -0,0 +1,168 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../base/common/event.js'; +import { IDisposable } from '../../../base/common/lifecycle.js'; +import { URI } from '../../../base/common/uri.js'; +import { ILanguageIdCodec } from './languages.js'; +import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; + +export const ILanguageService = createDecorator('languageService'); + +export interface ILanguageExtensionPoint { + id: string; + extensions?: string[]; + filenames?: string[]; + filenamePatterns?: string[]; + firstLine?: string; + aliases?: string[]; + mimetypes?: string[]; + configuration?: URI; + /** + * @internal + */ + icon?: ILanguageIcon; +} + +export interface ILanguageSelection { + readonly languageId: string; + readonly onDidChange: Event; +} + +export interface ILanguageNameIdPair { + readonly languageName: string; + readonly languageId: string; +} + +export interface ILanguageIcon { + readonly light: URI; + readonly dark: URI; +} + +export interface ILanguageService { + readonly _serviceBrand: undefined; + + /** + * A codec which can encode and decode a string `languageId` as a number. + */ + readonly languageIdCodec: ILanguageIdCodec; + + /** + * An event emitted when basic language features are requested for the first time. + * This event is emitted when embedded languages are encountered (e.g. JS code block inside Markdown) + * or when a language is associated to a text model. + * + * **Note**: Basic language features refers to language configuration related features. + * **Note**: This event is a superset of `onDidRequestRichLanguageFeatures` + */ + onDidRequestBasicLanguageFeatures: Event; + + /** + * An event emitted when rich language features are requested for the first time. + * This event is emitted when a language is associated to a text model. + * + * **Note**: Rich language features refers to tokenizers, language features based on providers, etc. + * **Note**: This event is a subset of `onDidRequestRichLanguageFeatures` + */ + onDidRequestRichLanguageFeatures: Event; + + /** + * An event emitted when languages have changed. + */ + onDidChange: Event; + + /** + * Register a language. + */ + registerLanguage(def: ILanguageExtensionPoint): IDisposable; + + /** + * Check if `languageId` is registered. + */ + isRegisteredLanguageId(languageId: string): boolean; + + /** + * Get a list of all registered languages. + */ + getRegisteredLanguageIds(): string[]; + + /** + * Get a list of all registered languages with a name. + * If a language is explicitly registered without a name, it will not be part of the result. + * The result is sorted using by name case insensitive. + */ + getSortedRegisteredLanguageNames(): ILanguageNameIdPair[]; + + /** + * Get the preferred language name for a language. + */ + getLanguageName(languageId: string): string | null; + + /** + * Get the mimetype for a language. + */ + getMimeType(languageId: string): string | null; + + /** + * Get the default icon for the language. + */ + getIcon(languageId: string): ILanguageIcon | null; + + /** + * Get all file extensions for a language. + */ + getExtensions(languageId: string): ReadonlyArray; + + /** + * Get all file names for a language. + */ + getFilenames(languageId: string): ReadonlyArray; + + /** + * Get all language configuration files for a language. + */ + getConfigurationFiles(languageId: string): ReadonlyArray; + + /** + * Look up a language by its name case insensitive. + */ + getLanguageIdByLanguageName(languageName: string): string | null; + + /** + * Look up a language by its mime type. + */ + getLanguageIdByMimeType(mimeType: string | null | undefined): string | null; + + /** + * Guess the language id for a resource. + */ + guessLanguageIdByFilepathOrFirstLine(resource: URI, firstLine?: string): string | null; + + /** + * Will fall back to 'plaintext' if `languageId` is unknown. + */ + createById(languageId: string | null | undefined): ILanguageSelection; + + /** + * Will fall back to 'plaintext' if `mimeType` is unknown. + */ + createByMimeType(mimeType: string | null | undefined): ILanguageSelection; + + /** + * Will fall back to 'plaintext' if the `languageId` cannot be determined. + */ + createByFilepathOrFirstLine(resource: URI | null, firstLine?: string): ILanguageSelection; + + /** + * Request basic language features for a language. + */ + requestBasicLanguageFeatures(languageId: string): void; + + /** + * Request rich language features for a language. + */ + requestRichLanguageFeatures(languageId: string): void; + +} diff --git a/src/vs/editor/common/language/languageSelector.ts b/src/vs/editor/common/language/languageSelector.ts new file mode 100644 index 00000000000..78e782b5920 --- /dev/null +++ b/src/vs/editor/common/language/languageSelector.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRelativePattern, match as matchGlobPattern } from '../../../base/common/glob.js'; +import { URI } from '../../../base/common/uri.js'; +import { normalize } from '../../../base/common/path.js'; + +export interface LanguageFilter { + readonly language?: string; + readonly scheme?: string; + readonly pattern?: string | IRelativePattern; + readonly notebookType?: string; + /** + * This provider is implemented in the UI thread. + */ + readonly hasAccessToAllModels?: boolean; + readonly exclusive?: boolean; + + /** + * This provider comes from a builtin extension. + */ + readonly isBuiltin?: boolean; +} + +export type LanguageSelector = string | LanguageFilter | ReadonlyArray; + +export function score(selector: LanguageSelector | undefined, candidateUri: URI, candidateLanguage: string, candidateIsSynchronized: boolean, candidateNotebookUri: URI | undefined, candidateNotebookType: string | undefined): number { + + if (Array.isArray(selector)) { + // array -> take max individual value + let ret = 0; + for (const filter of selector) { + const value = score(filter, candidateUri, candidateLanguage, candidateIsSynchronized, candidateNotebookUri, candidateNotebookType); + if (value === 10) { + return value; // already at the highest + } + if (value > ret) { + ret = value; + } + } + return ret; + + } else if (typeof selector === 'string') { + + if (!candidateIsSynchronized) { + return 0; + } + + // short-hand notion, desugars to + // 'fooLang' -> { language: 'fooLang'} + // '*' -> { language: '*' } + if (selector === '*') { + return 5; + } else if (selector === candidateLanguage) { + return 10; + } else { + return 0; + } + + } else if (selector) { + // filter -> select accordingly, use defaults for scheme + const { language, pattern, scheme, hasAccessToAllModels, notebookType } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768 + + if (!candidateIsSynchronized && !hasAccessToAllModels) { + return 0; + } + + // selector targets a notebook -> use the notebook uri instead + // of the "normal" document uri. + if (notebookType && candidateNotebookUri) { + candidateUri = candidateNotebookUri; + } + + let ret = 0; + + if (scheme) { + if (scheme === candidateUri.scheme) { + ret = 10; + } else if (scheme === '*') { + ret = 5; + } else { + return 0; + } + } + + if (language) { + if (language === candidateLanguage) { + ret = 10; + } else if (language === '*') { + ret = Math.max(ret, 5); + } else { + return 0; + } + } + + if (notebookType) { + if (notebookType === candidateNotebookType) { + ret = 10; + } else if (notebookType === '*' && candidateNotebookType !== undefined) { + ret = Math.max(ret, 5); + } else { + return 0; + } + } + + if (pattern) { + let normalizedPattern: string | IRelativePattern; + if (typeof pattern === 'string') { + normalizedPattern = pattern; + } else { + // Since this pattern has a `base` property, we need + // to normalize this path first before passing it on + // because we will compare it against `Uri.fsPath` + // which uses platform specific separators. + // Refs: https://github.com/microsoft/vscode/issues/99938 + normalizedPattern = { ...pattern, base: normalize(pattern.base) }; + } + + if (normalizedPattern === candidateUri.fsPath || matchGlobPattern(normalizedPattern, candidateUri.fsPath)) { + ret = 10; + } else { + return 0; + } + } + + return ret; + + } else { + return 0; + } +} + + +export function targetsNotebooks(selector: LanguageSelector): boolean { + if (typeof selector === 'string') { + return false; + } else if (Array.isArray(selector)) { + return selector.some(targetsNotebooks); + } else { + return !!(selector).notebookType; + } +} diff --git a/src/vs/editor/common/language/languages.ts b/src/vs/editor/common/language/languages.ts new file mode 100644 index 00000000000..79c30e912a6 --- /dev/null +++ b/src/vs/editor/common/language/languages.ts @@ -0,0 +1,2462 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../base/common/buffer.js'; +import { CancellationToken } from '../../../base/common/cancellation.js'; +import { Codicon } from '../../../base/common/codicons.js'; +import { Color } from '../../../base/common/color.js'; +import { IReadonlyVSDataTransfer } from '../../../base/common/dataTransfer.js'; +import { Event } from '../../../base/common/event.js'; +import { HierarchicalKind } from '../../../base/common/hierarchicalKind.js'; +import { IMarkdownString } from '../../../base/common/htmlContent.js'; +import { IDisposable } from '../../../base/common/lifecycle.js'; +import { ThemeIcon } from '../../../base/common/themables.js'; +import { URI, UriComponents } from '../../../base/common/uri.js'; +import { EditOperation, ISingleEditOperation } from './core/editOperation.js'; +import { IPosition, Position } from './core/position.js'; +import { IRange, Range } from './core/range.js'; +import { Selection } from './core/selection.js'; +import { LanguageId } from './encodedTokenAttributes.js'; +import { LanguageSelector } from './languageSelector.js'; +import * as model from './model.js'; +import { TokenizationRegistry as TokenizationRegistryImpl } from './tokenizationRegistry.js'; +import { ContiguousMultilineTokens } from './tokens/contiguousMultilineTokens.js'; +import { localize } from '../../../nls.js'; +import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; +import { IMarkerData } from '../../../platform/markers/common/markers.js'; +import { IModelTokensChangedEvent } from './textModelEvents.js'; +import { ITextModel } from './model.js'; +import { TokenUpdate } from './model/tokenStore.js'; +import { ITextModelTreeSitter } from './services/treeSitterParserService.js'; +import type * as Parser from '@vscode/tree-sitter-wasm'; + +/** + * @internal + */ +export interface ILanguageIdCodec { + encodeLanguageId(languageId: string): LanguageId; + decodeLanguageId(languageId: LanguageId): string; +} + +export class Token { + _tokenBrand: void = undefined; + + constructor( + public readonly offset: number, + public readonly type: string, + public readonly language: string, + ) { + } + + public toString(): string { + return '(' + this.offset + ', ' + this.type + ')'; + } +} + +/** + * @internal + */ +export class TokenizationResult { + _tokenizationResultBrand: void = undefined; + + constructor( + public readonly tokens: Token[], + public readonly endState: IState, + ) { + } +} + +/** + * @internal + */ +export class EncodedTokenizationResult { + _encodedTokenizationResultBrand: void = undefined; + + constructor( + /** + * The tokens in binary format. Each token occupies two array indices. For token i: + * - at offset 2*i => startIndex + * - at offset 2*i + 1 => metadata + * + */ + public readonly tokens: Uint32Array, + public readonly endState: IState, + ) { + } +} + +export interface SyntaxNode { + startIndex: number; + endIndex: number; + startPosition: IPosition; + endPosition: IPosition; +} + +export interface QueryCapture { + name: string; + text?: string; + node: SyntaxNode; + encodedLanguageId: number; +} + +/** + * An intermediate interface for scaffolding the new tree sitter tokenization support. Not final. + * @internal + */ +export interface ITreeSitterTokenizationSupport { + /** + * exposed for testing + */ + getTokensInRange(textModel: ITextModel, range: Range, rangeStartOffset: number, rangeEndOffset: number): TokenUpdate[] | undefined; + tokenizeEncoded(lineNumber: number, textModel: model.ITextModel): void; + captureAtPosition(lineNumber: number, column: number, textModel: model.ITextModel): QueryCapture[]; + captureAtRangeTree(range: Range, tree: Parser.Tree, textModelTreeSitter: ITextModelTreeSitter): QueryCapture[]; + onDidChangeTokens: Event<{ textModel: model.ITextModel; changes: IModelTokensChangedEvent }>; + onDidChangeBackgroundTokenization: Event<{ textModel: model.ITextModel }>; + tokenizeEncodedInstrumented(lineNumber: number, textModel: model.ITextModel): { result: Uint32Array; captureTime: number; metadataTime: number } | undefined; + guessTokensForLinesContent(lineNumber: number, textModel: model.ITextModel, lines: string[]): Uint32Array[] | undefined; +} + +/** + * @internal + */ +export interface ITokenizationSupport { + /** + * If true, the background tokenizer will only be used to verify tokens against the default background tokenizer. + * Used for debugging. + */ + readonly backgroundTokenizerShouldOnlyVerifyTokens?: boolean; + + getInitialState(): IState; + + tokenize(line: string, hasEOL: boolean, state: IState): TokenizationResult; + + tokenizeEncoded(line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult; + + /** + * Can be/return undefined if default background tokenization should be used. + */ + createBackgroundTokenizer?(textModel: model.ITextModel, store: IBackgroundTokenizationStore): IBackgroundTokenizer | undefined; +} + +/** + * @internal + */ +export interface IBackgroundTokenizer extends IDisposable { + /** + * Instructs the background tokenizer to set the tokens for the given range again. + * + * This might be necessary if the renderer overwrote those tokens with heuristically computed ones for some viewport, + * when the change does not even propagate to that viewport. + */ + requestTokens(startLineNumber: number, endLineNumberExclusive: number): void; + + reportMismatchingTokens?(lineNumber: number): void; +} + +/** + * @internal + */ +export interface IBackgroundTokenizationStore { + setTokens(tokens: ContiguousMultilineTokens[]): void; + + setEndState(lineNumber: number, state: IState): void; + + /** + * Should be called to indicate that the background tokenization has finished for now. + * (This triggers bracket pair colorization to re-parse the bracket pairs with token information) + */ + backgroundTokenizationFinished(): void; +} + +/** + * The state of the tokenizer between two lines. + * It is useful to store flags such as in multiline comment, etc. + * The model will clone the previous line's state and pass it in to tokenize the next line. + */ +export interface IState { + clone(): IState; + equals(other: IState): boolean; +} + +/** + * A provider result represents the values a provider, like the {@link HoverProvider}, + * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves + * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a + * thenable. + */ +export type ProviderResult = T | undefined | null | Thenable; + +/** + * A hover represents additional information for a symbol or word. Hovers are + * rendered in a tooltip-like widget. + */ +export interface Hover { + /** + * The contents of this hover. + */ + contents: IMarkdownString[]; + + /** + * The range to which this hover applies. When missing, the + * editor will use the range at the current position or the + * current position itself. + */ + range?: IRange; + + /** + * Can increase the verbosity of the hover + */ + canIncreaseVerbosity?: boolean; + + /** + * Can decrease the verbosity of the hover + */ + canDecreaseVerbosity?: boolean; +} + +/** + * The hover provider interface defines the contract between extensions and + * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ +export interface HoverProvider { + /** + * Provide a hover for the given position, context and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideHover(model: model.ITextModel, position: Position, token: CancellationToken, context?: HoverContext): ProviderResult; +} + +export interface HoverContext { + /** + * Hover verbosity request + */ + verbosityRequest?: HoverVerbosityRequest; +} + +export interface HoverVerbosityRequest { + /** + * The delta by which to increase/decrease the hover verbosity level + */ + verbosityDelta: number; + /** + * The previous hover for the same position + */ + previousHover: THover; +} + +export enum HoverVerbosityAction { + /** + * Increase the verbosity of the hover + */ + Increase, + /** + * Decrease the verbosity of the hover + */ + Decrease +} + +/** + * An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are + * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. + * @internal + */ +export interface EvaluatableExpression { + /** + * The range to which this expression applies. + */ + range: IRange; + /** + * This expression overrides the expression extracted from the range. + */ + expression?: string; +} + + +/** + * The evaluatable expression provider interface defines the contract between extensions and + * the debug hover. + * @internal + */ +export interface EvaluatableExpressionProvider { + /** + * Provide a hover for the given position and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. + * @internal + */ +export interface InlineValueContext { + frameId: number; + stoppedLocation: Range; +} + +/** + * Provide inline value as text. + * @internal + */ +export interface InlineValueText { + type: 'text'; + range: IRange; + text: string; +} + +/** + * Provide inline value through a variable lookup. + * @internal + */ +export interface InlineValueVariableLookup { + type: 'variable'; + range: IRange; + variableName?: string; + caseSensitiveLookup: boolean; +} + +/** + * Provide inline value through an expression evaluation. + * @internal + */ +export interface InlineValueExpression { + type: 'expression'; + range: IRange; + expression?: string; +} + +/** + * Inline value information can be provided by different means: + * - directly as a text value (class InlineValueText). + * - as a name to use for a variable lookup (class InlineValueVariableLookup) + * - as an evaluatable expression (class InlineValueEvaluatableExpression) + * The InlineValue types combines all inline value types into one type. + * @internal + */ +export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueExpression; + +/** + * The inline values provider interface defines the contract between extensions and + * the debugger's inline values feature. + * @internal + */ +export interface InlineValuesProvider { + /** + */ + onDidChangeInlineValues?: Event | undefined; + /** + * Provide the "inline values" for the given range and document. Multiple hovers at the same + * position will be merged by the editor. A hover can have a range which defaults + * to the word range at the position when omitted. + */ + provideInlineValues(model: model.ITextModel, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; +} + +export const enum CompletionItemKind { + Method, + Function, + Constructor, + Field, + Variable, + Class, + Struct, + Interface, + Module, + Property, + Event, + Operator, + Unit, + Value, + Constant, + Enum, + EnumMember, + Keyword, + Text, + Color, + File, + Reference, + Customcolor, + Folder, + TypeParameter, + User, + Issue, + Snippet, // <- highest value (used for compare!) +} + +/** + * @internal + */ +export namespace CompletionItemKinds { + + const byKind = new Map(); + byKind.set(CompletionItemKind.Method, Codicon.symbolMethod); + byKind.set(CompletionItemKind.Function, Codicon.symbolFunction); + byKind.set(CompletionItemKind.Constructor, Codicon.symbolConstructor); + byKind.set(CompletionItemKind.Field, Codicon.symbolField); + byKind.set(CompletionItemKind.Variable, Codicon.symbolVariable); + byKind.set(CompletionItemKind.Class, Codicon.symbolClass); + byKind.set(CompletionItemKind.Struct, Codicon.symbolStruct); + byKind.set(CompletionItemKind.Interface, Codicon.symbolInterface); + byKind.set(CompletionItemKind.Module, Codicon.symbolModule); + byKind.set(CompletionItemKind.Property, Codicon.symbolProperty); + byKind.set(CompletionItemKind.Event, Codicon.symbolEvent); + byKind.set(CompletionItemKind.Operator, Codicon.symbolOperator); + byKind.set(CompletionItemKind.Unit, Codicon.symbolUnit); + byKind.set(CompletionItemKind.Value, Codicon.symbolValue); + byKind.set(CompletionItemKind.Enum, Codicon.symbolEnum); + byKind.set(CompletionItemKind.Constant, Codicon.symbolConstant); + byKind.set(CompletionItemKind.Enum, Codicon.symbolEnum); + byKind.set(CompletionItemKind.EnumMember, Codicon.symbolEnumMember); + byKind.set(CompletionItemKind.Keyword, Codicon.symbolKeyword); + byKind.set(CompletionItemKind.Snippet, Codicon.symbolSnippet); + byKind.set(CompletionItemKind.Text, Codicon.symbolText); + byKind.set(CompletionItemKind.Color, Codicon.symbolColor); + byKind.set(CompletionItemKind.File, Codicon.symbolFile); + byKind.set(CompletionItemKind.Reference, Codicon.symbolReference); + byKind.set(CompletionItemKind.Customcolor, Codicon.symbolCustomColor); + byKind.set(CompletionItemKind.Folder, Codicon.symbolFolder); + byKind.set(CompletionItemKind.TypeParameter, Codicon.symbolTypeParameter); + byKind.set(CompletionItemKind.User, Codicon.account); + byKind.set(CompletionItemKind.Issue, Codicon.issues); + + /** + * @internal + */ + export function toIcon(kind: CompletionItemKind): ThemeIcon { + let codicon = byKind.get(kind); + if (!codicon) { + console.info('No codicon found for CompletionItemKind ' + kind); + codicon = Codicon.symbolProperty; + } + return codicon; + } + + /** + * @internal + */ + export function toLabel(kind: CompletionItemKind): string { + switch (kind) { + case CompletionItemKind.Method: return localize('suggestWidget.kind.method', 'Method'); + case CompletionItemKind.Function: return localize('suggestWidget.kind.function', 'Function'); + case CompletionItemKind.Constructor: return localize('suggestWidget.kind.constructor', 'Constructor'); + case CompletionItemKind.Field: return localize('suggestWidget.kind.field', 'Field'); + case CompletionItemKind.Variable: return localize('suggestWidget.kind.variable', 'Variable'); + case CompletionItemKind.Class: return localize('suggestWidget.kind.class', 'Class'); + case CompletionItemKind.Struct: return localize('suggestWidget.kind.struct', 'Struct'); + case CompletionItemKind.Interface: return localize('suggestWidget.kind.interface', 'Interface'); + case CompletionItemKind.Module: return localize('suggestWidget.kind.module', 'Module'); + case CompletionItemKind.Property: return localize('suggestWidget.kind.property', 'Property'); + case CompletionItemKind.Event: return localize('suggestWidget.kind.event', 'Event'); + case CompletionItemKind.Operator: return localize('suggestWidget.kind.operator', 'Operator'); + case CompletionItemKind.Unit: return localize('suggestWidget.kind.unit', 'Unit'); + case CompletionItemKind.Value: return localize('suggestWidget.kind.value', 'Value'); + case CompletionItemKind.Constant: return localize('suggestWidget.kind.constant', 'Constant'); + case CompletionItemKind.Enum: return localize('suggestWidget.kind.enum', 'Enum'); + case CompletionItemKind.EnumMember: return localize('suggestWidget.kind.enumMember', 'Enum Member'); + case CompletionItemKind.Keyword: return localize('suggestWidget.kind.keyword', 'Keyword'); + case CompletionItemKind.Text: return localize('suggestWidget.kind.text', 'Text'); + case CompletionItemKind.Color: return localize('suggestWidget.kind.color', 'Color'); + case CompletionItemKind.File: return localize('suggestWidget.kind.file', 'File'); + case CompletionItemKind.Reference: return localize('suggestWidget.kind.reference', 'Reference'); + case CompletionItemKind.Customcolor: return localize('suggestWidget.kind.customcolor', 'Custom Color'); + case CompletionItemKind.Folder: return localize('suggestWidget.kind.folder', 'Folder'); + case CompletionItemKind.TypeParameter: return localize('suggestWidget.kind.typeParameter', 'Type Parameter'); + case CompletionItemKind.User: return localize('suggestWidget.kind.user', 'User'); + case CompletionItemKind.Issue: return localize('suggestWidget.kind.issue', 'Issue'); + case CompletionItemKind.Snippet: return localize('suggestWidget.kind.snippet', 'Snippet'); + default: return ''; + } + } + + const data = new Map(); + data.set('method', CompletionItemKind.Method); + data.set('function', CompletionItemKind.Function); + data.set('constructor', CompletionItemKind.Constructor); + data.set('field', CompletionItemKind.Field); + data.set('variable', CompletionItemKind.Variable); + data.set('class', CompletionItemKind.Class); + data.set('struct', CompletionItemKind.Struct); + data.set('interface', CompletionItemKind.Interface); + data.set('module', CompletionItemKind.Module); + data.set('property', CompletionItemKind.Property); + data.set('event', CompletionItemKind.Event); + data.set('operator', CompletionItemKind.Operator); + data.set('unit', CompletionItemKind.Unit); + data.set('value', CompletionItemKind.Value); + data.set('constant', CompletionItemKind.Constant); + data.set('enum', CompletionItemKind.Enum); + data.set('enum-member', CompletionItemKind.EnumMember); + data.set('enumMember', CompletionItemKind.EnumMember); + data.set('keyword', CompletionItemKind.Keyword); + data.set('snippet', CompletionItemKind.Snippet); + data.set('text', CompletionItemKind.Text); + data.set('color', CompletionItemKind.Color); + data.set('file', CompletionItemKind.File); + data.set('reference', CompletionItemKind.Reference); + data.set('customcolor', CompletionItemKind.Customcolor); + data.set('folder', CompletionItemKind.Folder); + data.set('type-parameter', CompletionItemKind.TypeParameter); + data.set('typeParameter', CompletionItemKind.TypeParameter); + data.set('account', CompletionItemKind.User); + data.set('issue', CompletionItemKind.Issue); + + /** + * @internal + */ + export function fromString(value: string): CompletionItemKind; + /** + * @internal + */ + export function fromString(value: string, strict: true): CompletionItemKind | undefined; + /** + * @internal + */ + export function fromString(value: string, strict?: boolean): CompletionItemKind | undefined { + let res = data.get(value); + if (typeof res === 'undefined' && !strict) { + res = CompletionItemKind.Property; + } + return res; + } +} + +export interface CompletionItemLabel { + label: string; + detail?: string; + description?: string; +} + +export const enum CompletionItemTag { + Deprecated = 1 +} + +export const enum CompletionItemInsertTextRule { + None = 0, + + /** + * Adjust whitespace/indentation of multiline insert texts to + * match the current line indentation. + */ + KeepWhitespace = 0b001, + + /** + * `insertText` is a snippet. + */ + InsertAsSnippet = 0b100, +} + +export interface CompletionItemRanges { + insert: IRange; + replace: IRange; +} + +/** + * A completion item represents a text snippet that is + * proposed to complete text that is being typed. + */ +export interface CompletionItem { + /** + * The label of this completion item. By default + * this is also the text that is inserted when selecting + * this completion. + */ + label: string | CompletionItemLabel; + /** + * The kind of this completion item. Based on the kind + * an icon is chosen by the editor. + */ + kind: CompletionItemKind; + /** + * A modifier to the `kind` which affect how the item + * is rendered, e.g. Deprecated is rendered with a strikeout + */ + tags?: ReadonlyArray; + /** + * A human-readable string with additional information + * about this item, like type or symbol information. + */ + detail?: string; + /** + * A human-readable string that represents a doc-comment. + */ + documentation?: string | IMarkdownString; + /** + * A string that should be used when comparing this item + * with other items. When `falsy` the {@link CompletionItem.label label} + * is used. + */ + sortText?: string; + /** + * A string that should be used when filtering a set of + * completion items. When `falsy` the {@link CompletionItem.label label} + * is used. + */ + filterText?: string; + /** + * Select this item when showing. *Note* that only one completion item can be selected and + * that the editor decides which item that is. The rule is that the *first* item of those + * that match best is selected. + */ + preselect?: boolean; + /** + * A string or snippet that should be inserted in a document when selecting + * this completion. + */ + insertText: string; + /** + * Additional rules (as bitmask) that should be applied when inserting + * this completion. + */ + insertTextRules?: CompletionItemInsertTextRule; + /** + * A range of text that should be replaced by this completion item. + * + * *Note:* The range must be a {@link Range.isSingleLine single line} and it must + * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. + */ + range: IRange | CompletionItemRanges; + /** + * An optional set of characters that when pressed while this completion is active will accept it first and + * then type that character. *Note* that all commit characters should have `length=1` and that superfluous + * characters will be ignored. + */ + commitCharacters?: string[]; + /** + * An optional array of additional text edits that are applied when + * selecting this completion. Edits must not overlap with the main edit + * nor with themselves. + */ + additionalTextEdits?: ISingleEditOperation[]; + /** + * A command that should be run upon acceptance of this item. + */ + command?: Command; + /** + * A command that should be run upon acceptance of this item. + */ + action?: Command; + /** + * @internal + */ + extensionId?: ExtensionIdentifier; + + /** + * @internal + */ + _id?: [number, number]; +} + +export interface CompletionList { + suggestions: CompletionItem[]; + incomplete?: boolean; + dispose?(): void; + + /** + * @internal + */ + duration?: number; +} + +/** + * Info provided on partial acceptance. + */ +export interface PartialAcceptInfo { + kind: PartialAcceptTriggerKind; + acceptedLength: number; +} + +/** + * How a partial acceptance was triggered. + */ +export const enum PartialAcceptTriggerKind { + Word = 0, + Line = 1, + Suggest = 2, +} + +/** + * How a suggest provider was triggered. + */ +export const enum CompletionTriggerKind { + Invoke = 0, + TriggerCharacter = 1, + TriggerForIncompleteCompletions = 2 +} +/** + * Contains additional information about the context in which + * {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered. + */ +export interface CompletionContext { + /** + * How the completion was triggered. + */ + triggerKind: CompletionTriggerKind; + /** + * Character that triggered the completion item provider. + * + * `undefined` if provider was not triggered by a character. + */ + triggerCharacter?: string; +} +/** + * The completion item provider interface defines the contract between extensions and + * the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). + * + * When computing *complete* completion items is expensive, providers can optionally implement + * the `resolveCompletionItem`-function. In that case it is enough to return completion + * items with a {@link CompletionItem.label label} from the + * {@link CompletionItemProvider.provideCompletionItems provideCompletionItems}-function. Subsequently, + * when a completion item is shown in the UI and gains focus this provider is asked to resolve + * the item, like adding {@link CompletionItem.documentation doc-comment} or {@link CompletionItem.detail details}. + */ +export interface CompletionItemProvider { + + /** + * Used to identify completions in the (debug) UI and telemetry. This isn't the extension identifier because extensions + * often contribute multiple completion item providers. + * + * @internal + */ + _debugDisplayName: string; + + triggerCharacters?: string[]; + /** + * Provide completion items for the given position and document. + */ + provideCompletionItems(model: model.ITextModel, position: Position, context: CompletionContext, token: CancellationToken): ProviderResult; + + /** + * Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment} + * or {@link CompletionItem.detail details}. + * + * The editor will only resolve a completion item once. + */ + resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult; +} + +/** + * How an {@link InlineCompletionsProvider inline completion provider} was triggered. + */ +export enum InlineCompletionTriggerKind { + /** + * Completion was triggered automatically while editing. + * It is sufficient to return a single completion item in this case. + */ + Automatic = 0, + + /** + * Completion was triggered explicitly by a user gesture. + * Return multiple completion items to enable cycling through them. + */ + Explicit = 1, +} + +export interface InlineCompletionContext { + + /** + * How the completion was triggered. + */ + readonly triggerKind: InlineCompletionTriggerKind; + readonly selectedSuggestionInfo: SelectedSuggestionInfo | undefined; + /** + * @experimental + * @internal + */ + readonly userPrompt?: string | undefined; + /** + * @experimental + * @internal + */ + readonly requestUuid?: string | undefined; + + readonly includeInlineEdits: boolean; + readonly includeInlineCompletions: boolean; +} + +export class SelectedSuggestionInfo { + constructor( + public readonly range: IRange, + public readonly text: string, + public readonly completionKind: CompletionItemKind, + public readonly isSnippetText: boolean, + ) { + } + + public equals(other: SelectedSuggestionInfo) { + return Range.lift(this.range).equalsRange(other.range) + && this.text === other.text + && this.completionKind === other.completionKind + && this.isSnippetText === other.isSnippetText; + } +} + +export interface InlineCompletion { + /** + * The text to insert. + * If the text contains a line break, the range must end at the end of a line. + * If existing text should be replaced, the existing text must be a prefix of the text to insert. + * + * The text can also be a snippet. In that case, a preview with default parameters is shown. + * When accepting the suggestion, the full snippet is inserted. + */ + readonly insertText: string | { snippet: string }; + + /** + * A text that is used to decide if this inline completion should be shown. + * An inline completion is shown if the text to replace is a subword of the filter text. + */ + readonly filterText?: string; + + /** + * An optional array of additional text edits that are applied when + * selecting this completion. Edits must not overlap with the main edit + * nor with themselves. + */ + readonly additionalTextEdits?: ISingleEditOperation[]; + + /** + * The range to replace. + * Must begin and end on the same line. + */ + readonly range?: IRange; + + readonly command?: Command; + + readonly action?: Command; + + /** + * Is called the first time an inline completion is shown. + * @deprecated. Use `onDidShow` of the provider instead. + */ + readonly shownCommand?: Command; + + /** + * If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed. + * Defaults to `false`. + */ + readonly completeBracketPairs?: boolean; + + readonly isInlineEdit?: boolean; + readonly showInlineEditMenu?: boolean; + + readonly showRange?: IRange; + + readonly warning?: InlineCompletionWarning; +} + +export interface InlineCompletionWarning { + message: IMarkdownString | string; + icon?: IconPath; +} + +/** + * TODO: add `| URI | { light: URI; dark: URI }`. +*/ +export type IconPath = ThemeIcon; + +export interface InlineCompletions { + readonly items: readonly TItem[]; + /** + * A list of commands associated with the inline completions of this list. + */ + readonly commands?: Command[]; + + readonly suppressSuggestions?: boolean | undefined; + + /** + * When set and the user types a suggestion without derivating from it, the inline suggestion is not updated. + */ + readonly enableForwardStability?: boolean | undefined; +} + +export type InlineCompletionProviderGroupId = string; + +export interface InlineCompletionsProvider { + provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + + /** + * @experimental + * @internal + */ + provideInlineEditsForRange?(model: model.ITextModel, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult; + + /** + * Will be called when an item is shown. + * @param updatedInsertText Is useful to understand bracket completion. + */ + handleItemDidShow?(completions: T, item: T['items'][number], updatedInsertText: string): void; + + /** + * Will be called when an item is partially accepted. TODO: also handle full acceptance here! + * @param acceptedCharacters Deprecated. Use `info.acceptedCharacters` instead. + */ + handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number, info: PartialAcceptInfo): void; + + handleRejection?(completions: T, item: T['items'][number]): void; + + /** + * Will be called when a completions list is no longer in use and can be garbage-collected. + */ + freeInlineCompletions(completions: T): void; + + /** + * Only used for {@link yieldsToGroupIds}. + * Multiple providers can have the same group id. + */ + groupId?: InlineCompletionProviderGroupId; + + /** + * Returns a list of preferred provider {@link groupId}s. + * The current provider is only requested for completions if no provider with a preferred group id returned a result. + */ + yieldsToGroupIds?: InlineCompletionProviderGroupId[]; + + displayName?: string; + + debounceDelayMs?: number; + + toString?(): string; +} + +export interface CodeAction { + title: string; + command?: Command; + edit?: WorkspaceEdit; + diagnostics?: IMarkerData[]; + kind?: string; + isPreferred?: boolean; + isAI?: boolean; + disabled?: string; + ranges?: IRange[]; +} + +export const enum CodeActionTriggerType { + Invoke = 1, + Auto = 2, +} + +/** + * @internal + */ +export interface CodeActionContext { + only?: string; + trigger: CodeActionTriggerType; +} + +export interface CodeActionList extends IDisposable { + readonly actions: ReadonlyArray; +} + +/** + * The code action interface defines the contract between extensions and + * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. + * @internal + */ +export interface CodeActionProvider { + + displayName?: string; + + extensionId?: string; + + /** + * Provide commands for the given document and range. + */ + provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult; + + /** + * Given a code action fill in the edit. Will only invoked when missing. + */ + resolveCodeAction?(codeAction: CodeAction, token: CancellationToken): ProviderResult; + + /** + * Optional list of CodeActionKinds that this provider returns. + */ + readonly providedCodeActionKinds?: ReadonlyArray; + + readonly documentation?: ReadonlyArray<{ readonly kind: string; readonly command: Command }>; + + /** + * @internal + */ + _getAdditionalMenuItems?(context: CodeActionContext, actions: readonly CodeAction[]): Command[]; +} + +/** + * @internal + */ +export interface DocumentPasteEdit { + readonly title: string; + readonly kind: HierarchicalKind; + readonly handledMimeType?: string; + yieldTo?: readonly DropYieldTo[]; + insertText: string | { readonly snippet: string }; + additionalEdit?: WorkspaceEdit; +} + +/** + * @internal + */ +export enum DocumentPasteTriggerKind { + Automatic = 0, + PasteAs = 1, +} + +/** + * @internal + */ +export interface DocumentPasteContext { + readonly only?: HierarchicalKind; + readonly triggerKind: DocumentPasteTriggerKind; +} + +/** + * @internal + */ +export interface DocumentPasteEditsSession { + edits: readonly DocumentPasteEdit[]; + dispose(): void; +} + +/** + * @internal + */ +export interface DocumentPasteEditProvider { + readonly id?: string; + readonly copyMimeTypes: readonly string[]; + readonly pasteMimeTypes: readonly string[]; + readonly providedPasteEditKinds: readonly HierarchicalKind[]; + + prepareDocumentPaste?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise; + + provideDocumentPasteEdits?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise; + + resolveDocumentPasteEdit?(edit: DocumentPasteEdit, token: CancellationToken): Promise; +} + +/** + * Represents a parameter of a callable-signature. A parameter can + * have a label and a doc-comment. + */ +export interface ParameterInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string | [number, number]; + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | IMarkdownString; +} +/** + * Represents the signature of something callable. A signature + * can have a label, like a function-name, a doc-comment, and + * a set of parameters. + */ +export interface SignatureInformation { + /** + * The label of this signature. Will be shown in + * the UI. + */ + label: string; + /** + * The human-readable doc-comment of this signature. Will be shown + * in the UI but can be omitted. + */ + documentation?: string | IMarkdownString; + /** + * The parameters of this signature. + */ + parameters: ParameterInformation[]; + /** + * Index of the active parameter. + * + * If provided, this is used in place of `SignatureHelp.activeSignature`. + */ + activeParameter?: number; +} +/** + * Signature help represents the signature of something + * callable. There can be multiple signatures but only one + * active and only one active parameter. + */ +export interface SignatureHelp { + /** + * One or more signatures. + */ + signatures: SignatureInformation[]; + /** + * The active signature. + */ + activeSignature: number; + /** + * The active parameter of the active signature. + */ + activeParameter: number; +} + +export interface SignatureHelpResult extends IDisposable { + value: SignatureHelp; +} + +export enum SignatureHelpTriggerKind { + Invoke = 1, + TriggerCharacter = 2, + ContentChange = 3, +} + +export interface SignatureHelpContext { + readonly triggerKind: SignatureHelpTriggerKind; + readonly triggerCharacter?: string; + readonly isRetrigger: boolean; + readonly activeSignatureHelp?: SignatureHelp; +} + +/** + * The signature help provider interface defines the contract between extensions and + * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. + */ +export interface SignatureHelpProvider { + + readonly signatureHelpTriggerCharacters?: ReadonlyArray; + readonly signatureHelpRetriggerCharacters?: ReadonlyArray; + + /** + * Provide help for the signature at the given position and document. + */ + provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; +} + +/** + * A document highlight kind. + */ +export enum DocumentHighlightKind { + /** + * A textual occurrence. + */ + Text, + /** + * Read-access of a symbol, like reading a variable. + */ + Read, + /** + * Write-access of a symbol, like writing to a variable. + */ + Write +} +/** + * A document highlight is a range inside a text document which deserves + * special attention. Usually a document highlight is visualized by changing + * the background color of its range. + */ +export interface DocumentHighlight { + /** + * The range this highlight applies to. + */ + range: IRange; + /** + * The highlight kind, default is {@link DocumentHighlightKind.Text text}. + */ + kind?: DocumentHighlightKind; +} + +/** + * Represents a set of document highlights for a specific URI. + */ +export interface MultiDocumentHighlight { + /** + * The URI of the document that the highlights belong to. + */ + uri: URI; + + /** + * The set of highlights for the document. + */ + highlights: DocumentHighlight[]; +} + +/** + * The document highlight provider interface defines the contract between extensions and + * the word-highlight-feature. + */ +export interface DocumentHighlightProvider { + /** + * Provide a set of document highlights, like all occurrences of a variable or + * all exit-points of a function. + */ + provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * A provider that can provide document highlights across multiple documents. + */ +export interface MultiDocumentHighlightProvider { + readonly selector: LanguageSelector; + + /** + * Provide a Map of URI --> document highlights, like all occurrences of a variable or + * all exit-points of a function. + * + * Used in cases such as split view, notebooks, etc. where there can be multiple documents + * with shared symbols. + * + * @param primaryModel The primary text model. + * @param position The position at which to provide document highlights. + * @param otherModels The other text models to search for document highlights. + * @param token A cancellation token. + * @returns A map of URI to document highlights. + */ + provideMultiDocumentHighlights(primaryModel: model.ITextModel, position: Position, otherModels: model.ITextModel[], token: CancellationToken): ProviderResult>; +} + +/** + * The linked editing range provider interface defines the contract between extensions and + * the linked editing feature. + */ +export interface LinkedEditingRangeProvider { + + /** + * Provide a list of ranges that can be edited together. + */ + provideLinkedEditingRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. + */ +export interface LinkedEditingRanges { + /** + * A list of ranges that can be edited together. The ranges must have + * identical length and text content. The ranges cannot overlap + */ + ranges: IRange[]; + + /** + * An optional word pattern that describes valid contents for the given ranges. + * If no pattern is provided, the language configuration's word pattern will be used. + */ + wordPattern?: RegExp; +} + +/** + * Value-object that contains additional information when + * requesting references. + */ +export interface ReferenceContext { + /** + * Include the declaration of the current symbol. + */ + includeDeclaration: boolean; +} +/** + * The reference provider interface defines the contract between extensions and + * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. + */ +export interface ReferenceProvider { + /** + * Provide a set of project-wide references for the given position and document. + */ + provideReferences(model: model.ITextModel, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; +} + +/** + * Represents a location inside a resource, such as a line + * inside a text file. + */ +export interface Location { + /** + * The resource identifier of this location. + */ + uri: URI; + /** + * The document range of this locations. + */ + range: IRange; +} + +export interface LocationLink { + /** + * A range to select where this link originates from. + */ + originSelectionRange?: IRange; + + /** + * The target uri this link points to. + */ + uri: URI; + + /** + * The full range this link points to. + */ + range: IRange; + + /** + * A range to select this link points to. Must be contained + * in `LocationLink.range`. + */ + targetSelectionRange?: IRange; +} + +/** + * @internal + */ +export function isLocationLink(thing: any): thing is LocationLink { + return thing + && URI.isUri((thing as LocationLink).uri) + && Range.isIRange((thing as LocationLink).range) + && (Range.isIRange((thing as LocationLink).originSelectionRange) || Range.isIRange((thing as LocationLink).targetSelectionRange)); +} + +/** + * @internal + */ +export function isLocation(thing: any): thing is Location { + return thing + && URI.isUri((thing as Location).uri) + && Range.isIRange((thing as Location).range); +} + + +export type Definition = Location | Location[] | LocationLink[]; + +/** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ +export interface DefinitionProvider { + /** + * Provide the definition of the symbol at the given position and document. + */ + provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * The definition provider interface defines the contract between extensions and + * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) + * and peek definition features. + */ +export interface DeclarationProvider { + /** + * Provide the declaration of the symbol at the given position and document. + */ + provideDeclaration(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * The implementation provider interface defines the contract between extensions and + * the go to implementation feature. + */ +export interface ImplementationProvider { + /** + * Provide the implementation of the symbol at the given position and document. + */ + provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * The type definition provider interface defines the contract between extensions and + * the go to type definition feature. + */ +export interface TypeDefinitionProvider { + /** + * Provide the type definition of the symbol at the given position and document. + */ + provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +/** + * A symbol kind. + */ +export const enum SymbolKind { + File = 0, + Module = 1, + Namespace = 2, + Package = 3, + Class = 4, + Method = 5, + Property = 6, + Field = 7, + Constructor = 8, + Enum = 9, + Interface = 10, + Function = 11, + Variable = 12, + Constant = 13, + String = 14, + Number = 15, + Boolean = 16, + Array = 17, + Object = 18, + Key = 19, + Null = 20, + EnumMember = 21, + Struct = 22, + Event = 23, + Operator = 24, + TypeParameter = 25 +} + +/** + * @internal + */ +export const symbolKindNames: { [symbol: number]: string } = { + [SymbolKind.Array]: localize('Array', "array"), + [SymbolKind.Boolean]: localize('Boolean', "boolean"), + [SymbolKind.Class]: localize('Class', "class"), + [SymbolKind.Constant]: localize('Constant', "constant"), + [SymbolKind.Constructor]: localize('Constructor', "constructor"), + [SymbolKind.Enum]: localize('Enum', "enumeration"), + [SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"), + [SymbolKind.Event]: localize('Event', "event"), + [SymbolKind.Field]: localize('Field', "field"), + [SymbolKind.File]: localize('File', "file"), + [SymbolKind.Function]: localize('Function', "function"), + [SymbolKind.Interface]: localize('Interface', "interface"), + [SymbolKind.Key]: localize('Key', "key"), + [SymbolKind.Method]: localize('Method', "method"), + [SymbolKind.Module]: localize('Module', "module"), + [SymbolKind.Namespace]: localize('Namespace', "namespace"), + [SymbolKind.Null]: localize('Null', "null"), + [SymbolKind.Number]: localize('Number', "number"), + [SymbolKind.Object]: localize('Object', "object"), + [SymbolKind.Operator]: localize('Operator', "operator"), + [SymbolKind.Package]: localize('Package', "package"), + [SymbolKind.Property]: localize('Property', "property"), + [SymbolKind.String]: localize('String', "string"), + [SymbolKind.Struct]: localize('Struct', "struct"), + [SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"), + [SymbolKind.Variable]: localize('Variable', "variable"), +}; + +/** + * @internal + */ +export function getAriaLabelForSymbol(symbolName: string, kind: SymbolKind): string { + return localize('symbolAriaLabel', '{0} ({1})', symbolName, symbolKindNames[kind]); +} + +export const enum SymbolTag { + Deprecated = 1, +} + +/** + * @internal + */ +export namespace SymbolKinds { + + const byKind = new Map(); + byKind.set(SymbolKind.File, Codicon.symbolFile); + byKind.set(SymbolKind.Module, Codicon.symbolModule); + byKind.set(SymbolKind.Namespace, Codicon.symbolNamespace); + byKind.set(SymbolKind.Package, Codicon.symbolPackage); + byKind.set(SymbolKind.Class, Codicon.symbolClass); + byKind.set(SymbolKind.Method, Codicon.symbolMethod); + byKind.set(SymbolKind.Property, Codicon.symbolProperty); + byKind.set(SymbolKind.Field, Codicon.symbolField); + byKind.set(SymbolKind.Constructor, Codicon.symbolConstructor); + byKind.set(SymbolKind.Enum, Codicon.symbolEnum); + byKind.set(SymbolKind.Interface, Codicon.symbolInterface); + byKind.set(SymbolKind.Function, Codicon.symbolFunction); + byKind.set(SymbolKind.Variable, Codicon.symbolVariable); + byKind.set(SymbolKind.Constant, Codicon.symbolConstant); + byKind.set(SymbolKind.String, Codicon.symbolString); + byKind.set(SymbolKind.Number, Codicon.symbolNumber); + byKind.set(SymbolKind.Boolean, Codicon.symbolBoolean); + byKind.set(SymbolKind.Array, Codicon.symbolArray); + byKind.set(SymbolKind.Object, Codicon.symbolObject); + byKind.set(SymbolKind.Key, Codicon.symbolKey); + byKind.set(SymbolKind.Null, Codicon.symbolNull); + byKind.set(SymbolKind.EnumMember, Codicon.symbolEnumMember); + byKind.set(SymbolKind.Struct, Codicon.symbolStruct); + byKind.set(SymbolKind.Event, Codicon.symbolEvent); + byKind.set(SymbolKind.Operator, Codicon.symbolOperator); + byKind.set(SymbolKind.TypeParameter, Codicon.symbolTypeParameter); + /** + * @internal + */ + export function toIcon(kind: SymbolKind): ThemeIcon { + let icon = byKind.get(kind); + if (!icon) { + console.info('No codicon found for SymbolKind ' + kind); + icon = Codicon.symbolProperty; + } + return icon; + } + + const byCompletionKind = new Map(); + byCompletionKind.set(SymbolKind.File, CompletionItemKind.File); + byCompletionKind.set(SymbolKind.Module, CompletionItemKind.Module); + byCompletionKind.set(SymbolKind.Namespace, CompletionItemKind.Module); + byCompletionKind.set(SymbolKind.Package, CompletionItemKind.Module); + byCompletionKind.set(SymbolKind.Class, CompletionItemKind.Class); + byCompletionKind.set(SymbolKind.Method, CompletionItemKind.Method); + byCompletionKind.set(SymbolKind.Property, CompletionItemKind.Property); + byCompletionKind.set(SymbolKind.Field, CompletionItemKind.Field); + byCompletionKind.set(SymbolKind.Constructor, CompletionItemKind.Constructor); + byCompletionKind.set(SymbolKind.Enum, CompletionItemKind.Enum); + byCompletionKind.set(SymbolKind.Interface, CompletionItemKind.Interface); + byCompletionKind.set(SymbolKind.Function, CompletionItemKind.Function); + byCompletionKind.set(SymbolKind.Variable, CompletionItemKind.Variable); + byCompletionKind.set(SymbolKind.Constant, CompletionItemKind.Constant); + byCompletionKind.set(SymbolKind.String, CompletionItemKind.Text); + byCompletionKind.set(SymbolKind.Number, CompletionItemKind.Value); + byCompletionKind.set(SymbolKind.Boolean, CompletionItemKind.Value); + byCompletionKind.set(SymbolKind.Array, CompletionItemKind.Value); + byCompletionKind.set(SymbolKind.Object, CompletionItemKind.Value); + byCompletionKind.set(SymbolKind.Key, CompletionItemKind.Keyword); + byCompletionKind.set(SymbolKind.Null, CompletionItemKind.Value); + byCompletionKind.set(SymbolKind.EnumMember, CompletionItemKind.EnumMember); + byCompletionKind.set(SymbolKind.Struct, CompletionItemKind.Struct); + byCompletionKind.set(SymbolKind.Event, CompletionItemKind.Event); + byCompletionKind.set(SymbolKind.Operator, CompletionItemKind.Operator); + byCompletionKind.set(SymbolKind.TypeParameter, CompletionItemKind.TypeParameter); + /** + * @internal + */ + export function toCompletionKind(kind: SymbolKind): CompletionItemKind { + let completionKind = byCompletionKind.get(kind); + if (completionKind === undefined) { + console.info('No completion kind found for SymbolKind ' + kind); + completionKind = CompletionItemKind.File; + } + return completionKind; + } +} + +export interface DocumentSymbol { + name: string; + detail: string; + kind: SymbolKind; + tags: ReadonlyArray; + containerName?: string; + range: IRange; + selectionRange: IRange; + children?: DocumentSymbol[]; +} + +/** + * The document symbol provider interface defines the contract between extensions and + * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. + */ +export interface DocumentSymbolProvider { + + displayName?: string; + + /** + * Provide symbol information for the given document. + */ + provideDocumentSymbols(model: model.ITextModel, token: CancellationToken): ProviderResult; +} + +export interface TextEdit { + range: IRange; + text: string; + eol?: model.EndOfLineSequence; +} + +/** @internal */ +export abstract class TextEdit { + static asEditOperation(edit: TextEdit): ISingleEditOperation { + return EditOperation.replace(Range.lift(edit.range), edit.text); + } + static isTextEdit(thing: any): thing is TextEdit { + const possibleTextEdit = thing as TextEdit; + return typeof possibleTextEdit.text === 'string' && Range.isIRange(possibleTextEdit.range); + } +} + +/** + * Interface used to format a model + */ +export interface FormattingOptions { + /** + * Size of a tab in spaces. + */ + tabSize: number; + /** + * Prefer spaces over tabs. + */ + insertSpaces: boolean; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface DocumentFormattingEditProvider { + + /** + * @internal + */ + readonly extensionId?: ExtensionIdentifier; + + readonly displayName?: string; + + /** + * Provide formatting edits for a whole document. + */ + provideDocumentFormattingEdits(model: model.ITextModel, options: FormattingOptions, token: CancellationToken): ProviderResult; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface DocumentRangeFormattingEditProvider { + /** + * @internal + */ + readonly extensionId?: ExtensionIdentifier; + + readonly displayName?: string; + + /** + * Provide formatting edits for a range in a document. + * + * The given range is a hint and providers can decide to format a smaller + * or larger range. Often this is done by adjusting the start and end + * of the range to full syntax nodes. + */ + provideDocumentRangeFormattingEdits(model: model.ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; + + provideDocumentRangesFormattingEdits?(model: model.ITextModel, ranges: Range[], options: FormattingOptions, token: CancellationToken): ProviderResult; +} +/** + * The document formatting provider interface defines the contract between extensions and + * the formatting-feature. + */ +export interface OnTypeFormattingEditProvider { + + + /** + * @internal + */ + readonly extensionId?: ExtensionIdentifier; + + autoFormatTriggerCharacters: string[]; + + /** + * Provide formatting edits after a character has been typed. + * + * The given position and character should hint to the provider + * what range the position to expand to, like find the matching `{` + * when `}` has been entered. + */ + provideOnTypeFormattingEdits(model: model.ITextModel, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; +} + +/** + * @internal + */ +export interface IInplaceReplaceSupportResult { + value: string; + range: IRange; +} + +/** + * A link inside the editor. + */ +export interface ILink { + range: IRange; + url?: URI | string; + tooltip?: string; +} + +export interface ILinksList { + links: ILink[]; + dispose?(): void; +} +/** + * A provider of links. + */ +export interface LinkProvider { + provideLinks(model: model.ITextModel, token: CancellationToken): ProviderResult; + resolveLink?: (link: ILink, token: CancellationToken) => ProviderResult; +} + +/** + * A color in RGBA format. + */ +export interface IColor { + + /** + * The red component in the range [0-1]. + */ + readonly red: number; + + /** + * The green component in the range [0-1]. + */ + readonly green: number; + + /** + * The blue component in the range [0-1]. + */ + readonly blue: number; + + /** + * The alpha component in the range [0-1]. + */ + readonly alpha: number; +} + +/** + * String representations for a color + */ +export interface IColorPresentation { + /** + * The label of this color presentation. It will be shown on the color + * picker header. By default this is also the text that is inserted when selecting + * this color presentation. + */ + label: string; + /** + * An {@link TextEdit edit} which is applied to a document when selecting + * this presentation for the color. + */ + textEdit?: TextEdit; + /** + * An optional array of additional {@link TextEdit text edits} that are applied when + * selecting this color presentation. + */ + additionalTextEdits?: TextEdit[]; +} + +/** + * A color range is a range in a text model which represents a color. + */ +export interface IColorInformation { + + /** + * The range within the model. + */ + range: IRange; + + /** + * The color represented in this range. + */ + color: IColor; +} + +/** + * A provider of colors for editor models. + */ +export interface DocumentColorProvider { + /** + * Provides the color ranges for a specific model. + */ + provideDocumentColors(model: model.ITextModel, token: CancellationToken): ProviderResult; + /** + * Provide the string representations for a color. + */ + provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): ProviderResult; +} + +export interface SelectionRange { + range: IRange; +} + +export interface SelectionRangeProvider { + /** + * Provide ranges that should be selected from the given position. + */ + provideSelectionRanges(model: model.ITextModel, positions: Position[], token: CancellationToken): ProviderResult; +} + +export interface FoldingContext { +} +/** + * A provider of folding ranges for editor models. + */ +export interface FoldingRangeProvider { + + /** + * @internal + */ + readonly id?: string; + + /** + * An optional event to signal that the folding ranges from this provider have changed. + */ + onDidChange?: Event; + + /** + * Provides the folding ranges for a specific model. + */ + provideFoldingRanges(model: model.ITextModel, context: FoldingContext, token: CancellationToken): ProviderResult; +} + +export interface FoldingRange { + + /** + * The one-based start line of the range to fold. The folded area starts after the line's last character. + */ + start: number; + + /** + * The one-based end line of the range to fold. The folded area ends with the line's last character. + */ + end: number; + + /** + * Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or + * {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands + * like 'Fold all comments'. See + * {@link FoldingRangeKind} for an enumeration of standardized kinds. + */ + kind?: FoldingRangeKind; +} +export class FoldingRangeKind { + /** + * Kind for folding range representing a comment. The value of the kind is 'comment'. + */ + static readonly Comment = new FoldingRangeKind('comment'); + /** + * Kind for folding range representing a import. The value of the kind is 'imports'. + */ + static readonly Imports = new FoldingRangeKind('imports'); + /** + * Kind for folding range representing regions (for example marked by `#region`, `#endregion`). + * The value of the kind is 'region'. + */ + static readonly Region = new FoldingRangeKind('region'); + + /** + * Returns a {@link FoldingRangeKind} for the given value. + * + * @param value of the kind. + */ + static fromValue(value: string) { + switch (value) { + case 'comment': return FoldingRangeKind.Comment; + case 'imports': return FoldingRangeKind.Imports; + case 'region': return FoldingRangeKind.Region; + } + return new FoldingRangeKind(value); + } + + /** + * Creates a new {@link FoldingRangeKind}. + * + * @param value of the kind. + */ + public constructor(public value: string) { + } +} + + +export interface WorkspaceEditMetadata { + needsConfirmation: boolean; + label: string; + description?: string; + /** + * @internal + */ + iconPath?: ThemeIcon | URI | { light: URI; dark: URI }; +} + +export interface WorkspaceFileEditOptions { + overwrite?: boolean; + ignoreIfNotExists?: boolean; + ignoreIfExists?: boolean; + recursive?: boolean; + copy?: boolean; + folder?: boolean; + skipTrashBin?: boolean; + maxSize?: number; + + /** + * @internal + */ + contents?: Promise; +} + +export interface IWorkspaceFileEdit { + oldResource?: URI; + newResource?: URI; + options?: WorkspaceFileEditOptions; + metadata?: WorkspaceEditMetadata; +} + +export interface IWorkspaceTextEdit { + resource: URI; + textEdit: TextEdit & { insertAsSnippet?: boolean; keepWhitespace?: boolean }; + versionId: number | undefined; + metadata?: WorkspaceEditMetadata; +} + +export interface WorkspaceEdit { + edits: Array; +} + +export interface ICustomEdit { + readonly resource: URI; + readonly metadata?: WorkspaceEditMetadata; + undo(): Promise | void; + redo(): Promise | void; +} + +export interface Rejection { + rejectReason?: string; +} +export interface RenameLocation { + range: IRange; + text: string; +} + +export interface RenameProvider { + provideRenameEdits(model: model.ITextModel, position: Position, newName: string, token: CancellationToken): ProviderResult; + resolveRenameLocation?(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; +} + +export enum NewSymbolNameTag { + AIGenerated = 1 +} + +export enum NewSymbolNameTriggerKind { + Invoke = 0, + Automatic = 1, +} + +export interface NewSymbolName { + readonly newSymbolName: string; + readonly tags?: readonly NewSymbolNameTag[]; +} + +export interface NewSymbolNamesProvider { + supportsAutomaticNewSymbolNamesTriggerKind?: Promise; + provideNewSymbolNames(model: model.ITextModel, range: IRange, triggerKind: NewSymbolNameTriggerKind, token: CancellationToken): ProviderResult; +} + +export interface Command { + id: string; + title: string; + tooltip?: string; + arguments?: any[]; +} + +/** + * @internal + */ +export namespace Command { + + /** + * @internal + */ + export function is(obj: any): obj is Command { + if (!obj || typeof obj !== 'object') { + return false; + } + return typeof (obj).id === 'string' && + typeof (obj).title === 'string'; + } +} + +/** + * @internal + */ +export interface CommentThreadTemplate { + controllerHandle: number; + label: string; + acceptInputCommand?: Command; + additionalCommands?: Command[]; + deleteCommand?: Command; +} + +/** + * @internal + */ +export interface CommentInfo { + extensionId?: string; + threads: CommentThread[]; + pendingCommentThreads?: PendingCommentThread[]; + commentingRanges: CommentingRanges; +} + + +/** + * @internal + */ +export interface CommentingRangeResourceHint { + schemes: readonly string[]; +} + +/** + * @internal + */ +export enum CommentThreadCollapsibleState { + /** + * Determines an item is collapsed + */ + Collapsed = 0, + /** + * Determines an item is expanded + */ + Expanded = 1 +} + +/** + * @internal + */ +export enum CommentThreadState { + Unresolved = 0, + Resolved = 1 +} + +/** + * @internal + */ +export enum CommentThreadApplicability { + Current = 0, + Outdated = 1 +} + +/** + * @internal + */ +export interface CommentWidget { + commentThread: CommentThread; + comment?: Comment; + input: string; + onDidChangeInput: Event; +} + +/** + * @internal + */ +export interface CommentInput { + value: string; + uri: URI; +} + +export interface CommentThreadRevealOptions { + preserveFocus: boolean; + focusReply: boolean; +} + +/** + * @internal + */ +export interface CommentThread { + isDocumentCommentThread(): this is CommentThread; + commentThreadHandle: number; + controllerHandle: number; + extensionId?: string; + threadId: string; + resource: string | null; + range: T | undefined; + label: string | undefined; + contextValue: string | undefined; + comments: ReadonlyArray | undefined; + onDidChangeComments: Event; + collapsibleState?: CommentThreadCollapsibleState; + initialCollapsibleState?: CommentThreadCollapsibleState; + onDidChangeInitialCollapsibleState: Event; + state?: CommentThreadState; + applicability?: CommentThreadApplicability; + canReply: boolean; + input?: CommentInput; + onDidChangeInput: Event; + onDidChangeLabel: Event; + onDidChangeCollapsibleState: Event; + onDidChangeState: Event; + onDidChangeCanReply: Event; + isDisposed: boolean; + isTemplate: boolean; +} + +/** + * @internal + */ +export interface AddedCommentThread extends CommentThread { + editorId?: string; +} + +/** + * @internal + */ + +export interface CommentingRanges { + readonly resource: URI; + ranges: IRange[]; + fileComments: boolean; +} + +export interface CommentAuthorInformation { + name: string; + iconPath?: UriComponents; + +} + +/** + * @internal + */ +export interface CommentReaction { + readonly label?: string; + readonly iconPath?: UriComponents; + readonly count?: number; + readonly hasReacted?: boolean; + readonly canEdit?: boolean; + readonly reactors?: readonly string[]; +} + +/** + * @internal + */ +export interface CommentOptions { + /** + * An optional string to show on the comment input box when it's collapsed. + */ + prompt?: string; + + /** + * An optional string to show as placeholder in the comment input box when it's focused. + */ + placeHolder?: string; +} + +/** + * @internal + */ +export enum CommentMode { + Editing = 0, + Preview = 1 +} + +/** + * @internal + */ +export enum CommentState { + Published = 0, + Draft = 1 +} + +/** + * @internal + */ +export interface Comment { + readonly uniqueIdInThread: number; + readonly body: string | IMarkdownString; + readonly userName: string; + readonly userIconPath?: UriComponents; + readonly contextValue?: string; + readonly commentReactions?: CommentReaction[]; + readonly label?: string; + readonly mode?: CommentMode; + readonly timestamp?: string; +} + +export interface PendingCommentThread { + range: IRange | undefined; + uri: URI; + uniqueOwner: string; + isReply: boolean; + comment: PendingComment; +} + +export interface PendingComment { + body: string; + cursor: IPosition; +} + +/** + * @internal + */ +export interface CommentThreadChangedEvent { + /** + * Pending comment threads. + */ + readonly pending: PendingCommentThread[]; + + /** + * Added comment threads. + */ + readonly added: AddedCommentThread[]; + + /** + * Removed comment threads. + */ + readonly removed: CommentThread[]; + + /** + * Changed comment threads. + */ + readonly changed: CommentThread[]; +} + +export interface CodeLens { + range: IRange; + id?: string; + command?: Command; +} + +export interface CodeLensList { + lenses: CodeLens[]; + dispose?(): void; +} + +export interface CodeLensProvider { + onDidChange?: Event; + provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult; + resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; +} + + +export enum InlayHintKind { + Type = 1, + Parameter = 2, +} + +export interface InlayHintLabelPart { + label: string; + tooltip?: string | IMarkdownString; + // collapsible?: boolean; + command?: Command; + location?: Location; +} + +export interface InlayHint { + label: string | InlayHintLabelPart[]; + tooltip?: string | IMarkdownString; + textEdits?: TextEdit[]; + position: IPosition; + kind?: InlayHintKind; + paddingLeft?: boolean; + paddingRight?: boolean; +} + +export interface InlayHintList { + hints: InlayHint[]; + dispose(): void; +} + +export interface InlayHintsProvider { + displayName?: string; + onDidChangeInlayHints?: Event; + provideInlayHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; + resolveInlayHint?(hint: InlayHint, token: CancellationToken): ProviderResult; +} + +export interface SemanticTokensLegend { + readonly tokenTypes: string[]; + readonly tokenModifiers: string[]; +} + +export interface SemanticTokens { + readonly resultId?: string; + readonly data: Uint32Array; +} + +export interface SemanticTokensEdit { + readonly start: number; + readonly deleteCount: number; + readonly data?: Uint32Array; +} + +export interface SemanticTokensEdits { + readonly resultId?: string; + readonly edits: SemanticTokensEdit[]; +} + +export interface DocumentSemanticTokensProvider { + onDidChange?: Event; + getLegend(): SemanticTokensLegend; + provideDocumentSemanticTokens(model: model.ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult; + releaseDocumentSemanticTokens(resultId: string | undefined): void; +} + +export interface DocumentRangeSemanticTokensProvider { + getLegend(): SemanticTokensLegend; + provideDocumentRangeSemanticTokens(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; +} + +/** + * @internal + */ +export interface ITokenizationSupportChangedEvent { + changedLanguages: string[]; + changedColorMap: boolean; +} + +/** + * @internal + */ +export interface ILazyTokenizationSupport { + get tokenizationSupport(): Promise; +} + +/** + * @internal + */ +export class LazyTokenizationSupport implements IDisposable, ILazyTokenizationSupport { + private _tokenizationSupport: Promise | null = null; + + constructor(private readonly createSupport: () => Promise) { + } + + dispose(): void { + if (this._tokenizationSupport) { + this._tokenizationSupport.then((support) => { + if (support) { + support.dispose(); + } + }); + } + } + + get tokenizationSupport(): Promise { + if (!this._tokenizationSupport) { + this._tokenizationSupport = this.createSupport(); + } + return this._tokenizationSupport; + } +} + +/** + * @internal + */ +export interface ITokenizationRegistry { + + /** + * An event triggered when: + * - a tokenization support is registered, unregistered or changed. + * - the color map is changed. + */ + onDidChange: Event; + + /** + * Fire a change event for a language. + * This is useful for languages that embed other languages. + */ + handleChange(languageIds: string[]): void; + + /** + * Register a tokenization support. + */ + register(languageId: string, support: TSupport): IDisposable; + + /** + * Register a tokenization support factory. + */ + registerFactory(languageId: string, factory: ILazyTokenizationSupport): IDisposable; + + /** + * Get or create the tokenization support for a language. + * Returns `null` if not found. + */ + getOrCreate(languageId: string): Promise; + + /** + * Get the tokenization support for a language. + * Returns `null` if not found. + */ + get(languageId: string): TSupport | null; + + /** + * Returns false if a factory is still pending. + */ + isResolved(languageId: string): boolean; + + /** + * Set the new color map that all tokens will use in their ColorId binary encoded bits for foreground and background. + */ + setColorMap(colorMap: Color[]): void; + + getColorMap(): Color[] | null; + + getDefaultBackground(): Color | null; +} + +/** + * @internal + */ +export const TokenizationRegistry: ITokenizationRegistry = new TokenizationRegistryImpl(); + +/** + * @internal + */ +export const TreeSitterTokenizationRegistry: ITokenizationRegistry = new TokenizationRegistryImpl(); + +/** + * @internal + */ +export enum ExternalUriOpenerPriority { + None = 0, + Option = 1, + Default = 2, + Preferred = 3, +} + +/** + * @internal + */ +export type DropYieldTo = { readonly kind: HierarchicalKind } | { readonly mimeType: string }; + +/** + * @internal + */ +export interface DocumentDropEdit { + readonly title: string; + readonly kind: HierarchicalKind | undefined; + readonly handledMimeType?: string; + readonly yieldTo?: readonly DropYieldTo[]; + insertText: string | { readonly snippet: string }; + additionalEdit?: WorkspaceEdit; +} + +/** + * @internal + */ +export interface DocumentDropEditsSession { + edits: readonly DocumentDropEdit[]; + dispose(): void; +} + +/** + * @internal + */ +export interface DocumentDropEditProvider { + readonly id?: string; + readonly dropMimeTypes?: readonly string[]; + readonly providedDropEditKinds?: readonly HierarchicalKind[]; + + provideDocumentDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult; + resolveDocumentDropEdit?(edit: DocumentDropEdit, token: CancellationToken): Promise; +} + +export interface IInlineEdit { + text: string; + range: IRange; + showRange?: IRange; + accepted?: Command; + rejected?: Command; + shown?: Command; + commands?: Command[]; + action?: Command; +} + +export interface IInlineEditContext { + triggerKind: InlineEditTriggerKind; + + /** + * @experimental + * @internal + */ + requestUuid?: string; +} + +export enum InlineEditTriggerKind { + Invoke = 0, + Automatic = 1, +} + +export interface InlineEditProvider { + displayName?: string; + provideInlineEdit(model: model.ITextModel, context: IInlineEditContext, token: CancellationToken): ProviderResult; + freeInlineEdit(edit: T): void; +} diff --git a/src/vs/editor/common/language/model.ts b/src/vs/editor/common/language/model.ts new file mode 100644 index 00000000000..91d17b431ce --- /dev/null +++ b/src/vs/editor/common/language/model.ts @@ -0,0 +1,1523 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../base/common/event.js'; +import { IMarkdownString } from '../../../base/common/htmlContent.js'; +import { IDisposable } from '../../../base/common/lifecycle.js'; +import { equals } from '../../../base/common/objects.js'; +import { ThemeColor } from '../../../base/common/themables.js'; +import { URI } from '../../../base/common/uri.js'; +import { ISingleEditOperation } from './core/editOperation.js'; +import { IPosition, Position } from './core/position.js'; +import { IRange, Range } from './core/range.js'; +import { Selection } from './core/selection.js'; +import { TextChange } from './core/textChange.js'; +import { WordCharacterClassifier } from './core/wordCharacterClassifier.js'; +import { IWordAtPosition } from './core/wordHelper.js'; +import { FormattingOptions } from './languages.js'; +import { ILanguageSelection } from './language.js'; +import { IBracketPairsTextModelPart } from '../../../editor/common/textModelBracketPairs.js'; +import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelInjectedTextChangedEvent } from './textModelEvents.js'; +import { IGuidesTextModelPart } from '../../../editor/common/textModelGuides.js'; +import { ITokenizationTextModelPart } from './tokenizationTextModelPart.js'; +import { UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js'; +import { TokenArray } from './tokens/tokenArray.js'; +import { IEditorModel } from '../editorCommon.js'; + +/** + * Vertical Lane in the overview ruler of the editor. + */ +export enum OverviewRulerLane { + Left = 1, + Center = 2, + Right = 4, + Full = 7 +} + +/** + * Vertical Lane in the glyph margin of the editor. + */ +export enum GlyphMarginLane { + Left = 1, + Center = 2, + Right = 3, +} + +export interface IGlyphMarginLanesModel { + /** + * The number of lanes that should be rendered in the editor. + */ + readonly requiredLanes: number; + + /** + * Gets the lanes that should be rendered starting at a given line number. + */ + getLanesAtLine(lineNumber: number): GlyphMarginLane[]; + + /** + * Resets the model and ensures it can contain at least `maxLine` lines. + */ + reset(maxLine: number): void; + + /** + * Registers that a lane should be visible at the Range in the model. + * @param persist - if true, notes that the lane should always be visible, + * even on lines where there's no specific request for that lane. + */ + push(lane: GlyphMarginLane, range: Range, persist?: boolean): void; +} + +/** + * Position in the minimap to render the decoration. + */ +export const enum MinimapPosition { + Inline = 1, + Gutter = 2 +} + +/** + * Section header style. + */ +export const enum MinimapSectionHeaderStyle { + Normal = 1, + Underlined = 2 +} + +export interface IDecorationOptions { + /** + * CSS color to render. + * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry + */ + color: string | ThemeColor | undefined; + /** + * CSS color to render. + * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry + */ + darkColor?: string | ThemeColor; +} + +export interface IModelDecorationGlyphMarginOptions { + /** + * The position in the glyph margin. + */ + position: GlyphMarginLane; + + /** + * Whether the glyph margin lane in {@link position} should be rendered even + * outside of this decoration's range. + */ + persistLane?: boolean; +} + +/** + * Options for rendering a model decoration in the overview ruler. + */ +export interface IModelDecorationOverviewRulerOptions extends IDecorationOptions { + /** + * The position in the overview ruler. + */ + position: OverviewRulerLane; +} + +/** + * Options for rendering a model decoration in the minimap. + */ +export interface IModelDecorationMinimapOptions extends IDecorationOptions { + /** + * The position in the minimap. + */ + position: MinimapPosition; + /** + * If the decoration is for a section header, which header style. + */ + sectionHeaderStyle?: MinimapSectionHeaderStyle | null; + /** + * If the decoration is for a section header, the header text. + */ + sectionHeaderText?: string | null; +} + +/** + * Options for a model decoration. + */ +export interface IModelDecorationOptions { + /** + * A debug description that can be used for inspecting model decorations. + * @internal + */ + description: string; + /** + * Customize the growing behavior of the decoration when typing at the edges of the decoration. + * Defaults to TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges + */ + stickiness?: TrackedRangeStickiness; + /** + * CSS class name describing the decoration. + */ + className?: string | null; + /** + * Indicates whether the decoration should span across the entire line when it continues onto the next line. + */ + shouldFillLineOnLineBreak?: boolean | null; + blockClassName?: string | null; + /** + * Indicates if this block should be rendered after the last line. + * In this case, the range must be empty and set to the last line. + */ + blockIsAfterEnd?: boolean | null; + blockDoesNotCollapse?: boolean | null; + blockPadding?: [top: number, right: number, bottom: number, left: number] | null; + + /** + * Message to be rendered when hovering over the glyph margin decoration. + */ + glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[] | null; + /** + * Array of MarkdownString to render as the decoration message. + */ + hoverMessage?: IMarkdownString | IMarkdownString[] | null; + /** + * Array of MarkdownString to render as the line number message. + */ + lineNumberHoverMessage?: IMarkdownString | IMarkdownString[] | null; + /** + * Should the decoration expand to encompass a whole line. + */ + isWholeLine?: boolean; + /** + * Always render the decoration (even when the range it encompasses is collapsed). + */ + showIfCollapsed?: boolean; + /** + * Collapse the decoration if its entire range is being replaced via an edit. + * @internal + */ + collapseOnReplaceEdit?: boolean; + /** + * Specifies the stack order of a decoration. + * A decoration with greater stack order is always in front of a decoration with + * a lower stack order when the decorations are on the same line. + */ + zIndex?: number; + /** + * If set, render this decoration in the overview ruler. + */ + overviewRuler?: IModelDecorationOverviewRulerOptions | null; + /** + * If set, render this decoration in the minimap. + */ + minimap?: IModelDecorationMinimapOptions | null; + /** + * If set, the decoration will be rendered in the glyph margin with this CSS class name. + */ + glyphMarginClassName?: string | null; + /** + * If set and the decoration has {@link glyphMarginClassName} set, render this decoration + * with the specified {@link IModelDecorationGlyphMarginOptions} in the glyph margin. + */ + glyphMargin?: IModelDecorationGlyphMarginOptions | null; + /** + * If set, the decoration will be rendered in the lines decorations with this CSS class name. + */ + linesDecorationsClassName?: string | null; + /** + * Controls the tooltip text of the line decoration. + */ + linesDecorationsTooltip?: string | null; + /** + * If set, the decoration will be rendered on the line number. + */ + lineNumberClassName?: string | null; + /** + * If set, the decoration will be rendered in the lines decorations with this CSS class name, but only for the first line in case of line wrapping. + */ + firstLineDecorationClassName?: string | null; + /** + * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. + */ + marginClassName?: string | null; + /** + * If set, the decoration will be rendered inline with the text with this CSS class name. + * Please use this only for CSS rules that must impact the text. For example, use `className` + * to have a background color decoration. + */ + inlineClassName?: string | null; + /** + * If there is an `inlineClassName` which affects letter spacing. + */ + inlineClassNameAffectsLetterSpacing?: boolean; + /** + * If set, the decoration will be rendered before the text with this CSS class name. + */ + beforeContentClassName?: string | null; + /** + * If set, the decoration will be rendered after the text with this CSS class name. + */ + afterContentClassName?: string | null; + /** + * If set, text will be injected in the view after the range. + */ + after?: InjectedTextOptions | null; + + /** + * If set, text will be injected in the view before the range. + */ + before?: InjectedTextOptions | null; + + /** + * If set, this decoration will not be rendered for comment tokens. + * @internal + */ + hideInCommentTokens?: boolean | null; + + /** + * If set, this decoration will not be rendered for string tokens. + * @internal + */ + hideInStringTokens?: boolean | null; +} + +/** + * Configures text that is injected into the view without changing the underlying document. +*/ +export interface InjectedTextOptions { + /** + * Sets the text to inject. Must be a single line. + */ + readonly content: string; + + /** + * @internal + */ + readonly tokens?: TokenArray | null; + + /** + * If set, the decoration will be rendered inline with the text with this CSS class name. + */ + readonly inlineClassName?: string | null; + + /** + * If there is an `inlineClassName` which affects letter spacing. + */ + readonly inlineClassNameAffectsLetterSpacing?: boolean; + + /** + * This field allows to attach data to this injected text. + * The data can be read when injected texts at a given position are queried. + */ + readonly attachedData?: unknown; + + /** + * Configures cursor stops around injected text. + * Defaults to {@link InjectedTextCursorStops.Both}. + */ + readonly cursorStops?: InjectedTextCursorStops | null; +} + +export enum InjectedTextCursorStops { + Both, + Right, + Left, + None +} + +/** + * New model decorations. + */ +export interface IModelDeltaDecoration { + /** + * Range that this decoration covers. + */ + range: IRange; + /** + * Options associated with this decoration. + */ + options: IModelDecorationOptions; +} + +/** + * A decoration in the model. + */ +export interface IModelDecoration { + /** + * Identifier for a decoration. + */ + readonly id: string; + /** + * Identifier for a decoration's owner. + */ + readonly ownerId: number; + /** + * Range that this decoration covers. + */ + readonly range: Range; + /** + * Options associated with this decoration. + */ + readonly options: IModelDecorationOptions; +} + +/** + * An accessor that can add, change or remove model decorations. + * @internal + */ +export interface IModelDecorationsChangeAccessor { + /** + * Add a new decoration. + * @param range Range that this decoration covers. + * @param options Options associated with this decoration. + * @return An unique identifier associated with this decoration. + */ + addDecoration(range: IRange, options: IModelDecorationOptions): string; + /** + * Change the range that an existing decoration covers. + * @param id The unique identifier associated with the decoration. + * @param newRange The new range that this decoration covers. + */ + changeDecoration(id: string, newRange: IRange): void; + /** + * Change the options associated with an existing decoration. + * @param id The unique identifier associated with the decoration. + * @param newOptions The new options associated with this decoration. + */ + changeDecorationOptions(id: string, newOptions: IModelDecorationOptions): void; + /** + * Remove an existing decoration. + * @param id The unique identifier associated with the decoration. + */ + removeDecoration(id: string): void; + /** + * Perform a minimum amount of operations, in order to transform the decorations + * identified by `oldDecorations` to the decorations described by `newDecorations` + * and returns the new identifiers associated with the resulting decorations. + * + * @param oldDecorations Array containing previous decorations identifiers. + * @param newDecorations Array describing what decorations should result after the call. + * @return An array containing the new decorations identifiers. + */ + deltaDecorations(oldDecorations: readonly string[], newDecorations: readonly IModelDeltaDecoration[]): string[]; +} + +/** + * End of line character preference. + */ +export const enum EndOfLinePreference { + /** + * Use the end of line character identified in the text buffer. + */ + TextDefined = 0, + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * The default end of line to use when instantiating models. + */ +export const enum DefaultEndOfLine { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 1, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 2 +} + +/** + * End of line character preference. + */ +export const enum EndOfLineSequence { + /** + * Use line feed (\n) as the end of line character. + */ + LF = 0, + /** + * Use carriage return and line feed (\r\n) as the end of line character. + */ + CRLF = 1 +} + +/** + * An identifier for a single edit operation. + * @internal + */ +export interface ISingleEditOperationIdentifier { + /** + * Identifier major + */ + major: number; + /** + * Identifier minor + */ + minor: number; +} + +/** + * A single edit operation, that has an identifier. + */ +export interface IIdentifiedSingleEditOperation extends ISingleEditOperation { + /** + * An identifier associated with this single edit operation. + * @internal + */ + identifier?: ISingleEditOperationIdentifier | null; + /** + * This indicates that this operation is inserting automatic whitespace + * that can be removed on next model edit operation if `config.trimAutoWhitespace` is true. + * @internal + */ + isAutoWhitespaceEdit?: boolean; + /** + * This indicates that this operation is in a set of operations that are tracked and should not be "simplified". + * @internal + */ + _isTracked?: boolean; +} + +export interface IValidEditOperation { + /** + * An identifier associated with this single edit operation. + * @internal + */ + identifier: ISingleEditOperationIdentifier | null; + /** + * The range to replace. This can be empty to emulate a simple insert. + */ + range: Range; + /** + * The text to replace with. This can be empty to emulate a simple delete. + */ + text: string; + /** + * @internal + */ + textChange: TextChange; +} + +/** + * A callback that can compute the cursor state after applying a series of edit operations. + */ +export interface ICursorStateComputer { + /** + * A callback that can compute the resulting cursors state after some edit operations have been executed. + */ + (inverseEditOperations: IValidEditOperation[]): Selection[] | null; +} + +export class TextModelResolvedOptions { + _textModelResolvedOptionsBrand: void = undefined; + + readonly tabSize: number; + readonly indentSize: number; + private readonly _indentSizeIsTabSize: boolean; + readonly insertSpaces: boolean; + readonly defaultEOL: DefaultEndOfLine; + readonly trimAutoWhitespace: boolean; + readonly bracketPairColorizationOptions: BracketPairColorizationOptions; + + public get originalIndentSize(): number | 'tabSize' { + return this._indentSizeIsTabSize ? 'tabSize' : this.indentSize; + } + + /** + * @internal + */ + constructor(src: { + tabSize: number; + indentSize: number | 'tabSize'; + insertSpaces: boolean; + defaultEOL: DefaultEndOfLine; + trimAutoWhitespace: boolean; + bracketPairColorizationOptions: BracketPairColorizationOptions; + }) { + this.tabSize = Math.max(1, src.tabSize | 0); + if (src.indentSize === 'tabSize') { + this.indentSize = this.tabSize; + this._indentSizeIsTabSize = true; + } else { + this.indentSize = Math.max(1, src.indentSize | 0); + this._indentSizeIsTabSize = false; + } + this.insertSpaces = Boolean(src.insertSpaces); + this.defaultEOL = src.defaultEOL | 0; + this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); + this.bracketPairColorizationOptions = src.bracketPairColorizationOptions; + } + + /** + * @internal + */ + public equals(other: TextModelResolvedOptions): boolean { + return ( + this.tabSize === other.tabSize + && this._indentSizeIsTabSize === other._indentSizeIsTabSize + && this.indentSize === other.indentSize + && this.insertSpaces === other.insertSpaces + && this.defaultEOL === other.defaultEOL + && this.trimAutoWhitespace === other.trimAutoWhitespace + && equals(this.bracketPairColorizationOptions, other.bracketPairColorizationOptions) + ); + } + + /** + * @internal + */ + public createChangeEvent(newOpts: TextModelResolvedOptions): IModelOptionsChangedEvent { + return { + tabSize: this.tabSize !== newOpts.tabSize, + indentSize: this.indentSize !== newOpts.indentSize, + insertSpaces: this.insertSpaces !== newOpts.insertSpaces, + trimAutoWhitespace: this.trimAutoWhitespace !== newOpts.trimAutoWhitespace, + }; + } +} + +/** + * @internal + */ +export interface ITextModelCreationOptions { + tabSize: number; + indentSize: number | 'tabSize'; + insertSpaces: boolean; + detectIndentation: boolean; + trimAutoWhitespace: boolean; + defaultEOL: DefaultEndOfLine; + isForSimpleWidget: boolean; + largeFileOptimizations: boolean; + bracketPairColorizationOptions: BracketPairColorizationOptions; +} + +export interface BracketPairColorizationOptions { + enabled: boolean; + independentColorPoolPerBracketType: boolean; +} + +export interface ITextModelUpdateOptions { + tabSize?: number; + indentSize?: number | 'tabSize'; + insertSpaces?: boolean; + trimAutoWhitespace?: boolean; + bracketColorizationOptions?: BracketPairColorizationOptions; +} + +export class FindMatch { + _findMatchBrand: void = undefined; + + public readonly range: Range; + public readonly matches: string[] | null; + + /** + * @internal + */ + constructor(range: Range, matches: string[] | null) { + this.range = range; + this.matches = matches; + } +} + +/** + * Describes the behavior of decorations when typing/editing near their edges. + * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` + */ +export const enum TrackedRangeStickiness { + AlwaysGrowsWhenTypingAtEdges = 0, + NeverGrowsWhenTypingAtEdges = 1, + GrowsOnlyWhenTypingBefore = 2, + GrowsOnlyWhenTypingAfter = 3, +} + +/** + * Text snapshot that works like an iterator. + * Will try to return chunks of roughly ~64KB size. + * Will return null when finished. + */ +export interface ITextSnapshot { + read(): string | null; +} + +/** + * @internal + */ +export function isITextSnapshot(obj: any): obj is ITextSnapshot { + return (obj && typeof obj.read === 'function'); +} + +/** + * A model. + */ +export interface ITextModel { + + /** + * Gets the resource associated with this editor model. + */ + readonly uri: URI; + + /** + * A unique identifier associated with this model. + */ + readonly id: string; + + /** + * This model is constructed for a simple widget code editor. + * @internal + */ + readonly isForSimpleWidget: boolean; + + /** + * If true, the text model might contain RTL. + * If false, the text model **contains only** contain LTR. + * @internal + */ + mightContainRTL(): boolean; + + /** + * If true, the text model might contain LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). + * If false, the text model definitely does not contain these. + * @internal + */ + mightContainUnusualLineTerminators(): boolean; + + /** + * @internal + */ + removeUnusualLineTerminators(selections?: Selection[]): void; + + /** + * If true, the text model might contain non basic ASCII. + * If false, the text model **contains only** basic ASCII. + * @internal + */ + mightContainNonBasicASCII(): boolean; + + /** + * Get the resolved options for this model. + */ + getOptions(): TextModelResolvedOptions; + + /** + * Get the formatting options for this model. + * @internal + */ + getFormattingOptions(): FormattingOptions; + + /** + * Get the current version id of the model. + * Anytime a change happens to the model (even undo/redo), + * the version id is incremented. + */ + getVersionId(): number; + + /** + * Get the alternative version id of the model. + * This alternative version id is not always incremented, + * it will return the same values in the case of undo-redo. + */ + getAlternativeVersionId(): number; + + /** + * Replace the entire text buffer value contained in this model. + */ + setValue(newValue: string | ITextSnapshot): void; + + /** + * Get the text stored in this model. + * @param eol The end of line character preference. Defaults to `EndOfLinePreference.TextDefined`. + * @param preserverBOM Preserve a BOM character if it was detected when the model was constructed. + * @return The text. + */ + getValue(eol?: EndOfLinePreference, preserveBOM?: boolean): string; + + /** + * Get the text stored in this model. + * @param preserverBOM Preserve a BOM character if it was detected when the model was constructed. + * @return The text snapshot (it is safe to consume it asynchronously). + */ + createSnapshot(preserveBOM?: boolean): ITextSnapshot; + + /** + * Get the length of the text stored in this model. + */ + getValueLength(eol?: EndOfLinePreference, preserveBOM?: boolean): number; + + /** + * Check if the raw text stored in this model equals another raw text. + * @internal + */ + equalsTextBuffer(other: ITextBuffer): boolean; + + /** + * Get the underling text buffer. + * @internal + */ + getTextBuffer(): ITextBuffer; + + /** + * Get the text in a certain range. + * @param range The range describing what text to get. + * @param eol The end of line character preference. This will only be used for multiline ranges. Defaults to `EndOfLinePreference.TextDefined`. + * @return The text. + */ + getValueInRange(range: IRange, eol?: EndOfLinePreference): string; + + /** + * Get the length of text in a certain range. + * @param range The range describing what text length to get. + * @return The text length. + */ + getValueLengthInRange(range: IRange, eol?: EndOfLinePreference): number; + + /** + * Get the character count of text in a certain range. + * @param range The range describing what text length to get. + */ + getCharacterCountInRange(range: IRange, eol?: EndOfLinePreference): number; + + /** + * Splits characters in two buckets. First bucket (A) is of characters that + * sit in lines with length < `LONG_LINE_BOUNDARY`. Second bucket (B) is of + * characters that sit in lines with length >= `LONG_LINE_BOUNDARY`. + * If count(B) > count(A) return true. Returns false otherwise. + * @internal + */ + isDominatedByLongLines(): boolean; + + /** + * Get the number of lines in the model. + */ + getLineCount(): number; + + /** + * Get the text for a certain line. + */ + getLineContent(lineNumber: number): string; + + /** + * Get the text length for a certain line. + */ + getLineLength(lineNumber: number): number; + + /** + * Get the text for all lines. + */ + getLinesContent(): string[]; + + /** + * Get the end of line sequence predominantly used in the text buffer. + * @return EOL char sequence (e.g.: '\n' or '\r\n'). + */ + getEOL(): string; + + /** + * Get the end of line sequence predominantly used in the text buffer. + */ + getEndOfLineSequence(): EndOfLineSequence; + + /** + * Get the minimum legal column for line at `lineNumber` + */ + getLineMinColumn(lineNumber: number): number; + + /** + * Get the maximum legal column for line at `lineNumber` + */ + getLineMaxColumn(lineNumber: number): number; + + /** + * Returns the column before the first non whitespace character for line at `lineNumber`. + * Returns 0 if line is empty or contains only whitespace. + */ + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + + /** + * Returns the column after the last non whitespace character for line at `lineNumber`. + * Returns 0 if line is empty or contains only whitespace. + */ + getLineLastNonWhitespaceColumn(lineNumber: number): number; + + /** + * Create a valid position. + */ + validatePosition(position: IPosition): Position; + + /** + * Advances the given position by the given offset (negative offsets are also accepted) + * and returns it as a new valid position. + * + * If the offset and position are such that their combination goes beyond the beginning or + * end of the model, throws an exception. + * + * If the offset is such that the new position would be in the middle of a multi-byte + * line terminator, throws an exception. + */ + modifyPosition(position: IPosition, offset: number): Position; + + /** + * Create a valid range. + */ + validateRange(range: IRange): Range; + + /** + * Verifies the range is valid. + */ + isValidRange(range: IRange): boolean; + + /** + * Converts the position to a zero-based offset. + * + * The position will be [adjusted](#TextDocument.validatePosition). + * + * @param position A position. + * @return A valid zero-based offset. + */ + getOffsetAt(position: IPosition): number; + + /** + * Converts a zero-based offset to a position. + * + * @param offset A zero-based offset. + * @return A valid [position](#Position). + */ + getPositionAt(offset: number): Position; + + /** + * Get a range covering the entire model. + */ + getFullModelRange(): Range; + + /** + * Returns if the model was disposed or not. + */ + isDisposed(): boolean; + + /** + * This model is so large that it would not be a good idea to sync it over + * to web workers or other places. + * @internal + */ + isTooLargeForSyncing(): boolean; + + /** + * The file is so large, that even tokenization is disabled. + * @internal + */ + isTooLargeForTokenization(): boolean; + + /** + * The file is so large, that operations on it might be too large for heap + * and can lead to OOM crashes so they should be disabled. + * @internal + */ + isTooLargeForHeapOperation(): boolean; + + /** + * Search the model. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchOnlyEditableRange Limit the searching to only search inside the editable range of the model. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @param limitResultCount Limit the number of results + * @return The ranges where the matches are. It is empty if not matches have been found. + */ + findMatches(searchString: string, searchOnlyEditableRange: boolean, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + /** + * Search the model. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchScope Limit the searching to only search inside these ranges. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @param limitResultCount Limit the number of results + * @return The ranges where the matches are. It is empty if no matches have been found. + */ + findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; + /** + * Search the model for the next match. Loops to the beginning of the model if needed. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchStart Start the searching at the specified position. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @return The range where the next match is. It is null if no next match has been found. + */ + findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; + /** + * Search the model for the previous match. Loops to the end of the model if needed. + * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. + * @param searchStart Start the searching at the specified position. + * @param isRegex Used to indicate that `searchString` is a regular expression. + * @param matchCase Force the matching to match lower/upper case exactly. + * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. + * @param captureMatches The result will contain the captured groups. + * @return The range where the previous match is. It is null if no previous match has been found. + */ + findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; + + + /** + * Get the language associated with this model. + */ + getLanguageId(): string; + + /** + * Set the current language mode associated with the model. + * @param languageId The new language. + * @param source The source of the call that set the language. + * @internal + */ + setLanguage(languageId: string, source?: string): void; + + /** + * Set the current language mode associated with the model. + * @param languageSelection The new language selection. + * @param source The source of the call that set the language. + * @internal + */ + setLanguage(languageSelection: ILanguageSelection, source?: string): void; + + /** + * Returns the real (inner-most) language mode at a given position. + * The result might be inaccurate. Use `forceTokenization` to ensure accurate tokens. + * @internal + */ + getLanguageIdAtPosition(lineNumber: number, column: number): string; + + /** + * Get the word under or besides `position`. + * @param position The position to look for a word. + * @return The word under or besides `position`. Might be null. + */ + getWordAtPosition(position: IPosition): IWordAtPosition | null; + + /** + * Get the word under or besides `position` trimmed to `position`.column + * @param position The position to look for a word. + * @return The word under or besides `position`. Will never be null. + */ + getWordUntilPosition(position: IPosition): IWordAtPosition; + + /** + * Change the decorations. The callback will be called with a change accessor + * that becomes invalid as soon as the callback finishes executing. + * This allows for all events to be queued up until the change + * is completed. Returns whatever the callback returns. + * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. + * @internal + */ + changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T | null; + + /** + * Perform a minimum amount of operations, in order to transform the decorations + * identified by `oldDecorations` to the decorations described by `newDecorations` + * and returns the new identifiers associated with the resulting decorations. + * + * @param oldDecorations Array containing previous decorations identifiers. + * @param newDecorations Array describing what decorations should result after the call. + * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. + * @return An array containing the new decorations identifiers. + */ + deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[], ownerId?: number): string[]; + + /** + * Remove all decorations that have been added with this specific ownerId. + * @param ownerId The owner id to search for. + * @internal + */ + removeAllDecorationsWithOwnerId(ownerId: number): void; + + /** + * Get the options associated with a decoration. + * @param id The decoration id. + * @return The decoration options or null if the decoration was not found. + */ + getDecorationOptions(id: string): IModelDecorationOptions | null; + + /** + * Get the range associated with a decoration. + * @param id The decoration id. + * @return The decoration range or null if the decoration was not found. + */ + getDecorationRange(id: string): Range | null; + + /** + * Gets all the decorations for the line `lineNumber` as an array. + * @param lineNumber The line number + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @return An array with the decorations + */ + getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations for the lines between `startLineNumber` and `endLineNumber` as an array. + * @param startLineNumber The start line number + * @param endLineNumber The end line number + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @return An array with the decorations + */ + getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. + * So for now it returns all the decorations on the same line as `range`. + * @param range The range to search in + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + * @param onlyMinimapDecorations If set, it will return only decorations that render in the minimap. + * @param onlyMarginDecorations If set, it will return only decorations that render in the glyph margin. + * @return An array with the decorations + */ + getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + */ + getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all decorations that render in the glyph margin as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + */ + getAllMarginDecorations(ownerId?: number): IModelDecoration[]; + + /** + * Gets all the decorations that should be rendered in the overview ruler as an array. + * @param ownerId If set, it will ignore decorations belonging to other owners. + * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). + */ + getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; + + /** + * Gets all the decorations that contain injected text. + * @param ownerId If set, it will ignore decorations belonging to other owners. + */ + getInjectedTextDecorations(ownerId?: number): IModelDecoration[]; + + /** + * @internal + */ + _getTrackedRange(id: string): Range | null; + + /** + * @internal + */ + _setTrackedRange(id: string | null, newRange: null, newStickiness: TrackedRangeStickiness): null; + /** + * @internal + */ + _setTrackedRange(id: string | null, newRange: Range, newStickiness: TrackedRangeStickiness): string; + + /** + * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). + */ + normalizeIndentation(str: string): string; + + /** + * Change the options of this model. + */ + updateOptions(newOpts: ITextModelUpdateOptions): void; + + /** + * Detect the indentation options for this model from its content. + */ + detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; + + /** + * Close the current undo-redo element. + * This offers a way to create an undo/redo stop point. + */ + pushStackElement(): void; + + /** + * Open the current undo-redo element. + * This offers a way to remove the current undo/redo stop point. + */ + popStackElement(): void; + + /** + * Push edit operations, basically editing the model. This is the preferred way + * of editing the model. The edit operations will land on the undo stack. + * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. + * @param editOperations The edit operations. + * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. + * @return The cursor state returned by the `cursorStateComputer`. + */ + pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; + /** + * @internal + */ + pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer, group?: UndoRedoGroup): Selection[] | null; + + /** + * Change the end of line sequence. This is the preferred way of + * changing the eol sequence. This will land on the undo stack. + */ + pushEOL(eol: EndOfLineSequence): void; + + /** + * Edit the model without adding the edits to the undo stack. + * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. + * @param operations The edit operations. + * @return If desired, the inverse edit operations, that, when applied, will bring the model back to the previous state. + */ + applyEdits(operations: IIdentifiedSingleEditOperation[]): void; + applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: false): void; + applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: true): IValidEditOperation[]; + + /** + * Change the end of line sequence without recording in the undo stack. + * This can have dire consequences on the undo stack! See @pushEOL for the preferred way. + */ + setEOL(eol: EndOfLineSequence): void; + + /** + * @internal + */ + _applyUndo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + + /** + * @internal + */ + _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; + + /** + * Undo edit operations until the previous undo/redo point. + * The inverse edit operations will be pushed on the redo stack. + * @internal + */ + undo(): void | Promise; + + /** + * Is there anything in the undo stack? + * @internal + */ + canUndo(): boolean; + + /** + * Redo edit operations until the next undo/redo point. + * The inverse edit operations will be pushed on the undo stack. + * @internal + */ + redo(): void | Promise; + + /** + * Is there anything in the redo stack? + * @internal + */ + canRedo(): boolean; + + /** + * @deprecated Please use `onDidChangeContent` instead. + * An event emitted when the contents of the model have changed. + * @internal + * @event + */ + readonly onDidChangeContentOrInjectedText: Event; + /** + * An event emitted when the contents of the model have changed. + * @event + */ + onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable; + /** + * An event emitted when decorations of the model have changed. + * @event + */ + readonly onDidChangeDecorations: Event; + /** + * An event emitted when the model options have changed. + * @event + */ + readonly onDidChangeOptions: Event; + /** + * An event emitted when the language associated with the model has changed. + * @event + */ + readonly onDidChangeLanguage: Event; + /** + * An event emitted when the language configuration associated with the model has changed. + * @event + */ + readonly onDidChangeLanguageConfiguration: Event; + /** + * An event emitted when the tokens associated with the model have changed. + * @event + * @internal + */ + readonly onDidChangeTokens: Event; + /** + * An event emitted when the model has been attached to the first editor or detached from the last editor. + * @event + */ + readonly onDidChangeAttached: Event; + /** + * An event emitted right before disposing the model. + * @event + */ + readonly onWillDispose: Event; + + /** + * Destroy this model. + */ + dispose(): void; + + /** + * @internal + */ + onBeforeAttached(): IAttachedView; + + /** + * @internal + */ + onBeforeDetached(view: IAttachedView): void; + + /** + * Returns if this model is attached to an editor or not. + */ + isAttachedToEditor(): boolean; + + /** + * Returns the count of editors this model is attached to. + * @internal + */ + getAttachedEditorCount(): number; + + /** + * Among all positions that are projected to the same position in the underlying text model as + * the given position, select a unique position as indicated by the affinity. + * + * PositionAffinity.Left: + * The normalized position must be equal or left to the requested position. + * + * PositionAffinity.Right: + * The normalized position must be equal or right to the requested position. + * + * @internal + */ + normalizePosition(position: Position, affinity: PositionAffinity): Position; + + /** + * Gets the column at which indentation stops at a given line. + * @internal + */ + getLineIndentColumn(lineNumber: number): number; + + /** + * Returns an object that can be used to query brackets. + * @internal + */ + readonly bracketPairs: IBracketPairsTextModelPart; + + /** + * Returns an object that can be used to query indent guides. + * @internal + */ + readonly guides: IGuidesTextModelPart; + + /** + * @internal + */ + readonly tokenization: ITokenizationTextModelPart; +} + +/** + * @internal + */ +export function isITextModel(obj: IEditorModel): obj is ITextModel { + return Boolean(obj && (obj as ITextModel).uri); +} + +/** + * @internal + */ +export interface IAttachedView { + /** + * @param stabilized Indicates if the visible lines are probably going to change soon or can be considered stable. + * Is true on reveal range and false on scroll. + * Tokenizers should tokenize synchronously if stabilized is true. + */ + setVisibleLines(visibleLines: { startLineNumber: number; endLineNumber: number }[], stabilized: boolean): void; +} + +export const enum PositionAffinity { + /** + * Prefers the left most position. + */ + Left = 0, + + /** + * Prefers the right most position. + */ + Right = 1, + + /** + * No preference. + */ + None = 2, + + /** + * If the given position is on injected text, prefers the position left of it. + */ + LeftOfInjectedText = 3, + + /** + * If the given position is on injected text, prefers the position right of it. + */ + RightOfInjectedText = 4, +} + +/** + * @internal + */ +export interface ITextBufferBuilder { + acceptChunk(chunk: string): void; + finish(): ITextBufferFactory; +} + +/** + * @internal + */ +export interface ITextBufferFactory { + create(defaultEOL: DefaultEndOfLine): { textBuffer: ITextBuffer; disposable: IDisposable }; + getFirstLineText(lengthLimit: number): string; +} + +/** + * @internal + */ +export const enum ModelConstants { + FIRST_LINE_DETECTION_LENGTH_LIMIT = 1000 +} + +/** + * @internal + */ +export class ValidAnnotatedEditOperation implements IIdentifiedSingleEditOperation { + constructor( + public readonly identifier: ISingleEditOperationIdentifier | null, + public readonly range: Range, + public readonly text: string | null, + public readonly forceMoveMarkers: boolean, + public readonly isAutoWhitespaceEdit: boolean, + public readonly _isTracked: boolean, + ) { } +} + +/** + * @internal + * + * `lineNumber` is 1 based. + */ +export interface IReadonlyTextBuffer { + onDidChangeContent: Event; + equals(other: ITextBuffer): boolean; + mightContainRTL(): boolean; + mightContainUnusualLineTerminators(): boolean; + resetMightContainUnusualLineTerminators(): void; + mightContainNonBasicASCII(): boolean; + getBOM(): string; + getEOL(): string; + + getOffsetAt(lineNumber: number, column: number): number; + getPositionAt(offset: number): Position; + getRangeAt(offset: number, length: number): Range; + + getValueInRange(range: Range, eol: EndOfLinePreference): string; + createSnapshot(preserveBOM: boolean): ITextSnapshot; + getValueLengthInRange(range: Range, eol: EndOfLinePreference): number; + getCharacterCountInRange(range: Range, eol: EndOfLinePreference): number; + getLength(): number; + getLineCount(): number; + getLinesContent(): string[]; + getLineContent(lineNumber: number): string; + getLineCharCode(lineNumber: number, index: number): number; + getCharCode(offset: number): number; + getLineLength(lineNumber: number): number; + getLineMinColumn(lineNumber: number): number; + getLineMaxColumn(lineNumber: number): number; + getLineFirstNonWhitespaceColumn(lineNumber: number): number; + getLineLastNonWhitespaceColumn(lineNumber: number): number; + findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; + + /** + * Get nearest chunk of text after `offset` in the text buffer. + */ + getNearestChunk(offset: number): string; +} + +/** + * @internal + */ +export class SearchData { + + /** + * The regex to search for. Always defined. + */ + public readonly regex: RegExp; + /** + * The word separator classifier. + */ + public readonly wordSeparators: WordCharacterClassifier | null; + /** + * The simple string to search for (if possible). + */ + public readonly simpleSearch: string | null; + + constructor(regex: RegExp, wordSeparators: WordCharacterClassifier | null, simpleSearch: string | null) { + this.regex = regex; + this.wordSeparators = wordSeparators; + this.simpleSearch = simpleSearch; + } +} + +/** + * @internal + */ +export interface ITextBuffer extends IReadonlyTextBuffer, IDisposable { + setEOL(newEOL: '\r\n' | '\n'): void; + applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; +} + +/** + * @internal + */ +export class ApplyEditsResult { + + constructor( + public readonly reverseEdits: IValidEditOperation[] | null, + public readonly changes: IInternalModelContentChange[], + public readonly trimAutoWhitespaceLineNumbers: number[] | null + ) { } + +} + +/** + * @internal + */ +export interface IInternalModelContentChange extends IModelContentChange { + range: Range; + forceMoveMarkers: boolean; +} + +/** + * @internal + */ +export function shouldSynchronizeModel(model: ITextModel): boolean { + return ( + !model.isTooLargeForSyncing() && !model.isForSimpleWidget + ); +} diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsImpl.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsImpl.ts similarity index 98% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsImpl.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsImpl.ts index 546daf83fdc..3d700a0d52b 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsImpl.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsImpl.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CallbackIterable, compareBy } from '../../../../base/common/arrays.js'; -import { Emitter } from '../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import { CallbackIterable, compareBy } from '../../../../../base/common/arrays.js'; +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { IPosition, Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; -import { ILanguageConfigurationService, LanguageConfigurationServiceChangeEvent } from '../../languages/languageConfigurationRegistry.js'; -import { ignoreBracketsInToken } from '../../languages/supports.js'; -import { LanguageBracketsConfiguration } from '../../languages/supports/languageBracketsConfiguration.js'; -import { BracketsUtils, RichEditBracket, RichEditBrackets } from '../../languages/supports/richEditBrackets.js'; +import { ILanguageConfigurationService, LanguageConfigurationServiceChangeEvent } from '../../../languages/languageConfigurationRegistry.js'; +import { ignoreBracketsInToken } from '../../../languages/supports.js'; +import { LanguageBracketsConfiguration } from '../../../languages/supports/languageBracketsConfiguration.js'; +import { BracketsUtils, RichEditBracket, RichEditBrackets } from '../../../languages/supports/richEditBrackets.js'; import { BracketPairsTree } from './bracketPairsTree/bracketPairsTree.js'; import { TextModel } from '../textModel.js'; -import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from '../../textModelBracketPairs.js'; +import { BracketInfo, BracketPairInfo, BracketPairWithMinIndentationInfo, IBracketPairsTextModelPart, IFoundBracket } from '../../../../../editor/common/textModelBracketPairs.js'; import { IModelContentChangedEvent, IModelLanguageChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../../textModelEvents.js'; import { LineTokens } from '../../tokens/lineTokens.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts similarity index 99% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts index a5141b51624..1e99d748646 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/ast.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BugIndicatingError } from '../../../../../base/common/errors.js'; +import { BugIndicatingError } from '../../../../../../base/common/errors.js'; import { CursorColumns } from '../../../core/cursorColumns.js'; -import { BracketKind } from '../../../languages/supports/languageBracketsConfiguration.js'; +import { BracketKind } from '../../../../languages/supports/languageBracketsConfiguration.js'; import { ITextModel } from '../../../model.js'; import { Length, lengthAdd, lengthGetLineCount, lengthToObj, lengthZero } from './length.js'; import { SmallImmutableSet } from './smallImmutableSet.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.ts similarity index 100% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.ts diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts similarity index 97% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts index 42a40e99797..d9b53adb043 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from '../../../../../base/common/event.js'; -import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { Emitter } from '../../../../../../base/common/event.js'; +import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { Range } from '../../../core/range.js'; import { ITextModel } from '../../../model.js'; -import { BracketInfo, BracketPairWithMinIndentationInfo, IFoundBracket } from '../../../textModelBracketPairs.js'; +import { BracketInfo, BracketPairWithMinIndentationInfo, IFoundBracket } from '../../../../../../editor/common/textModelBracketPairs.js'; import { TextModel } from '../../textModel.js'; import { IModelContentChangedEvent, IModelTokensChangedEvent } from '../../../textModelEvents.js'; -import { ResolvedLanguageConfiguration } from '../../../languages/languageConfigurationRegistry.js'; +import { ResolvedLanguageConfiguration } from '../../../../languages/languageConfigurationRegistry.js'; import { AstNode, AstNodeKind } from './ast.js'; import { TextEditInfo } from './beforeEditPositionMapper.js'; import { LanguageAgnosticBracketTokens } from './brackets.js'; @@ -20,9 +20,9 @@ import { DenseKeyProvider } from './smallImmutableSet.js'; import { FastTokenizer, TextBufferTokenizer } from './tokenizer.js'; import { BackgroundTokenizationState } from '../../../tokenizationTextModelPart.js'; import { Position } from '../../../core/position.js'; -import { CallbackIterable } from '../../../../../base/common/arrays.js'; +import { CallbackIterable } from '../../../../../../base/common/arrays.js'; import { combineTextEditInfos } from './combineTextEditInfos.js'; -import { ClosingBracketKind, OpeningBracketKind } from '../../../languages/supports/languageBracketsConfiguration.js'; +import { ClosingBracketKind, OpeningBracketKind } from '../../../../languages/supports/languageBracketsConfiguration.js'; export class BracketPairsTree extends Disposable { private readonly didChangeEmitter = new Emitter(); diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts similarity index 94% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts index c0445ee917c..cb69e9c1f4c 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/brackets.ts @@ -2,9 +2,9 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { escapeRegExpCharacters } from '../../../../../base/common/strings.js'; -import { ResolvedLanguageConfiguration } from '../../../languages/languageConfigurationRegistry.js'; -import { BracketKind } from '../../../languages/supports/languageBracketsConfiguration.js'; +import { escapeRegExpCharacters } from '../../../../../../base/common/strings.js'; +import { ResolvedLanguageConfiguration } from '../../../../languages/languageConfigurationRegistry.js'; +import { BracketKind } from '../../../../languages/supports/languageBracketsConfiguration.js'; import { BracketAstNode } from './ast.js'; import { toLength } from './length.js'; import { DenseKeyProvider, identityKeyProvider, SmallImmutableSet } from './smallImmutableSet.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts similarity index 98% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts index d522d0916ac..d38f6047b57 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ArrayQueue } from '../../../../../base/common/arrays.js'; +import { ArrayQueue } from '../../../../../../base/common/arrays.js'; import { TextEditInfo } from './beforeEditPositionMapper.js'; import { Length, lengthAdd, lengthDiffNonNegative, lengthEquals, lengthIsZero, lengthToObj, lengthZero, sumLengths } from './length.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.ts similarity index 100% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.ts diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.ts similarity index 98% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.ts index b858a7d4d79..b998f324285 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { splitLines } from '../../../../../base/common/strings.js'; +import { splitLines } from '../../../../../../base/common/strings.js'; import { Position } from '../../../core/position.js'; import { Range } from '../../../core/range.js'; import { TextLength } from '../../../core/textLength.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/nodeReader.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/nodeReader.ts similarity index 100% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/nodeReader.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/nodeReader.ts diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/parser.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/parser.ts similarity index 100% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/parser.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/parser.ts diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts similarity index 100% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.ts diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts similarity index 99% rename from src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts index ae8cce61d56..cfb6703aa5d 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { NotSupportedError } from '../../../../../base/common/errors.js'; +import { NotSupportedError } from '../../../../../../base/common/errors.js'; import { StandardTokenType, TokenMetadata } from '../../../encodedTokenAttributes.js'; import { IViewLineTokens } from '../../../tokens/lineTokens.js'; import { BracketAstNode, TextAstNode } from './ast.js'; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts similarity index 92% rename from src/vs/editor/common/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts index 2b431cb64bd..3662e83731f 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Color } from '../../../../base/common/color.js'; -import { Emitter } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Color } from '../../../../../base/common/color.js'; +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; import { Range } from '../../core/range.js'; import { BracketPairColorizationOptions, IModelDecoration } from '../../model.js'; -import { BracketInfo } from '../../textModelBracketPairs.js'; +import { BracketInfo } from '../../../../../editor/common/textModelBracketPairs.js'; import { DecorationProvider } from '../decorationProvider.js'; import { TextModel } from '../textModel.js'; import { editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketHighlightingUnexpectedBracketForeground } from '../../core/editorColorRegistry.js'; -import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; +import { registerThemingParticipant } from '../../../../../platform/theme/common/themeService.js'; import { IModelOptionsChangedEvent } from '../../textModelEvents.js'; export class ColorizedBracketPairsDecorationProvider extends Disposable implements DecorationProvider { diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/fixBrackets.ts b/src/vs/editor/common/language/model/bracketPairsTextModelPart/fixBrackets.ts similarity index 96% rename from src/vs/editor/common/model/bracketPairsTextModelPart/fixBrackets.ts rename to src/vs/editor/common/language/model/bracketPairsTextModelPart/fixBrackets.ts index e35608b5bdf..38ce16a0d81 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/fixBrackets.ts +++ b/src/vs/editor/common/language/model/bracketPairsTextModelPart/fixBrackets.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILanguageConfigurationService } from '../../languages/languageConfigurationRegistry.js'; +import { ILanguageConfigurationService } from '../../../languages/languageConfigurationRegistry.js'; import { AstNode, AstNodeKind } from './bracketPairsTree/ast.js'; import { LanguageAgnosticBracketTokens } from './bracketPairsTree/brackets.js'; import { Length, lengthAdd, lengthGetColumnCountIfZeroLineCount, lengthZero } from './bracketPairsTree/length.js'; diff --git a/src/vs/editor/common/model/decorationProvider.ts b/src/vs/editor/common/language/model/decorationProvider.ts similarity index 96% rename from src/vs/editor/common/model/decorationProvider.ts rename to src/vs/editor/common/language/model/decorationProvider.ts index 0cb68740e2d..ed8e94ac9e7 100644 --- a/src/vs/editor/common/model/decorationProvider.ts +++ b/src/vs/editor/common/language/model/decorationProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; +import { Event } from '../../../../base/common/event.js'; import { Range } from '../core/range.js'; import { IModelDecoration } from '../model.js'; diff --git a/src/vs/editor/common/model/editStack.ts b/src/vs/editor/common/language/model/editStack.ts similarity index 97% rename from src/vs/editor/common/model/editStack.ts rename to src/vs/editor/common/language/model/editStack.ts index b7a5fe5378d..939c57666b2 100644 --- a/src/vs/editor/common/model/editStack.ts +++ b/src/vs/editor/common/language/model/editStack.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from '../../../nls.js'; -import { onUnexpectedError } from '../../../base/common/errors.js'; +import * as nls from '../../../../nls.js'; +import { onUnexpectedError } from '../../../../base/common/errors.js'; import { Selection } from '../core/selection.js'; import { EndOfLineSequence, ICursorStateComputer, IValidEditOperation, ITextModel } from '../model.js'; import { TextModel } from './textModel.js'; -import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js'; -import { URI } from '../../../base/common/uri.js'; +import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement, UndoRedoGroup } from '../../../../platform/undoRedo/common/undoRedo.js'; +import { URI } from '../../../../base/common/uri.js'; import { TextChange, compressConsecutiveTextChanges } from '../core/textChange.js'; -import * as buffer from '../../../base/common/buffer.js'; -import { IDisposable } from '../../../base/common/lifecycle.js'; -import { basename } from '../../../base/common/resources.js'; +import * as buffer from '../../../../base/common/buffer.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { basename } from '../../../../base/common/resources.js'; import { ISingleEditOperation } from '../core/editOperation.js'; function uriGetComparisonKey(resource: URI): string { diff --git a/src/vs/editor/common/model/fixedArray.ts b/src/vs/editor/common/language/model/fixedArray.ts similarity index 96% rename from src/vs/editor/common/model/fixedArray.ts rename to src/vs/editor/common/language/model/fixedArray.ts index 1ebfe07cba7..9be577fa134 100644 --- a/src/vs/editor/common/model/fixedArray.ts +++ b/src/vs/editor/common/language/model/fixedArray.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { arrayInsert } from '../../../base/common/arrays.js'; +import { arrayInsert } from '../../../../base/common/arrays.js'; /** * An array that avoids being sparse by always diff --git a/src/vs/editor/common/model/guidesTextModelPart.ts b/src/vs/editor/common/language/model/guidesTextModelPart.ts similarity index 98% rename from src/vs/editor/common/model/guidesTextModelPart.ts rename to src/vs/editor/common/language/model/guidesTextModelPart.ts index 157717fa9c1..d98d7311f7a 100644 --- a/src/vs/editor/common/model/guidesTextModelPart.ts +++ b/src/vs/editor/common/language/model/guidesTextModelPart.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { findLast } from '../../../base/common/arraysFind.js'; -import * as strings from '../../../base/common/strings.js'; +import { findLast } from '../../../../base/common/arraysFind.js'; +import * as strings from '../../../../base/common/strings.js'; import { CursorColumns } from '../core/cursorColumns.js'; import { IPosition, Position } from '../core/position.js'; import { Range } from '../core/range.js'; import type { TextModel } from './textModel.js'; import { TextModelPart } from './textModelPart.js'; import { computeIndentLevel } from './utils.js'; -import { ILanguageConfigurationService, ResolvedLanguageConfiguration } from '../languages/languageConfigurationRegistry.js'; -import { BracketGuideOptions, HorizontalGuidesState, IActiveIndentGuideInfo, IGuidesTextModelPart, IndentGuide, IndentGuideHorizontalLine } from '../textModelGuides.js'; -import { BugIndicatingError } from '../../../base/common/errors.js'; +import { ILanguageConfigurationService, ResolvedLanguageConfiguration } from '../../languages/languageConfigurationRegistry.js'; +import { BracketGuideOptions, HorizontalGuidesState, IActiveIndentGuideInfo, IGuidesTextModelPart, IndentGuide, IndentGuideHorizontalLine } from '../../../../editor/common/textModelGuides.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; export class GuidesTextModelPart extends TextModelPart implements IGuidesTextModelPart { constructor( diff --git a/src/vs/editor/common/model/indentationGuesser.ts b/src/vs/editor/common/language/model/indentationGuesser.ts similarity index 99% rename from src/vs/editor/common/model/indentationGuesser.ts rename to src/vs/editor/common/language/model/indentationGuesser.ts index ba6a7f64089..369dd6a5409 100644 --- a/src/vs/editor/common/model/indentationGuesser.ts +++ b/src/vs/editor/common/language/model/indentationGuesser.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; +import { CharCode } from '../../../../base/common/charCode.js'; import { ITextBuffer } from '../model.js'; class SpacesDiffResult { diff --git a/src/vs/editor/common/model/intervalTree.ts b/src/vs/editor/common/language/model/intervalTree.ts similarity index 100% rename from src/vs/editor/common/model/intervalTree.ts rename to src/vs/editor/common/language/model/intervalTree.ts diff --git a/src/vs/editor/common/model/mirrorTextModel.ts b/src/vs/editor/common/language/model/mirrorTextModel.ts similarity index 97% rename from src/vs/editor/common/model/mirrorTextModel.ts rename to src/vs/editor/common/language/model/mirrorTextModel.ts index 59482286e01..8b30fb3b30e 100644 --- a/src/vs/editor/common/model/mirrorTextModel.ts +++ b/src/vs/editor/common/language/model/mirrorTextModel.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { splitLines } from '../../../base/common/strings.js'; -import { URI } from '../../../base/common/uri.js'; +import { splitLines } from '../../../../base/common/strings.js'; +import { URI } from '../../../../base/common/uri.js'; import { Position } from '../core/position.js'; import { IRange } from '../core/range.js'; import { IModelContentChange } from '../textModelEvents.js'; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeBase.ts similarity index 99% rename from src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts rename to src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeBase.ts index 0d52df0cc51..24696fee2b5 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase.ts +++ b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeBase.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../../base/common/charCode.js'; +import { CharCode } from '../../../../../base/common/charCode.js'; import { Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; import { FindMatch, ITextSnapshot, SearchData } from '../../model.js'; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts similarity index 99% rename from src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts rename to src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts index 42d1995e367..5cb4f1d538b 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts +++ b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../../base/common/event.js'; -import * as strings from '../../../../base/common/strings.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; +import * as strings from '../../../../../base/common/strings.js'; import { Position } from '../../core/position.js'; import { Range } from '../../core/range.js'; import { ApplyEditsResult, EndOfLinePreference, FindMatch, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot, ValidAnnotatedEditOperation, IValidEditOperation, SearchData } from '../../model.js'; import { PieceTreeBase, StringBuffer } from './pieceTreeBase.js'; import { countEOL, StringEOL } from '../../core/eolCounter.js'; import { TextChange } from '../../core/textChange.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; export interface IValidatedEditOperation { sortIndex: number; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts similarity index 96% rename from src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts rename to src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts index 3ee4d87991a..5be8a713132 100644 --- a/src/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts +++ b/src/vs/editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../../base/common/charCode.js'; -import { IDisposable } from '../../../../base/common/lifecycle.js'; -import * as strings from '../../../../base/common/strings.js'; +import { CharCode } from '../../../../../base/common/charCode.js'; +import { IDisposable } from '../../../../../base/common/lifecycle.js'; +import * as strings from '../../../../../base/common/strings.js'; import { DefaultEndOfLine, ITextBuffer, ITextBufferBuilder, ITextBufferFactory } from '../../model.js'; import { StringBuffer, createLineStarts, createLineStartsFast } from './pieceTreeBase.js'; import { PieceTreeTextBuffer } from './pieceTreeTextBuffer.js'; diff --git a/src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts b/src/vs/editor/common/language/model/pieceTreeTextBuffer/rbTreeBase.ts similarity index 100% rename from src/vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase.ts rename to src/vs/editor/common/language/model/pieceTreeTextBuffer/rbTreeBase.ts diff --git a/src/vs/editor/common/model/prefixSumComputer.ts b/src/vs/editor/common/language/model/prefixSumComputer.ts similarity index 98% rename from src/vs/editor/common/model/prefixSumComputer.ts rename to src/vs/editor/common/language/model/prefixSumComputer.ts index 08376d50d8e..d51e66c22f5 100644 --- a/src/vs/editor/common/model/prefixSumComputer.ts +++ b/src/vs/editor/common/language/model/prefixSumComputer.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { arrayInsert } from '../../../base/common/arrays.js'; -import { toUint32 } from '../../../base/common/uint.js'; +import { arrayInsert } from '../../../../base/common/arrays.js'; +import { toUint32 } from '../../../../base/common/uint.js'; export class PrefixSumComputer { diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/language/model/textModel.ts similarity index 98% rename from src/vs/editor/common/model/textModel.ts rename to src/vs/editor/common/language/model/textModel.ts index c17724ca9ad..ea233e3e207 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/language/model/textModel.ts @@ -3,18 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ArrayQueue, pushMany } from '../../../base/common/arrays.js'; -import { VSBuffer, VSBufferReadableStream } from '../../../base/common/buffer.js'; -import { Color } from '../../../base/common/color.js'; -import { BugIndicatingError, illegalArgument, onUnexpectedError } from '../../../base/common/errors.js'; -import { Emitter, Event } from '../../../base/common/event.js'; -import { IMarkdownString } from '../../../base/common/htmlContent.js'; -import { Disposable, IDisposable, MutableDisposable, combinedDisposable } from '../../../base/common/lifecycle.js'; -import { listenStream } from '../../../base/common/stream.js'; -import * as strings from '../../../base/common/strings.js'; -import { ThemeColor } from '../../../base/common/themables.js'; -import { Constants } from '../../../base/common/uint.js'; -import { URI } from '../../../base/common/uri.js'; +import { ArrayQueue, pushMany } from '../../../../base/common/arrays.js'; +import { VSBuffer, VSBufferReadableStream } from '../../../../base/common/buffer.js'; +import { Color } from '../../../../base/common/color.js'; +import { BugIndicatingError, illegalArgument, onUnexpectedError } from '../../../../base/common/errors.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { IMarkdownString } from '../../../../base/common/htmlContent.js'; +import { Disposable, IDisposable, MutableDisposable, combinedDisposable } from '../../../../base/common/lifecycle.js'; +import { listenStream } from '../../../../base/common/stream.js'; +import * as strings from '../../../../base/common/strings.js'; +import { ThemeColor } from '../../../../base/common/themables.js'; +import { Constants } from '../../../../base/common/uint.js'; +import { URI } from '../../../../base/common/uri.js'; import { ISingleEditOperation } from '../core/editOperation.js'; import { countEOL } from '../core/eolCounter.js'; import { normalizeIndentation } from '../core/indentation.js'; @@ -25,8 +25,8 @@ import { TextChange } from '../core/textChange.js'; import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; import { IWordAtPosition } from '../core/wordHelper.js'; import { FormattingOptions } from '../languages.js'; -import { ILanguageSelection, ILanguageService } from '../languages/language.js'; -import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js'; +import { ILanguageSelection, ILanguageService } from '../language.js'; +import { ILanguageConfigurationService } from '../../languages/languageConfigurationRegistry.js'; import * as model from '../model.js'; import { BracketPairsTextModelPart } from './bracketPairsTextModelPart/bracketPairsImpl.js'; import { ColorizedBracketPairsDecorationProvider } from './bracketPairsTextModelPart/colorizedBracketPairsDecorationProvider.js'; @@ -39,13 +39,13 @@ import { PieceTreeTextBufferBuilder } from './pieceTreeTextBuffer/pieceTreeTextB import { SearchParams, TextModelSearch } from './textModelSearch.js'; import { TokenizationTextModelPart } from './tokenizationTextModelPart.js'; import { AttachedViews } from './tokens.js'; -import { IBracketPairsTextModelPart } from '../textModelBracketPairs.js'; +import { IBracketPairsTextModelPart } from '../../../../editor/common/textModelBracketPairs.js'; import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelOptionsChangedEvent, InternalModelContentChangeEvent, LineInjectedText, ModelInjectedTextChangedEvent, ModelRawChange, ModelRawContentChangedEvent, ModelRawEOLChanged, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from '../textModelEvents.js'; -import { IGuidesTextModelPart } from '../textModelGuides.js'; +import { IGuidesTextModelPart } from '../../../../editor/common/textModelGuides.js'; import { ITokenizationTextModelPart } from '../tokenizationTextModelPart.js'; -import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; -import { IColorTheme } from '../../../platform/theme/common/themeService.js'; -import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoGroup } from '../../../platform/undoRedo/common/undoRedo.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IColorTheme } from '../../../../platform/theme/common/themeService.js'; +import { IUndoRedoService, ResourceEditStackSnapshot, UndoRedoGroup } from '../../../../platform/undoRedo/common/undoRedo.js'; import { TokenArray } from '../tokens/tokenArray.js'; export function createTextBufferFactory(text: string): model.ITextBufferFactory { diff --git a/src/vs/editor/common/model/textModelOffsetEdit.ts b/src/vs/editor/common/language/model/textModelOffsetEdit.ts similarity index 96% rename from src/vs/editor/common/model/textModelOffsetEdit.ts rename to src/vs/editor/common/language/model/textModelOffsetEdit.ts index 4cc02d88c2e..b1afe20b268 100644 --- a/src/vs/editor/common/model/textModelOffsetEdit.ts +++ b/src/vs/editor/common/language/model/textModelOffsetEdit.ts @@ -7,7 +7,7 @@ import { EditOperation } from '../core/editOperation.js'; import { Range } from '../core/range.js'; import { OffsetEdit, SingleOffsetEdit } from '../core/offsetEdit.js'; import { OffsetRange } from '../core/offsetRange.js'; -import { DetailedLineRangeMapping } from '../diff/rangeMapping.js'; +import { DetailedLineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { ITextModel, IIdentifiedSingleEditOperation } from '../model.js'; import { IModelContentChange } from '../textModelEvents.js'; diff --git a/src/vs/editor/common/model/textModelPart.ts b/src/vs/editor/common/language/model/textModelPart.ts similarity index 90% rename from src/vs/editor/common/model/textModelPart.ts rename to src/vs/editor/common/language/model/textModelPart.ts index d1b2bebc823..89abe4488e1 100644 --- a/src/vs/editor/common/model/textModelPart.ts +++ b/src/vs/editor/common/language/model/textModelPart.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from '../../../base/common/lifecycle.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; export class TextModelPart extends Disposable { private _isDisposed = false; diff --git a/src/vs/editor/common/model/textModelSearch.ts b/src/vs/editor/common/language/model/textModelSearch.ts similarity index 99% rename from src/vs/editor/common/model/textModelSearch.ts rename to src/vs/editor/common/language/model/textModelSearch.ts index ae259f035d5..89497ebd8cb 100644 --- a/src/vs/editor/common/model/textModelSearch.ts +++ b/src/vs/editor/common/language/model/textModelSearch.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; -import * as strings from '../../../base/common/strings.js'; +import { CharCode } from '../../../../base/common/charCode.js'; +import * as strings from '../../../../base/common/strings.js'; import { WordCharacterClass, WordCharacterClassifier, getMapForWordSeparators } from '../core/wordCharacterClassifier.js'; import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; diff --git a/src/vs/editor/common/model/textModelText.ts b/src/vs/editor/common/language/model/textModelText.ts similarity index 100% rename from src/vs/editor/common/model/textModelText.ts rename to src/vs/editor/common/language/model/textModelText.ts diff --git a/src/vs/editor/common/model/textModelTokens.ts b/src/vs/editor/common/language/model/textModelTokens.ts similarity index 98% rename from src/vs/editor/common/model/textModelTokens.ts rename to src/vs/editor/common/language/model/textModelTokens.ts index b3253f37b4d..cc4146f774d 100644 --- a/src/vs/editor/common/model/textModelTokens.ts +++ b/src/vs/editor/common/language/model/textModelTokens.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IdleDeadline, runWhenGlobalIdle } from '../../../base/common/async.js'; -import { BugIndicatingError, onUnexpectedError } from '../../../base/common/errors.js'; -import { setTimeout0 } from '../../../base/common/platform.js'; -import { StopWatch } from '../../../base/common/stopwatch.js'; +import { IdleDeadline, runWhenGlobalIdle } from '../../../../base/common/async.js'; +import { BugIndicatingError, onUnexpectedError } from '../../../../base/common/errors.js'; +import { setTimeout0 } from '../../../../base/common/platform.js'; +import { StopWatch } from '../../../../base/common/stopwatch.js'; import { countEOL } from '../core/eolCounter.js'; import { LineRange } from '../core/lineRange.js'; import { OffsetRange } from '../core/offsetRange.js'; import { Position } from '../core/position.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; import { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTokenizer, ILanguageIdCodec, IState, ITokenizationSupport } from '../languages.js'; -import { nullTokenizeEncoded } from '../languages/nullTokenize.js'; +import { nullTokenizeEncoded } from '../../languages/nullTokenize.js' import { ITextModel } from '../model.js'; import { FixedArray } from './fixedArray.js'; import { IModelContentChange } from '../textModelEvents.js'; diff --git a/src/vs/editor/common/model/tokenStore.ts b/src/vs/editor/common/language/model/tokenStore.ts similarity index 99% rename from src/vs/editor/common/model/tokenStore.ts rename to src/vs/editor/common/language/model/tokenStore.ts index 4582e09703e..289f16a1150 100644 --- a/src/vs/editor/common/model/tokenStore.ts +++ b/src/vs/editor/common/language/model/tokenStore.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IDisposable } from '../../../base/common/lifecycle.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ITextModel } from '../model.js'; class ListNode implements IDisposable { diff --git a/src/vs/editor/common/model/tokenizationTextModelPart.ts b/src/vs/editor/common/language/model/tokenizationTextModelPart.ts similarity index 98% rename from src/vs/editor/common/model/tokenizationTextModelPart.ts rename to src/vs/editor/common/language/model/tokenizationTextModelPart.ts index 84dea7fdba6..d987d06df03 100644 --- a/src/vs/editor/common/model/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/language/model/tokenizationTextModelPart.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; -import { BugIndicatingError, onUnexpectedError } from '../../../base/common/errors.js'; -import { Emitter, Event } from '../../../base/common/event.js'; -import { DisposableMap, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; +import { CharCode } from '../../../../base/common/charCode.js'; +import { BugIndicatingError, onUnexpectedError } from '../../../../base/common/errors.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { DisposableMap, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { countEOL } from '../core/eolCounter.js'; import { LineRange } from '../core/lineRange.js'; import { IPosition, Position } from '../core/position.js'; @@ -14,8 +14,8 @@ import { Range } from '../core/range.js'; import { IWordAtPosition, getWordAtText } from '../core/wordHelper.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; import { IBackgroundTokenizationStore, IBackgroundTokenizer, ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry, TreeSitterTokenizationRegistry } from '../languages.js'; -import { ILanguageService } from '../languages/language.js'; -import { ILanguageConfigurationService, LanguageConfigurationServiceChangeEvent, ResolvedLanguageConfiguration } from '../languages/languageConfigurationRegistry.js'; +import { ILanguageService } from '../language.js'; +import { ILanguageConfigurationService, LanguageConfigurationServiceChangeEvent, ResolvedLanguageConfiguration } from '../../languages/languageConfigurationRegistry.js' import { IAttachedView } from '../model.js'; import { BracketPairsTextModelPart } from './bracketPairsTextModelPart/bracketPairsImpl.js'; import { TextModel } from './textModel.js'; @@ -31,7 +31,7 @@ import { ContiguousTokensStore } from '../tokens/contiguousTokensStore.js'; import { LineTokens } from '../tokens/lineTokens.js'; import { SparseMultilineTokens } from '../tokens/sparseMultilineTokens.js'; import { SparseTokensStore } from '../tokens/sparseTokensStore.js'; -import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; export class TokenizationTextModelPart extends TextModelPart implements ITokenizationTextModelPart { private readonly _semanticTokens: SparseTokensStore = new SparseTokensStore(this._languageService.languageIdCodec); diff --git a/src/vs/editor/common/model/tokens.ts b/src/vs/editor/common/language/model/tokens.ts similarity index 94% rename from src/vs/editor/common/model/tokens.ts rename to src/vs/editor/common/language/model/tokens.ts index ad99df1f29b..8007fdd9836 100644 --- a/src/vs/editor/common/model/tokens.ts +++ b/src/vs/editor/common/language/model/tokens.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { equals } from '../../../base/common/arrays.js'; -import { RunOnceScheduler } from '../../../base/common/async.js'; -import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable } from '../../../base/common/lifecycle.js'; +import { equals } from '../../../../base/common/arrays.js'; +import { RunOnceScheduler } from '../../../../base/common/async.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; import { LineRange } from '../core/lineRange.js'; import { StandardTokenType } from '../encodedTokenAttributes.js'; import { ILanguageIdCodec } from '../languages.js'; diff --git a/src/vs/editor/common/model/treeSitterTokenStoreService.ts b/src/vs/editor/common/language/model/treeSitterTokenStoreService.ts similarity index 96% rename from src/vs/editor/common/model/treeSitterTokenStoreService.ts rename to src/vs/editor/common/language/model/treeSitterTokenStoreService.ts index b2ae5b3bd0d..2f426d93b27 100644 --- a/src/vs/editor/common/model/treeSitterTokenStoreService.ts +++ b/src/vs/editor/common/language/model/treeSitterTokenStoreService.ts @@ -6,9 +6,9 @@ import { Range } from '../core/range.js'; import { ITextModel } from '../model.js'; import { TokenQuality, TokenStore, TokenUpdate } from './tokenStore.js'; -import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; -import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IModelContentChangedEvent } from '../textModelEvents.js'; export interface ITreeSitterTokenizationStoreService { diff --git a/src/vs/editor/common/model/treeSitterTokens.ts b/src/vs/editor/common/language/model/treeSitterTokens.ts similarity index 97% rename from src/vs/editor/common/model/treeSitterTokens.ts rename to src/vs/editor/common/language/model/treeSitterTokens.ts index 55c0d9bf196..d7ed6071e40 100644 --- a/src/vs/editor/common/model/treeSitterTokens.ts +++ b/src/vs/editor/common/language/model/treeSitterTokens.ts @@ -9,11 +9,11 @@ import { StandardTokenType } from '../encodedTokenAttributes.js'; import { TextModel } from './textModel.js'; import { IModelContentChangedEvent } from '../textModelEvents.js'; import { AbstractTokens } from './tokens.js'; -import { IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js'; +import { IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { ITreeSitterTokenizationStoreService } from './treeSitterTokenStoreService.js'; import { Range } from '../core/range.js'; import { BackgroundTokenizationState } from '../tokenizationTextModelPart.js'; -import { Emitter, Event } from '../../../base/common/event.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; export class TreeSitterTokens extends AbstractTokens { private _tokenizationSupport: ITreeSitterTokenizationSupport | null = null; diff --git a/src/vs/editor/common/model/utils.ts b/src/vs/editor/common/language/model/utils.ts similarity index 93% rename from src/vs/editor/common/model/utils.ts rename to src/vs/editor/common/language/model/utils.ts index 5fde2c377c1..bf1e3c8c6e5 100644 --- a/src/vs/editor/common/model/utils.ts +++ b/src/vs/editor/common/language/model/utils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; +import { CharCode } from '../../../../base/common/charCode.js'; /** * Returns: diff --git a/src/vs/editor/common/services/editorBaseApi.ts b/src/vs/editor/common/language/services/editorBaseApi.ts similarity index 79% rename from src/vs/editor/common/services/editorBaseApi.ts rename to src/vs/editor/common/language/services/editorBaseApi.ts index 6fbc1c4df93..6eaf0ec55ac 100644 --- a/src/vs/editor/common/services/editorBaseApi.ts +++ b/src/vs/editor/common/language/services/editorBaseApi.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CancellationTokenSource } from '../../../base/common/cancellation.js'; -import { Emitter } from '../../../base/common/event.js'; -import { KeyChord, KeyMod as ConstKeyMod } from '../../../base/common/keyCodes.js'; -import { URI } from '../../../base/common/uri.js'; +import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { KeyChord, KeyMod as ConstKeyMod } from '../../../../base/common/keyCodes.js'; +import { URI } from '../../../../base/common/uri.js'; import { Position } from '../core/position.js'; import { Range } from '../core/range.js'; import { Selection } from '../core/selection.js'; import { Token } from '../languages.js'; -import * as standaloneEnums from '../standalone/standaloneEnums.js'; +import * as standaloneEnums from '../../../../editor/common/standalone/standaloneEnums.js'; export class KeyMod { public static readonly CtrlCmd: number = ConstKeyMod.CtrlCmd; diff --git a/src/vs/editor/common/services/editorWebWorker.ts b/src/vs/editor/common/language/services/editorWebWorker.ts similarity index 93% rename from src/vs/editor/common/services/editorWebWorker.ts rename to src/vs/editor/common/language/services/editorWebWorker.ts index c5e8ea184f8..7af51beb175 100644 --- a/src/vs/editor/common/services/editorWebWorker.ts +++ b/src/vs/editor/common/language/services/editorWebWorker.ts @@ -3,28 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { stringDiff } from '../../../base/common/diff/diff.js'; -import { IDisposable } from '../../../base/common/lifecycle.js'; -import { URI } from '../../../base/common/uri.js'; -import { IWebWorkerServerRequestHandler } from '../../../base/common/worker/webWorker.js'; +import { stringDiff } from '../../../../base/common/diff/diff.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IWebWorkerServerRequestHandler } from '../../../../base/common/worker/webWorker.js'; import { Position } from '../core/position.js'; import { IRange, Range } from '../core/range.js'; import { EndOfLineSequence, ITextModel } from '../model.js'; import { IMirrorTextModel, IModelChangedEvent } from '../model/mirrorTextModel.js'; import { IColorInformation, IInplaceReplaceSupportResult, ILink, TextEdit } from '../languages.js'; -import { computeLinks } from '../languages/linkComputer.js'; -import { BasicInplaceReplace } from '../languages/supports/inplaceReplaceSupport.js'; +import { computeLinks } from '../../languages/linkComputer.js' +import { BasicInplaceReplace } from '../../languages/supports/inplaceReplaceSupport.js' import { DiffAlgorithmName, IDiffComputationResult, ILineChange, IUnicodeHighlightsResult } from './editorWorker.js'; import { createMonacoBaseAPI } from './editorBaseApi.js'; -import { StopWatch } from '../../../base/common/stopwatch.js'; +import { StopWatch } from '../../../../base/common/stopwatch.js'; import { UnicodeTextModelHighlighter, UnicodeHighlighterOptions } from './unicodeTextModelHighlighter.js'; -import { DiffComputer, IChange } from '../diff/legacyLinesDiffComputer.js'; -import { ILinesDiffComputer, ILinesDiffComputerOptions } from '../diff/linesDiffComputer.js'; -import { DetailedLineRangeMapping } from '../diff/rangeMapping.js'; -import { linesDiffComputers } from '../diff/linesDiffComputers.js'; -import { IDocumentDiffProviderOptions } from '../diff/documentDiffProvider.js'; -import { BugIndicatingError } from '../../../base/common/errors.js'; -import { computeDefaultDocumentColors } from '../languages/defaultDocumentColorsComputer.js'; +import { DiffComputer, IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; +import { ILinesDiffComputer, ILinesDiffComputerOptions } from '../../../../editor/common/diff/linesDiffComputer.js'; +import { DetailedLineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; +import { linesDiffComputers } from '../../../../editor/common/diff/linesDiffComputers.js'; +import { IDocumentDiffProviderOptions } from '../../../../editor/common/diff/documentDiffProvider.js'; +import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { computeDefaultDocumentColors } from '../../languages/defaultDocumentColorsComputer.js' import { FindSectionHeaderOptions, SectionHeader, findSectionHeaders } from './findSectionHeaders.js'; import { IRawModelData, IWorkerTextModelSyncChannelServer } from './textModelSync/textModelSync.protocol.js'; import { ICommonModel, WorkerTextModelSyncServer } from './textModelSync/textModelSync.impl.js'; diff --git a/src/vs/editor/common/services/editorWebWorkerMain.ts b/src/vs/editor/common/language/services/editorWebWorkerMain.ts similarity index 83% rename from src/vs/editor/common/services/editorWebWorkerMain.ts rename to src/vs/editor/common/language/services/editorWebWorkerMain.ts index 2094b0f73b5..ffe2f5bed9c 100644 --- a/src/vs/editor/common/services/editorWebWorkerMain.ts +++ b/src/vs/editor/common/language/services/editorWebWorkerMain.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { bootstrapWebWorker } from '../../../base/common/worker/webWorkerBootstrap.js'; +import { bootstrapWebWorker } from '../../../../base/common/worker/webWorkerBootstrap.js'; import { EditorWorker } from './editorWebWorker.js'; bootstrapWebWorker(() => new EditorWorker(null)); diff --git a/src/vs/editor/common/services/editorWorker.ts b/src/vs/editor/common/language/services/editorWorker.ts similarity index 91% rename from src/vs/editor/common/services/editorWorker.ts rename to src/vs/editor/common/language/services/editorWorker.ts index fc0f44fa458..fb41e36209e 100644 --- a/src/vs/editor/common/services/editorWorker.ts +++ b/src/vs/editor/common/language/services/editorWorker.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from '../../../base/common/uri.js'; +import { URI } from '../../../../base/common/uri.js'; import { IRange } from '../core/range.js'; -import { IDocumentDiff, IDocumentDiffProviderOptions } from '../diff/documentDiffProvider.js'; -import { IChange } from '../diff/legacyLinesDiffComputer.js'; +import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../../../editor/common/diff/documentDiffProvider.js'; +import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; import { IColorInformation, IInplaceReplaceSupportResult, TextEdit } from '../languages.js'; import { UnicodeHighlighterOptions } from './unicodeTextModelHighlighter.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import type { EditorWorker } from './editorWebWorker.js'; import { SectionHeader, FindSectionHeaderOptions } from './findSectionHeaders.js'; diff --git a/src/vs/editor/common/services/editorWorkerHost.ts b/src/vs/editor/common/language/services/editorWorkerHost.ts similarity index 89% rename from src/vs/editor/common/services/editorWorkerHost.ts rename to src/vs/editor/common/language/services/editorWorkerHost.ts index 6223c6b7464..282ff06c965 100644 --- a/src/vs/editor/common/services/editorWorkerHost.ts +++ b/src/vs/editor/common/language/services/editorWorkerHost.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWebWorkerServer, IWebWorkerClient } from '../../../base/common/worker/webWorker.js'; +import { IWebWorkerServer, IWebWorkerClient } from '../../../../base/common/worker/webWorker.js'; export abstract class EditorWorkerHost { public static CHANNEL_NAME = 'editorWorkerHost'; diff --git a/src/vs/editor/common/services/findSectionHeaders.ts b/src/vs/editor/common/language/services/findSectionHeaders.ts similarity index 98% rename from src/vs/editor/common/services/findSectionHeaders.ts rename to src/vs/editor/common/language/services/findSectionHeaders.ts index dece7ff1e13..636cab3d3a7 100644 --- a/src/vs/editor/common/services/findSectionHeaders.ts +++ b/src/vs/editor/common/language/services/findSectionHeaders.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IRange } from '../core/range.js'; -import { FoldingRules } from '../languages/languageConfiguration.js'; +import { FoldingRules } from '../../languages/languageConfiguration.js'; import { isMultilineRegexSource } from '../model/textModelSearch.js'; export interface ISectionHeaderFinderTarget { diff --git a/src/vs/editor/common/services/getIconClasses.ts b/src/vs/editor/common/language/services/getIconClasses.ts similarity index 90% rename from src/vs/editor/common/services/getIconClasses.ts rename to src/vs/editor/common/language/services/getIconClasses.ts index cb60d8b7bf5..37471c9cb27 100644 --- a/src/vs/editor/common/services/getIconClasses.ts +++ b/src/vs/editor/common/language/services/getIconClasses.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Schemas } from '../../../base/common/network.js'; -import { DataUri } from '../../../base/common/resources.js'; -import { URI, URI as uri } from '../../../base/common/uri.js'; -import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; -import { ILanguageService } from '../languages/language.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { DataUri } from '../../../../base/common/resources.js'; +import { URI, URI as uri } from '../../../../base/common/uri.js'; +import { PLAINTEXT_LANGUAGE_ID } from '../../languages/modesRegistry.js'; +import { ILanguageService } from '../language.js'; import { IModelService } from './model.js'; -import { FileKind } from '../../../platform/files/common/files.js'; -import { ThemeIcon } from '../../../base/common/themables.js'; +import { FileKind } from '../../../../platform/files/common/files.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; const fileIconDirectoryRegex = /(?:\/|^)(?:([^\/]+)\/)?([^\/]+)$/; diff --git a/src/vs/editor/common/services/languageFeatureDebounce.ts b/src/vs/editor/common/language/services/languageFeatureDebounce.ts similarity index 87% rename from src/vs/editor/common/services/languageFeatureDebounce.ts rename to src/vs/editor/common/language/services/languageFeatureDebounce.ts index f4a2b3b4d55..9c90d9cf592 100644 --- a/src/vs/editor/common/services/languageFeatureDebounce.ts +++ b/src/vs/editor/common/language/services/languageFeatureDebounce.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { doHash } from '../../../base/common/hash.js'; -import { LRUCache } from '../../../base/common/map.js'; -import { clamp, MovingAverage, SlidingWindowAverage } from '../../../base/common/numbers.js'; -import { LanguageFeatureRegistry } from '../languageFeatureRegistry.js'; +import { doHash } from '../../../../base/common/hash.js'; +import { LRUCache } from '../../../../base/common/map.js'; +import { clamp, MovingAverage, SlidingWindowAverage } from '../../../../base/common/numbers.js'; +import { LanguageFeatureRegistry } from '../../../../editor/common/languageFeatureRegistry.js'; import { ITextModel } from '../model.js'; -import { IEnvironmentService } from '../../../platform/environment/common/environment.js'; -import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; -import { ILogService } from '../../../platform/log/common/log.js'; -import { matchesScheme } from '../../../base/common/network.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { matchesScheme } from '../../../../base/common/network.js'; export const ILanguageFeatureDebounceService = createDecorator('ILanguageFeatureDebounceService'); diff --git a/src/vs/editor/common/services/languageFeatures.ts b/src/vs/editor/common/language/services/languageFeatures.ts similarity index 96% rename from src/vs/editor/common/services/languageFeatures.ts rename to src/vs/editor/common/language/services/languageFeatures.ts index 0379781a0de..26c85668f60 100644 --- a/src/vs/editor/common/services/languageFeatures.ts +++ b/src/vs/editor/common/language/services/languageFeatures.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LanguageFeatureRegistry, NotebookInfoResolver } from '../languageFeatureRegistry.js'; +import { LanguageFeatureRegistry, NotebookInfoResolver } from '../../../../editor/common/languageFeatureRegistry.js'; import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, DocumentHighlightProvider, DocumentDropEditProvider, DocumentPasteEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, MultiDocumentHighlightProvider, NewSymbolNamesProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, InlineEditProvider } from '../languages.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; export const ILanguageFeaturesService = createDecorator('ILanguageFeaturesService'); diff --git a/src/vs/editor/common/services/languageFeaturesService.ts b/src/vs/editor/common/language/services/languageFeaturesService.ts similarity index 95% rename from src/vs/editor/common/services/languageFeaturesService.ts rename to src/vs/editor/common/language/services/languageFeaturesService.ts index 02ac4418841..5cd9e91a4a7 100644 --- a/src/vs/editor/common/services/languageFeaturesService.ts +++ b/src/vs/editor/common/language/services/languageFeaturesService.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from '../../../base/common/uri.js'; -import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from '../languageFeatureRegistry.js'; +import { URI } from '../../../../base/common/uri.js'; +import { LanguageFeatureRegistry, NotebookInfo, NotebookInfoResolver } from '../../../../editor/common/languageFeatureRegistry.js'; import { CodeActionProvider, CodeLensProvider, CompletionItemProvider, DocumentPasteEditProvider, DeclarationProvider, DefinitionProvider, DocumentColorProvider, DocumentFormattingEditProvider, MultiDocumentHighlightProvider, DocumentHighlightProvider, DocumentDropEditProvider, DocumentRangeFormattingEditProvider, DocumentRangeSemanticTokensProvider, DocumentSemanticTokensProvider, DocumentSymbolProvider, EvaluatableExpressionProvider, FoldingRangeProvider, HoverProvider, ImplementationProvider, InlayHintsProvider, InlineCompletionsProvider, InlineValuesProvider, LinkedEditingRangeProvider, LinkProvider, OnTypeFormattingEditProvider, ReferenceProvider, RenameProvider, SelectionRangeProvider, SignatureHelpProvider, TypeDefinitionProvider, NewSymbolNamesProvider, InlineEditProvider } from '../languages.js'; -import { ILanguageFeaturesService } from './languageFeatures.js'; -import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; +import { ILanguageFeaturesService } from './languageFeatures.js' +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; export class LanguageFeaturesService implements ILanguageFeaturesService { diff --git a/src/vs/editor/common/services/languageService.ts b/src/vs/editor/common/language/services/languageService.ts similarity index 93% rename from src/vs/editor/common/services/languageService.ts rename to src/vs/editor/common/language/services/languageService.ts index dffe6ea3d2f..4865f630693 100644 --- a/src/vs/editor/common/services/languageService.ts +++ b/src/vs/editor/common/language/services/languageService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; -import { URI } from '../../../base/common/uri.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; import { LanguagesRegistry } from './languagesRegistry.js'; -import { ILanguageNameIdPair, ILanguageSelection, ILanguageService, ILanguageIcon, ILanguageExtensionPoint } from '../languages/language.js'; +import { ILanguageNameIdPair, ILanguageSelection, ILanguageService, ILanguageIcon, ILanguageExtensionPoint } from '../language.js'; import { ILanguageIdCodec, TokenizationRegistry } from '../languages.js'; -import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; -import { IObservable, observableFromEvent } from '../../../base/common/observable.js'; +import { PLAINTEXT_LANGUAGE_ID } from '../../languages/modesRegistry.js'; +import { IObservable, observableFromEvent } from '../../../../base/common/observable.js'; export class LanguageService extends Disposable implements ILanguageService { public _serviceBrand: undefined; diff --git a/src/vs/editor/common/services/languagesAssociations.ts b/src/vs/editor/common/language/services/languagesAssociations.ts similarity index 94% rename from src/vs/editor/common/services/languagesAssociations.ts rename to src/vs/editor/common/language/services/languagesAssociations.ts index 2bfe45a8746..603aa71a9dc 100644 --- a/src/vs/editor/common/services/languagesAssociations.ts +++ b/src/vs/editor/common/language/services/languagesAssociations.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ParsedPattern, parse } from '../../../base/common/glob.js'; -import { Mimes } from '../../../base/common/mime.js'; -import { Schemas } from '../../../base/common/network.js'; -import { basename, posix } from '../../../base/common/path.js'; -import { DataUri } from '../../../base/common/resources.js'; -import { startsWithUTF8BOM } from '../../../base/common/strings.js'; -import { URI } from '../../../base/common/uri.js'; -import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; +import { ParsedPattern, parse } from '../../../../base/common/glob.js'; +import { Mimes } from '../../../../base/common/mime.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { basename, posix } from '../../../../base/common/path.js'; +import { DataUri } from '../../../../base/common/resources.js'; +import { startsWithUTF8BOM } from '../../../../base/common/strings.js'; +import { URI } from '../../../../base/common/uri.js'; +import { PLAINTEXT_LANGUAGE_ID } from '../../languages/modesRegistry.js'; export interface ILanguageAssociation { readonly id: string; diff --git a/src/vs/editor/common/services/languagesRegistry.ts b/src/vs/editor/common/language/services/languagesRegistry.ts similarity index 95% rename from src/vs/editor/common/services/languagesRegistry.ts rename to src/vs/editor/common/language/services/languagesRegistry.ts index 3ea71cff4ee..880c2c6cc2d 100644 --- a/src/vs/editor/common/services/languagesRegistry.ts +++ b/src/vs/editor/common/language/services/languagesRegistry.ts @@ -3,17 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; -import { compareIgnoreCase, regExpLeadsToEndlessLoop } from '../../../base/common/strings.js'; -import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from './languagesAssociations.js'; -import { URI } from '../../../base/common/uri.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { compareIgnoreCase, regExpLeadsToEndlessLoop } from '../../../../base/common/strings.js'; +import { clearPlatformLanguageAssociations, getLanguageIds, registerPlatformLanguageAssociation } from './languagesAssociations.js' +import { URI } from '../../../../base/common/uri.js'; import { ILanguageIdCodec } from '../languages.js'; import { LanguageId } from '../encodedTokenAttributes.js'; -import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; -import { ILanguageExtensionPoint, ILanguageNameIdPair, ILanguageIcon } from '../languages/language.js'; -import { Extensions, IConfigurationRegistry } from '../../../platform/configuration/common/configurationRegistry.js'; -import { Registry } from '../../../platform/registry/common/platform.js'; +import { ModesRegistry, PLAINTEXT_LANGUAGE_ID } from '../../languages/modesRegistry.js'; +import { ILanguageExtensionPoint, ILanguageNameIdPair, ILanguageIcon } from '../language.js'; +import { Extensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; const hasOwnProperty = Object.prototype.hasOwnProperty; const NULL_LANGUAGE_ID = 'vs.editor.nullLanguage'; diff --git a/src/vs/editor/common/services/markerDecorations.ts b/src/vs/editor/common/language/services/markerDecorations.ts similarity index 71% rename from src/vs/editor/common/services/markerDecorations.ts rename to src/vs/editor/common/language/services/markerDecorations.ts index 66f1943b000..be4d390e83b 100644 --- a/src/vs/editor/common/services/markerDecorations.ts +++ b/src/vs/editor/common/language/services/markerDecorations.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { ITextModel, IModelDecoration } from '../model.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; -import { IMarker } from '../../../platform/markers/common/markers.js'; -import { Event } from '../../../base/common/event.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { IMarker } from '../../../../platform/markers/common/markers.js'; +import { Event } from '../../../../base/common/event.js'; import { Range } from '../core/range.js'; -import { URI } from '../../../base/common/uri.js'; -import { IDisposable } from '../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; export const IMarkerDecorationsService = createDecorator('markerDecorationsService'); diff --git a/src/vs/editor/common/services/markerDecorationsService.ts b/src/vs/editor/common/language/services/markerDecorationsService.ts similarity index 93% rename from src/vs/editor/common/services/markerDecorationsService.ts rename to src/vs/editor/common/language/services/markerDecorationsService.ts index c25a85a6915..274ee90b324 100644 --- a/src/vs/editor/common/services/markerDecorationsService.ts +++ b/src/vs/editor/common/language/services/markerDecorationsService.ts @@ -3,23 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from '../../../platform/markers/common/markers.js'; -import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; -import { URI } from '../../../base/common/uri.js'; +import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from '../../../../platform/markers/common/markers.js'; +import { Disposable, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; import { IModelDeltaDecoration, ITextModel, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, IModelDecoration, MinimapPosition, IModelDecorationMinimapOptions } from '../model.js'; import { ClassName } from '../model/intervalTree.js'; -import { themeColorFromId } from '../../../platform/theme/common/themeService.js'; -import { ThemeColor } from '../../../base/common/themables.js'; +import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; +import { ThemeColor } from '../../../../base/common/themables.js'; import { overviewRulerWarning, overviewRulerInfo, overviewRulerError } from '../core/editorColorRegistry.js'; import { IModelService } from './model.js'; import { Range } from '../core/range.js'; import { IMarkerDecorationsService } from './markerDecorations.js'; -import { Schemas } from '../../../base/common/network.js'; -import { Emitter, Event } from '../../../base/common/event.js'; -import { minimapInfo, minimapWarning, minimapError } from '../../../platform/theme/common/colorRegistry.js'; -import { BidirectionalMap, ResourceMap } from '../../../base/common/map.js'; -import { diffSets } from '../../../base/common/collections.js'; -import { Iterable } from '../../../base/common/iterator.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { minimapInfo, minimapWarning, minimapError } from '../../../../platform/theme/common/colorRegistry.js'; +import { BidirectionalMap, ResourceMap } from '../../../../base/common/map.js'; +import { diffSets } from '../../../../base/common/collections.js'; +import { Iterable } from '../../../../base/common/iterator.js'; export class MarkerDecorationsService extends Disposable implements IMarkerDecorationsService { diff --git a/src/vs/editor/common/services/model.ts b/src/vs/editor/common/language/services/model.ts similarity index 84% rename from src/vs/editor/common/services/model.ts rename to src/vs/editor/common/language/services/model.ts index 4bad6f50e4e..c6409d511e9 100644 --- a/src/vs/editor/common/services/model.ts +++ b/src/vs/editor/common/language/services/model.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; -import { URI } from '../../../base/common/uri.js'; +import { Event } from '../../../../base/common/event.js'; +import { URI } from '../../../../base/common/uri.js'; import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from '../model.js'; -import { ILanguageSelection } from '../languages/language.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { ILanguageSelection } from '../language.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from '../languages.js'; export const IModelService = createDecorator('modelService'); diff --git a/src/vs/editor/common/services/modelService.ts b/src/vs/editor/common/language/services/modelService.ts similarity index 96% rename from src/vs/editor/common/services/modelService.ts rename to src/vs/editor/common/language/services/modelService.ts index c0293db8390..a1b1e28c9ab 100644 --- a/src/vs/editor/common/services/modelService.ts +++ b/src/vs/editor/common/language/services/modelService.ts @@ -3,27 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable, IDisposable, DisposableStore } from '../../../base/common/lifecycle.js'; -import * as platform from '../../../base/common/platform.js'; -import { URI } from '../../../base/common/uri.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable, IDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import * as platform from '../../../../base/common/platform.js'; +import { URI } from '../../../../base/common/uri.js'; import { EditOperation, ISingleEditOperation } from '../core/editOperation.js'; import { Range } from '../core/range.js'; import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from '../model.js'; import { TextModel, createTextBuffer } from '../model/textModel.js'; import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; import { IModelLanguageChangedEvent } from '../textModelEvents.js'; -import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; -import { ILanguageSelection } from '../languages/language.js'; +import { PLAINTEXT_LANGUAGE_ID } from '../../languages/modesRegistry.js'; +import { ILanguageSelection } from '../language.js'; import { IModelService } from './model.js'; import { ITextResourcePropertiesService } from './textResourceConfiguration.js'; -import { IConfigurationChangeEvent, IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { IUndoRedoService, ResourceEditStackSnapshot } from '../../../platform/undoRedo/common/undoRedo.js'; -import { StringSHA1 } from '../../../base/common/hash.js'; +import { IConfigurationChangeEvent, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IUndoRedoService, ResourceEditStackSnapshot } from '../../../../platform/undoRedo/common/undoRedo.js'; +import { StringSHA1 } from '../../../../base/common/hash.js'; import { isEditStackElement } from '../model/editStack.js'; -import { Schemas } from '../../../base/common/network.js'; -import { equals } from '../../../base/common/objects.js'; -import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; +import { Schemas } from '../../../../base/common/network.js'; +import { equals } from '../../../../base/common/objects.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; function MODEL_ID(resource: URI): string { return resource.toString(); diff --git a/src/vs/editor/common/services/modelUndoRedoParticipant.ts b/src/vs/editor/common/language/services/modelUndoRedoParticipant.ts similarity index 92% rename from src/vs/editor/common/services/modelUndoRedoParticipant.ts rename to src/vs/editor/common/language/services/modelUndoRedoParticipant.ts index ffb0760eb26..aeb9ba4af4f 100644 --- a/src/vs/editor/common/services/modelUndoRedoParticipant.ts +++ b/src/vs/editor/common/language/services/modelUndoRedoParticipant.ts @@ -5,8 +5,8 @@ import { IModelService } from './model.js'; import { ITextModelService } from './resolverService.js'; -import { Disposable, IDisposable, dispose } from '../../../base/common/lifecycle.js'; -import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js'; +import { Disposable, IDisposable, dispose } from '../../../../base/common/lifecycle.js'; +import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; import { IUndoRedoDelegate, MultiModelEditStackElement } from '../model/editStack.js'; export class ModelUndoRedoParticipant extends Disposable implements IUndoRedoDelegate { diff --git a/src/vs/editor/common/services/resolverService.ts b/src/vs/editor/common/language/services/resolverService.ts similarity index 84% rename from src/vs/editor/common/services/resolverService.ts rename to src/vs/editor/common/language/services/resolverService.ts index 348f4b34121..604c77bca89 100644 --- a/src/vs/editor/common/services/resolverService.ts +++ b/src/vs/editor/common/language/services/resolverService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; -import { IMarkdownString } from '../../../base/common/htmlContent.js'; -import { IDisposable, IReference } from '../../../base/common/lifecycle.js'; -import { URI } from '../../../base/common/uri.js'; +import { Event } from '../../../../base/common/event.js'; +import { IMarkdownString } from '../../../../base/common/htmlContent.js'; +import { IDisposable, IReference } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; import { ITextModel, ITextSnapshot } from '../model.js'; -import { IResolvableEditorModel } from '../../../platform/editor/common/editor.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { IResolvableEditorModel } from '../../../../platform/editor/common/editor.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; export const ITextModelService = createDecorator('textModelService'); diff --git a/src/vs/editor/common/services/semanticTokensDto.ts b/src/vs/editor/common/language/services/semanticTokensDto.ts similarity index 97% rename from src/vs/editor/common/services/semanticTokensDto.ts rename to src/vs/editor/common/language/services/semanticTokensDto.ts index 4fd9cc88859..1302f3b87f7 100644 --- a/src/vs/editor/common/services/semanticTokensDto.ts +++ b/src/vs/editor/common/language/services/semanticTokensDto.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from '../../../base/common/buffer.js'; -import * as platform from '../../../base/common/platform.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import * as platform from '../../../../base/common/platform.js'; export interface IFullSemanticTokensDto { id: number; diff --git a/src/vs/editor/common/services/semanticTokensProviderStyling.ts b/src/vs/editor/common/language/services/semanticTokensProviderStyling.ts similarity index 98% rename from src/vs/editor/common/services/semanticTokensProviderStyling.ts rename to src/vs/editor/common/language/services/semanticTokensProviderStyling.ts index ca9026d66fe..d410d666f8f 100644 --- a/src/vs/editor/common/services/semanticTokensProviderStyling.ts +++ b/src/vs/editor/common/language/services/semanticTokensProviderStyling.ts @@ -5,10 +5,10 @@ import { SemanticTokensLegend, SemanticTokens } from '../languages.js'; import { FontStyle, MetadataConsts, TokenMetadata } from '../encodedTokenAttributes.js'; -import { IThemeService } from '../../../platform/theme/common/themeService.js'; -import { ILogService, LogLevel } from '../../../platform/log/common/log.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { ILogService, LogLevel } from '../../../../platform/log/common/log.js'; import { SparseMultilineTokens } from '../tokens/sparseMultilineTokens.js'; -import { ILanguageService } from '../languages/language.js'; +import { ILanguageService } from '../language.js'; const enum SemanticTokensProviderStylingConstants { NO_STYLING = 0b01111111111111111111111111111111 diff --git a/src/vs/editor/common/services/semanticTokensStyling.ts b/src/vs/editor/common/language/services/semanticTokensStyling.ts similarity index 90% rename from src/vs/editor/common/services/semanticTokensStyling.ts rename to src/vs/editor/common/language/services/semanticTokensStyling.ts index 6ee050718c9..024e0bb17d7 100644 --- a/src/vs/editor/common/services/semanticTokensStyling.ts +++ b/src/vs/editor/common/language/services/semanticTokensStyling.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from '../languages.js'; import { SemanticTokensProviderStyling } from './semanticTokensProviderStyling.js'; diff --git a/src/vs/editor/common/services/semanticTokensStylingService.ts b/src/vs/editor/common/language/services/semanticTokensStylingService.ts similarity index 81% rename from src/vs/editor/common/services/semanticTokensStylingService.ts rename to src/vs/editor/common/language/services/semanticTokensStylingService.ts index 5479905c409..e609778eb69 100644 --- a/src/vs/editor/common/services/semanticTokensStylingService.ts +++ b/src/vs/editor/common/language/services/semanticTokensStylingService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from '../../../base/common/lifecycle.js'; -import { ILanguageService } from '../languages/language.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { ILanguageService } from '../language.js'; import { DocumentTokensProvider } from './model.js'; -import { IThemeService } from '../../../platform/theme/common/themeService.js'; -import { ILogService } from '../../../platform/log/common/log.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; import { SemanticTokensProviderStyling } from './semanticTokensProviderStyling.js'; import { ISemanticTokensStylingService } from './semanticTokensStyling.js'; -import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; export class SemanticTokensStylingService extends Disposable implements ISemanticTokensStylingService { diff --git a/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts b/src/vs/editor/common/language/services/textModelSync/textModelSync.impl.ts similarity index 96% rename from src/vs/editor/common/services/textModelSync/textModelSync.impl.ts rename to src/vs/editor/common/language/services/textModelSync/textModelSync.impl.ts index ccab956b331..c233c9128a2 100644 --- a/src/vs/editor/common/services/textModelSync/textModelSync.impl.ts +++ b/src/vs/editor/common/language/services/textModelSync/textModelSync.impl.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IntervalTimer } from '../../../../base/common/async.js'; -import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IWebWorkerClient, IWebWorkerServer } from '../../../../base/common/worker/webWorker.js'; +import { IntervalTimer } from '../../../../../base/common/async.js'; +import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { IWebWorkerClient, IWebWorkerServer } from '../../../../../base/common/worker/webWorker.js'; import { IPosition, Position } from '../../core/position.js'; import { IRange, Range } from '../../core/range.js'; import { ensureValidWordDefinition, getWordAtText, IWordAtPosition } from '../../core/wordHelper.js'; -import { IDocumentColorComputerTarget } from '../../languages/defaultDocumentColorsComputer.js'; -import { ILinkComputerTarget } from '../../languages/linkComputer.js'; +import { IDocumentColorComputerTarget } from '../../../languages/defaultDocumentColorsComputer.js'; +import { ILinkComputerTarget } from '../../../languages/linkComputer.js'; import { MirrorTextModel as BaseMirrorModel, IModelChangedEvent } from '../../model/mirrorTextModel.js'; import { IMirrorModel, IWordRange } from '../editorWebWorker.js'; import { IModelService } from '../model.js'; diff --git a/src/vs/editor/common/services/textModelSync/textModelSync.protocol.ts b/src/vs/editor/common/language/services/textModelSync/textModelSync.protocol.ts similarity index 100% rename from src/vs/editor/common/services/textModelSync/textModelSync.protocol.ts rename to src/vs/editor/common/language/services/textModelSync/textModelSync.protocol.ts diff --git a/src/vs/editor/common/services/textResourceConfiguration.ts b/src/vs/editor/common/language/services/textResourceConfiguration.ts similarity index 93% rename from src/vs/editor/common/services/textResourceConfiguration.ts rename to src/vs/editor/common/language/services/textResourceConfiguration.ts index da2c0a8b653..4f12e135555 100644 --- a/src/vs/editor/common/services/textResourceConfiguration.ts +++ b/src/vs/editor/common/language/services/textResourceConfiguration.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; -import { URI } from '../../../base/common/uri.js'; +import { Event } from '../../../../base/common/event.js'; +import { URI } from '../../../../base/common/uri.js'; import { IPosition } from '../core/position.js'; -import { ConfigurationTarget, IConfigurationValue } from '../../../platform/configuration/common/configuration.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { ConfigurationTarget, IConfigurationValue } from '../../../../platform/configuration/common/configuration.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; export const ITextResourceConfigurationService = createDecorator('textResourceConfigurationService'); diff --git a/src/vs/editor/common/services/textResourceConfigurationService.ts b/src/vs/editor/common/language/services/textResourceConfigurationService.ts similarity index 94% rename from src/vs/editor/common/services/textResourceConfigurationService.ts rename to src/vs/editor/common/language/services/textResourceConfigurationService.ts index 04420d7f5e6..e9078f1665a 100644 --- a/src/vs/editor/common/services/textResourceConfigurationService.ts +++ b/src/vs/editor/common/language/services/textResourceConfigurationService.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../base/common/event.js'; -import { Disposable } from '../../../base/common/lifecycle.js'; -import { URI } from '../../../base/common/uri.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; import { IPosition, Position } from '../core/position.js'; -import { ILanguageService } from '../languages/language.js'; +import { ILanguageService } from '../language.js'; import { IModelService } from './model.js'; import { ITextResourceConfigurationService, ITextResourceConfigurationChangeEvent } from './textResourceConfiguration.js'; -import { IConfigurationService, ConfigurationTarget, IConfigurationValue, IConfigurationChangeEvent } from '../../../platform/configuration/common/configuration.js'; +import { IConfigurationService, ConfigurationTarget, IConfigurationValue, IConfigurationChangeEvent } from '../../../../platform/configuration/common/configuration.js'; export class TextResourceConfigurationService extends Disposable implements ITextResourceConfigurationService { diff --git a/src/vs/editor/common/services/treeSitter/cursorUtils.ts b/src/vs/editor/common/language/services/treeSitter/cursorUtils.ts similarity index 100% rename from src/vs/editor/common/services/treeSitter/cursorUtils.ts rename to src/vs/editor/common/language/services/treeSitter/cursorUtils.ts diff --git a/src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts b/src/vs/editor/common/language/services/treeSitter/textModelTreeSitter.ts similarity index 97% rename from src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts rename to src/vs/editor/common/language/services/treeSitter/textModelTreeSitter.ts index 5c7116a39e7..0fb4526d1a6 100644 --- a/src/vs/editor/common/services/treeSitter/textModelTreeSitter.ts +++ b/src/vs/editor/common/language/services/treeSitter/textModelTreeSitter.ts @@ -5,21 +5,21 @@ import type * as Parser from '@vscode/tree-sitter-wasm'; import { ITreeSitterParseResult, ITextModelTreeSitter, RangeChange, TreeParseUpdateEvent, ITreeSitterImporter, ModelTreeUpdateEvent } from '../treeSitterParserService.js'; -import { Disposable, DisposableStore, dispose, IDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, dispose, IDisposable } from '../../../../../base/common/lifecycle.js'; import { ITextModel } from '../../model.js'; import { IModelContentChange, IModelContentChangedEvent } from '../../textModelEvents.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { ILogService } from '../../../../platform/log/common/log.js'; -import { setTimeout0 } from '../../../../base/common/platform.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { CancellationToken, cancelOnDispose } from '../../../../base/common/cancellation.js'; +import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; +import { setTimeout0 } from '../../../../../base/common/platform.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; +import { CancellationToken, cancelOnDispose } from '../../../../../base/common/cancellation.js'; import { Range } from '../../core/range.js'; -import { LimitedQueue } from '../../../../base/common/async.js'; +import { LimitedQueue } from '../../../../../base/common/async.js'; import { TextLength } from '../../core/textLength.js'; import { TreeSitterLanguages } from './treeSitterLanguages.js'; -import { AppResourcePath, FileAccess } from '../../../../base/common/network.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; +import { AppResourcePath, FileAccess } from '../../../../../base/common/network.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { CancellationError, isCancellationError } from '../../../../../base/common/errors.js'; import { getClosestPreviousNodes, gotoNthChild, gotoParent, nextSiblingOrParentSibling } from './cursorUtils.js'; export interface TextModelTreeSitterItem { diff --git a/src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts b/src/vs/editor/common/language/services/treeSitter/treeSitterLanguages.ts similarity index 89% rename from src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts rename to src/vs/editor/common/language/services/treeSitter/treeSitterLanguages.ts index 322873229ae..23f72650264 100644 --- a/src/vs/editor/common/services/treeSitter/treeSitterLanguages.ts +++ b/src/vs/editor/common/language/services/treeSitter/treeSitterLanguages.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import type * as Parser from '@vscode/tree-sitter-wasm'; -import { AppResourcePath, FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../../base/common/network.js'; +import { AppResourcePath, FileAccess, nodeModulesAsarUnpackedPath, nodeModulesPath } from '../../../../../base/common/network.js'; import { ITreeSitterImporter } from '../treeSitterParserService.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { canASAR } from '../../../../amdX.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { PromiseResult } from '../../../../base/common/observable.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { canASAR } from '../../../../../amdX.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; +import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; +import { PromiseResult } from '../../../../../base/common/observable.js'; export const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; diff --git a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts b/src/vs/editor/common/language/services/treeSitter/treeSitterParserService.ts similarity index 93% rename from src/vs/editor/common/services/treeSitter/treeSitterParserService.ts rename to src/vs/editor/common/language/services/treeSitter/treeSitterParserService.ts index 174f31549d5..34213e30970 100644 --- a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts +++ b/src/vs/editor/common/language/services/treeSitter/treeSitterParserService.ts @@ -4,18 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import type * as Parser from '@vscode/tree-sitter-wasm'; -import { AppResourcePath, FileAccess } from '../../../../base/common/network.js'; +import { AppResourcePath, FileAccess } from '../../../../../base/common/network.js'; import { EDITOR_EXPERIMENTAL_PREFER_TREESITTER, ITreeSitterParserService, ITextModelTreeSitter, TreeUpdateEvent, ITreeSitterImporter, TREESITTER_ALLOWED_SUPPORT, ModelTreeUpdateEvent } from '../treeSitterParserService.js'; import { IModelService } from '../model.js'; -import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableMap, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ITextModel } from '../../model.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; +import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; import { TextModelTreeSitter, TextModelTreeSitterItem } from './textModelTreeSitter.js'; import { getModuleLocation, TreeSitterLanguages } from './treeSitterLanguages.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; const EDITOR_TREESITTER_TELEMETRY = 'editor.experimental.treeSitterTelemetry'; const FILENAME_TREESITTER_WASM = `tree-sitter.wasm`; diff --git a/src/vs/editor/common/services/treeSitterParserService.ts b/src/vs/editor/common/language/services/treeSitterParserService.ts similarity index 95% rename from src/vs/editor/common/services/treeSitterParserService.ts rename to src/vs/editor/common/language/services/treeSitterParserService.ts index a362e68f809..96fc31d9132 100644 --- a/src/vs/editor/common/services/treeSitterParserService.ts +++ b/src/vs/editor/common/language/services/treeSitterParserService.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import type * as Parser from '@vscode/tree-sitter-wasm'; -import { Event } from '../../../base/common/event.js'; +import { Event } from '../../../../base/common/event.js'; import { ITextModel } from '../model.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { Range } from '../core/range.js'; -import { importAMDNodeModule } from '../../../amdX.js'; +import { importAMDNodeModule } from '../../../../amdX.js'; import { IModelContentChangedEvent } from '../textModelEvents.js'; export const EDITOR_EXPERIMENTAL_PREFER_TREESITTER = 'editor.experimental.preferTreeSitter'; diff --git a/src/vs/editor/common/services/treeViewsDnd.ts b/src/vs/editor/common/language/services/treeViewsDnd.ts similarity index 100% rename from src/vs/editor/common/services/treeViewsDnd.ts rename to src/vs/editor/common/language/services/treeViewsDnd.ts diff --git a/src/vs/editor/common/services/treeViewsDndService.ts b/src/vs/editor/common/language/services/treeViewsDndService.ts similarity index 72% rename from src/vs/editor/common/services/treeViewsDndService.ts rename to src/vs/editor/common/language/services/treeViewsDndService.ts index e7b3ebb9d07..44052dc31c7 100644 --- a/src/vs/editor/common/services/treeViewsDndService.ts +++ b/src/vs/editor/common/language/services/treeViewsDndService.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; -import { VSDataTransfer } from '../../../base/common/dataTransfer.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { VSDataTransfer } from '../../../../base/common/dataTransfer.js'; import { ITreeViewsDnDService as ITreeViewsDnDServiceCommon, TreeViewsDnDService } from './treeViewsDnd.js'; export interface ITreeViewsDnDService extends ITreeViewsDnDServiceCommon { } diff --git a/src/vs/editor/common/services/unicodeTextModelHighlighter.ts b/src/vs/editor/common/language/services/unicodeTextModelHighlighter.ts similarity index 98% rename from src/vs/editor/common/services/unicodeTextModelHighlighter.ts rename to src/vs/editor/common/language/services/unicodeTextModelHighlighter.ts index d9e86c3e68b..f7ed203f334 100644 --- a/src/vs/editor/common/services/unicodeTextModelHighlighter.ts +++ b/src/vs/editor/common/language/services/unicodeTextModelHighlighter.ts @@ -5,9 +5,9 @@ import { IRange, Range } from '../core/range.js'; import { Searcher } from '../model/textModelSearch.js'; -import * as strings from '../../../base/common/strings.js'; +import * as strings from '../../../../base/common/strings.js'; import { IUnicodeHighlightsResult } from './editorWorker.js'; -import { assertNever } from '../../../base/common/assert.js'; +import { assertNever } from '../../../../base/common/assert.js'; import { DEFAULT_WORD_REGEXP, getWordAtText } from '../core/wordHelper.js'; export class UnicodeTextModelHighlighter { diff --git a/src/vs/editor/common/language/textModelEvents.ts b/src/vs/editor/common/language/textModelEvents.ts new file mode 100644 index 00000000000..c35d0472106 --- /dev/null +++ b/src/vs/editor/common/language/textModelEvents.ts @@ -0,0 +1,397 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRange } from './core/range.js'; +import { Selection } from './core/selection.js'; +import { IModelDecoration, InjectedTextOptions } from './model.js'; + +/** + * An event describing that the current language associated with a model has changed. + */ +export interface IModelLanguageChangedEvent { + /** + * Previous language + */ + readonly oldLanguage: string; + /** + * New language + */ + readonly newLanguage: string; + + /** + * Source of the call that caused the event. + */ + readonly source: string; +} + +/** + * An event describing that the language configuration associated with a model has changed. + */ +export interface IModelLanguageConfigurationChangedEvent { +} + +export interface IModelContentChange { + /** + * The old range that got replaced. + */ + readonly range: IRange; + /** + * The offset of the range that got replaced. + */ + readonly rangeOffset: number; + /** + * The length of the range that got replaced. + */ + readonly rangeLength: number; + /** + * The new text for the range. + */ + readonly text: string; +} + +/** + * An event describing a change in the text of a model. + */ +export interface IModelContentChangedEvent { + /** + * The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence. + */ + readonly changes: IModelContentChange[]; + /** + * The (new) end-of-line character. + */ + readonly eol: string; + /** + * The new version id the model has transitioned to. + */ + readonly versionId: number; + /** + * Flag that indicates that this event was generated while undoing. + */ + readonly isUndoing: boolean; + /** + * Flag that indicates that this event was generated while redoing. + */ + readonly isRedoing: boolean; + /** + * Flag that indicates that all decorations were lost with this edit. + * The model has been reset to a new value. + */ + readonly isFlush: boolean; + + /** + * Flag that indicates that this event describes an eol change. + */ + readonly isEolChange: boolean; +} + +/** + * An event describing that model decorations have changed. + */ +export interface IModelDecorationsChangedEvent { + readonly affectsMinimap: boolean; + readonly affectsOverviewRuler: boolean; + readonly affectsGlyphMargin: boolean; + readonly affectsLineNumber: boolean; +} + +/** + * An event describing that some ranges of lines have been tokenized (their tokens have changed). + * @internal + */ +export interface IModelTokensChangedEvent { + readonly semanticTokensApplied: boolean; + readonly ranges: { + /** + * The start of the range (inclusive) + */ + readonly fromLineNumber: number; + /** + * The end of the range (inclusive) + */ + readonly toLineNumber: number; + }[]; +} + +export interface IModelOptionsChangedEvent { + readonly tabSize: boolean; + readonly indentSize: boolean; + readonly insertSpaces: boolean; + readonly trimAutoWhitespace: boolean; +} + +/** + * @internal + */ +export const enum RawContentChangedType { + Flush = 1, + LineChanged = 2, + LinesDeleted = 3, + LinesInserted = 4, + EOLChanged = 5 +} + +/** + * An event describing that a model has been reset to a new value. + * @internal + */ +export class ModelRawFlush { + public readonly changeType = RawContentChangedType.Flush; +} + +/** + * Represents text injected on a line + * @internal + */ +export class LineInjectedText { + public static applyInjectedText(lineText: string, injectedTexts: LineInjectedText[] | null): string { + if (!injectedTexts || injectedTexts.length === 0) { + return lineText; + } + let result = ''; + let lastOriginalOffset = 0; + for (const injectedText of injectedTexts) { + result += lineText.substring(lastOriginalOffset, injectedText.column - 1); + lastOriginalOffset = injectedText.column - 1; + result += injectedText.options.content; + } + result += lineText.substring(lastOriginalOffset); + return result; + } + + public static fromDecorations(decorations: IModelDecoration[]): LineInjectedText[] { + const result: LineInjectedText[] = []; + for (const decoration of decorations) { + if (decoration.options.before && decoration.options.before.content.length > 0) { + result.push(new LineInjectedText( + decoration.ownerId, + decoration.range.startLineNumber, + decoration.range.startColumn, + decoration.options.before, + 0, + )); + } + if (decoration.options.after && decoration.options.after.content.length > 0) { + result.push(new LineInjectedText( + decoration.ownerId, + decoration.range.endLineNumber, + decoration.range.endColumn, + decoration.options.after, + 1, + )); + } + } + result.sort((a, b) => { + if (a.lineNumber === b.lineNumber) { + if (a.column === b.column) { + return a.order - b.order; + } + return a.column - b.column; + } + return a.lineNumber - b.lineNumber; + }); + return result; + } + + constructor( + public readonly ownerId: number, + public readonly lineNumber: number, + public readonly column: number, + public readonly options: InjectedTextOptions, + public readonly order: number + ) { } + + public withText(text: string): LineInjectedText { + return new LineInjectedText(this.ownerId, this.lineNumber, this.column, { ...this.options, content: text }, this.order); + } +} + +/** + * An event describing that a line has changed in a model. + * @internal + */ +export class ModelRawLineChanged { + public readonly changeType = RawContentChangedType.LineChanged; + /** + * The line that has changed. + */ + public readonly lineNumber: number; + /** + * The new value of the line. + */ + public readonly detail: string; + /** + * The injected text on the line. + */ + public readonly injectedText: LineInjectedText[] | null; + + constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) { + this.lineNumber = lineNumber; + this.detail = detail; + this.injectedText = injectedText; + } +} + +/** + * An event describing that line(s) have been deleted in a model. + * @internal + */ +export class ModelRawLinesDeleted { + public readonly changeType = RawContentChangedType.LinesDeleted; + /** + * At what line the deletion began (inclusive). + */ + public readonly fromLineNumber: number; + /** + * At what line the deletion stopped (inclusive). + */ + public readonly toLineNumber: number; + + constructor(fromLineNumber: number, toLineNumber: number) { + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + } +} + +/** + * An event describing that line(s) have been inserted in a model. + * @internal + */ +export class ModelRawLinesInserted { + public readonly changeType = RawContentChangedType.LinesInserted; + /** + * Before what line did the insertion begin + */ + public readonly fromLineNumber: number; + /** + * `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted + */ + public readonly toLineNumber: number; + /** + * The text that was inserted + */ + public readonly detail: string[]; + /** + * The injected texts for every inserted line. + */ + public readonly injectedTexts: (LineInjectedText[] | null)[]; + + constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) { + this.injectedTexts = injectedTexts; + this.fromLineNumber = fromLineNumber; + this.toLineNumber = toLineNumber; + this.detail = detail; + } +} + +/** + * An event describing that a model has had its EOL changed. + * @internal + */ +export class ModelRawEOLChanged { + public readonly changeType = RawContentChangedType.EOLChanged; +} + +/** + * @internal + */ +export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged; + +/** + * An event describing a change in the text of a model. + * @internal + */ +export class ModelRawContentChangedEvent { + + public readonly changes: ModelRawChange[]; + /** + * The new version id the model has transitioned to. + */ + public readonly versionId: number; + /** + * Flag that indicates that this event was generated while undoing. + */ + public readonly isUndoing: boolean; + /** + * Flag that indicates that this event was generated while redoing. + */ + public readonly isRedoing: boolean; + + public resultingSelection: Selection[] | null; + + constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) { + this.changes = changes; + this.versionId = versionId; + this.isUndoing = isUndoing; + this.isRedoing = isRedoing; + this.resultingSelection = null; + } + + public containsEvent(type: RawContentChangedType): boolean { + for (let i = 0, len = this.changes.length; i < len; i++) { + const change = this.changes[i]; + if (change.changeType === type) { + return true; + } + } + return false; + } + + public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent { + const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes); + const versionId = b.versionId; + const isUndoing = (a.isUndoing || b.isUndoing); + const isRedoing = (a.isRedoing || b.isRedoing); + return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing); + } +} + +/** + * An event describing a change in injected text. + * @internal + */ +export class ModelInjectedTextChangedEvent { + + public readonly changes: ModelRawLineChanged[]; + + constructor(changes: ModelRawLineChanged[]) { + this.changes = changes; + } +} + +/** + * @internal + */ +export class InternalModelContentChangeEvent { + constructor( + public readonly rawContentChangedEvent: ModelRawContentChangedEvent, + public readonly contentChangedEvent: IModelContentChangedEvent, + ) { } + + public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent { + const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent); + const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent); + return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent); + } + + private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent { + const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes); + const eol = b.eol; + const versionId = b.versionId; + const isUndoing = (a.isUndoing || b.isUndoing); + const isRedoing = (a.isRedoing || b.isRedoing); + const isFlush = (a.isFlush || b.isFlush); + const isEolChange = a.isEolChange && b.isEolChange; // both must be true to not confuse listeners who skip such edits + return { + changes: changes, + eol: eol, + isEolChange: isEolChange, + versionId: versionId, + isUndoing: isUndoing, + isRedoing: isRedoing, + isFlush: isFlush, + }; + } +} diff --git a/src/vs/editor/common/language/tokenizationRegistry.ts b/src/vs/editor/common/language/tokenizationRegistry.ts new file mode 100644 index 00000000000..3ea6613eb24 --- /dev/null +++ b/src/vs/editor/common/language/tokenizationRegistry.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Color } from '../../../base/common/color.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { ITokenizationRegistry, ITokenizationSupportChangedEvent, ILazyTokenizationSupport } from './languages.js'; +import { ColorId } from './encodedTokenAttributes.js'; + +export class TokenizationRegistry implements ITokenizationRegistry { + + private readonly _tokenizationSupports = new Map(); + private readonly _factories = new Map>(); + + private readonly _onDidChange = new Emitter(); + public readonly onDidChange: Event = this._onDidChange.event; + + private _colorMap: Color[] | null; + + constructor() { + this._colorMap = null; + } + + public handleChange(languageIds: string[]): void { + this._onDidChange.fire({ + changedLanguages: languageIds, + changedColorMap: false + }); + } + + public register(languageId: string, support: TSupport): IDisposable { + this._tokenizationSupports.set(languageId, support); + this.handleChange([languageId]); + return toDisposable(() => { + if (this._tokenizationSupports.get(languageId) !== support) { + return; + } + this._tokenizationSupports.delete(languageId); + this.handleChange([languageId]); + }); + } + + public get(languageId: string): TSupport | null { + return this._tokenizationSupports.get(languageId) || null; + } + + public registerFactory(languageId: string, factory: ILazyTokenizationSupport): IDisposable { + this._factories.get(languageId)?.dispose(); + const myData = new TokenizationSupportFactoryData(this, languageId, factory); + this._factories.set(languageId, myData); + return toDisposable(() => { + const v = this._factories.get(languageId); + if (!v || v !== myData) { + return; + } + this._factories.delete(languageId); + v.dispose(); + }); + } + + public async getOrCreate(languageId: string): Promise { + // check first if the support is already set + const tokenizationSupport = this.get(languageId); + if (tokenizationSupport) { + return tokenizationSupport; + } + + const factory = this._factories.get(languageId); + if (!factory || factory.isResolved) { + // no factory or factory.resolve already finished + return null; + } + + await factory.resolve(); + + return this.get(languageId); + } + + public isResolved(languageId: string): boolean { + const tokenizationSupport = this.get(languageId); + if (tokenizationSupport) { + return true; + } + + const factory = this._factories.get(languageId); + if (!factory || factory.isResolved) { + return true; + } + + return false; + } + + public setColorMap(colorMap: Color[]): void { + this._colorMap = colorMap; + this._onDidChange.fire({ + changedLanguages: Array.from(this._tokenizationSupports.keys()), + changedColorMap: true + }); + } + + public getColorMap(): Color[] | null { + return this._colorMap; + } + + public getDefaultBackground(): Color | null { + if (this._colorMap && this._colorMap.length > ColorId.DefaultBackground) { + return this._colorMap[ColorId.DefaultBackground]; + } + return null; + } +} + +class TokenizationSupportFactoryData extends Disposable { + + private _isDisposed: boolean = false; + private _resolvePromise: Promise | null = null; + private _isResolved: boolean = false; + + public get isResolved(): boolean { + return this._isResolved; + } + + constructor( + private readonly _registry: TokenizationRegistry, + private readonly _languageId: string, + private readonly _factory: ILazyTokenizationSupport, + ) { + super(); + } + + public override dispose(): void { + this._isDisposed = true; + super.dispose(); + } + + public async resolve(): Promise { + if (!this._resolvePromise) { + this._resolvePromise = this._create(); + } + return this._resolvePromise; + } + + private async _create(): Promise { + const value = await this._factory.tokenizationSupport; + this._isResolved = true; + if (value && !this._isDisposed) { + this._register(this._registry.register(this._languageId, value)); + } + } +} diff --git a/src/vs/editor/common/language/tokenizationTextModelPart.ts b/src/vs/editor/common/language/tokenizationTextModelPart.ts new file mode 100644 index 00000000000..6238c606552 --- /dev/null +++ b/src/vs/editor/common/language/tokenizationTextModelPart.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range } from './core/range.js'; +import { StandardTokenType } from './encodedTokenAttributes.js'; +import { LineTokens } from './tokens/lineTokens.js'; +import { SparseMultilineTokens } from './tokens/sparseMultilineTokens.js'; + +/** + * Provides tokenization related functionality of the text model. +*/ +export interface ITokenizationTextModelPart { + readonly hasTokens: boolean; + + /** + * Replaces all semantic tokens with the provided `tokens`. + * @internal + */ + setSemanticTokens(tokens: SparseMultilineTokens[] | null, isComplete: boolean): void; + + /** + * Merges the provided semantic tokens into existing semantic tokens. + * @internal + */ + setPartialSemanticTokens(range: Range, tokens: SparseMultilineTokens[] | null): void; + + /** + * @internal + */ + hasCompleteSemanticTokens(): boolean; + + /** + * @internal + */ + hasSomeSemanticTokens(): boolean; + + /** + * Flush all tokenization state. + * @internal + */ + resetTokenization(): void; + + /** + * Force tokenization information for `lineNumber` to be accurate. + * @internal + */ + forceTokenization(lineNumber: number): void; + + /** + * If it is cheap, force tokenization information for `lineNumber` to be accurate. + * This is based on a heuristic. + * @internal + */ + tokenizeIfCheap(lineNumber: number): void; + + /** + * Check if tokenization information is accurate for `lineNumber`. + * @internal + */ + hasAccurateTokensForLine(lineNumber: number): boolean; + + /** + * Check if calling `forceTokenization` for this `lineNumber` will be cheap (time-wise). + * This is based on a heuristic. + * @internal + */ + isCheapToTokenize(lineNumber: number): boolean; + + /** + * Get the tokens for the line `lineNumber`. + * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. + * @internal + */ + getLineTokens(lineNumber: number): LineTokens; + + /** + * Returns the standard token type for a character if the character were to be inserted at + * the given position. If the result cannot be accurate, it returns null. + * @internal + */ + getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType; + + /** + * Tokens the lines as if they were inserted at [lineNumber, lineNumber). + * @internal + */ + tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null; + + getLanguageId(): string; + getLanguageIdAtPosition(lineNumber: number, column: number): string; + + setLanguageId(languageId: string, source?: string): void; + + readonly backgroundTokenizationState: BackgroundTokenizationState; +} + +export const enum BackgroundTokenizationState { + InProgress = 1, + Completed = 2, +} diff --git a/src/vs/editor/common/language/tokens/contiguousMultilineTokens.ts b/src/vs/editor/common/language/tokens/contiguousMultilineTokens.ts new file mode 100644 index 00000000000..491aa5ad1d3 --- /dev/null +++ b/src/vs/editor/common/language/tokens/contiguousMultilineTokens.ts @@ -0,0 +1,229 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from '../../../../base/common/arrays.js'; +import { readUInt32BE, writeUInt32BE } from '../../../../base/common/buffer.js'; +import { Position } from '../core/position.js'; +import { IRange } from '../core/range.js'; +import { countEOL } from '../core/eolCounter.js'; +import { ContiguousTokensEditing } from './contiguousTokensEditing.js'; +import { LineRange } from '../core/lineRange.js'; + +/** + * Represents contiguous tokens over a contiguous range of lines. + */ +export class ContiguousMultilineTokens { + public static deserialize(buff: Uint8Array, offset: number, result: ContiguousMultilineTokens[]): number { + const view32 = new Uint32Array(buff.buffer); + const startLineNumber = readUInt32BE(buff, offset); offset += 4; + const count = readUInt32BE(buff, offset); offset += 4; + const tokens: Uint32Array[] = []; + for (let i = 0; i < count; i++) { + const byteCount = readUInt32BE(buff, offset); offset += 4; + tokens.push(view32.subarray(offset / 4, offset / 4 + byteCount / 4)); + offset += byteCount; + } + result.push(new ContiguousMultilineTokens(startLineNumber, tokens)); + return offset; + } + + /** + * The start line number for this block of tokens. + */ + private _startLineNumber: number; + + /** + * The tokens are stored in a binary format. There is an element for each line, + * so `tokens[index]` contains all tokens on line `startLineNumber + index`. + * + * On a specific line, each token occupies two array indices. For token i: + * - at offset 2*i => endOffset + * - at offset 2*i + 1 => metadata + * + */ + private _tokens: (Uint32Array | ArrayBuffer | null)[]; + + /** + * (Inclusive) start line number for these tokens. + */ + public get startLineNumber(): number { + return this._startLineNumber; + } + + /** + * (Inclusive) end line number for these tokens. + */ + public get endLineNumber(): number { + return this._startLineNumber + this._tokens.length - 1; + } + + constructor(startLineNumber: number, tokens: Uint32Array[]) { + this._startLineNumber = startLineNumber; + this._tokens = tokens; + } + + getLineRange(): LineRange { + return new LineRange(this._startLineNumber, this._startLineNumber + this._tokens.length); + } + + /** + * @see {@link _tokens} + */ + public getLineTokens(lineNumber: number): Uint32Array | ArrayBuffer | null { + return this._tokens[lineNumber - this._startLineNumber]; + } + + public appendLineTokens(lineTokens: Uint32Array): void { + this._tokens.push(lineTokens); + } + + public serializeSize(): number { + let result = 0; + result += 4; // 4 bytes for the start line number + result += 4; // 4 bytes for the line count + for (let i = 0; i < this._tokens.length; i++) { + const lineTokens = this._tokens[i]; + if (!(lineTokens instanceof Uint32Array)) { + throw new Error(`Not supported!`); + } + result += 4; // 4 bytes for the byte count + result += lineTokens.byteLength; + } + return result; + } + + public serialize(destination: Uint8Array, offset: number): number { + writeUInt32BE(destination, this._startLineNumber, offset); offset += 4; + writeUInt32BE(destination, this._tokens.length, offset); offset += 4; + for (let i = 0; i < this._tokens.length; i++) { + const lineTokens = this._tokens[i]; + if (!(lineTokens instanceof Uint32Array)) { + throw new Error(`Not supported!`); + } + writeUInt32BE(destination, lineTokens.byteLength, offset); offset += 4; + destination.set(new Uint8Array(lineTokens.buffer), offset); offset += lineTokens.byteLength; + } + return offset; + } + + public applyEdit(range: IRange, text: string): void { + const [eolCount, firstLineLength] = countEOL(text); + this._acceptDeleteRange(range); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); + } + + private _acceptDeleteRange(range: IRange): void { + if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + + const firstLineIndex = range.startLineNumber - this._startLineNumber; + const lastLineIndex = range.endLineNumber - this._startLineNumber; + + if (lastLineIndex < 0) { + // this deletion occurs entirely before this block, so we only need to adjust line numbers + const deletedLinesCount = lastLineIndex - firstLineIndex; + this._startLineNumber -= deletedLinesCount; + return; + } + + if (firstLineIndex >= this._tokens.length) { + // this deletion occurs entirely after this block, so there is nothing to do + return; + } + + if (firstLineIndex < 0 && lastLineIndex >= this._tokens.length) { + // this deletion completely encompasses this block + this._startLineNumber = 0; + this._tokens = []; + return; + } + + if (firstLineIndex === lastLineIndex) { + // a delete on a single line + this._tokens[firstLineIndex] = ContiguousTokensEditing.delete(this._tokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1); + return; + } + + if (firstLineIndex >= 0) { + // The first line survives + this._tokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[firstLineIndex], range.startColumn - 1); + + if (lastLineIndex < this._tokens.length) { + // The last line survives + const lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1); + + // Take remaining text on last line and append it to remaining text on first line + this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], lastLineTokens); + + // Delete middle lines + this._tokens.splice(firstLineIndex + 1, lastLineIndex - firstLineIndex); + } else { + // The last line does not survive + + // Take remaining text on last line and append it to remaining text on first line + this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], null); + + // Delete lines + this._tokens = this._tokens.slice(0, firstLineIndex + 1); + } + } else { + // The first line does not survive + + const deletedBefore = -firstLineIndex; + this._startLineNumber -= deletedBefore; + + // Remove beginning from last line + this._tokens[lastLineIndex] = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1); + + // Delete lines + this._tokens = this._tokens.slice(lastLineIndex); + } + } + + private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { + + if (eolCount === 0 && firstLineLength === 0) { + // Nothing to insert + return; + } + + const lineIndex = position.lineNumber - this._startLineNumber; + + if (lineIndex < 0) { + // this insertion occurs before this block, so we only need to adjust line numbers + this._startLineNumber += eolCount; + return; + } + + if (lineIndex >= this._tokens.length) { + // this insertion occurs after this block, so there is nothing to do + return; + } + + if (eolCount === 0) { + // Inserting text on one line + this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength); + return; + } + + this._tokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[lineIndex], position.column - 1); + this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength); + + this._insertLines(position.lineNumber, eolCount); + } + + private _insertLines(insertIndex: number, insertCount: number): void { + if (insertCount === 0) { + return; + } + const lineTokens: (Uint32Array | ArrayBuffer | null)[] = []; + for (let i = 0; i < insertCount; i++) { + lineTokens[i] = null; + } + this._tokens = arrays.arrayInsert(this._tokens, insertIndex, lineTokens); + } +} diff --git a/src/vs/editor/common/language/tokens/contiguousMultilineTokensBuilder.ts b/src/vs/editor/common/language/tokens/contiguousMultilineTokensBuilder.ts new file mode 100644 index 00000000000..f83710dd201 --- /dev/null +++ b/src/vs/editor/common/language/tokens/contiguousMultilineTokensBuilder.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { readUInt32BE, writeUInt32BE } from '../../../../base/common/buffer.js'; +import { ContiguousMultilineTokens } from './contiguousMultilineTokens.js'; + +export class ContiguousMultilineTokensBuilder { + + public static deserialize(buff: Uint8Array): ContiguousMultilineTokens[] { + let offset = 0; + const count = readUInt32BE(buff, offset); offset += 4; + const result: ContiguousMultilineTokens[] = []; + for (let i = 0; i < count; i++) { + offset = ContiguousMultilineTokens.deserialize(buff, offset, result); + } + return result; + } + + private readonly _tokens: ContiguousMultilineTokens[]; + + constructor() { + this._tokens = []; + } + + public add(lineNumber: number, lineTokens: Uint32Array): void { + if (this._tokens.length > 0) { + const last = this._tokens[this._tokens.length - 1]; + if (last.endLineNumber + 1 === lineNumber) { + // append + last.appendLineTokens(lineTokens); + return; + } + } + this._tokens.push(new ContiguousMultilineTokens(lineNumber, [lineTokens])); + } + + public finalize(): ContiguousMultilineTokens[] { + return this._tokens; + } + + public serialize(): Uint8Array { + const size = this._serializeSize(); + const result = new Uint8Array(size); + this._serialize(result); + return result; + } + + private _serializeSize(): number { + let result = 0; + result += 4; // 4 bytes for the count + for (let i = 0; i < this._tokens.length; i++) { + result += this._tokens[i].serializeSize(); + } + return result; + } + + private _serialize(destination: Uint8Array): void { + let offset = 0; + writeUInt32BE(destination, this._tokens.length, offset); offset += 4; + for (let i = 0; i < this._tokens.length; i++) { + offset = this._tokens[i].serialize(destination, offset); + } + } +} diff --git a/src/vs/editor/common/language/tokens/contiguousTokensEditing.ts b/src/vs/editor/common/language/tokens/contiguousTokensEditing.ts new file mode 100644 index 00000000000..28d444a2df0 --- /dev/null +++ b/src/vs/editor/common/language/tokens/contiguousTokensEditing.ts @@ -0,0 +1,144 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { LineTokens } from './lineTokens.js'; + +export const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer; + +export class ContiguousTokensEditing { + + public static deleteBeginning(lineTokens: Uint32Array | ArrayBuffer | null, toChIndex: number): Uint32Array | ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + return ContiguousTokensEditing.delete(lineTokens, 0, toChIndex); + } + + public static deleteEnding(lineTokens: Uint32Array | ArrayBuffer | null, fromChIndex: number): Uint32Array | ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + + const tokens = toUint32Array(lineTokens); + const lineTextLength = tokens[tokens.length - 2]; + return ContiguousTokensEditing.delete(lineTokens, fromChIndex, lineTextLength); + } + + public static delete(lineTokens: Uint32Array | ArrayBuffer | null, fromChIndex: number, toChIndex: number): Uint32Array | ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) { + return lineTokens; + } + + const tokens = toUint32Array(lineTokens); + const tokensCount = (tokens.length >>> 1); + + // special case: deleting everything + if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) { + return EMPTY_LINE_TOKENS; + } + + const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex); + const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0); + const fromTokenEndOffset = tokens[fromTokenIndex << 1]; + + if (toChIndex < fromTokenEndOffset) { + // the delete range is inside a single token + const delta = (toChIndex - fromChIndex); + for (let i = fromTokenIndex; i < tokensCount; i++) { + tokens[i << 1] -= delta; + } + return lineTokens; + } + + let dest: number; + let lastEnd: number; + if (fromTokenStartOffset !== fromChIndex) { + tokens[fromTokenIndex << 1] = fromChIndex; + dest = ((fromTokenIndex + 1) << 1); + lastEnd = fromChIndex; + } else { + dest = (fromTokenIndex << 1); + lastEnd = fromTokenStartOffset; + } + + const delta = (toChIndex - fromChIndex); + for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) { + const tokenEndOffset = tokens[tokenIndex << 1] - delta; + if (tokenEndOffset > lastEnd) { + tokens[dest++] = tokenEndOffset; + tokens[dest++] = tokens[(tokenIndex << 1) + 1]; + lastEnd = tokenEndOffset; + } + } + + if (dest === tokens.length) { + // nothing to trim + return lineTokens; + } + + const tmp = new Uint32Array(dest); + tmp.set(tokens.subarray(0, dest), 0); + return tmp.buffer; + } + + public static append(lineTokens: Uint32Array | ArrayBuffer | null, _otherTokens: Uint32Array | ArrayBuffer | null): Uint32Array | ArrayBuffer | null { + if (_otherTokens === EMPTY_LINE_TOKENS) { + return lineTokens; + } + if (lineTokens === EMPTY_LINE_TOKENS) { + return _otherTokens; + } + if (lineTokens === null) { + return lineTokens; + } + if (_otherTokens === null) { + // cannot determine combined line length... + return null; + } + const myTokens = toUint32Array(lineTokens); + const otherTokens = toUint32Array(_otherTokens); + const otherTokensCount = (otherTokens.length >>> 1); + + const result = new Uint32Array(myTokens.length + otherTokens.length); + result.set(myTokens, 0); + let dest = myTokens.length; + const delta = myTokens[myTokens.length - 2]; + for (let i = 0; i < otherTokensCount; i++) { + result[dest++] = otherTokens[(i << 1)] + delta; + result[dest++] = otherTokens[(i << 1) + 1]; + } + return result.buffer; + } + + public static insert(lineTokens: Uint32Array | ArrayBuffer | null, chIndex: number, textLength: number): Uint32Array | ArrayBuffer | null { + if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { + // nothing to do + return lineTokens; + } + + const tokens = toUint32Array(lineTokens); + const tokensCount = (tokens.length >>> 1); + + let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex); + if (fromTokenIndex > 0) { + const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1]; + if (fromTokenStartOffset === chIndex) { + fromTokenIndex--; + } + } + for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) { + tokens[tokenIndex << 1] += textLength; + } + return lineTokens; + } +} + +export function toUint32Array(arr: Uint32Array | ArrayBuffer): Uint32Array { + if (arr instanceof Uint32Array) { + return arr; + } else { + return new Uint32Array(arr); + } +} diff --git a/src/vs/editor/common/language/tokens/contiguousTokensStore.ts b/src/vs/editor/common/language/tokens/contiguousTokensStore.ts new file mode 100644 index 00000000000..25cf460b146 --- /dev/null +++ b/src/vs/editor/common/language/tokens/contiguousTokensStore.ts @@ -0,0 +1,257 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from '../../../../base/common/arrays.js'; +import { Position } from '../core/position.js'; +import { IRange } from '../core/range.js'; +import { ContiguousTokensEditing, EMPTY_LINE_TOKENS, toUint32Array } from './contiguousTokensEditing.js'; +import { LineTokens } from './lineTokens.js'; +import { ILanguageIdCodec } from '../languages.js'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata } from '../encodedTokenAttributes.js'; +import { ITextModel } from '../model.js'; +import { ContiguousMultilineTokens } from './contiguousMultilineTokens.js'; + +/** + * Represents contiguous tokens in a text model. + */ +export class ContiguousTokensStore { + private _lineTokens: (Uint32Array | ArrayBuffer | null)[]; + private _len: number; + private readonly _languageIdCodec: ILanguageIdCodec; + + constructor(languageIdCodec: ILanguageIdCodec) { + this._lineTokens = []; + this._len = 0; + this._languageIdCodec = languageIdCodec; + } + + public flush(): void { + this._lineTokens = []; + this._len = 0; + } + + get hasTokens(): boolean { + return this._lineTokens.length > 0; + } + + public getTokens(topLevelLanguageId: string, lineIndex: number, lineText: string): LineTokens { + let rawLineTokens: Uint32Array | ArrayBuffer | null = null; + if (lineIndex < this._len) { + rawLineTokens = this._lineTokens[lineIndex]; + } + + if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) { + return new LineTokens(toUint32Array(rawLineTokens), lineText, this._languageIdCodec); + } + + const lineTokens = new Uint32Array(2); + lineTokens[0] = lineText.length; + lineTokens[1] = getDefaultMetadata(this._languageIdCodec.encodeLanguageId(topLevelLanguageId)); + return new LineTokens(lineTokens, lineText, this._languageIdCodec); + } + + private static _massageTokens(topLevelLanguageId: LanguageId, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): Uint32Array | ArrayBuffer { + + const tokens = _tokens ? toUint32Array(_tokens) : null; + + if (lineTextLength === 0) { + let hasDifferentLanguageId = false; + if (tokens && tokens.length > 1) { + hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId); + } + + if (!hasDifferentLanguageId) { + return EMPTY_LINE_TOKENS; + } + } + + if (!tokens || tokens.length === 0) { + const tokens = new Uint32Array(2); + tokens[0] = lineTextLength; + tokens[1] = getDefaultMetadata(topLevelLanguageId); + return tokens.buffer; + } + + // Ensure the last token covers the end of the text + tokens[tokens.length - 2] = lineTextLength; + + if (tokens.byteOffset === 0 && tokens.byteLength === tokens.buffer.byteLength) { + // Store directly the ArrayBuffer pointer to save an object + return tokens.buffer; + } + return tokens; + } + + private _ensureLine(lineIndex: number): void { + while (lineIndex >= this._len) { + this._lineTokens[this._len] = null; + this._len++; + } + } + + private _deleteLines(start: number, deleteCount: number): void { + if (deleteCount === 0) { + return; + } + if (start + deleteCount > this._len) { + deleteCount = this._len - start; + } + this._lineTokens.splice(start, deleteCount); + this._len -= deleteCount; + } + + private _insertLines(insertIndex: number, insertCount: number): void { + if (insertCount === 0) { + return; + } + const lineTokens: (Uint32Array | ArrayBuffer | null)[] = []; + for (let i = 0; i < insertCount; i++) { + lineTokens[i] = null; + } + this._lineTokens = arrays.arrayInsert(this._lineTokens, insertIndex, lineTokens); + this._len += insertCount; + } + + public setTokens(topLevelLanguageId: string, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean { + const tokens = ContiguousTokensStore._massageTokens(this._languageIdCodec.encodeLanguageId(topLevelLanguageId), lineTextLength, _tokens); + this._ensureLine(lineIndex); + const oldTokens = this._lineTokens[lineIndex]; + this._lineTokens[lineIndex] = tokens; + + if (checkEquality) { + return !ContiguousTokensStore._equals(oldTokens, tokens); + } + return false; + } + + private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) { + if (!_a || !_b) { + return !_a && !_b; + } + + const a = toUint32Array(_a); + const b = toUint32Array(_b); + + if (a.length !== b.length) { + return false; + } + for (let i = 0, len = a.length; i < len; i++) { + if (a[i] !== b[i]) { + return false; + } + } + return true; + } + + //#region Editing + + public acceptEdit(range: IRange, eolCount: number, firstLineLength: number): void { + this._acceptDeleteRange(range); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); + } + + private _acceptDeleteRange(range: IRange): void { + + const firstLineIndex = range.startLineNumber - 1; + if (firstLineIndex >= this._len) { + return; + } + + if (range.startLineNumber === range.endLineNumber) { + if (range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + + this._lineTokens[firstLineIndex] = ContiguousTokensEditing.delete(this._lineTokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1); + return; + } + + this._lineTokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._lineTokens[firstLineIndex], range.startColumn - 1); + + const lastLineIndex = range.endLineNumber - 1; + let lastLineTokens: Uint32Array | ArrayBuffer | null = null; + if (lastLineIndex < this._len) { + lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._lineTokens[lastLineIndex], range.endColumn - 1); + } + + // Take remaining text on last line and append it to remaining text on first line + this._lineTokens[firstLineIndex] = ContiguousTokensEditing.append(this._lineTokens[firstLineIndex], lastLineTokens); + + // Delete middle lines + this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber); + } + + private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { + + if (eolCount === 0 && firstLineLength === 0) { + // Nothing to insert + return; + } + + const lineIndex = position.lineNumber - 1; + if (lineIndex >= this._len) { + return; + } + + if (eolCount === 0) { + // Inserting text on one line + this._lineTokens[lineIndex] = ContiguousTokensEditing.insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); + return; + } + + this._lineTokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._lineTokens[lineIndex], position.column - 1); + this._lineTokens[lineIndex] = ContiguousTokensEditing.insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); + + this._insertLines(position.lineNumber, eolCount); + } + + //#endregion + + public setMultilineTokens(tokens: ContiguousMultilineTokens[], textModel: ITextModel): { changes: { fromLineNumber: number; toLineNumber: number }[] } { + if (tokens.length === 0) { + return { changes: [] }; + } + + const ranges: { fromLineNumber: number; toLineNumber: number }[] = []; + + for (let i = 0, len = tokens.length; i < len; i++) { + const element = tokens[i]; + let minChangedLineNumber = 0; + let maxChangedLineNumber = 0; + let hasChange = false; + for (let lineNumber = element.startLineNumber; lineNumber <= element.endLineNumber; lineNumber++) { + if (hasChange) { + this.setTokens(textModel.getLanguageId(), lineNumber - 1, textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), false); + maxChangedLineNumber = lineNumber; + } else { + const lineHasChange = this.setTokens(textModel.getLanguageId(), lineNumber - 1, textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), true); + if (lineHasChange) { + hasChange = true; + minChangedLineNumber = lineNumber; + maxChangedLineNumber = lineNumber; + } + } + } + if (hasChange) { + ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber, }); + } + } + + return { changes: ranges }; + } +} + +function getDefaultMetadata(topLevelLanguageId: LanguageId): number { + return ( + (topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET) + | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) + | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + // If there is no grammar, we just take a guess and try to match brackets. + | (MetadataConsts.BALANCED_BRACKETS_MASK) + ) >>> 0; +} diff --git a/src/vs/editor/common/language/tokens/lineTokens.ts b/src/vs/editor/common/language/tokens/lineTokens.ts new file mode 100644 index 00000000000..a67abcb37ea --- /dev/null +++ b/src/vs/editor/common/language/tokens/lineTokens.ts @@ -0,0 +1,420 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ILanguageIdCodec } from '../languages.js'; +import { FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata, ITokenPresentation } from '../encodedTokenAttributes.js'; +import { IPosition } from '../core/position.js'; +import { ITextModel } from '../model.js'; +import { OffsetRange } from '../core/offsetRange.js'; +import { TokenArray, TokenArrayBuilder } from './tokenArray.js'; +import { onUnexpectedError } from '../../../../base/common/errors.js'; + + +export interface IViewLineTokens { + languageIdCodec: ILanguageIdCodec; + equals(other: IViewLineTokens): boolean; + getCount(): number; + getStandardTokenType(tokenIndex: number): StandardTokenType; + getForeground(tokenIndex: number): ColorId; + getEndOffset(tokenIndex: number): number; + getClassName(tokenIndex: number): string; + getInlineStyle(tokenIndex: number, colorMap: string[]): string; + getPresentation(tokenIndex: number): ITokenPresentation; + findTokenIndexAtOffset(offset: number): number; + getLineContent(): string; + getMetadata(tokenIndex: number): number; + getLanguageId(tokenIndex: number): string; + getTokenText(tokenIndex: number): string; + forEach(callback: (tokenIndex: number) => void): void; +} + +export class LineTokens implements IViewLineTokens { + public static createEmpty(lineContent: string, decoder: ILanguageIdCodec): LineTokens { + const defaultMetadata = LineTokens.defaultTokenMetadata; + + const tokens = new Uint32Array(2); + tokens[0] = lineContent.length; + tokens[1] = defaultMetadata; + + return new LineTokens(tokens, lineContent, decoder); + } + + public static createFromTextAndMetadata(data: { text: string; metadata: number }[], decoder: ILanguageIdCodec): LineTokens { + let offset: number = 0; + let fullText: string = ''; + const tokens = new Array(); + for (const { text, metadata } of data) { + tokens.push(offset + text.length, metadata); + offset += text.length; + fullText += text; + } + return new LineTokens(new Uint32Array(tokens), fullText, decoder); + } + + public static convertToEndOffset(tokens: Uint32Array, lineTextLength: number): void { + const tokenCount = (tokens.length >>> 1); + const lastTokenIndex = tokenCount - 1; + for (let tokenIndex = 0; tokenIndex < lastTokenIndex; tokenIndex++) { + tokens[tokenIndex << 1] = tokens[(tokenIndex + 1) << 1]; + } + tokens[lastTokenIndex << 1] = lineTextLength; + } + + public static findIndexInTokensArray(tokens: Uint32Array, desiredIndex: number): number { + if (tokens.length <= 2) { + return 0; + } + + let low = 0; + let high = (tokens.length >>> 1) - 1; + + while (low < high) { + + const mid = low + Math.floor((high - low) / 2); + const endOffset = tokens[(mid << 1)]; + + if (endOffset === desiredIndex) { + return mid + 1; + } else if (endOffset < desiredIndex) { + low = mid + 1; + } else if (endOffset > desiredIndex) { + high = mid; + } + } + + return low; + } + + _lineTokensBrand: void = undefined; + + private readonly _tokens: Uint32Array; + private readonly _tokensCount: number; + private readonly _text: string; + + public readonly languageIdCodec: ILanguageIdCodec; + + public static defaultTokenMetadata = ( + (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) + | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) + | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) + ) >>> 0; + + constructor(tokens: Uint32Array, text: string, decoder: ILanguageIdCodec) { + const tokensLength = tokens.length > 1 ? tokens[tokens.length - 2] : 0; + if (tokensLength !== text.length) { + onUnexpectedError(new Error('Token length and text length do not match!')); + } + this._tokens = tokens; + this._tokensCount = (this._tokens.length >>> 1); + this._text = text; + this.languageIdCodec = decoder; + } + + public equals(other: IViewLineTokens): boolean { + if (other instanceof LineTokens) { + return this.slicedEquals(other, 0, this._tokensCount); + } + return false; + } + + public slicedEquals(other: LineTokens, sliceFromTokenIndex: number, sliceTokenCount: number): boolean { + if (this._text !== other._text) { + return false; + } + if (this._tokensCount !== other._tokensCount) { + return false; + } + const from = (sliceFromTokenIndex << 1); + const to = from + (sliceTokenCount << 1); + for (let i = from; i < to; i++) { + if (this._tokens[i] !== other._tokens[i]) { + return false; + } + } + return true; + } + + public getLineContent(): string { + return this._text; + } + + public getCount(): number { + return this._tokensCount; + } + + public getStartOffset(tokenIndex: number): number { + if (tokenIndex > 0) { + return this._tokens[(tokenIndex - 1) << 1]; + } + return 0; + } + + public getMetadata(tokenIndex: number): number { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return metadata; + } + + public getLanguageId(tokenIndex: number): string { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + const languageId = TokenMetadata.getLanguageId(metadata); + return this.languageIdCodec.decodeLanguageId(languageId); + } + + public getStandardTokenType(tokenIndex: number): StandardTokenType { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getTokenType(metadata); + } + + public getForeground(tokenIndex: number): ColorId { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getForeground(metadata); + } + + public getClassName(tokenIndex: number): string { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getClassNameFromMetadata(metadata); + } + + public getInlineStyle(tokenIndex: number, colorMap: string[]): string { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getInlineStyleFromMetadata(metadata, colorMap); + } + + public getPresentation(tokenIndex: number): ITokenPresentation { + const metadata = this._tokens[(tokenIndex << 1) + 1]; + return TokenMetadata.getPresentationFromMetadata(metadata); + } + + public getEndOffset(tokenIndex: number): number { + return this._tokens[tokenIndex << 1]; + } + + /** + * Find the token containing offset `offset`. + * @param offset The search offset + * @return The index of the token containing the offset. + */ + public findTokenIndexAtOffset(offset: number): number { + return LineTokens.findIndexInTokensArray(this._tokens, offset); + } + + public inflate(): IViewLineTokens { + return this; + } + + public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): IViewLineTokens { + return new SliceLineTokens(this, startOffset, endOffset, deltaOffset); + } + + public sliceZeroCopy(range: OffsetRange): IViewLineTokens { + return this.sliceAndInflate(range.start, range.endExclusive, 0); + } + + /** + * @pure + * @param insertTokens Must be sorted by offset. + */ + public withInserted(insertTokens: { offset: number; text: string; tokenMetadata: number }[]): LineTokens { + if (insertTokens.length === 0) { + return this; + } + + let nextOriginalTokenIdx = 0; + let nextInsertTokenIdx = 0; + let text = ''; + const newTokens = new Array(); + + let originalEndOffset = 0; + while (true) { + const nextOriginalTokenEndOffset = nextOriginalTokenIdx < this._tokensCount ? this._tokens[nextOriginalTokenIdx << 1] : -1; + const nextInsertToken = nextInsertTokenIdx < insertTokens.length ? insertTokens[nextInsertTokenIdx] : null; + + if (nextOriginalTokenEndOffset !== -1 && (nextInsertToken === null || nextOriginalTokenEndOffset <= nextInsertToken.offset)) { + // original token ends before next insert token + text += this._text.substring(originalEndOffset, nextOriginalTokenEndOffset); + const metadata = this._tokens[(nextOriginalTokenIdx << 1) + 1]; + newTokens.push(text.length, metadata); + nextOriginalTokenIdx++; + originalEndOffset = nextOriginalTokenEndOffset; + + } else if (nextInsertToken) { + if (nextInsertToken.offset > originalEndOffset) { + // insert token is in the middle of the next token. + text += this._text.substring(originalEndOffset, nextInsertToken.offset); + const metadata = this._tokens[(nextOriginalTokenIdx << 1) + 1]; + newTokens.push(text.length, metadata); + originalEndOffset = nextInsertToken.offset; + } + + text += nextInsertToken.text; + newTokens.push(text.length, nextInsertToken.tokenMetadata); + nextInsertTokenIdx++; + } else { + break; + } + } + + return new LineTokens(new Uint32Array(newTokens), text, this.languageIdCodec); + } + + public getTokensInRange(range: OffsetRange): TokenArray { + const builder = new TokenArrayBuilder(); + + const startTokenIndex = this.findTokenIndexAtOffset(range.start); + const endTokenIndex = this.findTokenIndexAtOffset(range.endExclusive); + + for (let tokenIndex = startTokenIndex; tokenIndex <= endTokenIndex; tokenIndex++) { + const tokenRange = new OffsetRange(this.getStartOffset(tokenIndex), this.getEndOffset(tokenIndex)); + const length = tokenRange.intersectionLength(range); + if (length > 0) { + builder.add(length, this.getMetadata(tokenIndex)); + } + } + + return builder.build(); + } + + public getTokenText(tokenIndex: number): string { + const startOffset = this.getStartOffset(tokenIndex); + const endOffset = this.getEndOffset(tokenIndex); + const text = this._text.substring(startOffset, endOffset); + return text; + } + + public forEach(callback: (tokenIndex: number) => void): void { + const tokenCount = this.getCount(); + for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) { + callback(tokenIndex); + } + } + + toString(): string { + let result = ''; + this.forEach((i) => { + result += `[${this.getTokenText(i)}]{${this.getClassName(i)}}`; + }); + return result; + } +} + +class SliceLineTokens implements IViewLineTokens { + + private readonly _source: LineTokens; + private readonly _startOffset: number; + private readonly _endOffset: number; + private readonly _deltaOffset: number; + + private readonly _firstTokenIndex: number; + private readonly _tokensCount: number; + + public readonly languageIdCodec: ILanguageIdCodec; + + constructor(source: LineTokens, startOffset: number, endOffset: number, deltaOffset: number) { + this._source = source; + this._startOffset = startOffset; + this._endOffset = endOffset; + this._deltaOffset = deltaOffset; + this._firstTokenIndex = source.findTokenIndexAtOffset(startOffset); + this.languageIdCodec = source.languageIdCodec; + + this._tokensCount = 0; + for (let i = this._firstTokenIndex, len = source.getCount(); i < len; i++) { + const tokenStartOffset = source.getStartOffset(i); + if (tokenStartOffset >= endOffset) { + break; + } + this._tokensCount++; + } + } + + public getMetadata(tokenIndex: number): number { + return this._source.getMetadata(this._firstTokenIndex + tokenIndex); + } + + public getLanguageId(tokenIndex: number): string { + return this._source.getLanguageId(this._firstTokenIndex + tokenIndex); + } + + public getLineContent(): string { + return this._source.getLineContent().substring(this._startOffset, this._endOffset); + } + + public equals(other: IViewLineTokens): boolean { + if (other instanceof SliceLineTokens) { + return ( + this._startOffset === other._startOffset + && this._endOffset === other._endOffset + && this._deltaOffset === other._deltaOffset + && this._source.slicedEquals(other._source, this._firstTokenIndex, this._tokensCount) + ); + } + return false; + } + + public getCount(): number { + return this._tokensCount; + } + + public getStandardTokenType(tokenIndex: number): StandardTokenType { + return this._source.getStandardTokenType(this._firstTokenIndex + tokenIndex); + } + + public getForeground(tokenIndex: number): ColorId { + return this._source.getForeground(this._firstTokenIndex + tokenIndex); + } + + public getEndOffset(tokenIndex: number): number { + const tokenEndOffset = this._source.getEndOffset(this._firstTokenIndex + tokenIndex); + return Math.min(this._endOffset, tokenEndOffset) - this._startOffset + this._deltaOffset; + } + + public getClassName(tokenIndex: number): string { + return this._source.getClassName(this._firstTokenIndex + tokenIndex); + } + + public getInlineStyle(tokenIndex: number, colorMap: string[]): string { + return this._source.getInlineStyle(this._firstTokenIndex + tokenIndex, colorMap); + } + + public getPresentation(tokenIndex: number): ITokenPresentation { + return this._source.getPresentation(this._firstTokenIndex + tokenIndex); + } + + public findTokenIndexAtOffset(offset: number): number { + return this._source.findTokenIndexAtOffset(offset + this._startOffset - this._deltaOffset) - this._firstTokenIndex; + } + + public getTokenText(tokenIndex: number): string { + const adjustedTokenIndex = this._firstTokenIndex + tokenIndex; + const tokenStartOffset = this._source.getStartOffset(adjustedTokenIndex); + const tokenEndOffset = this._source.getEndOffset(adjustedTokenIndex); + let text = this._source.getTokenText(adjustedTokenIndex); + if (tokenStartOffset < this._startOffset) { + text = text.substring(this._startOffset - tokenStartOffset); + } + if (tokenEndOffset > this._endOffset) { + text = text.substring(0, text.length - (tokenEndOffset - this._endOffset)); + } + return text; + } + + public forEach(callback: (tokenIndex: number) => void): void { + for (let tokenIndex = 0; tokenIndex < this.getCount(); tokenIndex++) { + callback(tokenIndex); + } + } +} + +export function getStandardTokenTypeAtPosition(model: ITextModel, position: IPosition): StandardTokenType | undefined { + const lineNumber = position.lineNumber; + if (!model.tokenization.isCheapToTokenize(lineNumber)) { + return undefined; + } + model.tokenization.forceTokenization(lineNumber); + const lineTokens = model.tokenization.getLineTokens(lineNumber); + const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); + const tokenType = lineTokens.getStandardTokenType(tokenIndex); + return tokenType; +} diff --git a/src/vs/editor/common/language/tokens/sparseMultilineTokens.ts b/src/vs/editor/common/language/tokens/sparseMultilineTokens.ts new file mode 100644 index 00000000000..de9eab9d37e --- /dev/null +++ b/src/vs/editor/common/language/tokens/sparseMultilineTokens.ts @@ -0,0 +1,586 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CharCode } from '../../../../base/common/charCode.js'; +import { Position } from '../core/position.js'; +import { IRange, Range } from '../core/range.js'; +import { countEOL } from '../core/eolCounter.js'; + +/** + * Represents sparse tokens over a contiguous range of lines. + */ +export class SparseMultilineTokens { + + public static create(startLineNumber: number, tokens: Uint32Array): SparseMultilineTokens { + return new SparseMultilineTokens(startLineNumber, new SparseMultilineTokensStorage(tokens)); + } + + private _startLineNumber: number; + private _endLineNumber: number; + private readonly _tokens: SparseMultilineTokensStorage; + + /** + * (Inclusive) start line number for these tokens. + */ + public get startLineNumber(): number { + return this._startLineNumber; + } + + /** + * (Inclusive) end line number for these tokens. + */ + public get endLineNumber(): number { + return this._endLineNumber; + } + + private constructor(startLineNumber: number, tokens: SparseMultilineTokensStorage) { + this._startLineNumber = startLineNumber; + this._tokens = tokens; + this._endLineNumber = this._startLineNumber + this._tokens.getMaxDeltaLine(); + } + + public toString(): string { + return this._tokens.toString(this._startLineNumber); + } + + private _updateEndLineNumber(): void { + this._endLineNumber = this._startLineNumber + this._tokens.getMaxDeltaLine(); + } + + public isEmpty(): boolean { + return this._tokens.isEmpty(); + } + + public getLineTokens(lineNumber: number): SparseLineTokens | null { + if (this._startLineNumber <= lineNumber && lineNumber <= this._endLineNumber) { + return this._tokens.getLineTokens(lineNumber - this._startLineNumber); + } + return null; + } + + public getRange(): Range | null { + const deltaRange = this._tokens.getRange(); + if (!deltaRange) { + return deltaRange; + } + return new Range(this._startLineNumber + deltaRange.startLineNumber, deltaRange.startColumn, this._startLineNumber + deltaRange.endLineNumber, deltaRange.endColumn); + } + + public removeTokens(range: Range): void { + const startLineIndex = range.startLineNumber - this._startLineNumber; + const endLineIndex = range.endLineNumber - this._startLineNumber; + + this._startLineNumber += this._tokens.removeTokens(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); + this._updateEndLineNumber(); + } + + public split(range: Range): [SparseMultilineTokens, SparseMultilineTokens] { + // split tokens to two: + // a) all the tokens before `range` + // b) all the tokens after `range` + const startLineIndex = range.startLineNumber - this._startLineNumber; + const endLineIndex = range.endLineNumber - this._startLineNumber; + + const [a, b, bDeltaLine] = this._tokens.split(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); + return [new SparseMultilineTokens(this._startLineNumber, a), new SparseMultilineTokens(this._startLineNumber + bDeltaLine, b)]; + } + + public applyEdit(range: IRange, text: string): void { + const [eolCount, firstLineLength, lastLineLength] = countEOL(text); + this.acceptEdit(range, eolCount, firstLineLength, lastLineLength, text.length > 0 ? text.charCodeAt(0) : CharCode.Null); + } + + public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + this._acceptDeleteRange(range); + this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength, lastLineLength, firstCharCode); + this._updateEndLineNumber(); + } + + private _acceptDeleteRange(range: IRange): void { + if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) { + // Nothing to delete + return; + } + + const firstLineIndex = range.startLineNumber - this._startLineNumber; + const lastLineIndex = range.endLineNumber - this._startLineNumber; + + if (lastLineIndex < 0) { + // this deletion occurs entirely before this block, so we only need to adjust line numbers + const deletedLinesCount = lastLineIndex - firstLineIndex; + this._startLineNumber -= deletedLinesCount; + return; + } + + const tokenMaxDeltaLine = this._tokens.getMaxDeltaLine(); + + if (firstLineIndex >= tokenMaxDeltaLine + 1) { + // this deletion occurs entirely after this block, so there is nothing to do + return; + } + + if (firstLineIndex < 0 && lastLineIndex >= tokenMaxDeltaLine + 1) { + // this deletion completely encompasses this block + this._startLineNumber = 0; + this._tokens.clear(); + return; + } + + if (firstLineIndex < 0) { + const deletedBefore = -firstLineIndex; + this._startLineNumber -= deletedBefore; + + this._tokens.acceptDeleteRange(range.startColumn - 1, 0, 0, lastLineIndex, range.endColumn - 1); + } else { + this._tokens.acceptDeleteRange(0, firstLineIndex, range.startColumn - 1, lastLineIndex, range.endColumn - 1); + } + } + + private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + + if (eolCount === 0 && firstLineLength === 0) { + // Nothing to insert + return; + } + + const lineIndex = position.lineNumber - this._startLineNumber; + + if (lineIndex < 0) { + // this insertion occurs before this block, so we only need to adjust line numbers + this._startLineNumber += eolCount; + return; + } + + const tokenMaxDeltaLine = this._tokens.getMaxDeltaLine(); + + if (lineIndex >= tokenMaxDeltaLine + 1) { + // this insertion occurs after this block, so there is nothing to do + return; + } + + this._tokens.acceptInsertText(lineIndex, position.column - 1, eolCount, firstLineLength, lastLineLength, firstCharCode); + } +} + +class SparseMultilineTokensStorage { + /** + * The encoding of tokens is: + * 4*i deltaLine (from `startLineNumber`) + * 4*i+1 startCharacter (from the line start) + * 4*i+2 endCharacter (from the line start) + * 4*i+3 metadata + */ + private readonly _tokens: Uint32Array; + private _tokenCount: number; + + constructor(tokens: Uint32Array) { + this._tokens = tokens; + this._tokenCount = tokens.length / 4; + } + + public toString(startLineNumber: number): string { + const pieces: string[] = []; + for (let i = 0; i < this._tokenCount; i++) { + pieces.push(`(${this._getDeltaLine(i) + startLineNumber},${this._getStartCharacter(i)}-${this._getEndCharacter(i)})`); + } + return `[${pieces.join(',')}]`; + } + + public getMaxDeltaLine(): number { + const tokenCount = this._getTokenCount(); + if (tokenCount === 0) { + return -1; + } + return this._getDeltaLine(tokenCount - 1); + } + + public getRange(): Range | null { + const tokenCount = this._getTokenCount(); + if (tokenCount === 0) { + return null; + } + const startChar = this._getStartCharacter(0); + const maxDeltaLine = this._getDeltaLine(tokenCount - 1); + const endChar = this._getEndCharacter(tokenCount - 1); + return new Range(0, startChar + 1, maxDeltaLine, endChar + 1); + } + + private _getTokenCount(): number { + return this._tokenCount; + } + + private _getDeltaLine(tokenIndex: number): number { + return this._tokens[4 * tokenIndex]; + } + + private _getStartCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 1]; + } + + private _getEndCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 2]; + } + + public isEmpty(): boolean { + return (this._getTokenCount() === 0); + } + + public getLineTokens(deltaLine: number): SparseLineTokens | null { + let low = 0; + let high = this._getTokenCount() - 1; + + while (low < high) { + const mid = low + Math.floor((high - low) / 2); + const midDeltaLine = this._getDeltaLine(mid); + + if (midDeltaLine < deltaLine) { + low = mid + 1; + } else if (midDeltaLine > deltaLine) { + high = mid - 1; + } else { + let min = mid; + while (min > low && this._getDeltaLine(min - 1) === deltaLine) { + min--; + } + let max = mid; + while (max < high && this._getDeltaLine(max + 1) === deltaLine) { + max++; + } + return new SparseLineTokens(this._tokens.subarray(4 * min, 4 * max + 4)); + } + } + + if (this._getDeltaLine(low) === deltaLine) { + return new SparseLineTokens(this._tokens.subarray(4 * low, 4 * low + 4)); + } + + return null; + } + + public clear(): void { + this._tokenCount = 0; + } + + public removeTokens(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): number { + const tokens = this._tokens; + const tokenCount = this._tokenCount; + let newTokenCount = 0; + let hasDeletedTokens = false; + let firstDeltaLine = 0; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + const tokenDeltaLine = tokens[srcOffset]; + const tokenStartCharacter = tokens[srcOffset + 1]; + const tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if ( + (tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar)) + && (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar)) + ) { + hasDeletedTokens = true; + } else { + if (newTokenCount === 0) { + firstDeltaLine = tokenDeltaLine; + } + if (hasDeletedTokens) { + // must move the token to the left + const destOffset = 4 * newTokenCount; + tokens[destOffset] = tokenDeltaLine - firstDeltaLine; + tokens[destOffset + 1] = tokenStartCharacter; + tokens[destOffset + 2] = tokenEndCharacter; + tokens[destOffset + 3] = tokenMetadata; + } + newTokenCount++; + } + } + + this._tokenCount = newTokenCount; + + return firstDeltaLine; + } + + public split(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): [SparseMultilineTokensStorage, SparseMultilineTokensStorage, number] { + const tokens = this._tokens; + const tokenCount = this._tokenCount; + const aTokens: number[] = []; + const bTokens: number[] = []; + let destTokens: number[] = aTokens; + let destOffset = 0; + let destFirstDeltaLine: number = 0; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + const tokenDeltaLine = tokens[srcOffset]; + const tokenStartCharacter = tokens[srcOffset + 1]; + const tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if ((tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar))) { + if ((tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar))) { + // this token is touching the range + continue; + } else { + // this token is after the range + if (destTokens !== bTokens) { + // this token is the first token after the range + destTokens = bTokens; + destOffset = 0; + destFirstDeltaLine = tokenDeltaLine; + } + } + } + + destTokens[destOffset++] = tokenDeltaLine - destFirstDeltaLine; + destTokens[destOffset++] = tokenStartCharacter; + destTokens[destOffset++] = tokenEndCharacter; + destTokens[destOffset++] = tokenMetadata; + } + + return [new SparseMultilineTokensStorage(new Uint32Array(aTokens)), new SparseMultilineTokensStorage(new Uint32Array(bTokens)), destFirstDeltaLine]; + } + + public acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void { + // This is a bit complex, here are the cases I used to think about this: + // + // 1. The token starts before the deletion range + // 1a. The token is completely before the deletion range + // ----------- + // xxxxxxxxxxx + // 1b. The token starts before, the deletion range ends after the token + // ----------- + // xxxxxxxxxxx + // 1c. The token starts before, the deletion range ends precisely with the token + // --------------- + // xxxxxxxx + // 1d. The token starts before, the deletion range is inside the token + // --------------- + // xxxxx + // + // 2. The token starts at the same position with the deletion range + // 2a. The token starts at the same position, and ends inside the deletion range + // ------- + // xxxxxxxxxxx + // 2b. The token starts at the same position, and ends at the same position as the deletion range + // ---------- + // xxxxxxxxxx + // 2c. The token starts at the same position, and ends after the deletion range + // ------------- + // xxxxxxx + // + // 3. The token starts inside the deletion range + // 3a. The token is inside the deletion range + // ------- + // xxxxxxxxxxxxx + // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range + // ---------- + // xxxxxxxxxxxxx + // 3c. The token starts inside the deletion range, and ends after the deletion range + // ------------ + // xxxxxxxxxxx + // + // 4. The token starts after the deletion range + // ----------- + // xxxxxxxx + // + const tokens = this._tokens; + const tokenCount = this._tokenCount; + const deletedLineCount = (endDeltaLine - startDeltaLine); + let newTokenCount = 0; + let hasDeletedTokens = false; + for (let i = 0; i < tokenCount; i++) { + const srcOffset = 4 * i; + let tokenDeltaLine = tokens[srcOffset]; + let tokenStartCharacter = tokens[srcOffset + 1]; + let tokenEndCharacter = tokens[srcOffset + 2]; + const tokenMetadata = tokens[srcOffset + 3]; + + if (tokenDeltaLine < startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter <= startCharacter)) { + // 1a. The token is completely before the deletion range + // => nothing to do + newTokenCount++; + continue; + } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter < startCharacter) { + // 1b, 1c, 1d + // => the token survives, but it needs to shrink + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 1d. The token starts before, the deletion range is inside the token + // => the token shrinks by the deletion character count + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + // 1b. The token starts before, the deletion range ends after the token + // 1c. The token starts before, the deletion range ends precisely with the token + // => the token shrinks its ending to the deletion start + tokenEndCharacter = startCharacter; + } + } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter === startCharacter) { + // 2a, 2b, 2c + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 2c. The token starts at the same position, and ends after the deletion range + // => the token shrinks by the deletion character count + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + // 2a. The token starts at the same position, and ends inside the deletion range + // 2b. The token starts at the same position, and ends at the same position as the deletion range + // => the token is deleted + hasDeletedTokens = true; + continue; + } + } else if (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter < endCharacter)) { + // 3a, 3b, 3c + if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { + // 3c. The token starts inside the deletion range, and ends after the deletion range + // => the token moves to continue right after the deletion + tokenDeltaLine = startDeltaLine; + tokenStartCharacter = startCharacter; + tokenEndCharacter = tokenStartCharacter + (tokenEndCharacter - endCharacter); + } else { + // 3a. The token is inside the deletion range + // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range + // => the token is deleted + hasDeletedTokens = true; + continue; + } + } else if (tokenDeltaLine > endDeltaLine) { + // 4. (partial) The token starts after the deletion range, on a line below... + if (deletedLineCount === 0 && !hasDeletedTokens) { + // early stop, there is no need to walk all the tokens and do nothing... + newTokenCount = tokenCount; + break; + } + tokenDeltaLine -= deletedLineCount; + } else if (tokenDeltaLine === endDeltaLine && tokenStartCharacter >= endCharacter) { + // 4. (continued) The token starts after the deletion range, on the last line where a deletion occurs + if (horizontalShiftForFirstLineTokens && tokenDeltaLine === 0) { + tokenStartCharacter += horizontalShiftForFirstLineTokens; + tokenEndCharacter += horizontalShiftForFirstLineTokens; + } + tokenDeltaLine -= deletedLineCount; + tokenStartCharacter -= (endCharacter - startCharacter); + tokenEndCharacter -= (endCharacter - startCharacter); + } else { + throw new Error(`Not possible!`); + } + + const destOffset = 4 * newTokenCount; + tokens[destOffset] = tokenDeltaLine; + tokens[destOffset + 1] = tokenStartCharacter; + tokens[destOffset + 2] = tokenEndCharacter; + tokens[destOffset + 3] = tokenMetadata; + newTokenCount++; + } + + this._tokenCount = newTokenCount; + } + + public acceptInsertText(deltaLine: number, character: number, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + // Here are the cases I used to think about this: + // + // 1. The token is completely before the insertion point + // ----------- | + // 2. The token ends precisely at the insertion point + // -----------| + // 3. The token contains the insertion point + // -----|------ + // 4. The token starts precisely at the insertion point + // |----------- + // 5. The token is completely after the insertion point + // | ----------- + // + const isInsertingPreciselyOneWordCharacter = ( + eolCount === 0 + && firstLineLength === 1 + && ( + (firstCharCode >= CharCode.Digit0 && firstCharCode <= CharCode.Digit9) + || (firstCharCode >= CharCode.A && firstCharCode <= CharCode.Z) + || (firstCharCode >= CharCode.a && firstCharCode <= CharCode.z) + ) + ); + const tokens = this._tokens; + const tokenCount = this._tokenCount; + for (let i = 0; i < tokenCount; i++) { + const offset = 4 * i; + let tokenDeltaLine = tokens[offset]; + let tokenStartCharacter = tokens[offset + 1]; + let tokenEndCharacter = tokens[offset + 2]; + + if (tokenDeltaLine < deltaLine || (tokenDeltaLine === deltaLine && tokenEndCharacter < character)) { + // 1. The token is completely before the insertion point + // => nothing to do + continue; + } else if (tokenDeltaLine === deltaLine && tokenEndCharacter === character) { + // 2. The token ends precisely at the insertion point + // => expand the end character only if inserting precisely one character that is a word character + if (isInsertingPreciselyOneWordCharacter) { + tokenEndCharacter += 1; + } else { + continue; + } + } else if (tokenDeltaLine === deltaLine && tokenStartCharacter < character && character < tokenEndCharacter) { + // 3. The token contains the insertion point + if (eolCount === 0) { + // => just expand the end character + tokenEndCharacter += firstLineLength; + } else { + // => cut off the token + tokenEndCharacter = character; + } + } else { + // 4. or 5. + if (tokenDeltaLine === deltaLine && tokenStartCharacter === character) { + // 4. The token starts precisely at the insertion point + // => grow the token (by keeping its start constant) only if inserting precisely one character that is a word character + // => otherwise behave as in case 5. + if (isInsertingPreciselyOneWordCharacter) { + continue; + } + } + // => the token must move and keep its size constant + if (tokenDeltaLine === deltaLine) { + tokenDeltaLine += eolCount; + // this token is on the line where the insertion is taking place + if (eolCount === 0) { + tokenStartCharacter += firstLineLength; + tokenEndCharacter += firstLineLength; + } else { + const tokenLength = tokenEndCharacter - tokenStartCharacter; + tokenStartCharacter = lastLineLength + (tokenStartCharacter - character); + tokenEndCharacter = tokenStartCharacter + tokenLength; + } + } else { + tokenDeltaLine += eolCount; + } + } + + tokens[offset] = tokenDeltaLine; + tokens[offset + 1] = tokenStartCharacter; + tokens[offset + 2] = tokenEndCharacter; + } + } +} + +export class SparseLineTokens { + + private readonly _tokens: Uint32Array; + + constructor(tokens: Uint32Array) { + this._tokens = tokens; + } + + public getCount(): number { + return this._tokens.length / 4; + } + + public getStartCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 1]; + } + + public getEndCharacter(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 2]; + } + + public getMetadata(tokenIndex: number): number { + return this._tokens[4 * tokenIndex + 3]; + } +} diff --git a/src/vs/editor/common/language/tokens/sparseTokensStore.ts b/src/vs/editor/common/language/tokens/sparseTokensStore.ts new file mode 100644 index 00000000000..19246f4de58 --- /dev/null +++ b/src/vs/editor/common/language/tokens/sparseTokensStore.ts @@ -0,0 +1,244 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as arrays from '../../../../base/common/arrays.js'; +import { IRange, Range } from '../core/range.js'; +import { LineTokens } from './lineTokens.js'; +import { SparseMultilineTokens } from './sparseMultilineTokens.js'; +import { ILanguageIdCodec } from '../languages.js'; +import { MetadataConsts } from '../encodedTokenAttributes.js'; + +/** + * Represents sparse tokens in a text model. + */ +export class SparseTokensStore { + + private _pieces: SparseMultilineTokens[]; + private _isComplete: boolean; + private readonly _languageIdCodec: ILanguageIdCodec; + + constructor(languageIdCodec: ILanguageIdCodec) { + this._pieces = []; + this._isComplete = false; + this._languageIdCodec = languageIdCodec; + } + + public flush(): void { + this._pieces = []; + this._isComplete = false; + } + + public isEmpty(): boolean { + return (this._pieces.length === 0); + } + + public set(pieces: SparseMultilineTokens[] | null, isComplete: boolean): void { + this._pieces = pieces || []; + this._isComplete = isComplete; + } + + public setPartial(_range: Range, pieces: SparseMultilineTokens[]): Range { + // console.log(`setPartial ${_range} ${pieces.map(p => p.toString()).join(', ')}`); + + let range = _range; + if (pieces.length > 0) { + const _firstRange = pieces[0].getRange(); + const _lastRange = pieces[pieces.length - 1].getRange(); + if (!_firstRange || !_lastRange) { + return _range; + } + range = _range.plusRange(_firstRange).plusRange(_lastRange); + } + + let insertPosition: { index: number } | null = null; + for (let i = 0, len = this._pieces.length; i < len; i++) { + const piece = this._pieces[i]; + if (piece.endLineNumber < range.startLineNumber) { + // this piece is before the range + continue; + } + + if (piece.startLineNumber > range.endLineNumber) { + // this piece is after the range, so mark the spot before this piece + // as a good insertion position and stop looping + insertPosition = insertPosition || { index: i }; + break; + } + + // this piece might intersect with the range + piece.removeTokens(range); + + if (piece.isEmpty()) { + // remove the piece if it became empty + this._pieces.splice(i, 1); + i--; + len--; + continue; + } + + if (piece.endLineNumber < range.startLineNumber) { + // after removal, this piece is before the range + continue; + } + + if (piece.startLineNumber > range.endLineNumber) { + // after removal, this piece is after the range + insertPosition = insertPosition || { index: i }; + continue; + } + + // after removal, this piece contains the range + const [a, b] = piece.split(range); + if (a.isEmpty()) { + // this piece is actually after the range + insertPosition = insertPosition || { index: i }; + continue; + } + if (b.isEmpty()) { + // this piece is actually before the range + continue; + } + this._pieces.splice(i, 1, a, b); + i++; + len++; + + insertPosition = insertPosition || { index: i }; + } + + insertPosition = insertPosition || { index: this._pieces.length }; + + if (pieces.length > 0) { + this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces); + } + + // console.log(`I HAVE ${this._pieces.length} pieces`); + // console.log(`${this._pieces.map(p => p.toString()).join('\n')}`); + + return range; + } + + public isComplete(): boolean { + return this._isComplete; + } + + public addSparseTokens(lineNumber: number, aTokens: LineTokens): LineTokens { + if (aTokens.getLineContent().length === 0) { + // Don't do anything for empty lines + return aTokens; + } + + const pieces = this._pieces; + + if (pieces.length === 0) { + return aTokens; + } + + const pieceIndex = SparseTokensStore._findFirstPieceWithLine(pieces, lineNumber); + const bTokens = pieces[pieceIndex].getLineTokens(lineNumber); + + if (!bTokens) { + return aTokens; + } + + const aLen = aTokens.getCount(); + const bLen = bTokens.getCount(); + + let aIndex = 0; + const result: number[] = []; + let resultLen = 0; + let lastEndOffset = 0; + + const emitToken = (endOffset: number, metadata: number) => { + if (endOffset === lastEndOffset) { + return; + } + lastEndOffset = endOffset; + result[resultLen++] = endOffset; + result[resultLen++] = metadata; + }; + + for (let bIndex = 0; bIndex < bLen; bIndex++) { + const bStartCharacter = bTokens.getStartCharacter(bIndex); + const bEndCharacter = bTokens.getEndCharacter(bIndex); + const bMetadata = bTokens.getMetadata(bIndex); + + const bMask = ( + ((bMetadata & MetadataConsts.SEMANTIC_USE_ITALIC) ? MetadataConsts.ITALIC_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_BOLD) ? MetadataConsts.BOLD_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_UNDERLINE) ? MetadataConsts.UNDERLINE_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_STRIKETHROUGH) ? MetadataConsts.STRIKETHROUGH_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_FOREGROUND) ? MetadataConsts.FOREGROUND_MASK : 0) + | ((bMetadata & MetadataConsts.SEMANTIC_USE_BACKGROUND) ? MetadataConsts.BACKGROUND_MASK : 0) + ) >>> 0; + const aMask = (~bMask) >>> 0; + + // push any token from `a` that is before `b` + while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bStartCharacter) { + emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); + aIndex++; + } + + // push the token from `a` if it intersects the token from `b` + if (aIndex < aLen && aTokens.getStartOffset(aIndex) < bStartCharacter) { + emitToken(bStartCharacter, aTokens.getMetadata(aIndex)); + } + + // skip any tokens from `a` that are contained inside `b` + while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) { + emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); + aIndex++; + } + + if (aIndex < aLen) { + emitToken(bEndCharacter, (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); + if (aTokens.getEndOffset(aIndex) === bEndCharacter) { + // `a` ends exactly at the same spot as `b`! + aIndex++; + } + } else { + const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1); + + // push the token from `b` + emitToken(bEndCharacter, (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask)); + } + } + + // push the remaining tokens from `a` + while (aIndex < aLen) { + emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); + aIndex++; + } + + return new LineTokens(new Uint32Array(result), aTokens.getLineContent(), this._languageIdCodec); + } + + private static _findFirstPieceWithLine(pieces: SparseMultilineTokens[], lineNumber: number): number { + let low = 0; + let high = pieces.length - 1; + + while (low < high) { + let mid = low + Math.floor((high - low) / 2); + + if (pieces[mid].endLineNumber < lineNumber) { + low = mid + 1; + } else if (pieces[mid].startLineNumber > lineNumber) { + high = mid - 1; + } else { + while (mid > low && pieces[mid - 1].startLineNumber <= lineNumber && lineNumber <= pieces[mid - 1].endLineNumber) { + mid--; + } + return mid; + } + } + + return low; + } + + public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { + for (const piece of this._pieces) { + piece.acceptEdit(range, eolCount, firstLineLength, lastLineLength, firstCharCode); + } + } +} diff --git a/src/vs/editor/common/language/tokens/tokenArray.ts b/src/vs/editor/common/language/tokens/tokenArray.ts new file mode 100644 index 00000000000..bc089d1604a --- /dev/null +++ b/src/vs/editor/common/language/tokens/tokenArray.ts @@ -0,0 +1,104 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { OffsetRange } from '../core/offsetRange.js'; +import { ILanguageIdCodec } from '../languages.js'; +import { LineTokens } from './lineTokens.js'; + +/** + * This class represents a sequence of tokens. + * Conceptually, each token has a length and a metadata number. + * A token array might be used to annotate a string with metadata. + * Use {@link TokenArrayBuilder} to efficiently create a token array. + * + * TODO: Make this class more efficient (e.g. by using a Int32Array). +*/ +export class TokenArray { + public static fromLineTokens(lineTokens: LineTokens): TokenArray { + const tokenInfo: TokenInfo[] = []; + for (let i = 0; i < lineTokens.getCount(); i++) { + tokenInfo.push(new TokenInfo(lineTokens.getEndOffset(i) - lineTokens.getStartOffset(i), lineTokens.getMetadata(i))); + } + return TokenArray.create(tokenInfo); + } + + public static create(tokenInfo: TokenInfo[]): TokenArray { + return new TokenArray(tokenInfo); + } + + private constructor( + private readonly _tokenInfo: TokenInfo[], + ) { } + + public toLineTokens(lineContent: string, decoder: ILanguageIdCodec): LineTokens { + return LineTokens.createFromTextAndMetadata(this.map((r, t) => ({ text: r.substring(lineContent), metadata: t.metadata })), decoder); + } + + public forEach(cb: (range: OffsetRange, tokenInfo: TokenInfo) => void): void { + let lengthSum = 0; + for (const tokenInfo of this._tokenInfo) { + const range = new OffsetRange(lengthSum, lengthSum + tokenInfo.length); + cb(range, tokenInfo); + lengthSum += tokenInfo.length; + } + } + + public map(cb: (range: OffsetRange, tokenInfo: TokenInfo) => T): T[] { + const result: T[] = []; + let lengthSum = 0; + for (const tokenInfo of this._tokenInfo) { + const range = new OffsetRange(lengthSum, lengthSum + tokenInfo.length); + result.push(cb(range, tokenInfo)); + lengthSum += tokenInfo.length; + } + return result; + } + + public slice(range: OffsetRange): TokenArray { + const result: TokenInfo[] = []; + let lengthSum = 0; + for (const tokenInfo of this._tokenInfo) { + const tokenStart = lengthSum; + const tokenEndEx = tokenStart + tokenInfo.length; + if (tokenEndEx > range.start) { + if (tokenStart >= range.endExclusive) { + break; + } + + const deltaBefore = Math.max(0, range.start - tokenStart); + const deltaAfter = Math.max(0, tokenEndEx - range.endExclusive); + + result.push(new TokenInfo(tokenInfo.length - deltaBefore - deltaAfter, tokenInfo.metadata)); + } + + lengthSum += tokenInfo.length; + } + return TokenArray.create(result); + } +} + +export type TokenMetadata = number; + +export class TokenInfo { + constructor( + public readonly length: number, + public readonly metadata: TokenMetadata, + ) { } +} + +/** + * TODO: Make this class more efficient (e.g. by using a Int32Array). +*/ +export class TokenArrayBuilder { + private readonly _tokens: TokenInfo[] = []; + + public add(length: number, metadata: TokenMetadata): void { + this._tokens.push(new TokenInfo(length, metadata)); + } + + public build(): TokenArray { + return TokenArray.create(this._tokens); + } +} diff --git a/src/vs/editor/common/languageFeatureRegistry.ts b/src/vs/editor/common/languageFeatureRegistry.ts index 6bf6f851720..8a446ae2bcf 100644 --- a/src/vs/editor/common/languageFeatureRegistry.ts +++ b/src/vs/editor/common/languageFeatureRegistry.ts @@ -5,8 +5,8 @@ import { Emitter } from '../../base/common/event.js'; import { IDisposable, toDisposable } from '../../base/common/lifecycle.js'; -import { ITextModel, shouldSynchronizeModel } from './model.js'; -import { LanguageFilter, LanguageSelector, score } from './languageSelector.js'; +import { ITextModel, shouldSynchronizeModel } from '../../editor/common/language/model.js'; +import { LanguageFilter, LanguageSelector, score } from '../../editor/common/language/languageSelector.js'; import { URI } from '../../base/common/uri.js'; interface Entry { diff --git a/src/vs/editor/common/languageSelector.ts b/src/vs/editor/common/languageSelector.ts index 6374d380f48..6965c91e355 100644 --- a/src/vs/editor/common/languageSelector.ts +++ b/src/vs/editor/common/languageSelector.ts @@ -3,142 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRelativePattern, match as matchGlobPattern } from '../../base/common/glob.js'; -import { URI } from '../../base/common/uri.js'; -import { normalize } from '../../base/common/path.js'; - -export interface LanguageFilter { - readonly language?: string; - readonly scheme?: string; - readonly pattern?: string | IRelativePattern; - readonly notebookType?: string; - /** - * This provider is implemented in the UI thread. - */ - readonly hasAccessToAllModels?: boolean; - readonly exclusive?: boolean; - - /** - * This provider comes from a builtin extension. - */ - readonly isBuiltin?: boolean; -} - -export type LanguageSelector = string | LanguageFilter | ReadonlyArray; - -export function score(selector: LanguageSelector | undefined, candidateUri: URI, candidateLanguage: string, candidateIsSynchronized: boolean, candidateNotebookUri: URI | undefined, candidateNotebookType: string | undefined): number { - - if (Array.isArray(selector)) { - // array -> take max individual value - let ret = 0; - for (const filter of selector) { - const value = score(filter, candidateUri, candidateLanguage, candidateIsSynchronized, candidateNotebookUri, candidateNotebookType); - if (value === 10) { - return value; // already at the highest - } - if (value > ret) { - ret = value; - } - } - return ret; - - } else if (typeof selector === 'string') { - - if (!candidateIsSynchronized) { - return 0; - } - - // short-hand notion, desugars to - // 'fooLang' -> { language: 'fooLang'} - // '*' -> { language: '*' } - if (selector === '*') { - return 5; - } else if (selector === candidateLanguage) { - return 10; - } else { - return 0; - } - - } else if (selector) { - // filter -> select accordingly, use defaults for scheme - const { language, pattern, scheme, hasAccessToAllModels, notebookType } = selector as LanguageFilter; // TODO: microsoft/TypeScript#42768 - - if (!candidateIsSynchronized && !hasAccessToAllModels) { - return 0; - } - - // selector targets a notebook -> use the notebook uri instead - // of the "normal" document uri. - if (notebookType && candidateNotebookUri) { - candidateUri = candidateNotebookUri; - } - - let ret = 0; - - if (scheme) { - if (scheme === candidateUri.scheme) { - ret = 10; - } else if (scheme === '*') { - ret = 5; - } else { - return 0; - } - } - - if (language) { - if (language === candidateLanguage) { - ret = 10; - } else if (language === '*') { - ret = Math.max(ret, 5); - } else { - return 0; - } - } - - if (notebookType) { - if (notebookType === candidateNotebookType) { - ret = 10; - } else if (notebookType === '*' && candidateNotebookType !== undefined) { - ret = Math.max(ret, 5); - } else { - return 0; - } - } - - if (pattern) { - let normalizedPattern: string | IRelativePattern; - if (typeof pattern === 'string') { - normalizedPattern = pattern; - } else { - // Since this pattern has a `base` property, we need - // to normalize this path first before passing it on - // because we will compare it against `Uri.fsPath` - // which uses platform specific separators. - // Refs: https://github.com/microsoft/vscode/issues/99938 - normalizedPattern = { ...pattern, base: normalize(pattern.base) }; - } - - if (normalizedPattern === candidateUri.fsPath || matchGlobPattern(normalizedPattern, candidateUri.fsPath)) { - ret = 10; - } else { - return 0; - } - } - - return ret; - - } else { - return 0; - } -} - - -export function targetsNotebooks(selector: LanguageSelector): boolean { - if (typeof selector === 'string') { - return false; - } else if (Array.isArray(selector)) { - return selector.some(targetsNotebooks); - } else { - return !!(selector).notebookType; - } -} +export * from './language/languageSelector.js'; diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 0edb100dfea..c88149cfaba 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -3,2460 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { VSBuffer } from '../../base/common/buffer.js'; -import { CancellationToken } from '../../base/common/cancellation.js'; -import { Codicon } from '../../base/common/codicons.js'; -import { Color } from '../../base/common/color.js'; -import { IReadonlyVSDataTransfer } from '../../base/common/dataTransfer.js'; -import { Event } from '../../base/common/event.js'; -import { HierarchicalKind } from '../../base/common/hierarchicalKind.js'; -import { IMarkdownString } from '../../base/common/htmlContent.js'; -import { IDisposable } from '../../base/common/lifecycle.js'; -import { ThemeIcon } from '../../base/common/themables.js'; -import { URI, UriComponents } from '../../base/common/uri.js'; -import { EditOperation, ISingleEditOperation } from './core/editOperation.js'; -import { IPosition, Position } from './core/position.js'; -import { IRange, Range } from './core/range.js'; -import { Selection } from './core/selection.js'; -import { LanguageId } from './encodedTokenAttributes.js'; -import { LanguageSelector } from './languageSelector.js'; -import * as model from './model.js'; -import { TokenizationRegistry as TokenizationRegistryImpl } from './tokenizationRegistry.js'; -import { ContiguousMultilineTokens } from './tokens/contiguousMultilineTokens.js'; -import { localize } from '../../nls.js'; -import { ExtensionIdentifier } from '../../platform/extensions/common/extensions.js'; -import { IMarkerData } from '../../platform/markers/common/markers.js'; -import { IModelTokensChangedEvent } from './textModelEvents.js'; -import { ITextModel } from './model.js'; -import { TokenUpdate } from './model/tokenStore.js'; -import { ITextModelTreeSitter } from './services/treeSitterParserService.js'; -import type * as Parser from '@vscode/tree-sitter-wasm'; - -/** - * @internal - */ -export interface ILanguageIdCodec { - encodeLanguageId(languageId: string): LanguageId; - decodeLanguageId(languageId: LanguageId): string; -} - -export class Token { - _tokenBrand: void = undefined; - - constructor( - public readonly offset: number, - public readonly type: string, - public readonly language: string, - ) { - } - - public toString(): string { - return '(' + this.offset + ', ' + this.type + ')'; - } -} - -/** - * @internal - */ -export class TokenizationResult { - _tokenizationResultBrand: void = undefined; - - constructor( - public readonly tokens: Token[], - public readonly endState: IState, - ) { - } -} - -/** - * @internal - */ -export class EncodedTokenizationResult { - _encodedTokenizationResultBrand: void = undefined; - - constructor( - /** - * The tokens in binary format. Each token occupies two array indices. For token i: - * - at offset 2*i => startIndex - * - at offset 2*i + 1 => metadata - * - */ - public readonly tokens: Uint32Array, - public readonly endState: IState, - ) { - } -} - -export interface SyntaxNode { - startIndex: number; - endIndex: number; - startPosition: IPosition; - endPosition: IPosition; -} - -export interface QueryCapture { - name: string; - text?: string; - node: SyntaxNode; - encodedLanguageId: number; -} - -/** - * An intermediate interface for scaffolding the new tree sitter tokenization support. Not final. - * @internal - */ -export interface ITreeSitterTokenizationSupport { - /** - * exposed for testing - */ - getTokensInRange(textModel: ITextModel, range: Range, rangeStartOffset: number, rangeEndOffset: number): TokenUpdate[] | undefined; - tokenizeEncoded(lineNumber: number, textModel: model.ITextModel): void; - captureAtPosition(lineNumber: number, column: number, textModel: model.ITextModel): QueryCapture[]; - captureAtRangeTree(range: Range, tree: Parser.Tree, textModelTreeSitter: ITextModelTreeSitter): QueryCapture[]; - onDidChangeTokens: Event<{ textModel: model.ITextModel; changes: IModelTokensChangedEvent }>; - onDidChangeBackgroundTokenization: Event<{ textModel: model.ITextModel }>; - tokenizeEncodedInstrumented(lineNumber: number, textModel: model.ITextModel): { result: Uint32Array; captureTime: number; metadataTime: number } | undefined; - guessTokensForLinesContent(lineNumber: number, textModel: model.ITextModel, lines: string[]): Uint32Array[] | undefined; -} - -/** - * @internal - */ -export interface ITokenizationSupport { - /** - * If true, the background tokenizer will only be used to verify tokens against the default background tokenizer. - * Used for debugging. - */ - readonly backgroundTokenizerShouldOnlyVerifyTokens?: boolean; - - getInitialState(): IState; - - tokenize(line: string, hasEOL: boolean, state: IState): TokenizationResult; - - tokenizeEncoded(line: string, hasEOL: boolean, state: IState): EncodedTokenizationResult; - - /** - * Can be/return undefined if default background tokenization should be used. - */ - createBackgroundTokenizer?(textModel: model.ITextModel, store: IBackgroundTokenizationStore): IBackgroundTokenizer | undefined; -} - -/** - * @internal - */ -export interface IBackgroundTokenizer extends IDisposable { - /** - * Instructs the background tokenizer to set the tokens for the given range again. - * - * This might be necessary if the renderer overwrote those tokens with heuristically computed ones for some viewport, - * when the change does not even propagate to that viewport. - */ - requestTokens(startLineNumber: number, endLineNumberExclusive: number): void; - - reportMismatchingTokens?(lineNumber: number): void; -} - -/** - * @internal - */ -export interface IBackgroundTokenizationStore { - setTokens(tokens: ContiguousMultilineTokens[]): void; - - setEndState(lineNumber: number, state: IState): void; - - /** - * Should be called to indicate that the background tokenization has finished for now. - * (This triggers bracket pair colorization to re-parse the bracket pairs with token information) - */ - backgroundTokenizationFinished(): void; -} - -/** - * The state of the tokenizer between two lines. - * It is useful to store flags such as in multiline comment, etc. - * The model will clone the previous line's state and pass it in to tokenize the next line. - */ -export interface IState { - clone(): IState; - equals(other: IState): boolean; -} - -/** - * A provider result represents the values a provider, like the {@link HoverProvider}, - * may return. For once this is the actual result type `T`, like `Hover`, or a thenable that resolves - * to that type `T`. In addition, `null` and `undefined` can be returned - either directly or from a - * thenable. - */ -export type ProviderResult = T | undefined | null | Thenable; - -/** - * A hover represents additional information for a symbol or word. Hovers are - * rendered in a tooltip-like widget. - */ -export interface Hover { - /** - * The contents of this hover. - */ - contents: IMarkdownString[]; - - /** - * The range to which this hover applies. When missing, the - * editor will use the range at the current position or the - * current position itself. - */ - range?: IRange; - - /** - * Can increase the verbosity of the hover - */ - canIncreaseVerbosity?: boolean; - - /** - * Can decrease the verbosity of the hover - */ - canDecreaseVerbosity?: boolean; -} - -/** - * The hover provider interface defines the contract between extensions and - * the [hover](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ -export interface HoverProvider { - /** - * Provide a hover for the given position, context and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - */ - provideHover(model: model.ITextModel, position: Position, token: CancellationToken, context?: HoverContext): ProviderResult; -} - -export interface HoverContext { - /** - * Hover verbosity request - */ - verbosityRequest?: HoverVerbosityRequest; -} - -export interface HoverVerbosityRequest { - /** - * The delta by which to increase/decrease the hover verbosity level - */ - verbosityDelta: number; - /** - * The previous hover for the same position - */ - previousHover: THover; -} - -export enum HoverVerbosityAction { - /** - * Increase the verbosity of the hover - */ - Increase, - /** - * Decrease the verbosity of the hover - */ - Decrease -} - -/** - * An evaluatable expression represents additional information for an expression in a document. Evaluatable expressions are - * evaluated by a debugger or runtime and their result is rendered in a tooltip-like widget. - * @internal - */ -export interface EvaluatableExpression { - /** - * The range to which this expression applies. - */ - range: IRange; - /** - * This expression overrides the expression extracted from the range. - */ - expression?: string; -} - - -/** - * The evaluatable expression provider interface defines the contract between extensions and - * the debug hover. - * @internal - */ -export interface EvaluatableExpressionProvider { - /** - * Provide a hover for the given position and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - */ - provideEvaluatableExpression(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * A value-object that contains contextual information when requesting inline values from a InlineValuesProvider. - * @internal - */ -export interface InlineValueContext { - frameId: number; - stoppedLocation: Range; -} - -/** - * Provide inline value as text. - * @internal - */ -export interface InlineValueText { - type: 'text'; - range: IRange; - text: string; -} - -/** - * Provide inline value through a variable lookup. - * @internal - */ -export interface InlineValueVariableLookup { - type: 'variable'; - range: IRange; - variableName?: string; - caseSensitiveLookup: boolean; -} - -/** - * Provide inline value through an expression evaluation. - * @internal - */ -export interface InlineValueExpression { - type: 'expression'; - range: IRange; - expression?: string; -} - -/** - * Inline value information can be provided by different means: - * - directly as a text value (class InlineValueText). - * - as a name to use for a variable lookup (class InlineValueVariableLookup) - * - as an evaluatable expression (class InlineValueEvaluatableExpression) - * The InlineValue types combines all inline value types into one type. - * @internal - */ -export type InlineValue = InlineValueText | InlineValueVariableLookup | InlineValueExpression; - -/** - * The inline values provider interface defines the contract between extensions and - * the debugger's inline values feature. - * @internal - */ -export interface InlineValuesProvider { - /** - */ - onDidChangeInlineValues?: Event | undefined; - /** - * Provide the "inline values" for the given range and document. Multiple hovers at the same - * position will be merged by the editor. A hover can have a range which defaults - * to the word range at the position when omitted. - */ - provideInlineValues(model: model.ITextModel, viewPort: Range, context: InlineValueContext, token: CancellationToken): ProviderResult; -} - -export const enum CompletionItemKind { - Method, - Function, - Constructor, - Field, - Variable, - Class, - Struct, - Interface, - Module, - Property, - Event, - Operator, - Unit, - Value, - Constant, - Enum, - EnumMember, - Keyword, - Text, - Color, - File, - Reference, - Customcolor, - Folder, - TypeParameter, - User, - Issue, - Snippet, // <- highest value (used for compare!) -} - -/** - * @internal - */ -export namespace CompletionItemKinds { - - const byKind = new Map(); - byKind.set(CompletionItemKind.Method, Codicon.symbolMethod); - byKind.set(CompletionItemKind.Function, Codicon.symbolFunction); - byKind.set(CompletionItemKind.Constructor, Codicon.symbolConstructor); - byKind.set(CompletionItemKind.Field, Codicon.symbolField); - byKind.set(CompletionItemKind.Variable, Codicon.symbolVariable); - byKind.set(CompletionItemKind.Class, Codicon.symbolClass); - byKind.set(CompletionItemKind.Struct, Codicon.symbolStruct); - byKind.set(CompletionItemKind.Interface, Codicon.symbolInterface); - byKind.set(CompletionItemKind.Module, Codicon.symbolModule); - byKind.set(CompletionItemKind.Property, Codicon.symbolProperty); - byKind.set(CompletionItemKind.Event, Codicon.symbolEvent); - byKind.set(CompletionItemKind.Operator, Codicon.symbolOperator); - byKind.set(CompletionItemKind.Unit, Codicon.symbolUnit); - byKind.set(CompletionItemKind.Value, Codicon.symbolValue); - byKind.set(CompletionItemKind.Enum, Codicon.symbolEnum); - byKind.set(CompletionItemKind.Constant, Codicon.symbolConstant); - byKind.set(CompletionItemKind.Enum, Codicon.symbolEnum); - byKind.set(CompletionItemKind.EnumMember, Codicon.symbolEnumMember); - byKind.set(CompletionItemKind.Keyword, Codicon.symbolKeyword); - byKind.set(CompletionItemKind.Snippet, Codicon.symbolSnippet); - byKind.set(CompletionItemKind.Text, Codicon.symbolText); - byKind.set(CompletionItemKind.Color, Codicon.symbolColor); - byKind.set(CompletionItemKind.File, Codicon.symbolFile); - byKind.set(CompletionItemKind.Reference, Codicon.symbolReference); - byKind.set(CompletionItemKind.Customcolor, Codicon.symbolCustomColor); - byKind.set(CompletionItemKind.Folder, Codicon.symbolFolder); - byKind.set(CompletionItemKind.TypeParameter, Codicon.symbolTypeParameter); - byKind.set(CompletionItemKind.User, Codicon.account); - byKind.set(CompletionItemKind.Issue, Codicon.issues); - - /** - * @internal - */ - export function toIcon(kind: CompletionItemKind): ThemeIcon { - let codicon = byKind.get(kind); - if (!codicon) { - console.info('No codicon found for CompletionItemKind ' + kind); - codicon = Codicon.symbolProperty; - } - return codicon; - } - - /** - * @internal - */ - export function toLabel(kind: CompletionItemKind): string { - switch (kind) { - case CompletionItemKind.Method: return localize('suggestWidget.kind.method', 'Method'); - case CompletionItemKind.Function: return localize('suggestWidget.kind.function', 'Function'); - case CompletionItemKind.Constructor: return localize('suggestWidget.kind.constructor', 'Constructor'); - case CompletionItemKind.Field: return localize('suggestWidget.kind.field', 'Field'); - case CompletionItemKind.Variable: return localize('suggestWidget.kind.variable', 'Variable'); - case CompletionItemKind.Class: return localize('suggestWidget.kind.class', 'Class'); - case CompletionItemKind.Struct: return localize('suggestWidget.kind.struct', 'Struct'); - case CompletionItemKind.Interface: return localize('suggestWidget.kind.interface', 'Interface'); - case CompletionItemKind.Module: return localize('suggestWidget.kind.module', 'Module'); - case CompletionItemKind.Property: return localize('suggestWidget.kind.property', 'Property'); - case CompletionItemKind.Event: return localize('suggestWidget.kind.event', 'Event'); - case CompletionItemKind.Operator: return localize('suggestWidget.kind.operator', 'Operator'); - case CompletionItemKind.Unit: return localize('suggestWidget.kind.unit', 'Unit'); - case CompletionItemKind.Value: return localize('suggestWidget.kind.value', 'Value'); - case CompletionItemKind.Constant: return localize('suggestWidget.kind.constant', 'Constant'); - case CompletionItemKind.Enum: return localize('suggestWidget.kind.enum', 'Enum'); - case CompletionItemKind.EnumMember: return localize('suggestWidget.kind.enumMember', 'Enum Member'); - case CompletionItemKind.Keyword: return localize('suggestWidget.kind.keyword', 'Keyword'); - case CompletionItemKind.Text: return localize('suggestWidget.kind.text', 'Text'); - case CompletionItemKind.Color: return localize('suggestWidget.kind.color', 'Color'); - case CompletionItemKind.File: return localize('suggestWidget.kind.file', 'File'); - case CompletionItemKind.Reference: return localize('suggestWidget.kind.reference', 'Reference'); - case CompletionItemKind.Customcolor: return localize('suggestWidget.kind.customcolor', 'Custom Color'); - case CompletionItemKind.Folder: return localize('suggestWidget.kind.folder', 'Folder'); - case CompletionItemKind.TypeParameter: return localize('suggestWidget.kind.typeParameter', 'Type Parameter'); - case CompletionItemKind.User: return localize('suggestWidget.kind.user', 'User'); - case CompletionItemKind.Issue: return localize('suggestWidget.kind.issue', 'Issue'); - case CompletionItemKind.Snippet: return localize('suggestWidget.kind.snippet', 'Snippet'); - default: return ''; - } - } - - const data = new Map(); - data.set('method', CompletionItemKind.Method); - data.set('function', CompletionItemKind.Function); - data.set('constructor', CompletionItemKind.Constructor); - data.set('field', CompletionItemKind.Field); - data.set('variable', CompletionItemKind.Variable); - data.set('class', CompletionItemKind.Class); - data.set('struct', CompletionItemKind.Struct); - data.set('interface', CompletionItemKind.Interface); - data.set('module', CompletionItemKind.Module); - data.set('property', CompletionItemKind.Property); - data.set('event', CompletionItemKind.Event); - data.set('operator', CompletionItemKind.Operator); - data.set('unit', CompletionItemKind.Unit); - data.set('value', CompletionItemKind.Value); - data.set('constant', CompletionItemKind.Constant); - data.set('enum', CompletionItemKind.Enum); - data.set('enum-member', CompletionItemKind.EnumMember); - data.set('enumMember', CompletionItemKind.EnumMember); - data.set('keyword', CompletionItemKind.Keyword); - data.set('snippet', CompletionItemKind.Snippet); - data.set('text', CompletionItemKind.Text); - data.set('color', CompletionItemKind.Color); - data.set('file', CompletionItemKind.File); - data.set('reference', CompletionItemKind.Reference); - data.set('customcolor', CompletionItemKind.Customcolor); - data.set('folder', CompletionItemKind.Folder); - data.set('type-parameter', CompletionItemKind.TypeParameter); - data.set('typeParameter', CompletionItemKind.TypeParameter); - data.set('account', CompletionItemKind.User); - data.set('issue', CompletionItemKind.Issue); - - /** - * @internal - */ - export function fromString(value: string): CompletionItemKind; - /** - * @internal - */ - export function fromString(value: string, strict: true): CompletionItemKind | undefined; - /** - * @internal - */ - export function fromString(value: string, strict?: boolean): CompletionItemKind | undefined { - let res = data.get(value); - if (typeof res === 'undefined' && !strict) { - res = CompletionItemKind.Property; - } - return res; - } -} - -export interface CompletionItemLabel { - label: string; - detail?: string; - description?: string; -} - -export const enum CompletionItemTag { - Deprecated = 1 -} - -export const enum CompletionItemInsertTextRule { - None = 0, - - /** - * Adjust whitespace/indentation of multiline insert texts to - * match the current line indentation. - */ - KeepWhitespace = 0b001, - - /** - * `insertText` is a snippet. - */ - InsertAsSnippet = 0b100, -} - -export interface CompletionItemRanges { - insert: IRange; - replace: IRange; -} - -/** - * A completion item represents a text snippet that is - * proposed to complete text that is being typed. - */ -export interface CompletionItem { - /** - * The label of this completion item. By default - * this is also the text that is inserted when selecting - * this completion. - */ - label: string | CompletionItemLabel; - /** - * The kind of this completion item. Based on the kind - * an icon is chosen by the editor. - */ - kind: CompletionItemKind; - /** - * A modifier to the `kind` which affect how the item - * is rendered, e.g. Deprecated is rendered with a strikeout - */ - tags?: ReadonlyArray; - /** - * A human-readable string with additional information - * about this item, like type or symbol information. - */ - detail?: string; - /** - * A human-readable string that represents a doc-comment. - */ - documentation?: string | IMarkdownString; - /** - * A string that should be used when comparing this item - * with other items. When `falsy` the {@link CompletionItem.label label} - * is used. - */ - sortText?: string; - /** - * A string that should be used when filtering a set of - * completion items. When `falsy` the {@link CompletionItem.label label} - * is used. - */ - filterText?: string; - /** - * Select this item when showing. *Note* that only one completion item can be selected and - * that the editor decides which item that is. The rule is that the *first* item of those - * that match best is selected. - */ - preselect?: boolean; - /** - * A string or snippet that should be inserted in a document when selecting - * this completion. - */ - insertText: string; - /** - * Additional rules (as bitmask) that should be applied when inserting - * this completion. - */ - insertTextRules?: CompletionItemInsertTextRule; - /** - * A range of text that should be replaced by this completion item. - * - * *Note:* The range must be a {@link Range.isSingleLine single line} and it must - * {@link Range.contains contain} the position at which completion has been {@link CompletionItemProvider.provideCompletionItems requested}. - */ - range: IRange | CompletionItemRanges; - /** - * An optional set of characters that when pressed while this completion is active will accept it first and - * then type that character. *Note* that all commit characters should have `length=1` and that superfluous - * characters will be ignored. - */ - commitCharacters?: string[]; - /** - * An optional array of additional text edits that are applied when - * selecting this completion. Edits must not overlap with the main edit - * nor with themselves. - */ - additionalTextEdits?: ISingleEditOperation[]; - /** - * A command that should be run upon acceptance of this item. - */ - command?: Command; - /** - * A command that should be run upon acceptance of this item. - */ - action?: Command; - /** - * @internal - */ - extensionId?: ExtensionIdentifier; - - /** - * @internal - */ - _id?: [number, number]; -} - -export interface CompletionList { - suggestions: CompletionItem[]; - incomplete?: boolean; - dispose?(): void; - - /** - * @internal - */ - duration?: number; -} - -/** - * Info provided on partial acceptance. - */ -export interface PartialAcceptInfo { - kind: PartialAcceptTriggerKind; - acceptedLength: number; -} - -/** - * How a partial acceptance was triggered. - */ -export const enum PartialAcceptTriggerKind { - Word = 0, - Line = 1, - Suggest = 2, -} - -/** - * How a suggest provider was triggered. - */ -export const enum CompletionTriggerKind { - Invoke = 0, - TriggerCharacter = 1, - TriggerForIncompleteCompletions = 2 -} -/** - * Contains additional information about the context in which - * {@link CompletionItemProvider.provideCompletionItems completion provider} is triggered. - */ -export interface CompletionContext { - /** - * How the completion was triggered. - */ - triggerKind: CompletionTriggerKind; - /** - * Character that triggered the completion item provider. - * - * `undefined` if provider was not triggered by a character. - */ - triggerCharacter?: string; -} -/** - * The completion item provider interface defines the contract between extensions and - * the [IntelliSense](https://code.visualstudio.com/docs/editor/intellisense). - * - * When computing *complete* completion items is expensive, providers can optionally implement - * the `resolveCompletionItem`-function. In that case it is enough to return completion - * items with a {@link CompletionItem.label label} from the - * {@link CompletionItemProvider.provideCompletionItems provideCompletionItems}-function. Subsequently, - * when a completion item is shown in the UI and gains focus this provider is asked to resolve - * the item, like adding {@link CompletionItem.documentation doc-comment} or {@link CompletionItem.detail details}. - */ -export interface CompletionItemProvider { - - /** - * Used to identify completions in the (debug) UI and telemetry. This isn't the extension identifier because extensions - * often contribute multiple completion item providers. - * - * @internal - */ - _debugDisplayName: string; - - triggerCharacters?: string[]; - /** - * Provide completion items for the given position and document. - */ - provideCompletionItems(model: model.ITextModel, position: Position, context: CompletionContext, token: CancellationToken): ProviderResult; - - /** - * Given a completion item fill in more data, like {@link CompletionItem.documentation doc-comment} - * or {@link CompletionItem.detail details}. - * - * The editor will only resolve a completion item once. - */ - resolveCompletionItem?(item: CompletionItem, token: CancellationToken): ProviderResult; -} - -/** - * How an {@link InlineCompletionsProvider inline completion provider} was triggered. - */ -export enum InlineCompletionTriggerKind { - /** - * Completion was triggered automatically while editing. - * It is sufficient to return a single completion item in this case. - */ - Automatic = 0, - - /** - * Completion was triggered explicitly by a user gesture. - * Return multiple completion items to enable cycling through them. - */ - Explicit = 1, -} - -export interface InlineCompletionContext { - - /** - * How the completion was triggered. - */ - readonly triggerKind: InlineCompletionTriggerKind; - readonly selectedSuggestionInfo: SelectedSuggestionInfo | undefined; - /** - * @experimental - * @internal - */ - readonly userPrompt?: string | undefined; - /** - * @experimental - * @internal - */ - readonly requestUuid?: string | undefined; - - readonly includeInlineEdits: boolean; - readonly includeInlineCompletions: boolean; -} - -export class SelectedSuggestionInfo { - constructor( - public readonly range: IRange, - public readonly text: string, - public readonly completionKind: CompletionItemKind, - public readonly isSnippetText: boolean, - ) { - } - - public equals(other: SelectedSuggestionInfo) { - return Range.lift(this.range).equalsRange(other.range) - && this.text === other.text - && this.completionKind === other.completionKind - && this.isSnippetText === other.isSnippetText; - } -} - -export interface InlineCompletion { - /** - * The text to insert. - * If the text contains a line break, the range must end at the end of a line. - * If existing text should be replaced, the existing text must be a prefix of the text to insert. - * - * The text can also be a snippet. In that case, a preview with default parameters is shown. - * When accepting the suggestion, the full snippet is inserted. - */ - readonly insertText: string | { snippet: string }; - - /** - * A text that is used to decide if this inline completion should be shown. - * An inline completion is shown if the text to replace is a subword of the filter text. - */ - readonly filterText?: string; - - /** - * An optional array of additional text edits that are applied when - * selecting this completion. Edits must not overlap with the main edit - * nor with themselves. - */ - readonly additionalTextEdits?: ISingleEditOperation[]; - - /** - * The range to replace. - * Must begin and end on the same line. - */ - readonly range?: IRange; - - readonly command?: Command; - - readonly action?: Command; - - /** - * Is called the first time an inline completion is shown. - * @deprecated. Use `onDidShow` of the provider instead. - */ - readonly shownCommand?: Command; - - /** - * If set to `true`, unopened closing brackets are removed and unclosed opening brackets are closed. - * Defaults to `false`. - */ - readonly completeBracketPairs?: boolean; - - readonly isInlineEdit?: boolean; - readonly showInlineEditMenu?: boolean; - - readonly showRange?: IRange; - - readonly warning?: InlineCompletionWarning; -} - -export interface InlineCompletionWarning { - message: IMarkdownString | string; - icon?: IconPath; -} - -/** - * TODO: add `| URI | { light: URI; dark: URI }`. -*/ -export type IconPath = ThemeIcon; - -export interface InlineCompletions { - readonly items: readonly TItem[]; - /** - * A list of commands associated with the inline completions of this list. - */ - readonly commands?: Command[]; - - readonly suppressSuggestions?: boolean | undefined; - - /** - * When set and the user types a suggestion without derivating from it, the inline suggestion is not updated. - */ - readonly enableForwardStability?: boolean | undefined; -} - -export type InlineCompletionProviderGroupId = string; - -export interface InlineCompletionsProvider { - provideInlineCompletions(model: model.ITextModel, position: Position, context: InlineCompletionContext, token: CancellationToken): ProviderResult; - - /** - * @experimental - * @internal - */ - provideInlineEditsForRange?(model: model.ITextModel, range: Range, context: InlineCompletionContext, token: CancellationToken): ProviderResult; - - /** - * Will be called when an item is shown. - * @param updatedInsertText Is useful to understand bracket completion. - */ - handleItemDidShow?(completions: T, item: T['items'][number], updatedInsertText: string): void; - - /** - * Will be called when an item is partially accepted. TODO: also handle full acceptance here! - * @param acceptedCharacters Deprecated. Use `info.acceptedCharacters` instead. - */ - handlePartialAccept?(completions: T, item: T['items'][number], acceptedCharacters: number, info: PartialAcceptInfo): void; - - handleRejection?(completions: T, item: T['items'][number]): void; - - /** - * Will be called when a completions list is no longer in use and can be garbage-collected. - */ - freeInlineCompletions(completions: T): void; - - /** - * Only used for {@link yieldsToGroupIds}. - * Multiple providers can have the same group id. - */ - groupId?: InlineCompletionProviderGroupId; - - /** - * Returns a list of preferred provider {@link groupId}s. - * The current provider is only requested for completions if no provider with a preferred group id returned a result. - */ - yieldsToGroupIds?: InlineCompletionProviderGroupId[]; - - displayName?: string; - - debounceDelayMs?: number; - - toString?(): string; -} - -export interface CodeAction { - title: string; - command?: Command; - edit?: WorkspaceEdit; - diagnostics?: IMarkerData[]; - kind?: string; - isPreferred?: boolean; - isAI?: boolean; - disabled?: string; - ranges?: IRange[]; -} - -export const enum CodeActionTriggerType { - Invoke = 1, - Auto = 2, -} - -/** - * @internal - */ -export interface CodeActionContext { - only?: string; - trigger: CodeActionTriggerType; -} - -export interface CodeActionList extends IDisposable { - readonly actions: ReadonlyArray; -} - -/** - * The code action interface defines the contract between extensions and - * the [light bulb](https://code.visualstudio.com/docs/editor/editingevolved#_code-action) feature. - * @internal - */ -export interface CodeActionProvider { - - displayName?: string; - - extensionId?: string; - - /** - * Provide commands for the given document and range. - */ - provideCodeActions(model: model.ITextModel, range: Range | Selection, context: CodeActionContext, token: CancellationToken): ProviderResult; - - /** - * Given a code action fill in the edit. Will only invoked when missing. - */ - resolveCodeAction?(codeAction: CodeAction, token: CancellationToken): ProviderResult; - - /** - * Optional list of CodeActionKinds that this provider returns. - */ - readonly providedCodeActionKinds?: ReadonlyArray; - - readonly documentation?: ReadonlyArray<{ readonly kind: string; readonly command: Command }>; - - /** - * @internal - */ - _getAdditionalMenuItems?(context: CodeActionContext, actions: readonly CodeAction[]): Command[]; -} - -/** - * @internal - */ -export interface DocumentPasteEdit { - readonly title: string; - readonly kind: HierarchicalKind; - readonly handledMimeType?: string; - yieldTo?: readonly DropYieldTo[]; - insertText: string | { readonly snippet: string }; - additionalEdit?: WorkspaceEdit; -} - -/** - * @internal - */ -export enum DocumentPasteTriggerKind { - Automatic = 0, - PasteAs = 1, -} - -/** - * @internal - */ -export interface DocumentPasteContext { - readonly only?: HierarchicalKind; - readonly triggerKind: DocumentPasteTriggerKind; -} - -/** - * @internal - */ -export interface DocumentPasteEditsSession { - edits: readonly DocumentPasteEdit[]; - dispose(): void; -} - -/** - * @internal - */ -export interface DocumentPasteEditProvider { - readonly id?: string; - readonly copyMimeTypes: readonly string[]; - readonly pasteMimeTypes: readonly string[]; - readonly providedPasteEditKinds: readonly HierarchicalKind[]; - - prepareDocumentPaste?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise; - - provideDocumentPasteEdits?(model: model.ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise; - - resolveDocumentPasteEdit?(edit: DocumentPasteEdit, token: CancellationToken): Promise; -} - -/** - * Represents a parameter of a callable-signature. A parameter can - * have a label and a doc-comment. - */ -export interface ParameterInformation { - /** - * The label of this signature. Will be shown in - * the UI. - */ - label: string | [number, number]; - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string | IMarkdownString; -} -/** - * Represents the signature of something callable. A signature - * can have a label, like a function-name, a doc-comment, and - * a set of parameters. - */ -export interface SignatureInformation { - /** - * The label of this signature. Will be shown in - * the UI. - */ - label: string; - /** - * The human-readable doc-comment of this signature. Will be shown - * in the UI but can be omitted. - */ - documentation?: string | IMarkdownString; - /** - * The parameters of this signature. - */ - parameters: ParameterInformation[]; - /** - * Index of the active parameter. - * - * If provided, this is used in place of `SignatureHelp.activeSignature`. - */ - activeParameter?: number; -} -/** - * Signature help represents the signature of something - * callable. There can be multiple signatures but only one - * active and only one active parameter. - */ -export interface SignatureHelp { - /** - * One or more signatures. - */ - signatures: SignatureInformation[]; - /** - * The active signature. - */ - activeSignature: number; - /** - * The active parameter of the active signature. - */ - activeParameter: number; -} - -export interface SignatureHelpResult extends IDisposable { - value: SignatureHelp; -} - -export enum SignatureHelpTriggerKind { - Invoke = 1, - TriggerCharacter = 2, - ContentChange = 3, -} - -export interface SignatureHelpContext { - readonly triggerKind: SignatureHelpTriggerKind; - readonly triggerCharacter?: string; - readonly isRetrigger: boolean; - readonly activeSignatureHelp?: SignatureHelp; -} - -/** - * The signature help provider interface defines the contract between extensions and - * the [parameter hints](https://code.visualstudio.com/docs/editor/intellisense)-feature. - */ -export interface SignatureHelpProvider { - - readonly signatureHelpTriggerCharacters?: ReadonlyArray; - readonly signatureHelpRetriggerCharacters?: ReadonlyArray; - - /** - * Provide help for the signature at the given position and document. - */ - provideSignatureHelp(model: model.ITextModel, position: Position, token: CancellationToken, context: SignatureHelpContext): ProviderResult; -} - -/** - * A document highlight kind. - */ -export enum DocumentHighlightKind { - /** - * A textual occurrence. - */ - Text, - /** - * Read-access of a symbol, like reading a variable. - */ - Read, - /** - * Write-access of a symbol, like writing to a variable. - */ - Write -} -/** - * A document highlight is a range inside a text document which deserves - * special attention. Usually a document highlight is visualized by changing - * the background color of its range. - */ -export interface DocumentHighlight { - /** - * The range this highlight applies to. - */ - range: IRange; - /** - * The highlight kind, default is {@link DocumentHighlightKind.Text text}. - */ - kind?: DocumentHighlightKind; -} - -/** - * Represents a set of document highlights for a specific URI. - */ -export interface MultiDocumentHighlight { - /** - * The URI of the document that the highlights belong to. - */ - uri: URI; - - /** - * The set of highlights for the document. - */ - highlights: DocumentHighlight[]; -} - -/** - * The document highlight provider interface defines the contract between extensions and - * the word-highlight-feature. - */ -export interface DocumentHighlightProvider { - /** - * Provide a set of document highlights, like all occurrences of a variable or - * all exit-points of a function. - */ - provideDocumentHighlights(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * A provider that can provide document highlights across multiple documents. - */ -export interface MultiDocumentHighlightProvider { - readonly selector: LanguageSelector; - - /** - * Provide a Map of URI --> document highlights, like all occurrences of a variable or - * all exit-points of a function. - * - * Used in cases such as split view, notebooks, etc. where there can be multiple documents - * with shared symbols. - * - * @param primaryModel The primary text model. - * @param position The position at which to provide document highlights. - * @param otherModels The other text models to search for document highlights. - * @param token A cancellation token. - * @returns A map of URI to document highlights. - */ - provideMultiDocumentHighlights(primaryModel: model.ITextModel, position: Position, otherModels: model.ITextModel[], token: CancellationToken): ProviderResult>; -} - -/** - * The linked editing range provider interface defines the contract between extensions and - * the linked editing feature. - */ -export interface LinkedEditingRangeProvider { - - /** - * Provide a list of ranges that can be edited together. - */ - provideLinkedEditingRanges(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * Represents a list of ranges that can be edited together along with a word pattern to describe valid contents. - */ -export interface LinkedEditingRanges { - /** - * A list of ranges that can be edited together. The ranges must have - * identical length and text content. The ranges cannot overlap - */ - ranges: IRange[]; - - /** - * An optional word pattern that describes valid contents for the given ranges. - * If no pattern is provided, the language configuration's word pattern will be used. - */ - wordPattern?: RegExp; -} - -/** - * Value-object that contains additional information when - * requesting references. - */ -export interface ReferenceContext { - /** - * Include the declaration of the current symbol. - */ - includeDeclaration: boolean; -} -/** - * The reference provider interface defines the contract between extensions and - * the [find references](https://code.visualstudio.com/docs/editor/editingevolved#_peek)-feature. - */ -export interface ReferenceProvider { - /** - * Provide a set of project-wide references for the given position and document. - */ - provideReferences(model: model.ITextModel, position: Position, context: ReferenceContext, token: CancellationToken): ProviderResult; -} - -/** - * Represents a location inside a resource, such as a line - * inside a text file. - */ -export interface Location { - /** - * The resource identifier of this location. - */ - uri: URI; - /** - * The document range of this locations. - */ - range: IRange; -} - -export interface LocationLink { - /** - * A range to select where this link originates from. - */ - originSelectionRange?: IRange; - - /** - * The target uri this link points to. - */ - uri: URI; - - /** - * The full range this link points to. - */ - range: IRange; - - /** - * A range to select this link points to. Must be contained - * in `LocationLink.range`. - */ - targetSelectionRange?: IRange; -} - -/** - * @internal - */ -export function isLocationLink(thing: any): thing is LocationLink { - return thing - && URI.isUri((thing as LocationLink).uri) - && Range.isIRange((thing as LocationLink).range) - && (Range.isIRange((thing as LocationLink).originSelectionRange) || Range.isIRange((thing as LocationLink).targetSelectionRange)); -} - -/** - * @internal - */ -export function isLocation(thing: any): thing is Location { - return thing - && URI.isUri((thing as Location).uri) - && Range.isIRange((thing as Location).range); -} - - -export type Definition = Location | Location[] | LocationLink[]; - -/** - * The definition provider interface defines the contract between extensions and - * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) - * and peek definition features. - */ -export interface DefinitionProvider { - /** - * Provide the definition of the symbol at the given position and document. - */ - provideDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * The definition provider interface defines the contract between extensions and - * the [go to definition](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-definition) - * and peek definition features. - */ -export interface DeclarationProvider { - /** - * Provide the declaration of the symbol at the given position and document. - */ - provideDeclaration(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * The implementation provider interface defines the contract between extensions and - * the go to implementation feature. - */ -export interface ImplementationProvider { - /** - * Provide the implementation of the symbol at the given position and document. - */ - provideImplementation(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * The type definition provider interface defines the contract between extensions and - * the go to type definition feature. - */ -export interface TypeDefinitionProvider { - /** - * Provide the type definition of the symbol at the given position and document. - */ - provideTypeDefinition(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -/** - * A symbol kind. - */ -export const enum SymbolKind { - File = 0, - Module = 1, - Namespace = 2, - Package = 3, - Class = 4, - Method = 5, - Property = 6, - Field = 7, - Constructor = 8, - Enum = 9, - Interface = 10, - Function = 11, - Variable = 12, - Constant = 13, - String = 14, - Number = 15, - Boolean = 16, - Array = 17, - Object = 18, - Key = 19, - Null = 20, - EnumMember = 21, - Struct = 22, - Event = 23, - Operator = 24, - TypeParameter = 25 -} - -/** - * @internal - */ -export const symbolKindNames: { [symbol: number]: string } = { - [SymbolKind.Array]: localize('Array', "array"), - [SymbolKind.Boolean]: localize('Boolean', "boolean"), - [SymbolKind.Class]: localize('Class', "class"), - [SymbolKind.Constant]: localize('Constant', "constant"), - [SymbolKind.Constructor]: localize('Constructor', "constructor"), - [SymbolKind.Enum]: localize('Enum', "enumeration"), - [SymbolKind.EnumMember]: localize('EnumMember', "enumeration member"), - [SymbolKind.Event]: localize('Event', "event"), - [SymbolKind.Field]: localize('Field', "field"), - [SymbolKind.File]: localize('File', "file"), - [SymbolKind.Function]: localize('Function', "function"), - [SymbolKind.Interface]: localize('Interface', "interface"), - [SymbolKind.Key]: localize('Key', "key"), - [SymbolKind.Method]: localize('Method', "method"), - [SymbolKind.Module]: localize('Module', "module"), - [SymbolKind.Namespace]: localize('Namespace', "namespace"), - [SymbolKind.Null]: localize('Null', "null"), - [SymbolKind.Number]: localize('Number', "number"), - [SymbolKind.Object]: localize('Object', "object"), - [SymbolKind.Operator]: localize('Operator', "operator"), - [SymbolKind.Package]: localize('Package', "package"), - [SymbolKind.Property]: localize('Property', "property"), - [SymbolKind.String]: localize('String', "string"), - [SymbolKind.Struct]: localize('Struct', "struct"), - [SymbolKind.TypeParameter]: localize('TypeParameter', "type parameter"), - [SymbolKind.Variable]: localize('Variable', "variable"), -}; - -/** - * @internal - */ -export function getAriaLabelForSymbol(symbolName: string, kind: SymbolKind): string { - return localize('symbolAriaLabel', '{0} ({1})', symbolName, symbolKindNames[kind]); -} - -export const enum SymbolTag { - Deprecated = 1, -} - -/** - * @internal - */ -export namespace SymbolKinds { - - const byKind = new Map(); - byKind.set(SymbolKind.File, Codicon.symbolFile); - byKind.set(SymbolKind.Module, Codicon.symbolModule); - byKind.set(SymbolKind.Namespace, Codicon.symbolNamespace); - byKind.set(SymbolKind.Package, Codicon.symbolPackage); - byKind.set(SymbolKind.Class, Codicon.symbolClass); - byKind.set(SymbolKind.Method, Codicon.symbolMethod); - byKind.set(SymbolKind.Property, Codicon.symbolProperty); - byKind.set(SymbolKind.Field, Codicon.symbolField); - byKind.set(SymbolKind.Constructor, Codicon.symbolConstructor); - byKind.set(SymbolKind.Enum, Codicon.symbolEnum); - byKind.set(SymbolKind.Interface, Codicon.symbolInterface); - byKind.set(SymbolKind.Function, Codicon.symbolFunction); - byKind.set(SymbolKind.Variable, Codicon.symbolVariable); - byKind.set(SymbolKind.Constant, Codicon.symbolConstant); - byKind.set(SymbolKind.String, Codicon.symbolString); - byKind.set(SymbolKind.Number, Codicon.symbolNumber); - byKind.set(SymbolKind.Boolean, Codicon.symbolBoolean); - byKind.set(SymbolKind.Array, Codicon.symbolArray); - byKind.set(SymbolKind.Object, Codicon.symbolObject); - byKind.set(SymbolKind.Key, Codicon.symbolKey); - byKind.set(SymbolKind.Null, Codicon.symbolNull); - byKind.set(SymbolKind.EnumMember, Codicon.symbolEnumMember); - byKind.set(SymbolKind.Struct, Codicon.symbolStruct); - byKind.set(SymbolKind.Event, Codicon.symbolEvent); - byKind.set(SymbolKind.Operator, Codicon.symbolOperator); - byKind.set(SymbolKind.TypeParameter, Codicon.symbolTypeParameter); - /** - * @internal - */ - export function toIcon(kind: SymbolKind): ThemeIcon { - let icon = byKind.get(kind); - if (!icon) { - console.info('No codicon found for SymbolKind ' + kind); - icon = Codicon.symbolProperty; - } - return icon; - } - - const byCompletionKind = new Map(); - byCompletionKind.set(SymbolKind.File, CompletionItemKind.File); - byCompletionKind.set(SymbolKind.Module, CompletionItemKind.Module); - byCompletionKind.set(SymbolKind.Namespace, CompletionItemKind.Module); - byCompletionKind.set(SymbolKind.Package, CompletionItemKind.Module); - byCompletionKind.set(SymbolKind.Class, CompletionItemKind.Class); - byCompletionKind.set(SymbolKind.Method, CompletionItemKind.Method); - byCompletionKind.set(SymbolKind.Property, CompletionItemKind.Property); - byCompletionKind.set(SymbolKind.Field, CompletionItemKind.Field); - byCompletionKind.set(SymbolKind.Constructor, CompletionItemKind.Constructor); - byCompletionKind.set(SymbolKind.Enum, CompletionItemKind.Enum); - byCompletionKind.set(SymbolKind.Interface, CompletionItemKind.Interface); - byCompletionKind.set(SymbolKind.Function, CompletionItemKind.Function); - byCompletionKind.set(SymbolKind.Variable, CompletionItemKind.Variable); - byCompletionKind.set(SymbolKind.Constant, CompletionItemKind.Constant); - byCompletionKind.set(SymbolKind.String, CompletionItemKind.Text); - byCompletionKind.set(SymbolKind.Number, CompletionItemKind.Value); - byCompletionKind.set(SymbolKind.Boolean, CompletionItemKind.Value); - byCompletionKind.set(SymbolKind.Array, CompletionItemKind.Value); - byCompletionKind.set(SymbolKind.Object, CompletionItemKind.Value); - byCompletionKind.set(SymbolKind.Key, CompletionItemKind.Keyword); - byCompletionKind.set(SymbolKind.Null, CompletionItemKind.Value); - byCompletionKind.set(SymbolKind.EnumMember, CompletionItemKind.EnumMember); - byCompletionKind.set(SymbolKind.Struct, CompletionItemKind.Struct); - byCompletionKind.set(SymbolKind.Event, CompletionItemKind.Event); - byCompletionKind.set(SymbolKind.Operator, CompletionItemKind.Operator); - byCompletionKind.set(SymbolKind.TypeParameter, CompletionItemKind.TypeParameter); - /** - * @internal - */ - export function toCompletionKind(kind: SymbolKind): CompletionItemKind { - let completionKind = byCompletionKind.get(kind); - if (completionKind === undefined) { - console.info('No completion kind found for SymbolKind ' + kind); - completionKind = CompletionItemKind.File; - } - return completionKind; - } -} - -export interface DocumentSymbol { - name: string; - detail: string; - kind: SymbolKind; - tags: ReadonlyArray; - containerName?: string; - range: IRange; - selectionRange: IRange; - children?: DocumentSymbol[]; -} - -/** - * The document symbol provider interface defines the contract between extensions and - * the [go to symbol](https://code.visualstudio.com/docs/editor/editingevolved#_go-to-symbol)-feature. - */ -export interface DocumentSymbolProvider { - - displayName?: string; - - /** - * Provide symbol information for the given document. - */ - provideDocumentSymbols(model: model.ITextModel, token: CancellationToken): ProviderResult; -} - -export interface TextEdit { - range: IRange; - text: string; - eol?: model.EndOfLineSequence; -} - -/** @internal */ -export abstract class TextEdit { - static asEditOperation(edit: TextEdit): ISingleEditOperation { - return EditOperation.replace(Range.lift(edit.range), edit.text); - } - static isTextEdit(thing: any): thing is TextEdit { - const possibleTextEdit = thing as TextEdit; - return typeof possibleTextEdit.text === 'string' && Range.isIRange(possibleTextEdit.range); - } -} - -/** - * Interface used to format a model - */ -export interface FormattingOptions { - /** - * Size of a tab in spaces. - */ - tabSize: number; - /** - * Prefer spaces over tabs. - */ - insertSpaces: boolean; -} -/** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ -export interface DocumentFormattingEditProvider { - - /** - * @internal - */ - readonly extensionId?: ExtensionIdentifier; - - readonly displayName?: string; - - /** - * Provide formatting edits for a whole document. - */ - provideDocumentFormattingEdits(model: model.ITextModel, options: FormattingOptions, token: CancellationToken): ProviderResult; -} -/** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ -export interface DocumentRangeFormattingEditProvider { - /** - * @internal - */ - readonly extensionId?: ExtensionIdentifier; - - readonly displayName?: string; - - /** - * Provide formatting edits for a range in a document. - * - * The given range is a hint and providers can decide to format a smaller - * or larger range. Often this is done by adjusting the start and end - * of the range to full syntax nodes. - */ - provideDocumentRangeFormattingEdits(model: model.ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): ProviderResult; - - provideDocumentRangesFormattingEdits?(model: model.ITextModel, ranges: Range[], options: FormattingOptions, token: CancellationToken): ProviderResult; -} -/** - * The document formatting provider interface defines the contract between extensions and - * the formatting-feature. - */ -export interface OnTypeFormattingEditProvider { - - - /** - * @internal - */ - readonly extensionId?: ExtensionIdentifier; - - autoFormatTriggerCharacters: string[]; - - /** - * Provide formatting edits after a character has been typed. - * - * The given position and character should hint to the provider - * what range the position to expand to, like find the matching `{` - * when `}` has been entered. - */ - provideOnTypeFormattingEdits(model: model.ITextModel, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): ProviderResult; -} - -/** - * @internal - */ -export interface IInplaceReplaceSupportResult { - value: string; - range: IRange; -} - -/** - * A link inside the editor. - */ -export interface ILink { - range: IRange; - url?: URI | string; - tooltip?: string; -} - -export interface ILinksList { - links: ILink[]; - dispose?(): void; -} -/** - * A provider of links. - */ -export interface LinkProvider { - provideLinks(model: model.ITextModel, token: CancellationToken): ProviderResult; - resolveLink?: (link: ILink, token: CancellationToken) => ProviderResult; -} - -/** - * A color in RGBA format. - */ -export interface IColor { - - /** - * The red component in the range [0-1]. - */ - readonly red: number; - - /** - * The green component in the range [0-1]. - */ - readonly green: number; - - /** - * The blue component in the range [0-1]. - */ - readonly blue: number; - - /** - * The alpha component in the range [0-1]. - */ - readonly alpha: number; -} - -/** - * String representations for a color - */ -export interface IColorPresentation { - /** - * The label of this color presentation. It will be shown on the color - * picker header. By default this is also the text that is inserted when selecting - * this color presentation. - */ - label: string; - /** - * An {@link TextEdit edit} which is applied to a document when selecting - * this presentation for the color. - */ - textEdit?: TextEdit; - /** - * An optional array of additional {@link TextEdit text edits} that are applied when - * selecting this color presentation. - */ - additionalTextEdits?: TextEdit[]; -} - -/** - * A color range is a range in a text model which represents a color. - */ -export interface IColorInformation { - - /** - * The range within the model. - */ - range: IRange; - - /** - * The color represented in this range. - */ - color: IColor; -} - -/** - * A provider of colors for editor models. - */ -export interface DocumentColorProvider { - /** - * Provides the color ranges for a specific model. - */ - provideDocumentColors(model: model.ITextModel, token: CancellationToken): ProviderResult; - /** - * Provide the string representations for a color. - */ - provideColorPresentations(model: model.ITextModel, colorInfo: IColorInformation, token: CancellationToken): ProviderResult; -} - -export interface SelectionRange { - range: IRange; -} - -export interface SelectionRangeProvider { - /** - * Provide ranges that should be selected from the given position. - */ - provideSelectionRanges(model: model.ITextModel, positions: Position[], token: CancellationToken): ProviderResult; -} - -export interface FoldingContext { -} -/** - * A provider of folding ranges for editor models. - */ -export interface FoldingRangeProvider { - - /** - * @internal - */ - readonly id?: string; - - /** - * An optional event to signal that the folding ranges from this provider have changed. - */ - onDidChange?: Event; - - /** - * Provides the folding ranges for a specific model. - */ - provideFoldingRanges(model: model.ITextModel, context: FoldingContext, token: CancellationToken): ProviderResult; -} - -export interface FoldingRange { - - /** - * The one-based start line of the range to fold. The folded area starts after the line's last character. - */ - start: number; - - /** - * The one-based end line of the range to fold. The folded area ends with the line's last character. - */ - end: number; - - /** - * Describes the {@link FoldingRangeKind Kind} of the folding range such as {@link FoldingRangeKind.Comment Comment} or - * {@link FoldingRangeKind.Region Region}. The kind is used to categorize folding ranges and used by commands - * like 'Fold all comments'. See - * {@link FoldingRangeKind} for an enumeration of standardized kinds. - */ - kind?: FoldingRangeKind; -} -export class FoldingRangeKind { - /** - * Kind for folding range representing a comment. The value of the kind is 'comment'. - */ - static readonly Comment = new FoldingRangeKind('comment'); - /** - * Kind for folding range representing a import. The value of the kind is 'imports'. - */ - static readonly Imports = new FoldingRangeKind('imports'); - /** - * Kind for folding range representing regions (for example marked by `#region`, `#endregion`). - * The value of the kind is 'region'. - */ - static readonly Region = new FoldingRangeKind('region'); - - /** - * Returns a {@link FoldingRangeKind} for the given value. - * - * @param value of the kind. - */ - static fromValue(value: string) { - switch (value) { - case 'comment': return FoldingRangeKind.Comment; - case 'imports': return FoldingRangeKind.Imports; - case 'region': return FoldingRangeKind.Region; - } - return new FoldingRangeKind(value); - } - - /** - * Creates a new {@link FoldingRangeKind}. - * - * @param value of the kind. - */ - public constructor(public value: string) { - } -} - - -export interface WorkspaceEditMetadata { - needsConfirmation: boolean; - label: string; - description?: string; - /** - * @internal - */ - iconPath?: ThemeIcon | URI | { light: URI; dark: URI }; -} - -export interface WorkspaceFileEditOptions { - overwrite?: boolean; - ignoreIfNotExists?: boolean; - ignoreIfExists?: boolean; - recursive?: boolean; - copy?: boolean; - folder?: boolean; - skipTrashBin?: boolean; - maxSize?: number; - - /** - * @internal - */ - contents?: Promise; -} - -export interface IWorkspaceFileEdit { - oldResource?: URI; - newResource?: URI; - options?: WorkspaceFileEditOptions; - metadata?: WorkspaceEditMetadata; -} - -export interface IWorkspaceTextEdit { - resource: URI; - textEdit: TextEdit & { insertAsSnippet?: boolean; keepWhitespace?: boolean }; - versionId: number | undefined; - metadata?: WorkspaceEditMetadata; -} - -export interface WorkspaceEdit { - edits: Array; -} - -export interface ICustomEdit { - readonly resource: URI; - readonly metadata?: WorkspaceEditMetadata; - undo(): Promise | void; - redo(): Promise | void; -} - -export interface Rejection { - rejectReason?: string; -} -export interface RenameLocation { - range: IRange; - text: string; -} - -export interface RenameProvider { - provideRenameEdits(model: model.ITextModel, position: Position, newName: string, token: CancellationToken): ProviderResult; - resolveRenameLocation?(model: model.ITextModel, position: Position, token: CancellationToken): ProviderResult; -} - -export enum NewSymbolNameTag { - AIGenerated = 1 -} - -export enum NewSymbolNameTriggerKind { - Invoke = 0, - Automatic = 1, -} - -export interface NewSymbolName { - readonly newSymbolName: string; - readonly tags?: readonly NewSymbolNameTag[]; -} - -export interface NewSymbolNamesProvider { - supportsAutomaticNewSymbolNamesTriggerKind?: Promise; - provideNewSymbolNames(model: model.ITextModel, range: IRange, triggerKind: NewSymbolNameTriggerKind, token: CancellationToken): ProviderResult; -} - -export interface Command { - id: string; - title: string; - tooltip?: string; - arguments?: any[]; -} - -/** - * @internal - */ -export namespace Command { - - /** - * @internal - */ - export function is(obj: any): obj is Command { - if (!obj || typeof obj !== 'object') { - return false; - } - return typeof (obj).id === 'string' && - typeof (obj).title === 'string'; - } -} - -/** - * @internal - */ -export interface CommentThreadTemplate { - controllerHandle: number; - label: string; - acceptInputCommand?: Command; - additionalCommands?: Command[]; - deleteCommand?: Command; -} - -/** - * @internal - */ -export interface CommentInfo { - extensionId?: string; - threads: CommentThread[]; - pendingCommentThreads?: PendingCommentThread[]; - commentingRanges: CommentingRanges; -} - - -/** - * @internal - */ -export interface CommentingRangeResourceHint { - schemes: readonly string[]; -} - -/** - * @internal - */ -export enum CommentThreadCollapsibleState { - /** - * Determines an item is collapsed - */ - Collapsed = 0, - /** - * Determines an item is expanded - */ - Expanded = 1 -} - -/** - * @internal - */ -export enum CommentThreadState { - Unresolved = 0, - Resolved = 1 -} - -/** - * @internal - */ -export enum CommentThreadApplicability { - Current = 0, - Outdated = 1 -} - -/** - * @internal - */ -export interface CommentWidget { - commentThread: CommentThread; - comment?: Comment; - input: string; - onDidChangeInput: Event; -} - -/** - * @internal - */ -export interface CommentInput { - value: string; - uri: URI; -} - -export interface CommentThreadRevealOptions { - preserveFocus: boolean; - focusReply: boolean; -} - -/** - * @internal - */ -export interface CommentThread { - isDocumentCommentThread(): this is CommentThread; - commentThreadHandle: number; - controllerHandle: number; - extensionId?: string; - threadId: string; - resource: string | null; - range: T | undefined; - label: string | undefined; - contextValue: string | undefined; - comments: ReadonlyArray | undefined; - onDidChangeComments: Event; - collapsibleState?: CommentThreadCollapsibleState; - initialCollapsibleState?: CommentThreadCollapsibleState; - onDidChangeInitialCollapsibleState: Event; - state?: CommentThreadState; - applicability?: CommentThreadApplicability; - canReply: boolean; - input?: CommentInput; - onDidChangeInput: Event; - onDidChangeLabel: Event; - onDidChangeCollapsibleState: Event; - onDidChangeState: Event; - onDidChangeCanReply: Event; - isDisposed: boolean; - isTemplate: boolean; -} - -/** - * @internal - */ -export interface AddedCommentThread extends CommentThread { - editorId?: string; -} - -/** - * @internal - */ - -export interface CommentingRanges { - readonly resource: URI; - ranges: IRange[]; - fileComments: boolean; -} - -export interface CommentAuthorInformation { - name: string; - iconPath?: UriComponents; - -} - -/** - * @internal - */ -export interface CommentReaction { - readonly label?: string; - readonly iconPath?: UriComponents; - readonly count?: number; - readonly hasReacted?: boolean; - readonly canEdit?: boolean; - readonly reactors?: readonly string[]; -} - -/** - * @internal - */ -export interface CommentOptions { - /** - * An optional string to show on the comment input box when it's collapsed. - */ - prompt?: string; - - /** - * An optional string to show as placeholder in the comment input box when it's focused. - */ - placeHolder?: string; -} - -/** - * @internal - */ -export enum CommentMode { - Editing = 0, - Preview = 1 -} - -/** - * @internal - */ -export enum CommentState { - Published = 0, - Draft = 1 -} - -/** - * @internal - */ -export interface Comment { - readonly uniqueIdInThread: number; - readonly body: string | IMarkdownString; - readonly userName: string; - readonly userIconPath?: UriComponents; - readonly contextValue?: string; - readonly commentReactions?: CommentReaction[]; - readonly label?: string; - readonly mode?: CommentMode; - readonly timestamp?: string; -} - -export interface PendingCommentThread { - range: IRange | undefined; - uri: URI; - uniqueOwner: string; - isReply: boolean; - comment: PendingComment; -} - -export interface PendingComment { - body: string; - cursor: IPosition; -} - -/** - * @internal - */ -export interface CommentThreadChangedEvent { - /** - * Pending comment threads. - */ - readonly pending: PendingCommentThread[]; - - /** - * Added comment threads. - */ - readonly added: AddedCommentThread[]; - - /** - * Removed comment threads. - */ - readonly removed: CommentThread[]; - - /** - * Changed comment threads. - */ - readonly changed: CommentThread[]; -} - -export interface CodeLens { - range: IRange; - id?: string; - command?: Command; -} - -export interface CodeLensList { - lenses: CodeLens[]; - dispose?(): void; -} - -export interface CodeLensProvider { - onDidChange?: Event; - provideCodeLenses(model: model.ITextModel, token: CancellationToken): ProviderResult; - resolveCodeLens?(model: model.ITextModel, codeLens: CodeLens, token: CancellationToken): ProviderResult; -} - - -export enum InlayHintKind { - Type = 1, - Parameter = 2, -} - -export interface InlayHintLabelPart { - label: string; - tooltip?: string | IMarkdownString; - // collapsible?: boolean; - command?: Command; - location?: Location; -} - -export interface InlayHint { - label: string | InlayHintLabelPart[]; - tooltip?: string | IMarkdownString; - textEdits?: TextEdit[]; - position: IPosition; - kind?: InlayHintKind; - paddingLeft?: boolean; - paddingRight?: boolean; -} - -export interface InlayHintList { - hints: InlayHint[]; - dispose(): void; -} - -export interface InlayHintsProvider { - displayName?: string; - onDidChangeInlayHints?: Event; - provideInlayHints(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; - resolveInlayHint?(hint: InlayHint, token: CancellationToken): ProviderResult; -} - -export interface SemanticTokensLegend { - readonly tokenTypes: string[]; - readonly tokenModifiers: string[]; -} - -export interface SemanticTokens { - readonly resultId?: string; - readonly data: Uint32Array; -} - -export interface SemanticTokensEdit { - readonly start: number; - readonly deleteCount: number; - readonly data?: Uint32Array; -} - -export interface SemanticTokensEdits { - readonly resultId?: string; - readonly edits: SemanticTokensEdit[]; -} - -export interface DocumentSemanticTokensProvider { - onDidChange?: Event; - getLegend(): SemanticTokensLegend; - provideDocumentSemanticTokens(model: model.ITextModel, lastResultId: string | null, token: CancellationToken): ProviderResult; - releaseDocumentSemanticTokens(resultId: string | undefined): void; -} - -export interface DocumentRangeSemanticTokensProvider { - getLegend(): SemanticTokensLegend; - provideDocumentRangeSemanticTokens(model: model.ITextModel, range: Range, token: CancellationToken): ProviderResult; -} - -/** - * @internal - */ -export interface ITokenizationSupportChangedEvent { - changedLanguages: string[]; - changedColorMap: boolean; -} - -/** - * @internal - */ -export interface ILazyTokenizationSupport { - get tokenizationSupport(): Promise; -} - -/** - * @internal - */ -export class LazyTokenizationSupport implements IDisposable, ILazyTokenizationSupport { - private _tokenizationSupport: Promise | null = null; - - constructor(private readonly createSupport: () => Promise) { - } - - dispose(): void { - if (this._tokenizationSupport) { - this._tokenizationSupport.then((support) => { - if (support) { - support.dispose(); - } - }); - } - } - - get tokenizationSupport(): Promise { - if (!this._tokenizationSupport) { - this._tokenizationSupport = this.createSupport(); - } - return this._tokenizationSupport; - } -} - -/** - * @internal - */ -export interface ITokenizationRegistry { - - /** - * An event triggered when: - * - a tokenization support is registered, unregistered or changed. - * - the color map is changed. - */ - onDidChange: Event; - - /** - * Fire a change event for a language. - * This is useful for languages that embed other languages. - */ - handleChange(languageIds: string[]): void; - - /** - * Register a tokenization support. - */ - register(languageId: string, support: TSupport): IDisposable; - - /** - * Register a tokenization support factory. - */ - registerFactory(languageId: string, factory: ILazyTokenizationSupport): IDisposable; - - /** - * Get or create the tokenization support for a language. - * Returns `null` if not found. - */ - getOrCreate(languageId: string): Promise; - - /** - * Get the tokenization support for a language. - * Returns `null` if not found. - */ - get(languageId: string): TSupport | null; - - /** - * Returns false if a factory is still pending. - */ - isResolved(languageId: string): boolean; - - /** - * Set the new color map that all tokens will use in their ColorId binary encoded bits for foreground and background. - */ - setColorMap(colorMap: Color[]): void; - - getColorMap(): Color[] | null; - - getDefaultBackground(): Color | null; -} - -/** - * @internal - */ -export const TokenizationRegistry: ITokenizationRegistry = new TokenizationRegistryImpl(); - -/** - * @internal - */ -export const TreeSitterTokenizationRegistry: ITokenizationRegistry = new TokenizationRegistryImpl(); - -/** - * @internal - */ -export enum ExternalUriOpenerPriority { - None = 0, - Option = 1, - Default = 2, - Preferred = 3, -} - -/** - * @internal - */ -export type DropYieldTo = { readonly kind: HierarchicalKind } | { readonly mimeType: string }; - -/** - * @internal - */ -export interface DocumentDropEdit { - readonly title: string; - readonly kind: HierarchicalKind | undefined; - readonly handledMimeType?: string; - readonly yieldTo?: readonly DropYieldTo[]; - insertText: string | { readonly snippet: string }; - additionalEdit?: WorkspaceEdit; -} - -/** - * @internal - */ -export interface DocumentDropEditsSession { - edits: readonly DocumentDropEdit[]; - dispose(): void; -} - -/** - * @internal - */ -export interface DocumentDropEditProvider { - readonly id?: string; - readonly dropMimeTypes?: readonly string[]; - readonly providedDropEditKinds?: readonly HierarchicalKind[]; - - provideDocumentDropEdits(model: model.ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): ProviderResult; - resolveDocumentDropEdit?(edit: DocumentDropEdit, token: CancellationToken): Promise; -} - -export interface IInlineEdit { - text: string; - range: IRange; - showRange?: IRange; - accepted?: Command; - rejected?: Command; - shown?: Command; - commands?: Command[]; - action?: Command; -} - -export interface IInlineEditContext { - triggerKind: InlineEditTriggerKind; - - /** - * @experimental - * @internal - */ - requestUuid?: string; -} - -export enum InlineEditTriggerKind { - Invoke = 0, - Automatic = 1, -} - -export interface InlineEditProvider { - displayName?: string; - provideInlineEdit(model: model.ITextModel, context: IInlineEditContext, token: CancellationToken): ProviderResult; - freeInlineEdit(edit: T): void; -} +export * from './language/languages.js'; diff --git a/src/vs/editor/common/languages/autoIndent.ts b/src/vs/editor/common/languages/autoIndent.ts index d2083853ead..3329d77edd3 100644 --- a/src/vs/editor/common/languages/autoIndent.ts +++ b/src/vs/editor/common/languages/autoIndent.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from '../../../base/common/strings.js'; -import { Range } from '../core/range.js'; -import { ITextModel } from '../model.js'; +import { Range } from '../language/core/range.js'; +import { ITextModel } from '../language/model.js'; import { IndentAction } from './languageConfiguration.js'; import { IndentConsts } from './supports/indentRules.js'; import { EditorAutoIndentStrategy } from '../config/editorOptions.js'; import { ILanguageConfigurationService } from './languageConfigurationRegistry.js'; -import { IViewLineTokens } from '../tokens/lineTokens.js'; +import { IViewLineTokens } from '../language/tokens/lineTokens.js'; import { IndentationContextProcessor, isLanguageDifferentFromLineStart, ProcessedIndentRulesSupport } from './supports/indentationLineProcessor.js'; import { CursorConfiguration } from '../cursorCommon.js'; diff --git a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts index 74bb0757905..c086baf55ad 100644 --- a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts +++ b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Color, HSLA } from '../../../base/common/color.js'; -import { IPosition } from '../core/position.js'; -import { IRange } from '../core/range.js'; -import { IColor, IColorInformation } from '../languages.js'; +import { IPosition } from '../language/core/position.js'; +import { IRange } from '../language/core/range.js'; +import { IColor, IColorInformation } from '../language/languages.js'; export interface IDocumentColorComputerTarget { getValue(): string; diff --git a/src/vs/editor/common/languages/enterAction.ts b/src/vs/editor/common/languages/enterAction.ts index 454cc1e5b53..30b1a135803 100644 --- a/src/vs/editor/common/languages/enterAction.ts +++ b/src/vs/editor/common/languages/enterAction.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../core/range.js'; -import { ITextModel } from '../model.js'; +import { Range } from '../language/core/range.js'; +import { ITextModel } from '../language/model.js'; import { IndentAction, CompleteEnterAction } from './languageConfiguration.js'; import { EditorAutoIndentStrategy } from '../config/editorOptions.js'; import { getIndentationAtPosition, ILanguageConfigurationService } from './languageConfigurationRegistry.js'; diff --git a/src/vs/editor/common/languages/highlights/css.scm b/src/vs/editor/common/languages/highlights/css.scm deleted file mode 100644 index 6fb6e5c7e45..00000000000 --- a/src/vs/editor/common/languages/highlights/css.scm +++ /dev/null @@ -1,90 +0,0 @@ -; Order matters! Place lower precedence first. - -[ - "{" - "}" - "(" - ")" - "[" - "]" -] @punctuation.css - -[ - "*=" -] @keyword.operator.css - -(comment) @comment.block.css - -; Selectors - -(selectors) @meta.selector.css - -(class_selector) @entity.other.attribute-name.class.css - -(tag_name) @entity.name.tag.css - -(pseudo_class_selector) @entity.other.attribute-name.pseudo-class.css - -(attribute_name) @entity.other.attribute-name.css - -; @ Rules - -[ - ("@import") - ("@charset") - ("@namespace") - ("@media") - ("@supports") - ("@keyframes") -] @keyword.control.at-rule.css - -(keyword_query) @support.constant.media.css - -(keyframes_name) @variable.parameter.keyframe-list.css - -; Functions - -(function_name) @support.function.css - -; Properties - -(property_name) @support.type.property-name.css - -; Other values - -(plain_value) @support.constant.property-value.css - -; Strings - -((string_value) @string.quoted.single.css - (#match? @string.quoted.single.css "^'.*'$")) - -((string_value) @string.quoted.double.css - (#match? @string.quoted.double.css "^\".*\"$")) - -; Numbers - -([ - (integer_value) - (float_value) -]) @constant.numeric.css - -(unit) @keyword.other.unit.css - -; Special values - -(declaration - ((property_name) @support.type.property-name.css - (#eq? @support.type.property-name.css "font")) - (plain_value) @support.constant.font-name.css) - -((color_value) @constant.other.color.rgb-value.hex.css - (#match? @constant.other.color.rgb-value.hex.css "^#.*")) - -; Special Functions - -(call_expression - ((function_name) @support.function.url.css - (#eq? @support.function.url.css "url")) - (arguments - (plain_value) @variable.parameter.url.css)) diff --git a/src/vs/editor/common/languages/highlights/ini.scm b/src/vs/editor/common/languages/highlights/ini.scm deleted file mode 100644 index 2d68e7c76dd..00000000000 --- a/src/vs/editor/common/languages/highlights/ini.scm +++ /dev/null @@ -1,12 +0,0 @@ -; Order matters! Place lower precedence first. - -(section_name (text) @entity.name.section.group-title) - -(setting_name) @keyword.other.definition - -(setting ("=") @punctuation.separator.key-value) - -(comment) @comment.line.semicolon - -((setting_value) @string.quoted.double - (#match? @string.quoted.double "^\".*\"")) diff --git a/src/vs/editor/common/languages/highlights/regex.scm b/src/vs/editor/common/languages/highlights/regex.scm deleted file mode 100644 index 27b29813943..00000000000 --- a/src/vs/editor/common/languages/highlights/regex.scm +++ /dev/null @@ -1,96 +0,0 @@ -; Order matters! Place lower precedence first. -[ - "?" - "=" - "!" -] @keyword.operator.regexp - -[ - "(" - ")" -] @punctuation.definition.group.regexp - -[ - ">" - "{" - "}" -] @punctuation.regexp - -[ - "[" - "]" -] @punctuation.definition.character-class.regexp - -[ - "(?:" - "(?<" -] @punctuation.definition.group.assertion.regexp - -(lookaround_assertion ("!") @punctuation.definition.group.assertion.regexp) - -(named_capturing_group) @punctuation.definition.group.regexp - -(group_name) @variable.other.regexp - -[ - (control_letter_escape) - (non_boundary_assertion) -] @string.escape.regexp - -[ - (start_assertion) - (end_assertion) - (boundary_assertion) -] @keyword.control.anchor.regexp - -[ - ((identity_escape) @internal.regexp (#match? @internal.regexp "\\[^ux]")) -] @constant.character.escape.regexp - -( - ((identity_escape) @internal.regexp (#eq? @internal.regexp "\\u")) - . - (pattern_character) @constant.character.numeric.regexp - . - (pattern_character) @constant.character.numeric.regexp - . - (pattern_character) @constant.character.numeric.regexp - . - (pattern_character) @constant.character.numeric.regexp -) @constant.character.numeric.regexp - -( - ((identity_escape) @internal.regexp (#eq? @internal.regexp "\\x")) - . - (pattern_character) @constant.character.numeric.regexp - . - (pattern_character) @constant.character.numeric.regexp -) @constant.character.numeric.regexp - -[ - (character_class_escape) - (control_escape) -] @constant.other.character-class.regexp - -("|") @keyword.operator.or.regexp - -[ - "*" - "+" -] @keyword.operator.quantifier.regexp - -(count_quantifier) @keyword.operator.quantifier.regexp - -[ - (lazy) -] @keyword.operator.quantifier.regexp - -(optional ("?") @keyword.operator.quantifier.regexp) - -(character_class - [ - "^" @keyword.operator.negation.regexp - (class_range "-" @constant.other.character-class.range.regexp) - ]) - -(class_character) @constant.character-class.regexp diff --git a/src/vs/editor/common/languages/highlights/typescript.scm b/src/vs/editor/common/languages/highlights/typescript.scm deleted file mode 100644 index 860ef7027a1..00000000000 --- a/src/vs/editor/common/languages/highlights/typescript.scm +++ /dev/null @@ -1,488 +0,0 @@ -; Order matters! Place lower precedence first. - -; Variables - -(identifier) @variable.ts - -(_ - object: (identifier) @variable.other.object.ts) - -; Literals - -(this) @variable.language.this.ts -(super) @variable.language.super.ts - -(comment) @comment.ts - -; TODO: This doesn't seem to be working -(escape_sequence) @constant.character.escape.ts - -((string) @string.quoted.single.ts - (#match? @string.quoted.single.ts "^'.*'$")) - -((string) @string.quoted.double.ts - (#match? @string.quoted.double.ts "^\".*\"$")) - -([ - (template_string) - (template_literal_type) -] @string.template.ts) - -(string . - ([ - "\"" - "'" - ]) @punctuation.definition.string.begin.ts) - -(string - ([ - "\"" - "'" - ]) @punctuation.definition.string.end.ts .) - -(template_string . ("`") @punctuation.definition.string.template.begin.ts) - -(template_string ("`") @punctuation.definition.string.template.end.ts .) - -; NOTE: the typescript grammar doesn't break regex into nice parts so as to capture parts of it separately -(regex) @string.regexp.ts -(number) @constant.numeric.ts - -; Properties - -(member_expression - object: (this) - property: (property_identifier) @variable.ts) - -(member_expression - property: (property_identifier) @variable.other.constant.ts - (#match? @variable.other.constant.ts "^[A-Z][A-Z_]+$")) - -[ - (property_identifier) - (shorthand_property_identifier) - (shorthand_property_identifier_pattern)] @variable.ts - -; Function and method definitions - -(function_expression - name: (identifier) @entity.name.function.ts) -(function_signature - name: (identifier) @entity.name.function.ts) -(function_declaration - name: (identifier) @entity.name.function.ts) -(method_definition - name: (property_identifier) @meta.definition.method.ts @entity.name.function.ts - (#not-eq? @entity.name.function.ts "constructor")) -(method_definition - name: (property_identifier) @meta.definition.method.ts @storage.type.ts - (#eq? @storage.type.ts "constructor")) -(method_signature - name: (property_identifier) @meta.definition.method.ts @entity.name.function.ts) -(generator_function_declaration - "*" @keyword.generator.asterisk.ts) -(generator_function_declaration - name: (identifier) @meta.definition.function.ts @entity.name.function.ts) - -(pair - key: (property_identifier) @entity.name.function.ts - value: [(function_expression) (arrow_function)]) - -(assignment_expression - left: (member_expression - property: (property_identifier) @entity.name.function.ts) - right: [(function_expression) (arrow_function)]) - -(variable_declarator - name: (identifier) @entity.name.function.ts - value: [(function_expression) (arrow_function)]) - -(assignment_expression - left: (identifier) @entity.name.function.ts - right: [(function_expression) (arrow_function)]) - -(required_parameter - (identifier) @variable.parameter.ts) - -(required_parameter - (_ - ([ - (identifier) - (shorthand_property_identifier_pattern) - ]) @variable.parameter.ts)) - -(optional_parameter - (identifier) @variable.parameter.ts) - -(optional_parameter - (_ - ([ - (identifier) - (shorthand_property_identifier_pattern) - ]) @variable.parameter.ts)) - -(catch_clause - parameter: (identifier) @variable.parameter.ts) - -(index_signature - name: (identifier) @variable.parameter.ts) - -(arrow_function - parameter: (identifier) @variable.parameter.ts) - -; Function and method calls - -(call_expression - function: (identifier) @entity.name.function.ts) - -(call_expression - function: (member_expression - object: (identifier) @support.class.promise.ts) - (#eq? @support.class.promise.ts "Promise")) - -(call_expression - function: (member_expression - property: (property_identifier) @entity.name.function.ts)) - -(new_expression) @new.expr.ts - -(new_expression - constructor: (identifier) @entity.name.function.ts) - - -; Special identifiers - -(predefined_type) @support.type.ts -(predefined_type (["string" "boolean" "number" "any" "unknown" "never" "void"])) @support.type.primitive.ts - -(_ - (type_identifier) @entity.name.type.ts) - -(type_annotation - ([ - (type_identifier) - (nested_type_identifier) - ]) @meta.type.annotation.ts @entity.name.type.ts) - -(class_declaration - (type_identifier) @entity.name.type.class.ts) - -(type_alias_declaration - (type_identifier) @entity.name.type.alias.ts) -(type_alias_declaration - value: (_ - (type_identifier) @entity.name.type.ts)) - -(interface_declaration - (type_identifier) @entity.name.type.interface.ts) - -(internal_module - name: (identifier) @entity.name.type.ts) - -(enum_declaration - name: (identifier) @entity.name.type.enum.ts) - -( - [ - (_ name: (identifier)) - (shorthand_property_identifier) - (shorthand_property_identifier_pattern) - ] @variable.other.constant.ts - (#match? @variable.other.constant.ts "^[A-Z][A-Z_]+$")) - -(extends_clause - value: (identifier) @entity.other.inherited-class.ts) - -(extends_type_clause - type: (type_identifier) @entity.other.inherited-class.ts) - -(implements_clause - (type_identifier) @entity.other.inherited-class.ts) - -; Tokens - -[ - ";" - "?." - "." - "," - ":" - "?" -] @punctuation.delimiter.ts - -[ - "!" - "~" - "===" - "!==" - "&&" - "||" - "??" -] @keyword.operator.logical.ts - -(binary_expression ([ - "-" - "+" - "*" - "/" - "%" - "^" -]) @keyword.operator.arithmetic.ts) - -(binary_expression ([ - "<" - "<=" - ">" - ">=" -]) @keyword.operator.relational.ts) - -(unary_expression ([ - "-" -]) @keyword.operator.arithmetic.ts) - -[ - "=" -] @keyword.operator.assignment.ts - -(augmented_assignment_expression ([ - "-=" - "+=" - "*=" - "/=" - "%=" - "^=" - "&=" - "|=" - "&&=" - "||=" - "??=" -]) @keyword.operator.assignment.compound.ts) - -[ - "++" -] @keyword.operator.increment.ts - -[ - "--" -] @keyword.operator.decrement.ts - -[ - "**" - "**=" - "<<" - "<<=" - "==" - "!=" - ">>" - ">>=" - ">>>" - ">>>=" - "~" - "&" - "|" -] @keyword.operator.ts - -(union_type - ("|") @keyword.operator.type.ts) - -(intersection_type - ("&") @keyword.operator.type.ts) - -(type_annotation - (":") @keyword.operator.type.annotation.ts) - -(index_signature - (":") @keyword.operator.type.annotation.ts) - -(type_predicate_annotation - (":") @keyword.operator.type.annotation.ts) - -(conditional_type - ([ - "?" - ":" - ]) @keyword.operator.ternary.ts) - -[ - "{" - "}" - "(" - ")" - "[" - "]" -] @punctuation.ts - -(template_substitution - "${" @punctuation.definition.template-expression.begin.ts - "}" @punctuation.definition.template-expression.end.ts) - -(template_type - "${" @punctuation.definition.template-expression.begin.ts - "}" @punctuation.definition.template-expression.end.ts) - -(type_arguments - "<" @punctuation.definition.typeparameters.begin.ts - ">" @punctuation.definition.typeparameters.end.ts) - -(type_parameters - "<" @punctuation.definition.typeparameters.begin.ts - ">" @punctuation.definition.typeparameters.end.ts) - -; Keywords - -("typeof") @keyword.operator.expression.typeof.ts - -(binary_expression "instanceof" @keyword.operator.expression.instanceof.ts) - -("of") @keyword.operator.expression.of.ts - -("is") @keyword.operator.expression.is.ts - -[ - "delete" - "in" - "infer" - "keyof" -] @keyword.operator.expression.ts - -[ - "as" - "await" - "break" - "case" - "catch" - "continue" - "default" - "do" - "else" - "export" - "finally" - "for" - "from" - "if" - "import" - "require" - "return" - "satisfies" - "switch" - "throw" - "try" - "while" - "yield" -] @keyword.control.ts - -[ - "abstract" - "async" - "declare" - "extends" - "implements" - "override" - "private" - "protected" - "public" - "readonly" - "static" -] @storage.modifier.ts - -[ - "=>" - "class" - "const" - "enum" - "function" - "get" - "interface" - "let" - "namespace" - "set" - "var" -] @storage.type.ts - -("type") @storage.type.type.ts - -[ - "module" -] @storage.type.namespace.ts - -[ - "debugger" - "target" - "with" -] @keyword.ts - -(regex_flags) @keyword.ts - -(unary_expression - "void" @keyword.operator.expression.void.ts) - -[ - "new" -] @keyword.operator.new.ts - -(public_field_definition - ("?") @keyword.operator.optional.ts) - -(property_signature - ("?") @keyword.operator.optional.ts) - -(method_signature - ("?") @keyword.operator.optional.ts) - -(optional_parameter - ([ - "?" - ":" - ]) @keyword.operator.optional.ts) - -(ternary_expression - ([ - "?" - ":" - ]) @keyword.operator.ternary.ts) - -(optional_chain - ("?.") @punctuation.accessor.optional.ts) - -(rest_pattern - ("...") @keyword.operator.rest.ts) -(rest_type - ("...") @keyword.operator.rest.ts) - -(spread_element - ("...") @keyword.operator.spread.ts) - -; Language constants - -[ - (null) -] @constant.language.null.ts - -[ - (undefined) -] @constant.language.undefined.ts - -((identifier) @constant.language.nan.ts - (#eq? @constant.language.nan.ts "NaN")) - -((identifier) @constant.language.infinity.ts - (#eq? @constant.language.infinity.ts "Infinity")) - -[ - (true) -] @constant.language.boolean.true.ts - -[ - (false) -] @constant.language.boolean.false.ts - -(literal_type - [ - (null) - (undefined) - (true) - (false) - ] @support.type.builtin.ts) - -(namespace_import - "*" @constant.language.ts) diff --git a/src/vs/editor/common/languages/injections/typescript.scm b/src/vs/editor/common/languages/injections/typescript.scm deleted file mode 100644 index 5794b7c0741..00000000000 --- a/src/vs/editor/common/languages/injections/typescript.scm +++ /dev/null @@ -1,2 +0,0 @@ -((regex) @injection.content - (#set! injection.language "regex")) diff --git a/src/vs/editor/common/languages/languageConfiguration.ts b/src/vs/editor/common/languages/languageConfiguration.ts index b6aa902f78a..2b038c9da80 100644 --- a/src/vs/editor/common/languages/languageConfiguration.ts +++ b/src/vs/editor/common/languages/languageConfiguration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../base/common/charCode.js'; -import { StandardTokenType } from '../encodedTokenAttributes.js'; +import { StandardTokenType } from '../language/encodedTokenAttributes.js'; import { ScopedLineTokens } from './supports.js'; /** diff --git a/src/vs/editor/common/languages/languageConfigurationRegistry.ts b/src/vs/editor/common/languages/languageConfigurationRegistry.ts index f42b06d0b74..2e0c24e2c3d 100644 --- a/src/vs/editor/common/languages/languageConfigurationRegistry.ts +++ b/src/vs/editor/common/languages/languageConfigurationRegistry.ts @@ -6,8 +6,8 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, IDisposable, markAsSingleton, toDisposable } from '../../../base/common/lifecycle.js'; import * as strings from '../../../base/common/strings.js'; -import { ITextModel } from '../model.js'; -import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from '../core/wordHelper.js'; +import { ITextModel } from '../language/model.js'; +import { DEFAULT_WORD_REGEXP, ensureValidWordDefinition } from '../language/core/wordHelper.js'; import { EnterAction, FoldingRules, IAutoClosingPair, IndentationRule, LanguageConfiguration, AutoClosingPairs, CharacterPair, ExplicitLanguageConfiguration } from './languageConfiguration.js'; import { CharacterPairSupport } from './supports/characterPair.js'; import { BracketElectricCharacterSupport } from './supports/electricCharacter.js'; @@ -17,7 +17,7 @@ import { RichEditBrackets } from './supports/richEditBrackets.js'; import { EditorAutoIndentStrategy } from '../config/editorOptions.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; -import { ILanguageService } from './language.js'; +import { ILanguageService } from '../language/language.js'; import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; import { PLAINTEXT_LANGUAGE_ID } from './modesRegistry.js'; import { LanguageBracketsConfiguration } from './supports/languageBracketsConfiguration.js'; diff --git a/src/vs/editor/common/languages/linkComputer.ts b/src/vs/editor/common/languages/linkComputer.ts index 2c954d7f374..b2b0a86cc15 100644 --- a/src/vs/editor/common/languages/linkComputer.ts +++ b/src/vs/editor/common/languages/linkComputer.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../base/common/charCode.js'; -import { CharacterClassifier } from '../core/characterClassifier.js'; -import { ILink } from '../languages.js'; +import { CharacterClassifier } from '../language/core/characterClassifier.js'; +import { ILink } from '../language/languages.js'; export interface ILinkComputerTarget { getLineCount(): number; diff --git a/src/vs/editor/common/languages/modesRegistry.ts b/src/vs/editor/common/languages/modesRegistry.ts index 28fdbf80051..6681c8ab9c7 100644 --- a/src/vs/editor/common/languages/modesRegistry.ts +++ b/src/vs/editor/common/languages/modesRegistry.ts @@ -5,7 +5,7 @@ import * as nls from '../../../nls.js'; import { Emitter, Event } from '../../../base/common/event.js'; -import { ILanguageExtensionPoint } from './language.js'; +import { ILanguageExtensionPoint } from '../language/language.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { Mimes } from '../../../base/common/mime.js'; diff --git a/src/vs/editor/common/languages/nullTokenize.ts b/src/vs/editor/common/languages/nullTokenize.ts index 8966ab8b734..b480ff3bdc2 100644 --- a/src/vs/editor/common/languages/nullTokenize.ts +++ b/src/vs/editor/common/languages/nullTokenize.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Token, TokenizationResult, EncodedTokenizationResult, IState } from '../languages.js'; -import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from '../encodedTokenAttributes.js'; +import { Token, TokenizationResult, EncodedTokenizationResult, IState } from '../language/languages.js'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from '../language/encodedTokenAttributes.js'; export const NullState: IState = new class implements IState { public clone(): IState { diff --git a/src/vs/editor/common/languages/supports.ts b/src/vs/editor/common/languages/supports.ts index 10e352b96d2..00dab02e879 100644 --- a/src/vs/editor/common/languages/supports.ts +++ b/src/vs/editor/common/languages/supports.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewLineTokens, LineTokens } from '../tokens/lineTokens.js'; -import { StandardTokenType } from '../encodedTokenAttributes.js'; -import { ILanguageIdCodec } from '../languages.js'; +import { IViewLineTokens, LineTokens } from '../language/tokens/lineTokens.js'; +import { StandardTokenType } from '../language/encodedTokenAttributes.js'; +import { ILanguageIdCodec } from '../language/languages.js'; export function createScopedLineTokens(context: LineTokens, offset: number): ScopedLineTokens { const tokenCount = context.getCount(); diff --git a/src/vs/editor/common/languages/supports/indentationLineProcessor.ts b/src/vs/editor/common/languages/supports/indentationLineProcessor.ts index 762c13e4eb4..2ae962c51a7 100644 --- a/src/vs/editor/common/languages/supports/indentationLineProcessor.ts +++ b/src/vs/editor/common/languages/supports/indentationLineProcessor.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from '../../../../base/common/strings.js'; -import { Range } from '../../core/range.js'; -import { ITextModel } from '../../model.js'; +import { Range } from '../../language/core/range.js'; +import { ITextModel } from '../../language/model.js'; import { ILanguageConfigurationService } from '../languageConfigurationRegistry.js'; import { createScopedLineTokens, ScopedLineTokens } from '../supports.js'; import { IVirtualModel } from '../autoIndent.js'; -import { IViewLineTokens, LineTokens } from '../../tokens/lineTokens.js'; +import { IViewLineTokens, LineTokens } from '../../language/tokens/lineTokens.js'; import { IndentRulesSupport } from './indentRules.js'; -import { StandardTokenType } from '../../encodedTokenAttributes.js'; -import { Position } from '../../core/position.js'; +import { StandardTokenType } from '../../language/encodedTokenAttributes.js'; +import { Position } from '../../language/core/position.js'; /** * This class is a wrapper class around {@link IndentRulesSupport}. diff --git a/src/vs/editor/common/languages/supports/inplaceReplaceSupport.ts b/src/vs/editor/common/languages/supports/inplaceReplaceSupport.ts index f4443b24b94..51af71e5f00 100644 --- a/src/vs/editor/common/languages/supports/inplaceReplaceSupport.ts +++ b/src/vs/editor/common/languages/supports/inplaceReplaceSupport.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange } from '../../core/range.js'; -import { IInplaceReplaceSupportResult } from '../../languages.js'; +import { IRange } from '../../language/core/range.js'; +import { IInplaceReplaceSupportResult } from '../../language/languages.js'; export class BasicInplaceReplace { diff --git a/src/vs/editor/common/languages/supports/richEditBrackets.ts b/src/vs/editor/common/languages/supports/richEditBrackets.ts index c6436e60ef4..bfc9b94d704 100644 --- a/src/vs/editor/common/languages/supports/richEditBrackets.ts +++ b/src/vs/editor/common/languages/supports/richEditBrackets.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from '../../../../base/common/strings.js'; -import * as stringBuilder from '../../core/stringBuilder.js'; -import { Range } from '../../core/range.js'; +import * as stringBuilder from '../../language/core/stringBuilder.js'; +import { Range } from '../../language/core/range.js'; import { CharacterPair } from '../languageConfiguration.js'; interface InternalBracket { diff --git a/src/vs/editor/common/languages/supports/tokenization.ts b/src/vs/editor/common/languages/supports/tokenization.ts index f6322a09dda..8b9919823e7 100644 --- a/src/vs/editor/common/languages/supports/tokenization.ts +++ b/src/vs/editor/common/languages/supports/tokenization.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from '../../../../base/common/color.js'; -import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from '../../encodedTokenAttributes.js'; +import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts } from '../../language/encodedTokenAttributes.js'; export interface ITokenThemeRule { token: string; diff --git a/src/vs/editor/common/languages/textToHtmlTokenizer.ts b/src/vs/editor/common/languages/textToHtmlTokenizer.ts index b0745eda9f8..ab336355aad 100644 --- a/src/vs/editor/common/languages/textToHtmlTokenizer.ts +++ b/src/vs/editor/common/languages/textToHtmlTokenizer.ts @@ -5,11 +5,11 @@ import { CharCode } from '../../../base/common/charCode.js'; import * as strings from '../../../base/common/strings.js'; -import { IViewLineTokens, LineTokens } from '../tokens/lineTokens.js'; -import { ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from '../languages.js'; -import { LanguageId } from '../encodedTokenAttributes.js'; +import { IViewLineTokens, LineTokens } from '../language/tokens/lineTokens.js'; +import { ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from '../language/languages.js'; +import { LanguageId } from '../language/encodedTokenAttributes.js'; import { NullState, nullTokenizeEncoded } from './nullTokenize.js'; -import { ILanguageService } from './language.js'; +import { ILanguageService } from '../language/language.js'; export type IReducedTokenizationSupport = Omit; diff --git a/src/vs/editor/common/model.ts b/src/vs/editor/common/model.ts index 146c9830e04..98f31fd72c9 100644 --- a/src/vs/editor/common/model.ts +++ b/src/vs/editor/common/model.ts @@ -3,1521 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../base/common/event.js'; -import { IMarkdownString } from '../../base/common/htmlContent.js'; -import { IDisposable } from '../../base/common/lifecycle.js'; -import { equals } from '../../base/common/objects.js'; -import { ThemeColor } from '../../base/common/themables.js'; -import { URI } from '../../base/common/uri.js'; -import { ISingleEditOperation } from './core/editOperation.js'; -import { IPosition, Position } from './core/position.js'; -import { IRange, Range } from './core/range.js'; -import { Selection } from './core/selection.js'; -import { TextChange } from './core/textChange.js'; -import { WordCharacterClassifier } from './core/wordCharacterClassifier.js'; -import { IWordAtPosition } from './core/wordHelper.js'; -import { FormattingOptions } from './languages.js'; -import { ILanguageSelection } from './languages/language.js'; -import { IBracketPairsTextModelPart } from './textModelBracketPairs.js'; -import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, InternalModelContentChangeEvent, ModelInjectedTextChangedEvent } from './textModelEvents.js'; -import { IGuidesTextModelPart } from './textModelGuides.js'; -import { ITokenizationTextModelPart } from './tokenizationTextModelPart.js'; -import { UndoRedoGroup } from '../../platform/undoRedo/common/undoRedo.js'; -import { TokenArray } from './tokens/tokenArray.js'; -import { IEditorModel } from './editorCommon.js'; - -/** - * Vertical Lane in the overview ruler of the editor. - */ -export enum OverviewRulerLane { - Left = 1, - Center = 2, - Right = 4, - Full = 7 -} - -/** - * Vertical Lane in the glyph margin of the editor. - */ -export enum GlyphMarginLane { - Left = 1, - Center = 2, - Right = 3, -} - -export interface IGlyphMarginLanesModel { - /** - * The number of lanes that should be rendered in the editor. - */ - readonly requiredLanes: number; - - /** - * Gets the lanes that should be rendered starting at a given line number. - */ - getLanesAtLine(lineNumber: number): GlyphMarginLane[]; - - /** - * Resets the model and ensures it can contain at least `maxLine` lines. - */ - reset(maxLine: number): void; - - /** - * Registers that a lane should be visible at the Range in the model. - * @param persist - if true, notes that the lane should always be visible, - * even on lines where there's no specific request for that lane. - */ - push(lane: GlyphMarginLane, range: Range, persist?: boolean): void; -} - -/** - * Position in the minimap to render the decoration. - */ -export const enum MinimapPosition { - Inline = 1, - Gutter = 2 -} - -/** - * Section header style. - */ -export const enum MinimapSectionHeaderStyle { - Normal = 1, - Underlined = 2 -} - -export interface IDecorationOptions { - /** - * CSS color to render. - * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry - */ - color: string | ThemeColor | undefined; - /** - * CSS color to render. - * e.g.: rgba(100, 100, 100, 0.5) or a color from the color registry - */ - darkColor?: string | ThemeColor; -} - -export interface IModelDecorationGlyphMarginOptions { - /** - * The position in the glyph margin. - */ - position: GlyphMarginLane; - - /** - * Whether the glyph margin lane in {@link position} should be rendered even - * outside of this decoration's range. - */ - persistLane?: boolean; -} - -/** - * Options for rendering a model decoration in the overview ruler. - */ -export interface IModelDecorationOverviewRulerOptions extends IDecorationOptions { - /** - * The position in the overview ruler. - */ - position: OverviewRulerLane; -} - -/** - * Options for rendering a model decoration in the minimap. - */ -export interface IModelDecorationMinimapOptions extends IDecorationOptions { - /** - * The position in the minimap. - */ - position: MinimapPosition; - /** - * If the decoration is for a section header, which header style. - */ - sectionHeaderStyle?: MinimapSectionHeaderStyle | null; - /** - * If the decoration is for a section header, the header text. - */ - sectionHeaderText?: string | null; -} - -/** - * Options for a model decoration. - */ -export interface IModelDecorationOptions { - /** - * A debug description that can be used for inspecting model decorations. - * @internal - */ - description: string; - /** - * Customize the growing behavior of the decoration when typing at the edges of the decoration. - * Defaults to TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges - */ - stickiness?: TrackedRangeStickiness; - /** - * CSS class name describing the decoration. - */ - className?: string | null; - /** - * Indicates whether the decoration should span across the entire line when it continues onto the next line. - */ - shouldFillLineOnLineBreak?: boolean | null; - blockClassName?: string | null; - /** - * Indicates if this block should be rendered after the last line. - * In this case, the range must be empty and set to the last line. - */ - blockIsAfterEnd?: boolean | null; - blockDoesNotCollapse?: boolean | null; - blockPadding?: [top: number, right: number, bottom: number, left: number] | null; - - /** - * Message to be rendered when hovering over the glyph margin decoration. - */ - glyphMarginHoverMessage?: IMarkdownString | IMarkdownString[] | null; - /** - * Array of MarkdownString to render as the decoration message. - */ - hoverMessage?: IMarkdownString | IMarkdownString[] | null; - /** - * Array of MarkdownString to render as the line number message. - */ - lineNumberHoverMessage?: IMarkdownString | IMarkdownString[] | null; - /** - * Should the decoration expand to encompass a whole line. - */ - isWholeLine?: boolean; - /** - * Always render the decoration (even when the range it encompasses is collapsed). - */ - showIfCollapsed?: boolean; - /** - * Collapse the decoration if its entire range is being replaced via an edit. - * @internal - */ - collapseOnReplaceEdit?: boolean; - /** - * Specifies the stack order of a decoration. - * A decoration with greater stack order is always in front of a decoration with - * a lower stack order when the decorations are on the same line. - */ - zIndex?: number; - /** - * If set, render this decoration in the overview ruler. - */ - overviewRuler?: IModelDecorationOverviewRulerOptions | null; - /** - * If set, render this decoration in the minimap. - */ - minimap?: IModelDecorationMinimapOptions | null; - /** - * If set, the decoration will be rendered in the glyph margin with this CSS class name. - */ - glyphMarginClassName?: string | null; - /** - * If set and the decoration has {@link glyphMarginClassName} set, render this decoration - * with the specified {@link IModelDecorationGlyphMarginOptions} in the glyph margin. - */ - glyphMargin?: IModelDecorationGlyphMarginOptions | null; - /** - * If set, the decoration will be rendered in the lines decorations with this CSS class name. - */ - linesDecorationsClassName?: string | null; - /** - * Controls the tooltip text of the line decoration. - */ - linesDecorationsTooltip?: string | null; - /** - * If set, the decoration will be rendered on the line number. - */ - lineNumberClassName?: string | null; - /** - * If set, the decoration will be rendered in the lines decorations with this CSS class name, but only for the first line in case of line wrapping. - */ - firstLineDecorationClassName?: string | null; - /** - * If set, the decoration will be rendered in the margin (covering its full width) with this CSS class name. - */ - marginClassName?: string | null; - /** - * If set, the decoration will be rendered inline with the text with this CSS class name. - * Please use this only for CSS rules that must impact the text. For example, use `className` - * to have a background color decoration. - */ - inlineClassName?: string | null; - /** - * If there is an `inlineClassName` which affects letter spacing. - */ - inlineClassNameAffectsLetterSpacing?: boolean; - /** - * If set, the decoration will be rendered before the text with this CSS class name. - */ - beforeContentClassName?: string | null; - /** - * If set, the decoration will be rendered after the text with this CSS class name. - */ - afterContentClassName?: string | null; - /** - * If set, text will be injected in the view after the range. - */ - after?: InjectedTextOptions | null; - - /** - * If set, text will be injected in the view before the range. - */ - before?: InjectedTextOptions | null; - - /** - * If set, this decoration will not be rendered for comment tokens. - * @internal - */ - hideInCommentTokens?: boolean | null; - - /** - * If set, this decoration will not be rendered for string tokens. - * @internal - */ - hideInStringTokens?: boolean | null; -} - -/** - * Configures text that is injected into the view without changing the underlying document. -*/ -export interface InjectedTextOptions { - /** - * Sets the text to inject. Must be a single line. - */ - readonly content: string; - - /** - * @internal - */ - readonly tokens?: TokenArray | null; - - /** - * If set, the decoration will be rendered inline with the text with this CSS class name. - */ - readonly inlineClassName?: string | null; - - /** - * If there is an `inlineClassName` which affects letter spacing. - */ - readonly inlineClassNameAffectsLetterSpacing?: boolean; - - /** - * This field allows to attach data to this injected text. - * The data can be read when injected texts at a given position are queried. - */ - readonly attachedData?: unknown; - - /** - * Configures cursor stops around injected text. - * Defaults to {@link InjectedTextCursorStops.Both}. - */ - readonly cursorStops?: InjectedTextCursorStops | null; -} - -export enum InjectedTextCursorStops { - Both, - Right, - Left, - None -} - -/** - * New model decorations. - */ -export interface IModelDeltaDecoration { - /** - * Range that this decoration covers. - */ - range: IRange; - /** - * Options associated with this decoration. - */ - options: IModelDecorationOptions; -} - -/** - * A decoration in the model. - */ -export interface IModelDecoration { - /** - * Identifier for a decoration. - */ - readonly id: string; - /** - * Identifier for a decoration's owner. - */ - readonly ownerId: number; - /** - * Range that this decoration covers. - */ - readonly range: Range; - /** - * Options associated with this decoration. - */ - readonly options: IModelDecorationOptions; -} - -/** - * An accessor that can add, change or remove model decorations. - * @internal - */ -export interface IModelDecorationsChangeAccessor { - /** - * Add a new decoration. - * @param range Range that this decoration covers. - * @param options Options associated with this decoration. - * @return An unique identifier associated with this decoration. - */ - addDecoration(range: IRange, options: IModelDecorationOptions): string; - /** - * Change the range that an existing decoration covers. - * @param id The unique identifier associated with the decoration. - * @param newRange The new range that this decoration covers. - */ - changeDecoration(id: string, newRange: IRange): void; - /** - * Change the options associated with an existing decoration. - * @param id The unique identifier associated with the decoration. - * @param newOptions The new options associated with this decoration. - */ - changeDecorationOptions(id: string, newOptions: IModelDecorationOptions): void; - /** - * Remove an existing decoration. - * @param id The unique identifier associated with the decoration. - */ - removeDecoration(id: string): void; - /** - * Perform a minimum amount of operations, in order to transform the decorations - * identified by `oldDecorations` to the decorations described by `newDecorations` - * and returns the new identifiers associated with the resulting decorations. - * - * @param oldDecorations Array containing previous decorations identifiers. - * @param newDecorations Array describing what decorations should result after the call. - * @return An array containing the new decorations identifiers. - */ - deltaDecorations(oldDecorations: readonly string[], newDecorations: readonly IModelDeltaDecoration[]): string[]; -} - -/** - * End of line character preference. - */ -export const enum EndOfLinePreference { - /** - * Use the end of line character identified in the text buffer. - */ - TextDefined = 0, - /** - * Use line feed (\n) as the end of line character. - */ - LF = 1, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 2 -} - -/** - * The default end of line to use when instantiating models. - */ -export const enum DefaultEndOfLine { - /** - * Use line feed (\n) as the end of line character. - */ - LF = 1, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 2 -} - -/** - * End of line character preference. - */ -export const enum EndOfLineSequence { - /** - * Use line feed (\n) as the end of line character. - */ - LF = 0, - /** - * Use carriage return and line feed (\r\n) as the end of line character. - */ - CRLF = 1 -} - -/** - * An identifier for a single edit operation. - * @internal - */ -export interface ISingleEditOperationIdentifier { - /** - * Identifier major - */ - major: number; - /** - * Identifier minor - */ - minor: number; -} - -/** - * A single edit operation, that has an identifier. - */ -export interface IIdentifiedSingleEditOperation extends ISingleEditOperation { - /** - * An identifier associated with this single edit operation. - * @internal - */ - identifier?: ISingleEditOperationIdentifier | null; - /** - * This indicates that this operation is inserting automatic whitespace - * that can be removed on next model edit operation if `config.trimAutoWhitespace` is true. - * @internal - */ - isAutoWhitespaceEdit?: boolean; - /** - * This indicates that this operation is in a set of operations that are tracked and should not be "simplified". - * @internal - */ - _isTracked?: boolean; -} - -export interface IValidEditOperation { - /** - * An identifier associated with this single edit operation. - * @internal - */ - identifier: ISingleEditOperationIdentifier | null; - /** - * The range to replace. This can be empty to emulate a simple insert. - */ - range: Range; - /** - * The text to replace with. This can be empty to emulate a simple delete. - */ - text: string; - /** - * @internal - */ - textChange: TextChange; -} - -/** - * A callback that can compute the cursor state after applying a series of edit operations. - */ -export interface ICursorStateComputer { - /** - * A callback that can compute the resulting cursors state after some edit operations have been executed. - */ - (inverseEditOperations: IValidEditOperation[]): Selection[] | null; -} - -export class TextModelResolvedOptions { - _textModelResolvedOptionsBrand: void = undefined; - - readonly tabSize: number; - readonly indentSize: number; - private readonly _indentSizeIsTabSize: boolean; - readonly insertSpaces: boolean; - readonly defaultEOL: DefaultEndOfLine; - readonly trimAutoWhitespace: boolean; - readonly bracketPairColorizationOptions: BracketPairColorizationOptions; - - public get originalIndentSize(): number | 'tabSize' { - return this._indentSizeIsTabSize ? 'tabSize' : this.indentSize; - } - - /** - * @internal - */ - constructor(src: { - tabSize: number; - indentSize: number | 'tabSize'; - insertSpaces: boolean; - defaultEOL: DefaultEndOfLine; - trimAutoWhitespace: boolean; - bracketPairColorizationOptions: BracketPairColorizationOptions; - }) { - this.tabSize = Math.max(1, src.tabSize | 0); - if (src.indentSize === 'tabSize') { - this.indentSize = this.tabSize; - this._indentSizeIsTabSize = true; - } else { - this.indentSize = Math.max(1, src.indentSize | 0); - this._indentSizeIsTabSize = false; - } - this.insertSpaces = Boolean(src.insertSpaces); - this.defaultEOL = src.defaultEOL | 0; - this.trimAutoWhitespace = Boolean(src.trimAutoWhitespace); - this.bracketPairColorizationOptions = src.bracketPairColorizationOptions; - } - - /** - * @internal - */ - public equals(other: TextModelResolvedOptions): boolean { - return ( - this.tabSize === other.tabSize - && this._indentSizeIsTabSize === other._indentSizeIsTabSize - && this.indentSize === other.indentSize - && this.insertSpaces === other.insertSpaces - && this.defaultEOL === other.defaultEOL - && this.trimAutoWhitespace === other.trimAutoWhitespace - && equals(this.bracketPairColorizationOptions, other.bracketPairColorizationOptions) - ); - } - - /** - * @internal - */ - public createChangeEvent(newOpts: TextModelResolvedOptions): IModelOptionsChangedEvent { - return { - tabSize: this.tabSize !== newOpts.tabSize, - indentSize: this.indentSize !== newOpts.indentSize, - insertSpaces: this.insertSpaces !== newOpts.insertSpaces, - trimAutoWhitespace: this.trimAutoWhitespace !== newOpts.trimAutoWhitespace, - }; - } -} - -/** - * @internal - */ -export interface ITextModelCreationOptions { - tabSize: number; - indentSize: number | 'tabSize'; - insertSpaces: boolean; - detectIndentation: boolean; - trimAutoWhitespace: boolean; - defaultEOL: DefaultEndOfLine; - isForSimpleWidget: boolean; - largeFileOptimizations: boolean; - bracketPairColorizationOptions: BracketPairColorizationOptions; -} - -export interface BracketPairColorizationOptions { - enabled: boolean; - independentColorPoolPerBracketType: boolean; -} - -export interface ITextModelUpdateOptions { - tabSize?: number; - indentSize?: number | 'tabSize'; - insertSpaces?: boolean; - trimAutoWhitespace?: boolean; - bracketColorizationOptions?: BracketPairColorizationOptions; -} - -export class FindMatch { - _findMatchBrand: void = undefined; - - public readonly range: Range; - public readonly matches: string[] | null; - - /** - * @internal - */ - constructor(range: Range, matches: string[] | null) { - this.range = range; - this.matches = matches; - } -} - -/** - * Describes the behavior of decorations when typing/editing near their edges. - * Note: Please do not edit the values, as they very carefully match `DecorationRangeBehavior` - */ -export const enum TrackedRangeStickiness { - AlwaysGrowsWhenTypingAtEdges = 0, - NeverGrowsWhenTypingAtEdges = 1, - GrowsOnlyWhenTypingBefore = 2, - GrowsOnlyWhenTypingAfter = 3, -} - -/** - * Text snapshot that works like an iterator. - * Will try to return chunks of roughly ~64KB size. - * Will return null when finished. - */ -export interface ITextSnapshot { - read(): string | null; -} - -/** - * @internal - */ -export function isITextSnapshot(obj: any): obj is ITextSnapshot { - return (obj && typeof obj.read === 'function'); -} - -/** - * A model. - */ -export interface ITextModel { - - /** - * Gets the resource associated with this editor model. - */ - readonly uri: URI; - - /** - * A unique identifier associated with this model. - */ - readonly id: string; - - /** - * This model is constructed for a simple widget code editor. - * @internal - */ - readonly isForSimpleWidget: boolean; - - /** - * If true, the text model might contain RTL. - * If false, the text model **contains only** contain LTR. - * @internal - */ - mightContainRTL(): boolean; - - /** - * If true, the text model might contain LINE SEPARATOR (LS), PARAGRAPH SEPARATOR (PS). - * If false, the text model definitely does not contain these. - * @internal - */ - mightContainUnusualLineTerminators(): boolean; - - /** - * @internal - */ - removeUnusualLineTerminators(selections?: Selection[]): void; - - /** - * If true, the text model might contain non basic ASCII. - * If false, the text model **contains only** basic ASCII. - * @internal - */ - mightContainNonBasicASCII(): boolean; - - /** - * Get the resolved options for this model. - */ - getOptions(): TextModelResolvedOptions; - - /** - * Get the formatting options for this model. - * @internal - */ - getFormattingOptions(): FormattingOptions; - - /** - * Get the current version id of the model. - * Anytime a change happens to the model (even undo/redo), - * the version id is incremented. - */ - getVersionId(): number; - - /** - * Get the alternative version id of the model. - * This alternative version id is not always incremented, - * it will return the same values in the case of undo-redo. - */ - getAlternativeVersionId(): number; - - /** - * Replace the entire text buffer value contained in this model. - */ - setValue(newValue: string | ITextSnapshot): void; - - /** - * Get the text stored in this model. - * @param eol The end of line character preference. Defaults to `EndOfLinePreference.TextDefined`. - * @param preserverBOM Preserve a BOM character if it was detected when the model was constructed. - * @return The text. - */ - getValue(eol?: EndOfLinePreference, preserveBOM?: boolean): string; - - /** - * Get the text stored in this model. - * @param preserverBOM Preserve a BOM character if it was detected when the model was constructed. - * @return The text snapshot (it is safe to consume it asynchronously). - */ - createSnapshot(preserveBOM?: boolean): ITextSnapshot; - - /** - * Get the length of the text stored in this model. - */ - getValueLength(eol?: EndOfLinePreference, preserveBOM?: boolean): number; - - /** - * Check if the raw text stored in this model equals another raw text. - * @internal - */ - equalsTextBuffer(other: ITextBuffer): boolean; - - /** - * Get the underling text buffer. - * @internal - */ - getTextBuffer(): ITextBuffer; - - /** - * Get the text in a certain range. - * @param range The range describing what text to get. - * @param eol The end of line character preference. This will only be used for multiline ranges. Defaults to `EndOfLinePreference.TextDefined`. - * @return The text. - */ - getValueInRange(range: IRange, eol?: EndOfLinePreference): string; - - /** - * Get the length of text in a certain range. - * @param range The range describing what text length to get. - * @return The text length. - */ - getValueLengthInRange(range: IRange, eol?: EndOfLinePreference): number; - - /** - * Get the character count of text in a certain range. - * @param range The range describing what text length to get. - */ - getCharacterCountInRange(range: IRange, eol?: EndOfLinePreference): number; - - /** - * Splits characters in two buckets. First bucket (A) is of characters that - * sit in lines with length < `LONG_LINE_BOUNDARY`. Second bucket (B) is of - * characters that sit in lines with length >= `LONG_LINE_BOUNDARY`. - * If count(B) > count(A) return true. Returns false otherwise. - * @internal - */ - isDominatedByLongLines(): boolean; - - /** - * Get the number of lines in the model. - */ - getLineCount(): number; - - /** - * Get the text for a certain line. - */ - getLineContent(lineNumber: number): string; - - /** - * Get the text length for a certain line. - */ - getLineLength(lineNumber: number): number; - - /** - * Get the text for all lines. - */ - getLinesContent(): string[]; - - /** - * Get the end of line sequence predominantly used in the text buffer. - * @return EOL char sequence (e.g.: '\n' or '\r\n'). - */ - getEOL(): string; - - /** - * Get the end of line sequence predominantly used in the text buffer. - */ - getEndOfLineSequence(): EndOfLineSequence; - - /** - * Get the minimum legal column for line at `lineNumber` - */ - getLineMinColumn(lineNumber: number): number; - - /** - * Get the maximum legal column for line at `lineNumber` - */ - getLineMaxColumn(lineNumber: number): number; - - /** - * Returns the column before the first non whitespace character for line at `lineNumber`. - * Returns 0 if line is empty or contains only whitespace. - */ - getLineFirstNonWhitespaceColumn(lineNumber: number): number; - - /** - * Returns the column after the last non whitespace character for line at `lineNumber`. - * Returns 0 if line is empty or contains only whitespace. - */ - getLineLastNonWhitespaceColumn(lineNumber: number): number; - - /** - * Create a valid position. - */ - validatePosition(position: IPosition): Position; - - /** - * Advances the given position by the given offset (negative offsets are also accepted) - * and returns it as a new valid position. - * - * If the offset and position are such that their combination goes beyond the beginning or - * end of the model, throws an exception. - * - * If the offset is such that the new position would be in the middle of a multi-byte - * line terminator, throws an exception. - */ - modifyPosition(position: IPosition, offset: number): Position; - - /** - * Create a valid range. - */ - validateRange(range: IRange): Range; - - /** - * Verifies the range is valid. - */ - isValidRange(range: IRange): boolean; - - /** - * Converts the position to a zero-based offset. - * - * The position will be [adjusted](#TextDocument.validatePosition). - * - * @param position A position. - * @return A valid zero-based offset. - */ - getOffsetAt(position: IPosition): number; - - /** - * Converts a zero-based offset to a position. - * - * @param offset A zero-based offset. - * @return A valid [position](#Position). - */ - getPositionAt(offset: number): Position; - - /** - * Get a range covering the entire model. - */ - getFullModelRange(): Range; - - /** - * Returns if the model was disposed or not. - */ - isDisposed(): boolean; - - /** - * This model is so large that it would not be a good idea to sync it over - * to web workers or other places. - * @internal - */ - isTooLargeForSyncing(): boolean; - - /** - * The file is so large, that even tokenization is disabled. - * @internal - */ - isTooLargeForTokenization(): boolean; - - /** - * The file is so large, that operations on it might be too large for heap - * and can lead to OOM crashes so they should be disabled. - * @internal - */ - isTooLargeForHeapOperation(): boolean; - - /** - * Search the model. - * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchOnlyEditableRange Limit the searching to only search inside the editable range of the model. - * @param isRegex Used to indicate that `searchString` is a regular expression. - * @param matchCase Force the matching to match lower/upper case exactly. - * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. - * @param captureMatches The result will contain the captured groups. - * @param limitResultCount Limit the number of results - * @return The ranges where the matches are. It is empty if not matches have been found. - */ - findMatches(searchString: string, searchOnlyEditableRange: boolean, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; - /** - * Search the model. - * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchScope Limit the searching to only search inside these ranges. - * @param isRegex Used to indicate that `searchString` is a regular expression. - * @param matchCase Force the matching to match lower/upper case exactly. - * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. - * @param captureMatches The result will contain the captured groups. - * @param limitResultCount Limit the number of results - * @return The ranges where the matches are. It is empty if no matches have been found. - */ - findMatches(searchString: string, searchScope: IRange | IRange[], isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean, limitResultCount?: number): FindMatch[]; - /** - * Search the model for the next match. Loops to the beginning of the model if needed. - * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchStart Start the searching at the specified position. - * @param isRegex Used to indicate that `searchString` is a regular expression. - * @param matchCase Force the matching to match lower/upper case exactly. - * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. - * @param captureMatches The result will contain the captured groups. - * @return The range where the next match is. It is null if no next match has been found. - */ - findNextMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; - /** - * Search the model for the previous match. Loops to the end of the model if needed. - * @param searchString The string used to search. If it is a regular expression, set `isRegex` to true. - * @param searchStart Start the searching at the specified position. - * @param isRegex Used to indicate that `searchString` is a regular expression. - * @param matchCase Force the matching to match lower/upper case exactly. - * @param wordSeparators Force the matching to match entire words only. Pass null otherwise. - * @param captureMatches The result will contain the captured groups. - * @return The range where the previous match is. It is null if no previous match has been found. - */ - findPreviousMatch(searchString: string, searchStart: IPosition, isRegex: boolean, matchCase: boolean, wordSeparators: string | null, captureMatches: boolean): FindMatch | null; - - - /** - * Get the language associated with this model. - */ - getLanguageId(): string; - - /** - * Set the current language mode associated with the model. - * @param languageId The new language. - * @param source The source of the call that set the language. - * @internal - */ - setLanguage(languageId: string, source?: string): void; - - /** - * Set the current language mode associated with the model. - * @param languageSelection The new language selection. - * @param source The source of the call that set the language. - * @internal - */ - setLanguage(languageSelection: ILanguageSelection, source?: string): void; - - /** - * Returns the real (inner-most) language mode at a given position. - * The result might be inaccurate. Use `forceTokenization` to ensure accurate tokens. - * @internal - */ - getLanguageIdAtPosition(lineNumber: number, column: number): string; - - /** - * Get the word under or besides `position`. - * @param position The position to look for a word. - * @return The word under or besides `position`. Might be null. - */ - getWordAtPosition(position: IPosition): IWordAtPosition | null; - - /** - * Get the word under or besides `position` trimmed to `position`.column - * @param position The position to look for a word. - * @return The word under or besides `position`. Will never be null. - */ - getWordUntilPosition(position: IPosition): IWordAtPosition; - - /** - * Change the decorations. The callback will be called with a change accessor - * that becomes invalid as soon as the callback finishes executing. - * This allows for all events to be queued up until the change - * is completed. Returns whatever the callback returns. - * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. - * @internal - */ - changeDecorations(callback: (changeAccessor: IModelDecorationsChangeAccessor) => T, ownerId?: number): T | null; - - /** - * Perform a minimum amount of operations, in order to transform the decorations - * identified by `oldDecorations` to the decorations described by `newDecorations` - * and returns the new identifiers associated with the resulting decorations. - * - * @param oldDecorations Array containing previous decorations identifiers. - * @param newDecorations Array describing what decorations should result after the call. - * @param ownerId Identifies the editor id in which these decorations should appear. If no `ownerId` is provided, the decorations will appear in all editors that attach this model. - * @return An array containing the new decorations identifiers. - */ - deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[], ownerId?: number): string[]; - - /** - * Remove all decorations that have been added with this specific ownerId. - * @param ownerId The owner id to search for. - * @internal - */ - removeAllDecorationsWithOwnerId(ownerId: number): void; - - /** - * Get the options associated with a decoration. - * @param id The decoration id. - * @return The decoration options or null if the decoration was not found. - */ - getDecorationOptions(id: string): IModelDecorationOptions | null; - - /** - * Get the range associated with a decoration. - * @param id The decoration id. - * @return The decoration range or null if the decoration was not found. - */ - getDecorationRange(id: string): Range | null; - - /** - * Gets all the decorations for the line `lineNumber` as an array. - * @param lineNumber The line number - * @param ownerId If set, it will ignore decorations belonging to other owners. - * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). - * @return An array with the decorations - */ - getLineDecorations(lineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; - - /** - * Gets all the decorations for the lines between `startLineNumber` and `endLineNumber` as an array. - * @param startLineNumber The start line number - * @param endLineNumber The end line number - * @param ownerId If set, it will ignore decorations belonging to other owners. - * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). - * @return An array with the decorations - */ - getLinesDecorations(startLineNumber: number, endLineNumber: number, ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; - - /** - * Gets all the decorations in a range as an array. Only `startLineNumber` and `endLineNumber` from `range` are used for filtering. - * So for now it returns all the decorations on the same line as `range`. - * @param range The range to search in - * @param ownerId If set, it will ignore decorations belonging to other owners. - * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). - * @param onlyMinimapDecorations If set, it will return only decorations that render in the minimap. - * @param onlyMarginDecorations If set, it will return only decorations that render in the glyph margin. - * @return An array with the decorations - */ - getDecorationsInRange(range: IRange, ownerId?: number, filterOutValidation?: boolean, onlyMinimapDecorations?: boolean, onlyMarginDecorations?: boolean): IModelDecoration[]; - - /** - * Gets all the decorations as an array. - * @param ownerId If set, it will ignore decorations belonging to other owners. - * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). - */ - getAllDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; - - /** - * Gets all decorations that render in the glyph margin as an array. - * @param ownerId If set, it will ignore decorations belonging to other owners. - */ - getAllMarginDecorations(ownerId?: number): IModelDecoration[]; - - /** - * Gets all the decorations that should be rendered in the overview ruler as an array. - * @param ownerId If set, it will ignore decorations belonging to other owners. - * @param filterOutValidation If set, it will ignore decorations specific to validation (i.e. warnings, errors). - */ - getOverviewRulerDecorations(ownerId?: number, filterOutValidation?: boolean): IModelDecoration[]; - - /** - * Gets all the decorations that contain injected text. - * @param ownerId If set, it will ignore decorations belonging to other owners. - */ - getInjectedTextDecorations(ownerId?: number): IModelDecoration[]; - - /** - * @internal - */ - _getTrackedRange(id: string): Range | null; - - /** - * @internal - */ - _setTrackedRange(id: string | null, newRange: null, newStickiness: TrackedRangeStickiness): null; - /** - * @internal - */ - _setTrackedRange(id: string | null, newRange: Range, newStickiness: TrackedRangeStickiness): string; - - /** - * Normalize a string containing whitespace according to indentation rules (converts to spaces or to tabs). - */ - normalizeIndentation(str: string): string; - - /** - * Change the options of this model. - */ - updateOptions(newOpts: ITextModelUpdateOptions): void; - - /** - * Detect the indentation options for this model from its content. - */ - detectIndentation(defaultInsertSpaces: boolean, defaultTabSize: number): void; - - /** - * Close the current undo-redo element. - * This offers a way to create an undo/redo stop point. - */ - pushStackElement(): void; - - /** - * Open the current undo-redo element. - * This offers a way to remove the current undo/redo stop point. - */ - popStackElement(): void; - - /** - * Push edit operations, basically editing the model. This is the preferred way - * of editing the model. The edit operations will land on the undo stack. - * @param beforeCursorState The cursor state before the edit operations. This cursor state will be returned when `undo` or `redo` are invoked. - * @param editOperations The edit operations. - * @param cursorStateComputer A callback that can compute the resulting cursors state after the edit operations have been executed. - * @return The cursor state returned by the `cursorStateComputer`. - */ - pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer): Selection[] | null; - /** - * @internal - */ - pushEditOperations(beforeCursorState: Selection[] | null, editOperations: IIdentifiedSingleEditOperation[], cursorStateComputer: ICursorStateComputer, group?: UndoRedoGroup): Selection[] | null; - - /** - * Change the end of line sequence. This is the preferred way of - * changing the eol sequence. This will land on the undo stack. - */ - pushEOL(eol: EndOfLineSequence): void; - - /** - * Edit the model without adding the edits to the undo stack. - * This can have dire consequences on the undo stack! See @pushEditOperations for the preferred way. - * @param operations The edit operations. - * @return If desired, the inverse edit operations, that, when applied, will bring the model back to the previous state. - */ - applyEdits(operations: IIdentifiedSingleEditOperation[]): void; - applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: false): void; - applyEdits(operations: IIdentifiedSingleEditOperation[], computeUndoEdits: true): IValidEditOperation[]; - - /** - * Change the end of line sequence without recording in the undo stack. - * This can have dire consequences on the undo stack! See @pushEOL for the preferred way. - */ - setEOL(eol: EndOfLineSequence): void; - - /** - * @internal - */ - _applyUndo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; - - /** - * @internal - */ - _applyRedo(changes: TextChange[], eol: EndOfLineSequence, resultingAlternativeVersionId: number, resultingSelection: Selection[] | null): void; - - /** - * Undo edit operations until the previous undo/redo point. - * The inverse edit operations will be pushed on the redo stack. - * @internal - */ - undo(): void | Promise; - - /** - * Is there anything in the undo stack? - * @internal - */ - canUndo(): boolean; - - /** - * Redo edit operations until the next undo/redo point. - * The inverse edit operations will be pushed on the undo stack. - * @internal - */ - redo(): void | Promise; - - /** - * Is there anything in the redo stack? - * @internal - */ - canRedo(): boolean; - - /** - * @deprecated Please use `onDidChangeContent` instead. - * An event emitted when the contents of the model have changed. - * @internal - * @event - */ - readonly onDidChangeContentOrInjectedText: Event; - /** - * An event emitted when the contents of the model have changed. - * @event - */ - onDidChangeContent(listener: (e: IModelContentChangedEvent) => void): IDisposable; - /** - * An event emitted when decorations of the model have changed. - * @event - */ - readonly onDidChangeDecorations: Event; - /** - * An event emitted when the model options have changed. - * @event - */ - readonly onDidChangeOptions: Event; - /** - * An event emitted when the language associated with the model has changed. - * @event - */ - readonly onDidChangeLanguage: Event; - /** - * An event emitted when the language configuration associated with the model has changed. - * @event - */ - readonly onDidChangeLanguageConfiguration: Event; - /** - * An event emitted when the tokens associated with the model have changed. - * @event - * @internal - */ - readonly onDidChangeTokens: Event; - /** - * An event emitted when the model has been attached to the first editor or detached from the last editor. - * @event - */ - readonly onDidChangeAttached: Event; - /** - * An event emitted right before disposing the model. - * @event - */ - readonly onWillDispose: Event; - - /** - * Destroy this model. - */ - dispose(): void; - - /** - * @internal - */ - onBeforeAttached(): IAttachedView; - - /** - * @internal - */ - onBeforeDetached(view: IAttachedView): void; - - /** - * Returns if this model is attached to an editor or not. - */ - isAttachedToEditor(): boolean; - - /** - * Returns the count of editors this model is attached to. - * @internal - */ - getAttachedEditorCount(): number; - - /** - * Among all positions that are projected to the same position in the underlying text model as - * the given position, select a unique position as indicated by the affinity. - * - * PositionAffinity.Left: - * The normalized position must be equal or left to the requested position. - * - * PositionAffinity.Right: - * The normalized position must be equal or right to the requested position. - * - * @internal - */ - normalizePosition(position: Position, affinity: PositionAffinity): Position; - - /** - * Gets the column at which indentation stops at a given line. - * @internal - */ - getLineIndentColumn(lineNumber: number): number; - - /** - * Returns an object that can be used to query brackets. - * @internal - */ - readonly bracketPairs: IBracketPairsTextModelPart; - - /** - * Returns an object that can be used to query indent guides. - * @internal - */ - readonly guides: IGuidesTextModelPart; - - /** - * @internal - */ - readonly tokenization: ITokenizationTextModelPart; -} - -/** - * @internal - */ -export function isITextModel(obj: IEditorModel): obj is ITextModel { - return Boolean(obj && (obj as ITextModel).uri); -} - -/** - * @internal - */ -export interface IAttachedView { - /** - * @param stabilized Indicates if the visible lines are probably going to change soon or can be considered stable. - * Is true on reveal range and false on scroll. - * Tokenizers should tokenize synchronously if stabilized is true. - */ - setVisibleLines(visibleLines: { startLineNumber: number; endLineNumber: number }[], stabilized: boolean): void; -} - -export const enum PositionAffinity { - /** - * Prefers the left most position. - */ - Left = 0, - - /** - * Prefers the right most position. - */ - Right = 1, - - /** - * No preference. - */ - None = 2, - - /** - * If the given position is on injected text, prefers the position left of it. - */ - LeftOfInjectedText = 3, - - /** - * If the given position is on injected text, prefers the position right of it. - */ - RightOfInjectedText = 4, -} - -/** - * @internal - */ -export interface ITextBufferBuilder { - acceptChunk(chunk: string): void; - finish(): ITextBufferFactory; -} - -/** - * @internal - */ -export interface ITextBufferFactory { - create(defaultEOL: DefaultEndOfLine): { textBuffer: ITextBuffer; disposable: IDisposable }; - getFirstLineText(lengthLimit: number): string; -} - -/** - * @internal - */ -export const enum ModelConstants { - FIRST_LINE_DETECTION_LENGTH_LIMIT = 1000 -} - -/** - * @internal - */ -export class ValidAnnotatedEditOperation implements IIdentifiedSingleEditOperation { - constructor( - public readonly identifier: ISingleEditOperationIdentifier | null, - public readonly range: Range, - public readonly text: string | null, - public readonly forceMoveMarkers: boolean, - public readonly isAutoWhitespaceEdit: boolean, - public readonly _isTracked: boolean, - ) { } -} - -/** - * @internal - * - * `lineNumber` is 1 based. - */ -export interface IReadonlyTextBuffer { - onDidChangeContent: Event; - equals(other: ITextBuffer): boolean; - mightContainRTL(): boolean; - mightContainUnusualLineTerminators(): boolean; - resetMightContainUnusualLineTerminators(): void; - mightContainNonBasicASCII(): boolean; - getBOM(): string; - getEOL(): string; - - getOffsetAt(lineNumber: number, column: number): number; - getPositionAt(offset: number): Position; - getRangeAt(offset: number, length: number): Range; - - getValueInRange(range: Range, eol: EndOfLinePreference): string; - createSnapshot(preserveBOM: boolean): ITextSnapshot; - getValueLengthInRange(range: Range, eol: EndOfLinePreference): number; - getCharacterCountInRange(range: Range, eol: EndOfLinePreference): number; - getLength(): number; - getLineCount(): number; - getLinesContent(): string[]; - getLineContent(lineNumber: number): string; - getLineCharCode(lineNumber: number, index: number): number; - getCharCode(offset: number): number; - getLineLength(lineNumber: number): number; - getLineMinColumn(lineNumber: number): number; - getLineMaxColumn(lineNumber: number): number; - getLineFirstNonWhitespaceColumn(lineNumber: number): number; - getLineLastNonWhitespaceColumn(lineNumber: number): number; - findMatchesLineByLine(searchRange: Range, searchData: SearchData, captureMatches: boolean, limitResultCount: number): FindMatch[]; - - /** - * Get nearest chunk of text after `offset` in the text buffer. - */ - getNearestChunk(offset: number): string; -} - -/** - * @internal - */ -export class SearchData { - - /** - * The regex to search for. Always defined. - */ - public readonly regex: RegExp; - /** - * The word separator classifier. - */ - public readonly wordSeparators: WordCharacterClassifier | null; - /** - * The simple string to search for (if possible). - */ - public readonly simpleSearch: string | null; - - constructor(regex: RegExp, wordSeparators: WordCharacterClassifier | null, simpleSearch: string | null) { - this.regex = regex; - this.wordSeparators = wordSeparators; - this.simpleSearch = simpleSearch; - } -} - -/** - * @internal - */ -export interface ITextBuffer extends IReadonlyTextBuffer, IDisposable { - setEOL(newEOL: '\r\n' | '\n'): void; - applyEdits(rawOperations: ValidAnnotatedEditOperation[], recordTrimAutoWhitespace: boolean, computeUndoEdits: boolean): ApplyEditsResult; -} - -/** - * @internal - */ -export class ApplyEditsResult { - - constructor( - public readonly reverseEdits: IValidEditOperation[] | null, - public readonly changes: IInternalModelContentChange[], - public readonly trimAutoWhitespaceLineNumbers: number[] | null - ) { } - -} - -/** - * @internal - */ -export interface IInternalModelContentChange extends IModelContentChange { - range: Range; - forceMoveMarkers: boolean; -} - -/** - * @internal - */ -export function shouldSynchronizeModel(model: ITextModel): boolean { - return ( - !model.isTooLargeForSyncing() && !model.isForSimpleWidget - ); -} +export * from './language/model.js'; diff --git a/src/vs/editor/common/modelLineProjectionData.ts b/src/vs/editor/common/modelLineProjectionData.ts index f35a4adc6e5..cf1e8392899 100644 --- a/src/vs/editor/common/modelLineProjectionData.ts +++ b/src/vs/editor/common/modelLineProjectionData.ts @@ -6,9 +6,9 @@ import { assertNever } from '../../base/common/assert.js'; import { WrappingIndent } from './config/editorOptions.js'; import { FontInfo } from './config/fontInfo.js'; -import { Position } from './core/position.js'; -import { InjectedTextCursorStops, InjectedTextOptions, PositionAffinity } from './model.js'; -import { LineInjectedText } from './textModelEvents.js'; +import { Position } from '../../editor/common/language/core/position.js'; +import { InjectedTextCursorStops, InjectedTextOptions, PositionAffinity } from '../../editor/common/language/model.js'; +import { LineInjectedText } from '../../editor/common/language/textModelEvents.js'; /** * *input*: diff --git a/src/vs/editor/common/textModelBracketPairs.ts b/src/vs/editor/common/textModelBracketPairs.ts index 03fc83cff1f..155f070ceab 100644 --- a/src/vs/editor/common/textModelBracketPairs.ts +++ b/src/vs/editor/common/textModelBracketPairs.ts @@ -5,10 +5,10 @@ import { CallbackIterable } from '../../base/common/arrays.js'; import { Event } from '../../base/common/event.js'; -import { IPosition } from './core/position.js'; -import { IRange, Range } from './core/range.js'; +import { IPosition } from '../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../editor/common/language/core/range.js'; import { ClosingBracketKind, OpeningBracketKind } from './languages/supports/languageBracketsConfiguration.js'; -import { PairAstNode } from './model/bracketPairsTextModelPart/bracketPairsTree/ast.js'; +import { PairAstNode } from '../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/ast.js'; export interface IBracketPairsTextModelPart { /** diff --git a/src/vs/editor/common/textModelEvents.ts b/src/vs/editor/common/textModelEvents.ts index c35d0472106..ed7eed705e6 100644 --- a/src/vs/editor/common/textModelEvents.ts +++ b/src/vs/editor/common/textModelEvents.ts @@ -3,395 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange } from './core/range.js'; -import { Selection } from './core/selection.js'; -import { IModelDecoration, InjectedTextOptions } from './model.js'; - -/** - * An event describing that the current language associated with a model has changed. - */ -export interface IModelLanguageChangedEvent { - /** - * Previous language - */ - readonly oldLanguage: string; - /** - * New language - */ - readonly newLanguage: string; - - /** - * Source of the call that caused the event. - */ - readonly source: string; -} - -/** - * An event describing that the language configuration associated with a model has changed. - */ -export interface IModelLanguageConfigurationChangedEvent { -} - -export interface IModelContentChange { - /** - * The old range that got replaced. - */ - readonly range: IRange; - /** - * The offset of the range that got replaced. - */ - readonly rangeOffset: number; - /** - * The length of the range that got replaced. - */ - readonly rangeLength: number; - /** - * The new text for the range. - */ - readonly text: string; -} - -/** - * An event describing a change in the text of a model. - */ -export interface IModelContentChangedEvent { - /** - * The changes are ordered from the end of the document to the beginning, so they should be safe to apply in sequence. - */ - readonly changes: IModelContentChange[]; - /** - * The (new) end-of-line character. - */ - readonly eol: string; - /** - * The new version id the model has transitioned to. - */ - readonly versionId: number; - /** - * Flag that indicates that this event was generated while undoing. - */ - readonly isUndoing: boolean; - /** - * Flag that indicates that this event was generated while redoing. - */ - readonly isRedoing: boolean; - /** - * Flag that indicates that all decorations were lost with this edit. - * The model has been reset to a new value. - */ - readonly isFlush: boolean; - - /** - * Flag that indicates that this event describes an eol change. - */ - readonly isEolChange: boolean; -} - -/** - * An event describing that model decorations have changed. - */ -export interface IModelDecorationsChangedEvent { - readonly affectsMinimap: boolean; - readonly affectsOverviewRuler: boolean; - readonly affectsGlyphMargin: boolean; - readonly affectsLineNumber: boolean; -} - -/** - * An event describing that some ranges of lines have been tokenized (their tokens have changed). - * @internal - */ -export interface IModelTokensChangedEvent { - readonly semanticTokensApplied: boolean; - readonly ranges: { - /** - * The start of the range (inclusive) - */ - readonly fromLineNumber: number; - /** - * The end of the range (inclusive) - */ - readonly toLineNumber: number; - }[]; -} - -export interface IModelOptionsChangedEvent { - readonly tabSize: boolean; - readonly indentSize: boolean; - readonly insertSpaces: boolean; - readonly trimAutoWhitespace: boolean; -} - -/** - * @internal - */ -export const enum RawContentChangedType { - Flush = 1, - LineChanged = 2, - LinesDeleted = 3, - LinesInserted = 4, - EOLChanged = 5 -} - -/** - * An event describing that a model has been reset to a new value. - * @internal - */ -export class ModelRawFlush { - public readonly changeType = RawContentChangedType.Flush; -} - -/** - * Represents text injected on a line - * @internal - */ -export class LineInjectedText { - public static applyInjectedText(lineText: string, injectedTexts: LineInjectedText[] | null): string { - if (!injectedTexts || injectedTexts.length === 0) { - return lineText; - } - let result = ''; - let lastOriginalOffset = 0; - for (const injectedText of injectedTexts) { - result += lineText.substring(lastOriginalOffset, injectedText.column - 1); - lastOriginalOffset = injectedText.column - 1; - result += injectedText.options.content; - } - result += lineText.substring(lastOriginalOffset); - return result; - } - - public static fromDecorations(decorations: IModelDecoration[]): LineInjectedText[] { - const result: LineInjectedText[] = []; - for (const decoration of decorations) { - if (decoration.options.before && decoration.options.before.content.length > 0) { - result.push(new LineInjectedText( - decoration.ownerId, - decoration.range.startLineNumber, - decoration.range.startColumn, - decoration.options.before, - 0, - )); - } - if (decoration.options.after && decoration.options.after.content.length > 0) { - result.push(new LineInjectedText( - decoration.ownerId, - decoration.range.endLineNumber, - decoration.range.endColumn, - decoration.options.after, - 1, - )); - } - } - result.sort((a, b) => { - if (a.lineNumber === b.lineNumber) { - if (a.column === b.column) { - return a.order - b.order; - } - return a.column - b.column; - } - return a.lineNumber - b.lineNumber; - }); - return result; - } - - constructor( - public readonly ownerId: number, - public readonly lineNumber: number, - public readonly column: number, - public readonly options: InjectedTextOptions, - public readonly order: number - ) { } - - public withText(text: string): LineInjectedText { - return new LineInjectedText(this.ownerId, this.lineNumber, this.column, { ...this.options, content: text }, this.order); - } -} - -/** - * An event describing that a line has changed in a model. - * @internal - */ -export class ModelRawLineChanged { - public readonly changeType = RawContentChangedType.LineChanged; - /** - * The line that has changed. - */ - public readonly lineNumber: number; - /** - * The new value of the line. - */ - public readonly detail: string; - /** - * The injected text on the line. - */ - public readonly injectedText: LineInjectedText[] | null; - - constructor(lineNumber: number, detail: string, injectedText: LineInjectedText[] | null) { - this.lineNumber = lineNumber; - this.detail = detail; - this.injectedText = injectedText; - } -} - -/** - * An event describing that line(s) have been deleted in a model. - * @internal - */ -export class ModelRawLinesDeleted { - public readonly changeType = RawContentChangedType.LinesDeleted; - /** - * At what line the deletion began (inclusive). - */ - public readonly fromLineNumber: number; - /** - * At what line the deletion stopped (inclusive). - */ - public readonly toLineNumber: number; - - constructor(fromLineNumber: number, toLineNumber: number) { - this.fromLineNumber = fromLineNumber; - this.toLineNumber = toLineNumber; - } -} - -/** - * An event describing that line(s) have been inserted in a model. - * @internal - */ -export class ModelRawLinesInserted { - public readonly changeType = RawContentChangedType.LinesInserted; - /** - * Before what line did the insertion begin - */ - public readonly fromLineNumber: number; - /** - * `toLineNumber` - `fromLineNumber` + 1 denotes the number of lines that were inserted - */ - public readonly toLineNumber: number; - /** - * The text that was inserted - */ - public readonly detail: string[]; - /** - * The injected texts for every inserted line. - */ - public readonly injectedTexts: (LineInjectedText[] | null)[]; - - constructor(fromLineNumber: number, toLineNumber: number, detail: string[], injectedTexts: (LineInjectedText[] | null)[]) { - this.injectedTexts = injectedTexts; - this.fromLineNumber = fromLineNumber; - this.toLineNumber = toLineNumber; - this.detail = detail; - } -} - -/** - * An event describing that a model has had its EOL changed. - * @internal - */ -export class ModelRawEOLChanged { - public readonly changeType = RawContentChangedType.EOLChanged; -} - -/** - * @internal - */ -export type ModelRawChange = ModelRawFlush | ModelRawLineChanged | ModelRawLinesDeleted | ModelRawLinesInserted | ModelRawEOLChanged; - -/** - * An event describing a change in the text of a model. - * @internal - */ -export class ModelRawContentChangedEvent { - - public readonly changes: ModelRawChange[]; - /** - * The new version id the model has transitioned to. - */ - public readonly versionId: number; - /** - * Flag that indicates that this event was generated while undoing. - */ - public readonly isUndoing: boolean; - /** - * Flag that indicates that this event was generated while redoing. - */ - public readonly isRedoing: boolean; - - public resultingSelection: Selection[] | null; - - constructor(changes: ModelRawChange[], versionId: number, isUndoing: boolean, isRedoing: boolean) { - this.changes = changes; - this.versionId = versionId; - this.isUndoing = isUndoing; - this.isRedoing = isRedoing; - this.resultingSelection = null; - } - - public containsEvent(type: RawContentChangedType): boolean { - for (let i = 0, len = this.changes.length; i < len; i++) { - const change = this.changes[i]; - if (change.changeType === type) { - return true; - } - } - return false; - } - - public static merge(a: ModelRawContentChangedEvent, b: ModelRawContentChangedEvent): ModelRawContentChangedEvent { - const changes = ([] as ModelRawChange[]).concat(a.changes).concat(b.changes); - const versionId = b.versionId; - const isUndoing = (a.isUndoing || b.isUndoing); - const isRedoing = (a.isRedoing || b.isRedoing); - return new ModelRawContentChangedEvent(changes, versionId, isUndoing, isRedoing); - } -} - -/** - * An event describing a change in injected text. - * @internal - */ -export class ModelInjectedTextChangedEvent { - - public readonly changes: ModelRawLineChanged[]; - - constructor(changes: ModelRawLineChanged[]) { - this.changes = changes; - } -} - -/** - * @internal - */ -export class InternalModelContentChangeEvent { - constructor( - public readonly rawContentChangedEvent: ModelRawContentChangedEvent, - public readonly contentChangedEvent: IModelContentChangedEvent, - ) { } - - public merge(other: InternalModelContentChangeEvent): InternalModelContentChangeEvent { - const rawContentChangedEvent = ModelRawContentChangedEvent.merge(this.rawContentChangedEvent, other.rawContentChangedEvent); - const contentChangedEvent = InternalModelContentChangeEvent._mergeChangeEvents(this.contentChangedEvent, other.contentChangedEvent); - return new InternalModelContentChangeEvent(rawContentChangedEvent, contentChangedEvent); - } - - private static _mergeChangeEvents(a: IModelContentChangedEvent, b: IModelContentChangedEvent): IModelContentChangedEvent { - const changes = ([] as IModelContentChange[]).concat(a.changes).concat(b.changes); - const eol = b.eol; - const versionId = b.versionId; - const isUndoing = (a.isUndoing || b.isUndoing); - const isRedoing = (a.isRedoing || b.isRedoing); - const isFlush = (a.isFlush || b.isFlush); - const isEolChange = a.isEolChange && b.isEolChange; // both must be true to not confuse listeners who skip such edits - return { - changes: changes, - eol: eol, - isEolChange: isEolChange, - versionId: versionId, - isUndoing: isUndoing, - isRedoing: isRedoing, - isFlush: isFlush, - }; - } -} +export * from './language/textModelEvents.js'; diff --git a/src/vs/editor/common/textModelGuides.ts b/src/vs/editor/common/textModelGuides.ts index f1cd766ec3f..b789e534425 100644 --- a/src/vs/editor/common/textModelGuides.ts +++ b/src/vs/editor/common/textModelGuides.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IPosition } from './core/position.js'; +import { IPosition } from '../../editor/common/language/core/position.js'; export interface IGuidesTextModelPart { /** diff --git a/src/vs/editor/common/tokenizationRegistry.ts b/src/vs/editor/common/tokenizationRegistry.ts index d6c0c2f39bf..7ae4337ccdd 100644 --- a/src/vs/editor/common/tokenizationRegistry.ts +++ b/src/vs/editor/common/tokenizationRegistry.ts @@ -3,150 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Color } from '../../base/common/color.js'; -import { Emitter, Event } from '../../base/common/event.js'; -import { Disposable, IDisposable, toDisposable } from '../../base/common/lifecycle.js'; -import { ITokenizationRegistry, ITokenizationSupportChangedEvent, ILazyTokenizationSupport } from './languages.js'; -import { ColorId } from './encodedTokenAttributes.js'; - -export class TokenizationRegistry implements ITokenizationRegistry { - - private readonly _tokenizationSupports = new Map(); - private readonly _factories = new Map>(); - - private readonly _onDidChange = new Emitter(); - public readonly onDidChange: Event = this._onDidChange.event; - - private _colorMap: Color[] | null; - - constructor() { - this._colorMap = null; - } - - public handleChange(languageIds: string[]): void { - this._onDidChange.fire({ - changedLanguages: languageIds, - changedColorMap: false - }); - } - - public register(languageId: string, support: TSupport): IDisposable { - this._tokenizationSupports.set(languageId, support); - this.handleChange([languageId]); - return toDisposable(() => { - if (this._tokenizationSupports.get(languageId) !== support) { - return; - } - this._tokenizationSupports.delete(languageId); - this.handleChange([languageId]); - }); - } - - public get(languageId: string): TSupport | null { - return this._tokenizationSupports.get(languageId) || null; - } - - public registerFactory(languageId: string, factory: ILazyTokenizationSupport): IDisposable { - this._factories.get(languageId)?.dispose(); - const myData = new TokenizationSupportFactoryData(this, languageId, factory); - this._factories.set(languageId, myData); - return toDisposable(() => { - const v = this._factories.get(languageId); - if (!v || v !== myData) { - return; - } - this._factories.delete(languageId); - v.dispose(); - }); - } - - public async getOrCreate(languageId: string): Promise { - // check first if the support is already set - const tokenizationSupport = this.get(languageId); - if (tokenizationSupport) { - return tokenizationSupport; - } - - const factory = this._factories.get(languageId); - if (!factory || factory.isResolved) { - // no factory or factory.resolve already finished - return null; - } - - await factory.resolve(); - - return this.get(languageId); - } - - public isResolved(languageId: string): boolean { - const tokenizationSupport = this.get(languageId); - if (tokenizationSupport) { - return true; - } - - const factory = this._factories.get(languageId); - if (!factory || factory.isResolved) { - return true; - } - - return false; - } - - public setColorMap(colorMap: Color[]): void { - this._colorMap = colorMap; - this._onDidChange.fire({ - changedLanguages: Array.from(this._tokenizationSupports.keys()), - changedColorMap: true - }); - } - - public getColorMap(): Color[] | null { - return this._colorMap; - } - - public getDefaultBackground(): Color | null { - if (this._colorMap && this._colorMap.length > ColorId.DefaultBackground) { - return this._colorMap[ColorId.DefaultBackground]; - } - return null; - } -} - -class TokenizationSupportFactoryData extends Disposable { - - private _isDisposed: boolean = false; - private _resolvePromise: Promise | null = null; - private _isResolved: boolean = false; - - public get isResolved(): boolean { - return this._isResolved; - } - - constructor( - private readonly _registry: TokenizationRegistry, - private readonly _languageId: string, - private readonly _factory: ILazyTokenizationSupport, - ) { - super(); - } - - public override dispose(): void { - this._isDisposed = true; - super.dispose(); - } - - public async resolve(): Promise { - if (!this._resolvePromise) { - this._resolvePromise = this._create(); - } - return this._resolvePromise; - } - - private async _create(): Promise { - const value = await this._factory.tokenizationSupport; - this._isResolved = true; - if (value && !this._isDisposed) { - this._register(this._registry.register(this._languageId, value)); - } - } -} +export * from './language/tokenizationRegistry.js'; diff --git a/src/vs/editor/common/tokenizationTextModelPart.ts b/src/vs/editor/common/tokenizationTextModelPart.ts index 6238c606552..3ee18106f24 100644 --- a/src/vs/editor/common/tokenizationTextModelPart.ts +++ b/src/vs/editor/common/tokenizationTextModelPart.ts @@ -3,100 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from './core/range.js'; -import { StandardTokenType } from './encodedTokenAttributes.js'; -import { LineTokens } from './tokens/lineTokens.js'; -import { SparseMultilineTokens } from './tokens/sparseMultilineTokens.js'; - -/** - * Provides tokenization related functionality of the text model. -*/ -export interface ITokenizationTextModelPart { - readonly hasTokens: boolean; - - /** - * Replaces all semantic tokens with the provided `tokens`. - * @internal - */ - setSemanticTokens(tokens: SparseMultilineTokens[] | null, isComplete: boolean): void; - - /** - * Merges the provided semantic tokens into existing semantic tokens. - * @internal - */ - setPartialSemanticTokens(range: Range, tokens: SparseMultilineTokens[] | null): void; - - /** - * @internal - */ - hasCompleteSemanticTokens(): boolean; - - /** - * @internal - */ - hasSomeSemanticTokens(): boolean; - - /** - * Flush all tokenization state. - * @internal - */ - resetTokenization(): void; - - /** - * Force tokenization information for `lineNumber` to be accurate. - * @internal - */ - forceTokenization(lineNumber: number): void; - - /** - * If it is cheap, force tokenization information for `lineNumber` to be accurate. - * This is based on a heuristic. - * @internal - */ - tokenizeIfCheap(lineNumber: number): void; - - /** - * Check if tokenization information is accurate for `lineNumber`. - * @internal - */ - hasAccurateTokensForLine(lineNumber: number): boolean; - - /** - * Check if calling `forceTokenization` for this `lineNumber` will be cheap (time-wise). - * This is based on a heuristic. - * @internal - */ - isCheapToTokenize(lineNumber: number): boolean; - - /** - * Get the tokens for the line `lineNumber`. - * The tokens might be inaccurate. Use `forceTokenization` to ensure accurate tokens. - * @internal - */ - getLineTokens(lineNumber: number): LineTokens; - - /** - * Returns the standard token type for a character if the character were to be inserted at - * the given position. If the result cannot be accurate, it returns null. - * @internal - */ - getTokenTypeIfInsertingCharacter(lineNumber: number, column: number, character: string): StandardTokenType; - - /** - * Tokens the lines as if they were inserted at [lineNumber, lineNumber). - * @internal - */ - tokenizeLinesAt(lineNumber: number, lines: string[]): LineTokens[] | null; - - getLanguageId(): string; - getLanguageIdAtPosition(lineNumber: number, column: number): string; - - setLanguageId(languageId: string, source?: string): void; - - readonly backgroundTokenizationState: BackgroundTokenizationState; -} - -export const enum BackgroundTokenizationState { - InProgress = 1, - Completed = 2, -} +export * from './language/tokenizationTextModelPart.js'; diff --git a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts index 3e3e8886f1b..b3a9f52c405 100644 --- a/src/vs/editor/common/tokens/contiguousMultilineTokens.ts +++ b/src/vs/editor/common/tokens/contiguousMultilineTokens.ts @@ -3,227 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from '../../../base/common/arrays.js'; -import { readUInt32BE, writeUInt32BE } from '../../../base/common/buffer.js'; -import { Position } from '../core/position.js'; -import { IRange } from '../core/range.js'; -import { countEOL } from '../core/eolCounter.js'; -import { ContiguousTokensEditing } from './contiguousTokensEditing.js'; -import { LineRange } from '../core/lineRange.js'; - -/** - * Represents contiguous tokens over a contiguous range of lines. - */ -export class ContiguousMultilineTokens { - public static deserialize(buff: Uint8Array, offset: number, result: ContiguousMultilineTokens[]): number { - const view32 = new Uint32Array(buff.buffer); - const startLineNumber = readUInt32BE(buff, offset); offset += 4; - const count = readUInt32BE(buff, offset); offset += 4; - const tokens: Uint32Array[] = []; - for (let i = 0; i < count; i++) { - const byteCount = readUInt32BE(buff, offset); offset += 4; - tokens.push(view32.subarray(offset / 4, offset / 4 + byteCount / 4)); - offset += byteCount; - } - result.push(new ContiguousMultilineTokens(startLineNumber, tokens)); - return offset; - } - - /** - * The start line number for this block of tokens. - */ - private _startLineNumber: number; - - /** - * The tokens are stored in a binary format. There is an element for each line, - * so `tokens[index]` contains all tokens on line `startLineNumber + index`. - * - * On a specific line, each token occupies two array indices. For token i: - * - at offset 2*i => endOffset - * - at offset 2*i + 1 => metadata - * - */ - private _tokens: (Uint32Array | ArrayBuffer | null)[]; - - /** - * (Inclusive) start line number for these tokens. - */ - public get startLineNumber(): number { - return this._startLineNumber; - } - - /** - * (Inclusive) end line number for these tokens. - */ - public get endLineNumber(): number { - return this._startLineNumber + this._tokens.length - 1; - } - - constructor(startLineNumber: number, tokens: Uint32Array[]) { - this._startLineNumber = startLineNumber; - this._tokens = tokens; - } - - getLineRange(): LineRange { - return new LineRange(this._startLineNumber, this._startLineNumber + this._tokens.length); - } - - /** - * @see {@link _tokens} - */ - public getLineTokens(lineNumber: number): Uint32Array | ArrayBuffer | null { - return this._tokens[lineNumber - this._startLineNumber]; - } - - public appendLineTokens(lineTokens: Uint32Array): void { - this._tokens.push(lineTokens); - } - - public serializeSize(): number { - let result = 0; - result += 4; // 4 bytes for the start line number - result += 4; // 4 bytes for the line count - for (let i = 0; i < this._tokens.length; i++) { - const lineTokens = this._tokens[i]; - if (!(lineTokens instanceof Uint32Array)) { - throw new Error(`Not supported!`); - } - result += 4; // 4 bytes for the byte count - result += lineTokens.byteLength; - } - return result; - } - - public serialize(destination: Uint8Array, offset: number): number { - writeUInt32BE(destination, this._startLineNumber, offset); offset += 4; - writeUInt32BE(destination, this._tokens.length, offset); offset += 4; - for (let i = 0; i < this._tokens.length; i++) { - const lineTokens = this._tokens[i]; - if (!(lineTokens instanceof Uint32Array)) { - throw new Error(`Not supported!`); - } - writeUInt32BE(destination, lineTokens.byteLength, offset); offset += 4; - destination.set(new Uint8Array(lineTokens.buffer), offset); offset += lineTokens.byteLength; - } - return offset; - } - - public applyEdit(range: IRange, text: string): void { - const [eolCount, firstLineLength] = countEOL(text); - this._acceptDeleteRange(range); - this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); - } - - private _acceptDeleteRange(range: IRange): void { - if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) { - // Nothing to delete - return; - } - - const firstLineIndex = range.startLineNumber - this._startLineNumber; - const lastLineIndex = range.endLineNumber - this._startLineNumber; - - if (lastLineIndex < 0) { - // this deletion occurs entirely before this block, so we only need to adjust line numbers - const deletedLinesCount = lastLineIndex - firstLineIndex; - this._startLineNumber -= deletedLinesCount; - return; - } - - if (firstLineIndex >= this._tokens.length) { - // this deletion occurs entirely after this block, so there is nothing to do - return; - } - - if (firstLineIndex < 0 && lastLineIndex >= this._tokens.length) { - // this deletion completely encompasses this block - this._startLineNumber = 0; - this._tokens = []; - return; - } - - if (firstLineIndex === lastLineIndex) { - // a delete on a single line - this._tokens[firstLineIndex] = ContiguousTokensEditing.delete(this._tokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1); - return; - } - - if (firstLineIndex >= 0) { - // The first line survives - this._tokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[firstLineIndex], range.startColumn - 1); - - if (lastLineIndex < this._tokens.length) { - // The last line survives - const lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1); - - // Take remaining text on last line and append it to remaining text on first line - this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], lastLineTokens); - - // Delete middle lines - this._tokens.splice(firstLineIndex + 1, lastLineIndex - firstLineIndex); - } else { - // The last line does not survive - - // Take remaining text on last line and append it to remaining text on first line - this._tokens[firstLineIndex] = ContiguousTokensEditing.append(this._tokens[firstLineIndex], null); - - // Delete lines - this._tokens = this._tokens.slice(0, firstLineIndex + 1); - } - } else { - // The first line does not survive - - const deletedBefore = -firstLineIndex; - this._startLineNumber -= deletedBefore; - - // Remove beginning from last line - this._tokens[lastLineIndex] = ContiguousTokensEditing.deleteBeginning(this._tokens[lastLineIndex], range.endColumn - 1); - - // Delete lines - this._tokens = this._tokens.slice(lastLineIndex); - } - } - - private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { - - if (eolCount === 0 && firstLineLength === 0) { - // Nothing to insert - return; - } - - const lineIndex = position.lineNumber - this._startLineNumber; - - if (lineIndex < 0) { - // this insertion occurs before this block, so we only need to adjust line numbers - this._startLineNumber += eolCount; - return; - } - - if (lineIndex >= this._tokens.length) { - // this insertion occurs after this block, so there is nothing to do - return; - } - - if (eolCount === 0) { - // Inserting text on one line - this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength); - return; - } - - this._tokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._tokens[lineIndex], position.column - 1); - this._tokens[lineIndex] = ContiguousTokensEditing.insert(this._tokens[lineIndex], position.column - 1, firstLineLength); - - this._insertLines(position.lineNumber, eolCount); - } - - private _insertLines(insertIndex: number, insertCount: number): void { - if (insertCount === 0) { - return; - } - const lineTokens: (Uint32Array | ArrayBuffer | null)[] = []; - for (let i = 0; i < insertCount; i++) { - lineTokens[i] = null; - } - this._tokens = arrays.arrayInsert(this._tokens, insertIndex, lineTokens); - } -} +export * from '../language/tokens/contiguousMultilineTokens.js'; diff --git a/src/vs/editor/common/tokens/contiguousMultilineTokensBuilder.ts b/src/vs/editor/common/tokens/contiguousMultilineTokensBuilder.ts index f2b34cc9cd1..e10570f820f 100644 --- a/src/vs/editor/common/tokens/contiguousMultilineTokensBuilder.ts +++ b/src/vs/editor/common/tokens/contiguousMultilineTokensBuilder.ts @@ -3,64 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { readUInt32BE, writeUInt32BE } from '../../../base/common/buffer.js'; -import { ContiguousMultilineTokens } from './contiguousMultilineTokens.js'; - -export class ContiguousMultilineTokensBuilder { - - public static deserialize(buff: Uint8Array): ContiguousMultilineTokens[] { - let offset = 0; - const count = readUInt32BE(buff, offset); offset += 4; - const result: ContiguousMultilineTokens[] = []; - for (let i = 0; i < count; i++) { - offset = ContiguousMultilineTokens.deserialize(buff, offset, result); - } - return result; - } - - private readonly _tokens: ContiguousMultilineTokens[]; - - constructor() { - this._tokens = []; - } - - public add(lineNumber: number, lineTokens: Uint32Array): void { - if (this._tokens.length > 0) { - const last = this._tokens[this._tokens.length - 1]; - if (last.endLineNumber + 1 === lineNumber) { - // append - last.appendLineTokens(lineTokens); - return; - } - } - this._tokens.push(new ContiguousMultilineTokens(lineNumber, [lineTokens])); - } - - public finalize(): ContiguousMultilineTokens[] { - return this._tokens; - } - - public serialize(): Uint8Array { - const size = this._serializeSize(); - const result = new Uint8Array(size); - this._serialize(result); - return result; - } - - private _serializeSize(): number { - let result = 0; - result += 4; // 4 bytes for the count - for (let i = 0; i < this._tokens.length; i++) { - result += this._tokens[i].serializeSize(); - } - return result; - } - - private _serialize(destination: Uint8Array): void { - let offset = 0; - writeUInt32BE(destination, this._tokens.length, offset); offset += 4; - for (let i = 0; i < this._tokens.length; i++) { - offset = this._tokens[i].serialize(destination, offset); - } - } -} +export * from '../language/tokens/contiguousMultilineTokensBuilder.js'; diff --git a/src/vs/editor/common/tokens/contiguousTokensEditing.ts b/src/vs/editor/common/tokens/contiguousTokensEditing.ts index 28d444a2df0..9c89fc38754 100644 --- a/src/vs/editor/common/tokens/contiguousTokensEditing.ts +++ b/src/vs/editor/common/tokens/contiguousTokensEditing.ts @@ -3,142 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineTokens } from './lineTokens.js'; - -export const EMPTY_LINE_TOKENS = (new Uint32Array(0)).buffer; - -export class ContiguousTokensEditing { - - public static deleteBeginning(lineTokens: Uint32Array | ArrayBuffer | null, toChIndex: number): Uint32Array | ArrayBuffer | null { - if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { - return lineTokens; - } - return ContiguousTokensEditing.delete(lineTokens, 0, toChIndex); - } - - public static deleteEnding(lineTokens: Uint32Array | ArrayBuffer | null, fromChIndex: number): Uint32Array | ArrayBuffer | null { - if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { - return lineTokens; - } - - const tokens = toUint32Array(lineTokens); - const lineTextLength = tokens[tokens.length - 2]; - return ContiguousTokensEditing.delete(lineTokens, fromChIndex, lineTextLength); - } - - public static delete(lineTokens: Uint32Array | ArrayBuffer | null, fromChIndex: number, toChIndex: number): Uint32Array | ArrayBuffer | null { - if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS || fromChIndex === toChIndex) { - return lineTokens; - } - - const tokens = toUint32Array(lineTokens); - const tokensCount = (tokens.length >>> 1); - - // special case: deleting everything - if (fromChIndex === 0 && tokens[tokens.length - 2] === toChIndex) { - return EMPTY_LINE_TOKENS; - } - - const fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, fromChIndex); - const fromTokenStartOffset = (fromTokenIndex > 0 ? tokens[(fromTokenIndex - 1) << 1] : 0); - const fromTokenEndOffset = tokens[fromTokenIndex << 1]; - - if (toChIndex < fromTokenEndOffset) { - // the delete range is inside a single token - const delta = (toChIndex - fromChIndex); - for (let i = fromTokenIndex; i < tokensCount; i++) { - tokens[i << 1] -= delta; - } - return lineTokens; - } - - let dest: number; - let lastEnd: number; - if (fromTokenStartOffset !== fromChIndex) { - tokens[fromTokenIndex << 1] = fromChIndex; - dest = ((fromTokenIndex + 1) << 1); - lastEnd = fromChIndex; - } else { - dest = (fromTokenIndex << 1); - lastEnd = fromTokenStartOffset; - } - - const delta = (toChIndex - fromChIndex); - for (let tokenIndex = fromTokenIndex + 1; tokenIndex < tokensCount; tokenIndex++) { - const tokenEndOffset = tokens[tokenIndex << 1] - delta; - if (tokenEndOffset > lastEnd) { - tokens[dest++] = tokenEndOffset; - tokens[dest++] = tokens[(tokenIndex << 1) + 1]; - lastEnd = tokenEndOffset; - } - } - - if (dest === tokens.length) { - // nothing to trim - return lineTokens; - } - - const tmp = new Uint32Array(dest); - tmp.set(tokens.subarray(0, dest), 0); - return tmp.buffer; - } - - public static append(lineTokens: Uint32Array | ArrayBuffer | null, _otherTokens: Uint32Array | ArrayBuffer | null): Uint32Array | ArrayBuffer | null { - if (_otherTokens === EMPTY_LINE_TOKENS) { - return lineTokens; - } - if (lineTokens === EMPTY_LINE_TOKENS) { - return _otherTokens; - } - if (lineTokens === null) { - return lineTokens; - } - if (_otherTokens === null) { - // cannot determine combined line length... - return null; - } - const myTokens = toUint32Array(lineTokens); - const otherTokens = toUint32Array(_otherTokens); - const otherTokensCount = (otherTokens.length >>> 1); - - const result = new Uint32Array(myTokens.length + otherTokens.length); - result.set(myTokens, 0); - let dest = myTokens.length; - const delta = myTokens[myTokens.length - 2]; - for (let i = 0; i < otherTokensCount; i++) { - result[dest++] = otherTokens[(i << 1)] + delta; - result[dest++] = otherTokens[(i << 1) + 1]; - } - return result.buffer; - } - - public static insert(lineTokens: Uint32Array | ArrayBuffer | null, chIndex: number, textLength: number): Uint32Array | ArrayBuffer | null { - if (lineTokens === null || lineTokens === EMPTY_LINE_TOKENS) { - // nothing to do - return lineTokens; - } - - const tokens = toUint32Array(lineTokens); - const tokensCount = (tokens.length >>> 1); - - let fromTokenIndex = LineTokens.findIndexInTokensArray(tokens, chIndex); - if (fromTokenIndex > 0) { - const fromTokenStartOffset = tokens[(fromTokenIndex - 1) << 1]; - if (fromTokenStartOffset === chIndex) { - fromTokenIndex--; - } - } - for (let tokenIndex = fromTokenIndex; tokenIndex < tokensCount; tokenIndex++) { - tokens[tokenIndex << 1] += textLength; - } - return lineTokens; - } -} - -export function toUint32Array(arr: Uint32Array | ArrayBuffer): Uint32Array { - if (arr instanceof Uint32Array) { - return arr; - } else { - return new Uint32Array(arr); - } -} +export * from '../language/tokens/contiguousTokensEditing.js'; diff --git a/src/vs/editor/common/tokens/contiguousTokensStore.ts b/src/vs/editor/common/tokens/contiguousTokensStore.ts index 4134eee8507..09a9a299f77 100644 --- a/src/vs/editor/common/tokens/contiguousTokensStore.ts +++ b/src/vs/editor/common/tokens/contiguousTokensStore.ts @@ -3,255 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from '../../../base/common/arrays.js'; -import { Position } from '../core/position.js'; -import { IRange } from '../core/range.js'; -import { ContiguousTokensEditing, EMPTY_LINE_TOKENS, toUint32Array } from './contiguousTokensEditing.js'; -import { LineTokens } from './lineTokens.js'; -import { ILanguageIdCodec } from '../languages.js'; -import { LanguageId, FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata } from '../encodedTokenAttributes.js'; -import { ITextModel } from '../model.js'; -import { ContiguousMultilineTokens } from './contiguousMultilineTokens.js'; - -/** - * Represents contiguous tokens in a text model. - */ -export class ContiguousTokensStore { - private _lineTokens: (Uint32Array | ArrayBuffer | null)[]; - private _len: number; - private readonly _languageIdCodec: ILanguageIdCodec; - - constructor(languageIdCodec: ILanguageIdCodec) { - this._lineTokens = []; - this._len = 0; - this._languageIdCodec = languageIdCodec; - } - - public flush(): void { - this._lineTokens = []; - this._len = 0; - } - - get hasTokens(): boolean { - return this._lineTokens.length > 0; - } - - public getTokens(topLevelLanguageId: string, lineIndex: number, lineText: string): LineTokens { - let rawLineTokens: Uint32Array | ArrayBuffer | null = null; - if (lineIndex < this._len) { - rawLineTokens = this._lineTokens[lineIndex]; - } - - if (rawLineTokens !== null && rawLineTokens !== EMPTY_LINE_TOKENS) { - return new LineTokens(toUint32Array(rawLineTokens), lineText, this._languageIdCodec); - } - - const lineTokens = new Uint32Array(2); - lineTokens[0] = lineText.length; - lineTokens[1] = getDefaultMetadata(this._languageIdCodec.encodeLanguageId(topLevelLanguageId)); - return new LineTokens(lineTokens, lineText, this._languageIdCodec); - } - - private static _massageTokens(topLevelLanguageId: LanguageId, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null): Uint32Array | ArrayBuffer { - - const tokens = _tokens ? toUint32Array(_tokens) : null; - - if (lineTextLength === 0) { - let hasDifferentLanguageId = false; - if (tokens && tokens.length > 1) { - hasDifferentLanguageId = (TokenMetadata.getLanguageId(tokens[1]) !== topLevelLanguageId); - } - - if (!hasDifferentLanguageId) { - return EMPTY_LINE_TOKENS; - } - } - - if (!tokens || tokens.length === 0) { - const tokens = new Uint32Array(2); - tokens[0] = lineTextLength; - tokens[1] = getDefaultMetadata(topLevelLanguageId); - return tokens.buffer; - } - - // Ensure the last token covers the end of the text - tokens[tokens.length - 2] = lineTextLength; - - if (tokens.byteOffset === 0 && tokens.byteLength === tokens.buffer.byteLength) { - // Store directly the ArrayBuffer pointer to save an object - return tokens.buffer; - } - return tokens; - } - - private _ensureLine(lineIndex: number): void { - while (lineIndex >= this._len) { - this._lineTokens[this._len] = null; - this._len++; - } - } - - private _deleteLines(start: number, deleteCount: number): void { - if (deleteCount === 0) { - return; - } - if (start + deleteCount > this._len) { - deleteCount = this._len - start; - } - this._lineTokens.splice(start, deleteCount); - this._len -= deleteCount; - } - - private _insertLines(insertIndex: number, insertCount: number): void { - if (insertCount === 0) { - return; - } - const lineTokens: (Uint32Array | ArrayBuffer | null)[] = []; - for (let i = 0; i < insertCount; i++) { - lineTokens[i] = null; - } - this._lineTokens = arrays.arrayInsert(this._lineTokens, insertIndex, lineTokens); - this._len += insertCount; - } - - public setTokens(topLevelLanguageId: string, lineIndex: number, lineTextLength: number, _tokens: Uint32Array | ArrayBuffer | null, checkEquality: boolean): boolean { - const tokens = ContiguousTokensStore._massageTokens(this._languageIdCodec.encodeLanguageId(topLevelLanguageId), lineTextLength, _tokens); - this._ensureLine(lineIndex); - const oldTokens = this._lineTokens[lineIndex]; - this._lineTokens[lineIndex] = tokens; - - if (checkEquality) { - return !ContiguousTokensStore._equals(oldTokens, tokens); - } - return false; - } - - private static _equals(_a: Uint32Array | ArrayBuffer | null, _b: Uint32Array | ArrayBuffer | null) { - if (!_a || !_b) { - return !_a && !_b; - } - - const a = toUint32Array(_a); - const b = toUint32Array(_b); - - if (a.length !== b.length) { - return false; - } - for (let i = 0, len = a.length; i < len; i++) { - if (a[i] !== b[i]) { - return false; - } - } - return true; - } - - //#region Editing - - public acceptEdit(range: IRange, eolCount: number, firstLineLength: number): void { - this._acceptDeleteRange(range); - this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength); - } - - private _acceptDeleteRange(range: IRange): void { - - const firstLineIndex = range.startLineNumber - 1; - if (firstLineIndex >= this._len) { - return; - } - - if (range.startLineNumber === range.endLineNumber) { - if (range.startColumn === range.endColumn) { - // Nothing to delete - return; - } - - this._lineTokens[firstLineIndex] = ContiguousTokensEditing.delete(this._lineTokens[firstLineIndex], range.startColumn - 1, range.endColumn - 1); - return; - } - - this._lineTokens[firstLineIndex] = ContiguousTokensEditing.deleteEnding(this._lineTokens[firstLineIndex], range.startColumn - 1); - - const lastLineIndex = range.endLineNumber - 1; - let lastLineTokens: Uint32Array | ArrayBuffer | null = null; - if (lastLineIndex < this._len) { - lastLineTokens = ContiguousTokensEditing.deleteBeginning(this._lineTokens[lastLineIndex], range.endColumn - 1); - } - - // Take remaining text on last line and append it to remaining text on first line - this._lineTokens[firstLineIndex] = ContiguousTokensEditing.append(this._lineTokens[firstLineIndex], lastLineTokens); - - // Delete middle lines - this._deleteLines(range.startLineNumber, range.endLineNumber - range.startLineNumber); - } - - private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number): void { - - if (eolCount === 0 && firstLineLength === 0) { - // Nothing to insert - return; - } - - const lineIndex = position.lineNumber - 1; - if (lineIndex >= this._len) { - return; - } - - if (eolCount === 0) { - // Inserting text on one line - this._lineTokens[lineIndex] = ContiguousTokensEditing.insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); - return; - } - - this._lineTokens[lineIndex] = ContiguousTokensEditing.deleteEnding(this._lineTokens[lineIndex], position.column - 1); - this._lineTokens[lineIndex] = ContiguousTokensEditing.insert(this._lineTokens[lineIndex], position.column - 1, firstLineLength); - - this._insertLines(position.lineNumber, eolCount); - } - - //#endregion - - public setMultilineTokens(tokens: ContiguousMultilineTokens[], textModel: ITextModel): { changes: { fromLineNumber: number; toLineNumber: number }[] } { - if (tokens.length === 0) { - return { changes: [] }; - } - - const ranges: { fromLineNumber: number; toLineNumber: number }[] = []; - - for (let i = 0, len = tokens.length; i < len; i++) { - const element = tokens[i]; - let minChangedLineNumber = 0; - let maxChangedLineNumber = 0; - let hasChange = false; - for (let lineNumber = element.startLineNumber; lineNumber <= element.endLineNumber; lineNumber++) { - if (hasChange) { - this.setTokens(textModel.getLanguageId(), lineNumber - 1, textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), false); - maxChangedLineNumber = lineNumber; - } else { - const lineHasChange = this.setTokens(textModel.getLanguageId(), lineNumber - 1, textModel.getLineLength(lineNumber), element.getLineTokens(lineNumber), true); - if (lineHasChange) { - hasChange = true; - minChangedLineNumber = lineNumber; - maxChangedLineNumber = lineNumber; - } - } - } - if (hasChange) { - ranges.push({ fromLineNumber: minChangedLineNumber, toLineNumber: maxChangedLineNumber, }); - } - } - - return { changes: ranges }; - } -} - -function getDefaultMetadata(topLevelLanguageId: LanguageId): number { - return ( - (topLevelLanguageId << MetadataConsts.LANGUAGEID_OFFSET) - | (StandardTokenType.Other << MetadataConsts.TOKEN_TYPE_OFFSET) - | (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) - | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) - | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) - // If there is no grammar, we just take a guess and try to match brackets. - | (MetadataConsts.BALANCED_BRACKETS_MASK) - ) >>> 0; -} +export * from '../language/tokens/contiguousTokensStore.js'; diff --git a/src/vs/editor/common/tokens/lineTokens.ts b/src/vs/editor/common/tokens/lineTokens.ts index ebe43f09b96..908e92b1f44 100644 --- a/src/vs/editor/common/tokens/lineTokens.ts +++ b/src/vs/editor/common/tokens/lineTokens.ts @@ -3,418 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILanguageIdCodec } from '../languages.js'; -import { FontStyle, ColorId, StandardTokenType, MetadataConsts, TokenMetadata, ITokenPresentation } from '../encodedTokenAttributes.js'; -import { IPosition } from '../core/position.js'; -import { ITextModel } from '../model.js'; -import { OffsetRange } from '../core/offsetRange.js'; -import { TokenArray, TokenArrayBuilder } from './tokenArray.js'; -import { onUnexpectedError } from '../../../base/common/errors.js'; - - -export interface IViewLineTokens { - languageIdCodec: ILanguageIdCodec; - equals(other: IViewLineTokens): boolean; - getCount(): number; - getStandardTokenType(tokenIndex: number): StandardTokenType; - getForeground(tokenIndex: number): ColorId; - getEndOffset(tokenIndex: number): number; - getClassName(tokenIndex: number): string; - getInlineStyle(tokenIndex: number, colorMap: string[]): string; - getPresentation(tokenIndex: number): ITokenPresentation; - findTokenIndexAtOffset(offset: number): number; - getLineContent(): string; - getMetadata(tokenIndex: number): number; - getLanguageId(tokenIndex: number): string; - getTokenText(tokenIndex: number): string; - forEach(callback: (tokenIndex: number) => void): void; -} - -export class LineTokens implements IViewLineTokens { - public static createEmpty(lineContent: string, decoder: ILanguageIdCodec): LineTokens { - const defaultMetadata = LineTokens.defaultTokenMetadata; - - const tokens = new Uint32Array(2); - tokens[0] = lineContent.length; - tokens[1] = defaultMetadata; - - return new LineTokens(tokens, lineContent, decoder); - } - - public static createFromTextAndMetadata(data: { text: string; metadata: number }[], decoder: ILanguageIdCodec): LineTokens { - let offset: number = 0; - let fullText: string = ''; - const tokens = new Array(); - for (const { text, metadata } of data) { - tokens.push(offset + text.length, metadata); - offset += text.length; - fullText += text; - } - return new LineTokens(new Uint32Array(tokens), fullText, decoder); - } - - public static convertToEndOffset(tokens: Uint32Array, lineTextLength: number): void { - const tokenCount = (tokens.length >>> 1); - const lastTokenIndex = tokenCount - 1; - for (let tokenIndex = 0; tokenIndex < lastTokenIndex; tokenIndex++) { - tokens[tokenIndex << 1] = tokens[(tokenIndex + 1) << 1]; - } - tokens[lastTokenIndex << 1] = lineTextLength; - } - - public static findIndexInTokensArray(tokens: Uint32Array, desiredIndex: number): number { - if (tokens.length <= 2) { - return 0; - } - - let low = 0; - let high = (tokens.length >>> 1) - 1; - - while (low < high) { - - const mid = low + Math.floor((high - low) / 2); - const endOffset = tokens[(mid << 1)]; - - if (endOffset === desiredIndex) { - return mid + 1; - } else if (endOffset < desiredIndex) { - low = mid + 1; - } else if (endOffset > desiredIndex) { - high = mid; - } - } - - return low; - } - - _lineTokensBrand: void = undefined; - - private readonly _tokens: Uint32Array; - private readonly _tokensCount: number; - private readonly _text: string; - - public readonly languageIdCodec: ILanguageIdCodec; - - public static defaultTokenMetadata = ( - (FontStyle.None << MetadataConsts.FONT_STYLE_OFFSET) - | (ColorId.DefaultForeground << MetadataConsts.FOREGROUND_OFFSET) - | (ColorId.DefaultBackground << MetadataConsts.BACKGROUND_OFFSET) - ) >>> 0; - - constructor(tokens: Uint32Array, text: string, decoder: ILanguageIdCodec) { - const tokensLength = tokens.length > 1 ? tokens[tokens.length - 2] : 0; - if (tokensLength !== text.length) { - onUnexpectedError(new Error('Token length and text length do not match!')); - } - this._tokens = tokens; - this._tokensCount = (this._tokens.length >>> 1); - this._text = text; - this.languageIdCodec = decoder; - } - - public equals(other: IViewLineTokens): boolean { - if (other instanceof LineTokens) { - return this.slicedEquals(other, 0, this._tokensCount); - } - return false; - } - - public slicedEquals(other: LineTokens, sliceFromTokenIndex: number, sliceTokenCount: number): boolean { - if (this._text !== other._text) { - return false; - } - if (this._tokensCount !== other._tokensCount) { - return false; - } - const from = (sliceFromTokenIndex << 1); - const to = from + (sliceTokenCount << 1); - for (let i = from; i < to; i++) { - if (this._tokens[i] !== other._tokens[i]) { - return false; - } - } - return true; - } - - public getLineContent(): string { - return this._text; - } - - public getCount(): number { - return this._tokensCount; - } - - public getStartOffset(tokenIndex: number): number { - if (tokenIndex > 0) { - return this._tokens[(tokenIndex - 1) << 1]; - } - return 0; - } - - public getMetadata(tokenIndex: number): number { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return metadata; - } - - public getLanguageId(tokenIndex: number): string { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - const languageId = TokenMetadata.getLanguageId(metadata); - return this.languageIdCodec.decodeLanguageId(languageId); - } - - public getStandardTokenType(tokenIndex: number): StandardTokenType { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return TokenMetadata.getTokenType(metadata); - } - - public getForeground(tokenIndex: number): ColorId { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return TokenMetadata.getForeground(metadata); - } - - public getClassName(tokenIndex: number): string { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return TokenMetadata.getClassNameFromMetadata(metadata); - } - - public getInlineStyle(tokenIndex: number, colorMap: string[]): string { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return TokenMetadata.getInlineStyleFromMetadata(metadata, colorMap); - } - - public getPresentation(tokenIndex: number): ITokenPresentation { - const metadata = this._tokens[(tokenIndex << 1) + 1]; - return TokenMetadata.getPresentationFromMetadata(metadata); - } - - public getEndOffset(tokenIndex: number): number { - return this._tokens[tokenIndex << 1]; - } - - /** - * Find the token containing offset `offset`. - * @param offset The search offset - * @return The index of the token containing the offset. - */ - public findTokenIndexAtOffset(offset: number): number { - return LineTokens.findIndexInTokensArray(this._tokens, offset); - } - - public inflate(): IViewLineTokens { - return this; - } - - public sliceAndInflate(startOffset: number, endOffset: number, deltaOffset: number): IViewLineTokens { - return new SliceLineTokens(this, startOffset, endOffset, deltaOffset); - } - - public sliceZeroCopy(range: OffsetRange): IViewLineTokens { - return this.sliceAndInflate(range.start, range.endExclusive, 0); - } - - /** - * @pure - * @param insertTokens Must be sorted by offset. - */ - public withInserted(insertTokens: { offset: number; text: string; tokenMetadata: number }[]): LineTokens { - if (insertTokens.length === 0) { - return this; - } - - let nextOriginalTokenIdx = 0; - let nextInsertTokenIdx = 0; - let text = ''; - const newTokens = new Array(); - - let originalEndOffset = 0; - while (true) { - const nextOriginalTokenEndOffset = nextOriginalTokenIdx < this._tokensCount ? this._tokens[nextOriginalTokenIdx << 1] : -1; - const nextInsertToken = nextInsertTokenIdx < insertTokens.length ? insertTokens[nextInsertTokenIdx] : null; - - if (nextOriginalTokenEndOffset !== -1 && (nextInsertToken === null || nextOriginalTokenEndOffset <= nextInsertToken.offset)) { - // original token ends before next insert token - text += this._text.substring(originalEndOffset, nextOriginalTokenEndOffset); - const metadata = this._tokens[(nextOriginalTokenIdx << 1) + 1]; - newTokens.push(text.length, metadata); - nextOriginalTokenIdx++; - originalEndOffset = nextOriginalTokenEndOffset; - - } else if (nextInsertToken) { - if (nextInsertToken.offset > originalEndOffset) { - // insert token is in the middle of the next token. - text += this._text.substring(originalEndOffset, nextInsertToken.offset); - const metadata = this._tokens[(nextOriginalTokenIdx << 1) + 1]; - newTokens.push(text.length, metadata); - originalEndOffset = nextInsertToken.offset; - } - - text += nextInsertToken.text; - newTokens.push(text.length, nextInsertToken.tokenMetadata); - nextInsertTokenIdx++; - } else { - break; - } - } - - return new LineTokens(new Uint32Array(newTokens), text, this.languageIdCodec); - } - - public getTokensInRange(range: OffsetRange): TokenArray { - const builder = new TokenArrayBuilder(); - - const startTokenIndex = this.findTokenIndexAtOffset(range.start); - const endTokenIndex = this.findTokenIndexAtOffset(range.endExclusive); - - for (let tokenIndex = startTokenIndex; tokenIndex <= endTokenIndex; tokenIndex++) { - const tokenRange = new OffsetRange(this.getStartOffset(tokenIndex), this.getEndOffset(tokenIndex)); - const length = tokenRange.intersectionLength(range); - if (length > 0) { - builder.add(length, this.getMetadata(tokenIndex)); - } - } - - return builder.build(); - } - - public getTokenText(tokenIndex: number): string { - const startOffset = this.getStartOffset(tokenIndex); - const endOffset = this.getEndOffset(tokenIndex); - const text = this._text.substring(startOffset, endOffset); - return text; - } - - public forEach(callback: (tokenIndex: number) => void): void { - const tokenCount = this.getCount(); - for (let tokenIndex = 0; tokenIndex < tokenCount; tokenIndex++) { - callback(tokenIndex); - } - } - - toString(): string { - let result = ''; - this.forEach((i) => { - result += `[${this.getTokenText(i)}]{${this.getClassName(i)}}`; - }); - return result; - } -} - -class SliceLineTokens implements IViewLineTokens { - - private readonly _source: LineTokens; - private readonly _startOffset: number; - private readonly _endOffset: number; - private readonly _deltaOffset: number; - - private readonly _firstTokenIndex: number; - private readonly _tokensCount: number; - - public readonly languageIdCodec: ILanguageIdCodec; - - constructor(source: LineTokens, startOffset: number, endOffset: number, deltaOffset: number) { - this._source = source; - this._startOffset = startOffset; - this._endOffset = endOffset; - this._deltaOffset = deltaOffset; - this._firstTokenIndex = source.findTokenIndexAtOffset(startOffset); - this.languageIdCodec = source.languageIdCodec; - - this._tokensCount = 0; - for (let i = this._firstTokenIndex, len = source.getCount(); i < len; i++) { - const tokenStartOffset = source.getStartOffset(i); - if (tokenStartOffset >= endOffset) { - break; - } - this._tokensCount++; - } - } - - public getMetadata(tokenIndex: number): number { - return this._source.getMetadata(this._firstTokenIndex + tokenIndex); - } - - public getLanguageId(tokenIndex: number): string { - return this._source.getLanguageId(this._firstTokenIndex + tokenIndex); - } - - public getLineContent(): string { - return this._source.getLineContent().substring(this._startOffset, this._endOffset); - } - - public equals(other: IViewLineTokens): boolean { - if (other instanceof SliceLineTokens) { - return ( - this._startOffset === other._startOffset - && this._endOffset === other._endOffset - && this._deltaOffset === other._deltaOffset - && this._source.slicedEquals(other._source, this._firstTokenIndex, this._tokensCount) - ); - } - return false; - } - - public getCount(): number { - return this._tokensCount; - } - - public getStandardTokenType(tokenIndex: number): StandardTokenType { - return this._source.getStandardTokenType(this._firstTokenIndex + tokenIndex); - } - - public getForeground(tokenIndex: number): ColorId { - return this._source.getForeground(this._firstTokenIndex + tokenIndex); - } - - public getEndOffset(tokenIndex: number): number { - const tokenEndOffset = this._source.getEndOffset(this._firstTokenIndex + tokenIndex); - return Math.min(this._endOffset, tokenEndOffset) - this._startOffset + this._deltaOffset; - } - - public getClassName(tokenIndex: number): string { - return this._source.getClassName(this._firstTokenIndex + tokenIndex); - } - - public getInlineStyle(tokenIndex: number, colorMap: string[]): string { - return this._source.getInlineStyle(this._firstTokenIndex + tokenIndex, colorMap); - } - - public getPresentation(tokenIndex: number): ITokenPresentation { - return this._source.getPresentation(this._firstTokenIndex + tokenIndex); - } - - public findTokenIndexAtOffset(offset: number): number { - return this._source.findTokenIndexAtOffset(offset + this._startOffset - this._deltaOffset) - this._firstTokenIndex; - } - - public getTokenText(tokenIndex: number): string { - const adjustedTokenIndex = this._firstTokenIndex + tokenIndex; - const tokenStartOffset = this._source.getStartOffset(adjustedTokenIndex); - const tokenEndOffset = this._source.getEndOffset(adjustedTokenIndex); - let text = this._source.getTokenText(adjustedTokenIndex); - if (tokenStartOffset < this._startOffset) { - text = text.substring(this._startOffset - tokenStartOffset); - } - if (tokenEndOffset > this._endOffset) { - text = text.substring(0, text.length - (tokenEndOffset - this._endOffset)); - } - return text; - } - - public forEach(callback: (tokenIndex: number) => void): void { - for (let tokenIndex = 0; tokenIndex < this.getCount(); tokenIndex++) { - callback(tokenIndex); - } - } -} - -export function getStandardTokenTypeAtPosition(model: ITextModel, position: IPosition): StandardTokenType | undefined { - const lineNumber = position.lineNumber; - if (!model.tokenization.isCheapToTokenize(lineNumber)) { - return undefined; - } - model.tokenization.forceTokenization(lineNumber); - const lineTokens = model.tokenization.getLineTokens(lineNumber); - const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); - const tokenType = lineTokens.getStandardTokenType(tokenIndex); - return tokenType; -} +export * from '../language/tokens/lineTokens.js'; diff --git a/src/vs/editor/common/tokens/sparseMultilineTokens.ts b/src/vs/editor/common/tokens/sparseMultilineTokens.ts index 50887a06af6..8c6f3d7dc9d 100644 --- a/src/vs/editor/common/tokens/sparseMultilineTokens.ts +++ b/src/vs/editor/common/tokens/sparseMultilineTokens.ts @@ -3,584 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from '../../../base/common/charCode.js'; -import { Position } from '../core/position.js'; -import { IRange, Range } from '../core/range.js'; -import { countEOL } from '../core/eolCounter.js'; - -/** - * Represents sparse tokens over a contiguous range of lines. - */ -export class SparseMultilineTokens { - - public static create(startLineNumber: number, tokens: Uint32Array): SparseMultilineTokens { - return new SparseMultilineTokens(startLineNumber, new SparseMultilineTokensStorage(tokens)); - } - - private _startLineNumber: number; - private _endLineNumber: number; - private readonly _tokens: SparseMultilineTokensStorage; - - /** - * (Inclusive) start line number for these tokens. - */ - public get startLineNumber(): number { - return this._startLineNumber; - } - - /** - * (Inclusive) end line number for these tokens. - */ - public get endLineNumber(): number { - return this._endLineNumber; - } - - private constructor(startLineNumber: number, tokens: SparseMultilineTokensStorage) { - this._startLineNumber = startLineNumber; - this._tokens = tokens; - this._endLineNumber = this._startLineNumber + this._tokens.getMaxDeltaLine(); - } - - public toString(): string { - return this._tokens.toString(this._startLineNumber); - } - - private _updateEndLineNumber(): void { - this._endLineNumber = this._startLineNumber + this._tokens.getMaxDeltaLine(); - } - - public isEmpty(): boolean { - return this._tokens.isEmpty(); - } - - public getLineTokens(lineNumber: number): SparseLineTokens | null { - if (this._startLineNumber <= lineNumber && lineNumber <= this._endLineNumber) { - return this._tokens.getLineTokens(lineNumber - this._startLineNumber); - } - return null; - } - - public getRange(): Range | null { - const deltaRange = this._tokens.getRange(); - if (!deltaRange) { - return deltaRange; - } - return new Range(this._startLineNumber + deltaRange.startLineNumber, deltaRange.startColumn, this._startLineNumber + deltaRange.endLineNumber, deltaRange.endColumn); - } - - public removeTokens(range: Range): void { - const startLineIndex = range.startLineNumber - this._startLineNumber; - const endLineIndex = range.endLineNumber - this._startLineNumber; - - this._startLineNumber += this._tokens.removeTokens(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); - this._updateEndLineNumber(); - } - - public split(range: Range): [SparseMultilineTokens, SparseMultilineTokens] { - // split tokens to two: - // a) all the tokens before `range` - // b) all the tokens after `range` - const startLineIndex = range.startLineNumber - this._startLineNumber; - const endLineIndex = range.endLineNumber - this._startLineNumber; - - const [a, b, bDeltaLine] = this._tokens.split(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1); - return [new SparseMultilineTokens(this._startLineNumber, a), new SparseMultilineTokens(this._startLineNumber + bDeltaLine, b)]; - } - - public applyEdit(range: IRange, text: string): void { - const [eolCount, firstLineLength, lastLineLength] = countEOL(text); - this.acceptEdit(range, eolCount, firstLineLength, lastLineLength, text.length > 0 ? text.charCodeAt(0) : CharCode.Null); - } - - public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { - this._acceptDeleteRange(range); - this._acceptInsertText(new Position(range.startLineNumber, range.startColumn), eolCount, firstLineLength, lastLineLength, firstCharCode); - this._updateEndLineNumber(); - } - - private _acceptDeleteRange(range: IRange): void { - if (range.startLineNumber === range.endLineNumber && range.startColumn === range.endColumn) { - // Nothing to delete - return; - } - - const firstLineIndex = range.startLineNumber - this._startLineNumber; - const lastLineIndex = range.endLineNumber - this._startLineNumber; - - if (lastLineIndex < 0) { - // this deletion occurs entirely before this block, so we only need to adjust line numbers - const deletedLinesCount = lastLineIndex - firstLineIndex; - this._startLineNumber -= deletedLinesCount; - return; - } - - const tokenMaxDeltaLine = this._tokens.getMaxDeltaLine(); - - if (firstLineIndex >= tokenMaxDeltaLine + 1) { - // this deletion occurs entirely after this block, so there is nothing to do - return; - } - - if (firstLineIndex < 0 && lastLineIndex >= tokenMaxDeltaLine + 1) { - // this deletion completely encompasses this block - this._startLineNumber = 0; - this._tokens.clear(); - return; - } - - if (firstLineIndex < 0) { - const deletedBefore = -firstLineIndex; - this._startLineNumber -= deletedBefore; - - this._tokens.acceptDeleteRange(range.startColumn - 1, 0, 0, lastLineIndex, range.endColumn - 1); - } else { - this._tokens.acceptDeleteRange(0, firstLineIndex, range.startColumn - 1, lastLineIndex, range.endColumn - 1); - } - } - - private _acceptInsertText(position: Position, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { - - if (eolCount === 0 && firstLineLength === 0) { - // Nothing to insert - return; - } - - const lineIndex = position.lineNumber - this._startLineNumber; - - if (lineIndex < 0) { - // this insertion occurs before this block, so we only need to adjust line numbers - this._startLineNumber += eolCount; - return; - } - - const tokenMaxDeltaLine = this._tokens.getMaxDeltaLine(); - - if (lineIndex >= tokenMaxDeltaLine + 1) { - // this insertion occurs after this block, so there is nothing to do - return; - } - - this._tokens.acceptInsertText(lineIndex, position.column - 1, eolCount, firstLineLength, lastLineLength, firstCharCode); - } -} - -class SparseMultilineTokensStorage { - /** - * The encoding of tokens is: - * 4*i deltaLine (from `startLineNumber`) - * 4*i+1 startCharacter (from the line start) - * 4*i+2 endCharacter (from the line start) - * 4*i+3 metadata - */ - private readonly _tokens: Uint32Array; - private _tokenCount: number; - - constructor(tokens: Uint32Array) { - this._tokens = tokens; - this._tokenCount = tokens.length / 4; - } - - public toString(startLineNumber: number): string { - const pieces: string[] = []; - for (let i = 0; i < this._tokenCount; i++) { - pieces.push(`(${this._getDeltaLine(i) + startLineNumber},${this._getStartCharacter(i)}-${this._getEndCharacter(i)})`); - } - return `[${pieces.join(',')}]`; - } - - public getMaxDeltaLine(): number { - const tokenCount = this._getTokenCount(); - if (tokenCount === 0) { - return -1; - } - return this._getDeltaLine(tokenCount - 1); - } - - public getRange(): Range | null { - const tokenCount = this._getTokenCount(); - if (tokenCount === 0) { - return null; - } - const startChar = this._getStartCharacter(0); - const maxDeltaLine = this._getDeltaLine(tokenCount - 1); - const endChar = this._getEndCharacter(tokenCount - 1); - return new Range(0, startChar + 1, maxDeltaLine, endChar + 1); - } - - private _getTokenCount(): number { - return this._tokenCount; - } - - private _getDeltaLine(tokenIndex: number): number { - return this._tokens[4 * tokenIndex]; - } - - private _getStartCharacter(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 1]; - } - - private _getEndCharacter(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 2]; - } - - public isEmpty(): boolean { - return (this._getTokenCount() === 0); - } - - public getLineTokens(deltaLine: number): SparseLineTokens | null { - let low = 0; - let high = this._getTokenCount() - 1; - - while (low < high) { - const mid = low + Math.floor((high - low) / 2); - const midDeltaLine = this._getDeltaLine(mid); - - if (midDeltaLine < deltaLine) { - low = mid + 1; - } else if (midDeltaLine > deltaLine) { - high = mid - 1; - } else { - let min = mid; - while (min > low && this._getDeltaLine(min - 1) === deltaLine) { - min--; - } - let max = mid; - while (max < high && this._getDeltaLine(max + 1) === deltaLine) { - max++; - } - return new SparseLineTokens(this._tokens.subarray(4 * min, 4 * max + 4)); - } - } - - if (this._getDeltaLine(low) === deltaLine) { - return new SparseLineTokens(this._tokens.subarray(4 * low, 4 * low + 4)); - } - - return null; - } - - public clear(): void { - this._tokenCount = 0; - } - - public removeTokens(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): number { - const tokens = this._tokens; - const tokenCount = this._tokenCount; - let newTokenCount = 0; - let hasDeletedTokens = false; - let firstDeltaLine = 0; - for (let i = 0; i < tokenCount; i++) { - const srcOffset = 4 * i; - const tokenDeltaLine = tokens[srcOffset]; - const tokenStartCharacter = tokens[srcOffset + 1]; - const tokenEndCharacter = tokens[srcOffset + 2]; - const tokenMetadata = tokens[srcOffset + 3]; - - if ( - (tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar)) - && (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar)) - ) { - hasDeletedTokens = true; - } else { - if (newTokenCount === 0) { - firstDeltaLine = tokenDeltaLine; - } - if (hasDeletedTokens) { - // must move the token to the left - const destOffset = 4 * newTokenCount; - tokens[destOffset] = tokenDeltaLine - firstDeltaLine; - tokens[destOffset + 1] = tokenStartCharacter; - tokens[destOffset + 2] = tokenEndCharacter; - tokens[destOffset + 3] = tokenMetadata; - } - newTokenCount++; - } - } - - this._tokenCount = newTokenCount; - - return firstDeltaLine; - } - - public split(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): [SparseMultilineTokensStorage, SparseMultilineTokensStorage, number] { - const tokens = this._tokens; - const tokenCount = this._tokenCount; - const aTokens: number[] = []; - const bTokens: number[] = []; - let destTokens: number[] = aTokens; - let destOffset = 0; - let destFirstDeltaLine: number = 0; - for (let i = 0; i < tokenCount; i++) { - const srcOffset = 4 * i; - const tokenDeltaLine = tokens[srcOffset]; - const tokenStartCharacter = tokens[srcOffset + 1]; - const tokenEndCharacter = tokens[srcOffset + 2]; - const tokenMetadata = tokens[srcOffset + 3]; - - if ((tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar))) { - if ((tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar))) { - // this token is touching the range - continue; - } else { - // this token is after the range - if (destTokens !== bTokens) { - // this token is the first token after the range - destTokens = bTokens; - destOffset = 0; - destFirstDeltaLine = tokenDeltaLine; - } - } - } - - destTokens[destOffset++] = tokenDeltaLine - destFirstDeltaLine; - destTokens[destOffset++] = tokenStartCharacter; - destTokens[destOffset++] = tokenEndCharacter; - destTokens[destOffset++] = tokenMetadata; - } - - return [new SparseMultilineTokensStorage(new Uint32Array(aTokens)), new SparseMultilineTokensStorage(new Uint32Array(bTokens)), destFirstDeltaLine]; - } - - public acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void { - // This is a bit complex, here are the cases I used to think about this: - // - // 1. The token starts before the deletion range - // 1a. The token is completely before the deletion range - // ----------- - // xxxxxxxxxxx - // 1b. The token starts before, the deletion range ends after the token - // ----------- - // xxxxxxxxxxx - // 1c. The token starts before, the deletion range ends precisely with the token - // --------------- - // xxxxxxxx - // 1d. The token starts before, the deletion range is inside the token - // --------------- - // xxxxx - // - // 2. The token starts at the same position with the deletion range - // 2a. The token starts at the same position, and ends inside the deletion range - // ------- - // xxxxxxxxxxx - // 2b. The token starts at the same position, and ends at the same position as the deletion range - // ---------- - // xxxxxxxxxx - // 2c. The token starts at the same position, and ends after the deletion range - // ------------- - // xxxxxxx - // - // 3. The token starts inside the deletion range - // 3a. The token is inside the deletion range - // ------- - // xxxxxxxxxxxxx - // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range - // ---------- - // xxxxxxxxxxxxx - // 3c. The token starts inside the deletion range, and ends after the deletion range - // ------------ - // xxxxxxxxxxx - // - // 4. The token starts after the deletion range - // ----------- - // xxxxxxxx - // - const tokens = this._tokens; - const tokenCount = this._tokenCount; - const deletedLineCount = (endDeltaLine - startDeltaLine); - let newTokenCount = 0; - let hasDeletedTokens = false; - for (let i = 0; i < tokenCount; i++) { - const srcOffset = 4 * i; - let tokenDeltaLine = tokens[srcOffset]; - let tokenStartCharacter = tokens[srcOffset + 1]; - let tokenEndCharacter = tokens[srcOffset + 2]; - const tokenMetadata = tokens[srcOffset + 3]; - - if (tokenDeltaLine < startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter <= startCharacter)) { - // 1a. The token is completely before the deletion range - // => nothing to do - newTokenCount++; - continue; - } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter < startCharacter) { - // 1b, 1c, 1d - // => the token survives, but it needs to shrink - if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { - // 1d. The token starts before, the deletion range is inside the token - // => the token shrinks by the deletion character count - tokenEndCharacter -= (endCharacter - startCharacter); - } else { - // 1b. The token starts before, the deletion range ends after the token - // 1c. The token starts before, the deletion range ends precisely with the token - // => the token shrinks its ending to the deletion start - tokenEndCharacter = startCharacter; - } - } else if (tokenDeltaLine === startDeltaLine && tokenStartCharacter === startCharacter) { - // 2a, 2b, 2c - if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { - // 2c. The token starts at the same position, and ends after the deletion range - // => the token shrinks by the deletion character count - tokenEndCharacter -= (endCharacter - startCharacter); - } else { - // 2a. The token starts at the same position, and ends inside the deletion range - // 2b. The token starts at the same position, and ends at the same position as the deletion range - // => the token is deleted - hasDeletedTokens = true; - continue; - } - } else if (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter < endCharacter)) { - // 3a, 3b, 3c - if (tokenDeltaLine === endDeltaLine && tokenEndCharacter > endCharacter) { - // 3c. The token starts inside the deletion range, and ends after the deletion range - // => the token moves to continue right after the deletion - tokenDeltaLine = startDeltaLine; - tokenStartCharacter = startCharacter; - tokenEndCharacter = tokenStartCharacter + (tokenEndCharacter - endCharacter); - } else { - // 3a. The token is inside the deletion range - // 3b. The token starts inside the deletion range, and ends at the same position as the deletion range - // => the token is deleted - hasDeletedTokens = true; - continue; - } - } else if (tokenDeltaLine > endDeltaLine) { - // 4. (partial) The token starts after the deletion range, on a line below... - if (deletedLineCount === 0 && !hasDeletedTokens) { - // early stop, there is no need to walk all the tokens and do nothing... - newTokenCount = tokenCount; - break; - } - tokenDeltaLine -= deletedLineCount; - } else if (tokenDeltaLine === endDeltaLine && tokenStartCharacter >= endCharacter) { - // 4. (continued) The token starts after the deletion range, on the last line where a deletion occurs - if (horizontalShiftForFirstLineTokens && tokenDeltaLine === 0) { - tokenStartCharacter += horizontalShiftForFirstLineTokens; - tokenEndCharacter += horizontalShiftForFirstLineTokens; - } - tokenDeltaLine -= deletedLineCount; - tokenStartCharacter -= (endCharacter - startCharacter); - tokenEndCharacter -= (endCharacter - startCharacter); - } else { - throw new Error(`Not possible!`); - } - - const destOffset = 4 * newTokenCount; - tokens[destOffset] = tokenDeltaLine; - tokens[destOffset + 1] = tokenStartCharacter; - tokens[destOffset + 2] = tokenEndCharacter; - tokens[destOffset + 3] = tokenMetadata; - newTokenCount++; - } - - this._tokenCount = newTokenCount; - } - - public acceptInsertText(deltaLine: number, character: number, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { - // Here are the cases I used to think about this: - // - // 1. The token is completely before the insertion point - // ----------- | - // 2. The token ends precisely at the insertion point - // -----------| - // 3. The token contains the insertion point - // -----|------ - // 4. The token starts precisely at the insertion point - // |----------- - // 5. The token is completely after the insertion point - // | ----------- - // - const isInsertingPreciselyOneWordCharacter = ( - eolCount === 0 - && firstLineLength === 1 - && ( - (firstCharCode >= CharCode.Digit0 && firstCharCode <= CharCode.Digit9) - || (firstCharCode >= CharCode.A && firstCharCode <= CharCode.Z) - || (firstCharCode >= CharCode.a && firstCharCode <= CharCode.z) - ) - ); - const tokens = this._tokens; - const tokenCount = this._tokenCount; - for (let i = 0; i < tokenCount; i++) { - const offset = 4 * i; - let tokenDeltaLine = tokens[offset]; - let tokenStartCharacter = tokens[offset + 1]; - let tokenEndCharacter = tokens[offset + 2]; - - if (tokenDeltaLine < deltaLine || (tokenDeltaLine === deltaLine && tokenEndCharacter < character)) { - // 1. The token is completely before the insertion point - // => nothing to do - continue; - } else if (tokenDeltaLine === deltaLine && tokenEndCharacter === character) { - // 2. The token ends precisely at the insertion point - // => expand the end character only if inserting precisely one character that is a word character - if (isInsertingPreciselyOneWordCharacter) { - tokenEndCharacter += 1; - } else { - continue; - } - } else if (tokenDeltaLine === deltaLine && tokenStartCharacter < character && character < tokenEndCharacter) { - // 3. The token contains the insertion point - if (eolCount === 0) { - // => just expand the end character - tokenEndCharacter += firstLineLength; - } else { - // => cut off the token - tokenEndCharacter = character; - } - } else { - // 4. or 5. - if (tokenDeltaLine === deltaLine && tokenStartCharacter === character) { - // 4. The token starts precisely at the insertion point - // => grow the token (by keeping its start constant) only if inserting precisely one character that is a word character - // => otherwise behave as in case 5. - if (isInsertingPreciselyOneWordCharacter) { - continue; - } - } - // => the token must move and keep its size constant - if (tokenDeltaLine === deltaLine) { - tokenDeltaLine += eolCount; - // this token is on the line where the insertion is taking place - if (eolCount === 0) { - tokenStartCharacter += firstLineLength; - tokenEndCharacter += firstLineLength; - } else { - const tokenLength = tokenEndCharacter - tokenStartCharacter; - tokenStartCharacter = lastLineLength + (tokenStartCharacter - character); - tokenEndCharacter = tokenStartCharacter + tokenLength; - } - } else { - tokenDeltaLine += eolCount; - } - } - - tokens[offset] = tokenDeltaLine; - tokens[offset + 1] = tokenStartCharacter; - tokens[offset + 2] = tokenEndCharacter; - } - } -} - -export class SparseLineTokens { - - private readonly _tokens: Uint32Array; - - constructor(tokens: Uint32Array) { - this._tokens = tokens; - } - - public getCount(): number { - return this._tokens.length / 4; - } - - public getStartCharacter(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 1]; - } - - public getEndCharacter(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 2]; - } - - public getMetadata(tokenIndex: number): number { - return this._tokens[4 * tokenIndex + 3]; - } -} +export * from '../language/tokens/sparseMultilineTokens.js'; diff --git a/src/vs/editor/common/tokens/sparseTokensStore.ts b/src/vs/editor/common/tokens/sparseTokensStore.ts index dd89936c989..11d0a6be4bc 100644 --- a/src/vs/editor/common/tokens/sparseTokensStore.ts +++ b/src/vs/editor/common/tokens/sparseTokensStore.ts @@ -3,242 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as arrays from '../../../base/common/arrays.js'; -import { IRange, Range } from '../core/range.js'; -import { LineTokens } from './lineTokens.js'; -import { SparseMultilineTokens } from './sparseMultilineTokens.js'; -import { ILanguageIdCodec } from '../languages.js'; -import { MetadataConsts } from '../encodedTokenAttributes.js'; - -/** - * Represents sparse tokens in a text model. - */ -export class SparseTokensStore { - - private _pieces: SparseMultilineTokens[]; - private _isComplete: boolean; - private readonly _languageIdCodec: ILanguageIdCodec; - - constructor(languageIdCodec: ILanguageIdCodec) { - this._pieces = []; - this._isComplete = false; - this._languageIdCodec = languageIdCodec; - } - - public flush(): void { - this._pieces = []; - this._isComplete = false; - } - - public isEmpty(): boolean { - return (this._pieces.length === 0); - } - - public set(pieces: SparseMultilineTokens[] | null, isComplete: boolean): void { - this._pieces = pieces || []; - this._isComplete = isComplete; - } - - public setPartial(_range: Range, pieces: SparseMultilineTokens[]): Range { - // console.log(`setPartial ${_range} ${pieces.map(p => p.toString()).join(', ')}`); - - let range = _range; - if (pieces.length > 0) { - const _firstRange = pieces[0].getRange(); - const _lastRange = pieces[pieces.length - 1].getRange(); - if (!_firstRange || !_lastRange) { - return _range; - } - range = _range.plusRange(_firstRange).plusRange(_lastRange); - } - - let insertPosition: { index: number } | null = null; - for (let i = 0, len = this._pieces.length; i < len; i++) { - const piece = this._pieces[i]; - if (piece.endLineNumber < range.startLineNumber) { - // this piece is before the range - continue; - } - - if (piece.startLineNumber > range.endLineNumber) { - // this piece is after the range, so mark the spot before this piece - // as a good insertion position and stop looping - insertPosition = insertPosition || { index: i }; - break; - } - - // this piece might intersect with the range - piece.removeTokens(range); - - if (piece.isEmpty()) { - // remove the piece if it became empty - this._pieces.splice(i, 1); - i--; - len--; - continue; - } - - if (piece.endLineNumber < range.startLineNumber) { - // after removal, this piece is before the range - continue; - } - - if (piece.startLineNumber > range.endLineNumber) { - // after removal, this piece is after the range - insertPosition = insertPosition || { index: i }; - continue; - } - - // after removal, this piece contains the range - const [a, b] = piece.split(range); - if (a.isEmpty()) { - // this piece is actually after the range - insertPosition = insertPosition || { index: i }; - continue; - } - if (b.isEmpty()) { - // this piece is actually before the range - continue; - } - this._pieces.splice(i, 1, a, b); - i++; - len++; - - insertPosition = insertPosition || { index: i }; - } - - insertPosition = insertPosition || { index: this._pieces.length }; - - if (pieces.length > 0) { - this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces); - } - - // console.log(`I HAVE ${this._pieces.length} pieces`); - // console.log(`${this._pieces.map(p => p.toString()).join('\n')}`); - - return range; - } - - public isComplete(): boolean { - return this._isComplete; - } - - public addSparseTokens(lineNumber: number, aTokens: LineTokens): LineTokens { - if (aTokens.getLineContent().length === 0) { - // Don't do anything for empty lines - return aTokens; - } - - const pieces = this._pieces; - - if (pieces.length === 0) { - return aTokens; - } - - const pieceIndex = SparseTokensStore._findFirstPieceWithLine(pieces, lineNumber); - const bTokens = pieces[pieceIndex].getLineTokens(lineNumber); - - if (!bTokens) { - return aTokens; - } - - const aLen = aTokens.getCount(); - const bLen = bTokens.getCount(); - - let aIndex = 0; - const result: number[] = []; - let resultLen = 0; - let lastEndOffset = 0; - - const emitToken = (endOffset: number, metadata: number) => { - if (endOffset === lastEndOffset) { - return; - } - lastEndOffset = endOffset; - result[resultLen++] = endOffset; - result[resultLen++] = metadata; - }; - - for (let bIndex = 0; bIndex < bLen; bIndex++) { - const bStartCharacter = bTokens.getStartCharacter(bIndex); - const bEndCharacter = bTokens.getEndCharacter(bIndex); - const bMetadata = bTokens.getMetadata(bIndex); - - const bMask = ( - ((bMetadata & MetadataConsts.SEMANTIC_USE_ITALIC) ? MetadataConsts.ITALIC_MASK : 0) - | ((bMetadata & MetadataConsts.SEMANTIC_USE_BOLD) ? MetadataConsts.BOLD_MASK : 0) - | ((bMetadata & MetadataConsts.SEMANTIC_USE_UNDERLINE) ? MetadataConsts.UNDERLINE_MASK : 0) - | ((bMetadata & MetadataConsts.SEMANTIC_USE_STRIKETHROUGH) ? MetadataConsts.STRIKETHROUGH_MASK : 0) - | ((bMetadata & MetadataConsts.SEMANTIC_USE_FOREGROUND) ? MetadataConsts.FOREGROUND_MASK : 0) - | ((bMetadata & MetadataConsts.SEMANTIC_USE_BACKGROUND) ? MetadataConsts.BACKGROUND_MASK : 0) - ) >>> 0; - const aMask = (~bMask) >>> 0; - - // push any token from `a` that is before `b` - while (aIndex < aLen && aTokens.getEndOffset(aIndex) <= bStartCharacter) { - emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); - aIndex++; - } - - // push the token from `a` if it intersects the token from `b` - if (aIndex < aLen && aTokens.getStartOffset(aIndex) < bStartCharacter) { - emitToken(bStartCharacter, aTokens.getMetadata(aIndex)); - } - - // skip any tokens from `a` that are contained inside `b` - while (aIndex < aLen && aTokens.getEndOffset(aIndex) < bEndCharacter) { - emitToken(aTokens.getEndOffset(aIndex), (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); - aIndex++; - } - - if (aIndex < aLen) { - emitToken(bEndCharacter, (aTokens.getMetadata(aIndex) & aMask) | (bMetadata & bMask)); - if (aTokens.getEndOffset(aIndex) === bEndCharacter) { - // `a` ends exactly at the same spot as `b`! - aIndex++; - } - } else { - const aMergeIndex = Math.min(Math.max(0, aIndex - 1), aLen - 1); - - // push the token from `b` - emitToken(bEndCharacter, (aTokens.getMetadata(aMergeIndex) & aMask) | (bMetadata & bMask)); - } - } - - // push the remaining tokens from `a` - while (aIndex < aLen) { - emitToken(aTokens.getEndOffset(aIndex), aTokens.getMetadata(aIndex)); - aIndex++; - } - - return new LineTokens(new Uint32Array(result), aTokens.getLineContent(), this._languageIdCodec); - } - - private static _findFirstPieceWithLine(pieces: SparseMultilineTokens[], lineNumber: number): number { - let low = 0; - let high = pieces.length - 1; - - while (low < high) { - let mid = low + Math.floor((high - low) / 2); - - if (pieces[mid].endLineNumber < lineNumber) { - low = mid + 1; - } else if (pieces[mid].startLineNumber > lineNumber) { - high = mid - 1; - } else { - while (mid > low && pieces[mid - 1].startLineNumber <= lineNumber && lineNumber <= pieces[mid - 1].endLineNumber) { - mid--; - } - return mid; - } - } - - return low; - } - - public acceptEdit(range: IRange, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void { - for (const piece of this._pieces) { - piece.acceptEdit(range, eolCount, firstLineLength, lastLineLength, firstCharCode); - } - } -} +export * from '../language/tokens/sparseTokensStore.js'; diff --git a/src/vs/editor/common/tokens/tokenArray.ts b/src/vs/editor/common/tokens/tokenArray.ts index bc089d1604a..76bd67fdb6e 100644 --- a/src/vs/editor/common/tokens/tokenArray.ts +++ b/src/vs/editor/common/tokens/tokenArray.ts @@ -3,102 +3,4 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OffsetRange } from '../core/offsetRange.js'; -import { ILanguageIdCodec } from '../languages.js'; -import { LineTokens } from './lineTokens.js'; - -/** - * This class represents a sequence of tokens. - * Conceptually, each token has a length and a metadata number. - * A token array might be used to annotate a string with metadata. - * Use {@link TokenArrayBuilder} to efficiently create a token array. - * - * TODO: Make this class more efficient (e.g. by using a Int32Array). -*/ -export class TokenArray { - public static fromLineTokens(lineTokens: LineTokens): TokenArray { - const tokenInfo: TokenInfo[] = []; - for (let i = 0; i < lineTokens.getCount(); i++) { - tokenInfo.push(new TokenInfo(lineTokens.getEndOffset(i) - lineTokens.getStartOffset(i), lineTokens.getMetadata(i))); - } - return TokenArray.create(tokenInfo); - } - - public static create(tokenInfo: TokenInfo[]): TokenArray { - return new TokenArray(tokenInfo); - } - - private constructor( - private readonly _tokenInfo: TokenInfo[], - ) { } - - public toLineTokens(lineContent: string, decoder: ILanguageIdCodec): LineTokens { - return LineTokens.createFromTextAndMetadata(this.map((r, t) => ({ text: r.substring(lineContent), metadata: t.metadata })), decoder); - } - - public forEach(cb: (range: OffsetRange, tokenInfo: TokenInfo) => void): void { - let lengthSum = 0; - for (const tokenInfo of this._tokenInfo) { - const range = new OffsetRange(lengthSum, lengthSum + tokenInfo.length); - cb(range, tokenInfo); - lengthSum += tokenInfo.length; - } - } - - public map(cb: (range: OffsetRange, tokenInfo: TokenInfo) => T): T[] { - const result: T[] = []; - let lengthSum = 0; - for (const tokenInfo of this._tokenInfo) { - const range = new OffsetRange(lengthSum, lengthSum + tokenInfo.length); - result.push(cb(range, tokenInfo)); - lengthSum += tokenInfo.length; - } - return result; - } - - public slice(range: OffsetRange): TokenArray { - const result: TokenInfo[] = []; - let lengthSum = 0; - for (const tokenInfo of this._tokenInfo) { - const tokenStart = lengthSum; - const tokenEndEx = tokenStart + tokenInfo.length; - if (tokenEndEx > range.start) { - if (tokenStart >= range.endExclusive) { - break; - } - - const deltaBefore = Math.max(0, range.start - tokenStart); - const deltaAfter = Math.max(0, tokenEndEx - range.endExclusive); - - result.push(new TokenInfo(tokenInfo.length - deltaBefore - deltaAfter, tokenInfo.metadata)); - } - - lengthSum += tokenInfo.length; - } - return TokenArray.create(result); - } -} - -export type TokenMetadata = number; - -export class TokenInfo { - constructor( - public readonly length: number, - public readonly metadata: TokenMetadata, - ) { } -} - -/** - * TODO: Make this class more efficient (e.g. by using a Int32Array). -*/ -export class TokenArrayBuilder { - private readonly _tokens: TokenInfo[] = []; - - public add(length: number, metadata: TokenMetadata): void { - this._tokens.push(new TokenInfo(length, metadata)); - } - - public build(): TokenArray { - return TokenArray.create(this._tokens); - } -} +export * from '../language/tokens/tokenArray.js'; diff --git a/src/vs/editor/common/viewEvents.ts b/src/vs/editor/common/viewEvents.ts index ce107651bac..aaa169eeaf0 100644 --- a/src/vs/editor/common/viewEvents.ts +++ b/src/vs/editor/common/viewEvents.ts @@ -5,11 +5,11 @@ import { ScrollEvent } from '../../base/common/scrollable.js'; import { ConfigurationChangedEvent, EditorOption } from './config/editorOptions.js'; -import { Range } from './core/range.js'; -import { Selection } from './core/selection.js'; +import { Range } from '../../editor/common/language/core/range.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; import { CursorChangeReason } from './cursorEvents.js'; import { ScrollType } from './editorCommon.js'; -import { IModelDecorationsChangedEvent } from './textModelEvents.js'; +import { IModelDecorationsChangedEvent } from '../../editor/common/language/textModelEvents.js'; import { IColorTheme } from '../../platform/theme/common/themeService.js'; export const enum ViewEventType { diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index b19b34e040c..f08a0477b43 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -6,8 +6,8 @@ import * as nls from '../../../nls.js'; import { CharCode } from '../../../base/common/charCode.js'; import * as strings from '../../../base/common/strings.js'; -import { IViewLineTokens } from '../tokens/lineTokens.js'; -import { StringBuilder } from '../core/stringBuilder.js'; +import { IViewLineTokens } from '../../../editor/common/language/tokens/lineTokens.js'; +import { StringBuilder } from '../../../editor/common/language/core/stringBuilder.js'; import { LineDecoration, LineDecorationsNormalizer } from './lineDecorations.js'; import { InlineDecorationType } from '../viewModel.js'; import { LinePart, LinePartMetadata } from './linePart.js'; diff --git a/src/vs/editor/common/viewLayout/viewLinesViewportData.ts b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts index 91eaceca811..67ea482a114 100644 --- a/src/vs/editor/common/viewLayout/viewLinesViewportData.ts +++ b/src/vs/editor/common/viewLayout/viewLinesViewportData.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../core/range.js'; -import { Selection } from '../core/selection.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; import { IPartialViewLinesViewportData, IViewModel, IViewWhitespaceViewportData, ViewLineRenderingData, ViewModelDecoration } from '../viewModel.js'; /** diff --git a/src/vs/editor/common/viewModel.ts b/src/vs/editor/common/viewModel.ts index 1aefa04abeb..96dfc3eb4e9 100644 --- a/src/vs/editor/common/viewModel.ts +++ b/src/vs/editor/common/viewModel.ts @@ -6,16 +6,16 @@ import * as arrays from '../../base/common/arrays.js'; import { IScrollPosition, Scrollable } from '../../base/common/scrollable.js'; import * as strings from '../../base/common/strings.js'; -import { IPosition, Position } from './core/position.js'; -import { Range } from './core/range.js'; +import { IPosition, Position } from '../../editor/common/language/core/position.js'; +import { Range } from '../../editor/common/language/core/range.js'; import { CursorConfiguration, CursorState, EditOperationType, IColumnSelectData, ICursorSimpleModel, PartialCursorState } from './cursorCommon.js'; import { CursorChangeReason } from './cursorEvents.js'; import { INewScrollPosition, ScrollType } from './editorCommon.js'; import { EditorTheme } from './editorTheme.js'; -import { EndOfLinePreference, IGlyphMarginLanesModel, IModelDecorationOptions, ITextModel, PositionAffinity } from './model.js'; +import { EndOfLinePreference, IGlyphMarginLanesModel, IModelDecorationOptions, ITextModel, PositionAffinity } from '../../editor/common/language/model.js'; import { ILineBreaksComputer, InjectedText } from './modelLineProjectionData.js'; import { BracketGuideOptions, IActiveIndentGuideInfo, IndentGuide } from './textModelGuides.js'; -import { IViewLineTokens } from './tokens/lineTokens.js'; +import { IViewLineTokens } from '../../editor/common/language/tokens/lineTokens.js'; import { ViewEventHandler } from './viewEventHandler.js'; import { VerticalRevealType } from './viewEvents.js'; diff --git a/src/vs/editor/common/viewModel/glyphLanesModel.ts b/src/vs/editor/common/viewModel/glyphLanesModel.ts index 3efbb9e7cb2..e732d8dd047 100644 --- a/src/vs/editor/common/viewModel/glyphLanesModel.ts +++ b/src/vs/editor/common/viewModel/glyphLanesModel.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../core/range.js'; -import { GlyphMarginLane, IGlyphMarginLanesModel } from '../model.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { GlyphMarginLane, IGlyphMarginLanesModel } from '../../../editor/common/language/model.js'; const MAX_LANE = GlyphMarginLane.Right; diff --git a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts index a0f36078c5b..7e66d21cfd6 100644 --- a/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts +++ b/src/vs/editor/common/viewModel/minimapTokensColorTracker.ts @@ -5,9 +5,9 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, markAsSingleton } from '../../../base/common/lifecycle.js'; -import { RGBA8 } from '../core/rgba.js'; -import { TokenizationRegistry } from '../languages.js'; -import { ColorId } from '../encodedTokenAttributes.js'; +import { RGBA8 } from '../../../editor/common/language/core/rgba.js'; +import { TokenizationRegistry } from '../../../editor/common/language/languages.js'; +import { ColorId } from '../../../editor/common/language/encodedTokenAttributes.js'; export class MinimapTokensColorTracker extends Disposable { private static _INSTANCE: MinimapTokensColorTracker | null = null; diff --git a/src/vs/editor/common/viewModel/modelLineProjection.ts b/src/vs/editor/common/viewModel/modelLineProjection.ts index b01a1de2898..8acd2fb82cd 100644 --- a/src/vs/editor/common/viewModel/modelLineProjection.ts +++ b/src/vs/editor/common/viewModel/modelLineProjection.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineTokens } from '../tokens/lineTokens.js'; -import { Position } from '../core/position.js'; -import { IRange } from '../core/range.js'; -import { EndOfLinePreference, ITextModel, PositionAffinity } from '../model.js'; -import { LineInjectedText } from '../textModelEvents.js'; +import { LineTokens } from '../../../editor/common/language/tokens/lineTokens.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { EndOfLinePreference, ITextModel, PositionAffinity } from '../../../editor/common/language/model.js'; +import { LineInjectedText } from '../../../editor/common/language/textModelEvents.js'; import { InjectedText, ModelLineProjectionData } from '../modelLineProjectionData.js'; import { SingleLineInlineDecoration, ViewLineData } from '../viewModel.js'; diff --git a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts index 434e322b6f0..bc006f7805b 100644 --- a/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts +++ b/src/vs/editor/common/viewModel/monospaceLineBreaksComputer.ts @@ -6,10 +6,10 @@ import { CharCode } from '../../../base/common/charCode.js'; import * as strings from '../../../base/common/strings.js'; import { WrappingIndent, IComputedEditorOptions, EditorOption } from '../config/editorOptions.js'; -import { CharacterClassifier } from '../core/characterClassifier.js'; +import { CharacterClassifier } from '../../../editor/common/language/core/characterClassifier.js'; import { FontInfo } from '../config/fontInfo.js'; -import { LineInjectedText } from '../textModelEvents.js'; -import { InjectedTextOptions } from '../model.js'; +import { LineInjectedText } from '../../../editor/common/language/textModelEvents.js'; +import { InjectedTextOptions } from '../../../editor/common/language/model.js'; import { ILineBreaksComputerFactory, ILineBreaksComputer, ModelLineProjectionData } from '../modelLineProjectionData.js'; export class MonospaceLineBreaksComputerFactory implements ILineBreaksComputerFactory { diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index 553cc7d3389..7a2a241af4d 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from '../../../base/common/lifecycle.js'; -import { Position } from '../core/position.js'; -import { Range } from '../core/range.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { IEditorConfiguration } from '../config/editorConfiguration.js'; -import { IModelDecoration, ITextModel, PositionAffinity } from '../model.js'; +import { IModelDecoration, ITextModel, PositionAffinity } from '../../../editor/common/language/model.js'; import { IViewModelLines } from './viewModelLines.js'; import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from '../viewModel.js'; import { filterValidationDecorations } from '../config/editorOptions.js'; -import { StandardTokenType } from '../encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../editor/common/language/encodedTokenAttributes.js'; export interface IDecorationsViewportData { /** diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 7fe34c92995..bc6c7cf8609 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -14,17 +14,17 @@ import { ConfigurationChangedEvent, EditorOption, EDITOR_FONT_DEFAULTS, filterVa import { CursorsController } from '../cursor/cursor.js'; import { CursorConfiguration, CursorState, EditOperationType, IColumnSelectData, PartialCursorState } from '../cursorCommon.js'; import { CursorChangeReason } from '../cursorEvents.js'; -import { IPosition, Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { ISelection, Selection } from '../core/selection.js'; +import { IPosition, Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../editor/common/language/core/selection.js'; import { ICommand, ICursorState, IViewState, ScrollType } from '../editorCommon.js'; import { IEditorConfiguration } from '../config/editorConfiguration.js'; -import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IGlyphMarginLanesModel, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from '../model.js'; +import { EndOfLinePreference, IAttachedView, ICursorStateComputer, IGlyphMarginLanesModel, IIdentifiedSingleEditOperation, ITextModel, PositionAffinity, TrackedRangeStickiness } from '../../../editor/common/language/model.js'; import { IActiveIndentGuideInfo, BracketGuideOptions, IndentGuide } from '../textModelGuides.js'; -import { ModelDecorationMinimapOptions, ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from '../model/textModel.js'; -import * as textModelEvents from '../textModelEvents.js'; -import { TokenizationRegistry } from '../languages.js'; -import { ColorId } from '../encodedTokenAttributes.js'; +import { ModelDecorationMinimapOptions, ModelDecorationOptions, ModelDecorationOverviewRulerOptions } from '../../../editor/common/language/model/textModel.js'; +import * as textModelEvents from '../../../editor/common/language/textModelEvents.js'; +import { TokenizationRegistry } from '../../../editor/common/language/languages.js'; +import { ColorId } from '../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../languages/languageConfigurationRegistry.js'; import { PLAINTEXT_LANGUAGE_ID } from '../languages/modesRegistry.js'; import { tokenizeLineToHTML } from '../languages/textToHtmlTokenizer.js'; diff --git a/src/vs/editor/common/viewModel/viewModelLines.ts b/src/vs/editor/common/viewModel/viewModelLines.ts index 8c1142270c5..9b2451b59df 100644 --- a/src/vs/editor/common/viewModel/viewModelLines.ts +++ b/src/vs/editor/common/viewModel/viewModelLines.ts @@ -7,16 +7,16 @@ import * as arrays from '../../../base/common/arrays.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; import { WrappingIndent } from '../config/editorOptions.js'; import { FontInfo } from '../config/fontInfo.js'; -import { IPosition, Position } from '../core/position.js'; -import { Range } from '../core/range.js'; -import { IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from '../model.js'; +import { IPosition, Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { IModelDecoration, IModelDeltaDecoration, ITextModel, PositionAffinity } from '../../../editor/common/language/model.js'; import { IActiveIndentGuideInfo, BracketGuideOptions, IndentGuide, IndentGuideHorizontalLine } from '../textModelGuides.js'; -import { ModelDecorationOptions } from '../model/textModel.js'; -import { LineInjectedText } from '../textModelEvents.js'; +import { ModelDecorationOptions } from '../../../editor/common/language/model/textModel.js'; +import { LineInjectedText } from '../../../editor/common/language/textModelEvents.js'; import * as viewEvents from '../viewEvents.js'; import { createModelLineProjection, IModelLineProjection } from './modelLineProjection.js'; import { ILineBreaksComputer, ModelLineProjectionData, InjectedText, ILineBreaksComputerFactory } from '../modelLineProjectionData.js'; -import { ConstantTimePrefixSumComputer } from '../model/prefixSumComputer.js'; +import { ConstantTimePrefixSumComputer } from '../../../editor/common/language/model/prefixSumComputer.js'; import { ICoordinatesConverter, ViewLineData } from '../viewModel.js'; export interface IViewModelLines extends IDisposable { diff --git a/src/vs/editor/common/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModelEventDispatcher.ts index fc91875b319..3723486b0bb 100644 --- a/src/vs/editor/common/viewModelEventDispatcher.ts +++ b/src/vs/editor/common/viewModelEventDispatcher.ts @@ -7,10 +7,10 @@ import { ViewEventHandler } from './viewEventHandler.js'; import { ViewEvent } from './viewEvents.js'; import { IContentSizeChangedEvent } from './editorCommon.js'; import { Emitter } from '../../base/common/event.js'; -import { Selection } from './core/selection.js'; +import { Selection } from '../../editor/common/language/core/selection.js'; import { Disposable } from '../../base/common/lifecycle.js'; import { CursorChangeReason } from './cursorEvents.js'; -import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from './textModelEvents.js'; +import { IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent } from '../../editor/common/language/textModelEvents.js'; export class ViewModelEventDispatcher extends Disposable { diff --git a/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts b/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts index ad8a149b3e5..df3ffcb7b5a 100644 --- a/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts +++ b/src/vs/editor/contrib/anchorSelect/browser/anchorSelect.ts @@ -10,10 +10,10 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import './anchorSelect.css'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { TrackedRangeStickiness } from '../../../common/model.js'; +import { TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { localize, localize2 } from '../../../../nls.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; diff --git a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts index d121efd2500..f8f26cb649a 100644 --- a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts @@ -10,13 +10,13 @@ import './bracketMatching.css'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import * as nls from '../../../../nls.js'; import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; diff --git a/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts b/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts index e9d26789cf3..7a40b111410 100644 --- a/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts +++ b/src/vs/editor/contrib/bracketMatching/test/browser/bracketMatching.test.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { BracketMatchingController } from '../../browser/bracketMatching.js'; import { createCodeEditorServices, instantiateTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { instantiateTextModel } from '../../../../test/common/testTextModel.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('bracket matching', () => { diff --git a/src/vs/editor/contrib/caretOperations/browser/moveCaretCommand.ts b/src/vs/editor/contrib/caretOperations/browser/moveCaretCommand.ts index e3dabdc3318..d9a69a68935 100644 --- a/src/vs/editor/contrib/caretOperations/browser/moveCaretCommand.ts +++ b/src/vs/editor/contrib/caretOperations/browser/moveCaretCommand.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class MoveCaretCommand implements ICommand { diff --git a/src/vs/editor/contrib/caretOperations/browser/transpose.ts b/src/vs/editor/contrib/caretOperations/browser/transpose.ts index 35ac2207453..ffd840011be 100644 --- a/src/vs/editor/contrib/caretOperations/browser/transpose.ts +++ b/src/vs/editor/contrib/caretOperations/browser/transpose.ts @@ -8,7 +8,7 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, registerEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ReplaceCommand } from '../../../common/commands/replaceCommand.js'; import { MoveOperations } from '../../../common/cursor/cursorMoveOperations.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ICommand } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/caretOperations/test/browser/moveCarretCommand.test.ts b/src/vs/editor/contrib/caretOperations/test/browser/moveCarretCommand.test.ts index 254f228ede5..6bb0f4a9311 100644 --- a/src/vs/editor/contrib/caretOperations/test/browser/moveCarretCommand.test.ts +++ b/src/vs/editor/contrib/caretOperations/test/browser/moveCarretCommand.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { MoveCaretCommand } from '../../browser/moveCaretCommand.js'; import { testCommand } from '../../../../test/browser/testCommand.js'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeAction.ts b/src/vs/editor/contrib/codeAction/browser/codeAction.ts index c78ecc080d4..c85d9615938 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeAction.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeAction.ts @@ -18,13 +18,13 @@ import { IProgress, Progress } from '../../../../platform/progress/common/progre import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { IBulkEditService } from '../../../browser/services/bulkEditService.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IModelService } from '../../../common/services/model.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { TextModelCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { CodeActionFilter, CodeActionItem, CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource, filtersAction, mayIncludeActionsOfKind } from '../common/types.js'; @@ -166,6 +166,7 @@ export async function getCodeActions( ]; return new ManagedCodeActionSet(allActions, allDocumentation, disposables); } catch (err) { + await Promise.allSettled(promises); disposables.dispose(); throw err; } finally { diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index e65f9ab38da..8cce9ac790c 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -26,12 +26,12 @@ import { editorFindMatchHighlight, editorFindMatchHighlightBorder } from '../../ import { isHighContrast } from '../../../../platform/theme/common/theme.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { IPosition, Position } from '../../../common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; -import { CodeActionTriggerType } from '../../../common/languages.js'; -import { IModelDeltaDecoration } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { MessageController } from '../../message/browser/messageController.js'; import { CodeActionAutoApply, CodeActionFilter, CodeActionItem, CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from '../common/types.js'; import { ApplyCodeActionReason, applyCodeAction } from './codeAction.js'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts b/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts index faae3562de2..0ef1d1a555d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionKeybindingResolver.ts @@ -6,7 +6,7 @@ import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { ResolvedKeybinding } from '../../../../base/common/keybindings.js'; import { Lazy } from '../../../../base/common/lazy.js'; -import { CodeAction } from '../../../common/languages.js'; +import { CodeAction } from '../../../../editor/common/language/languages.js'; import { codeActionCommandId, fixAllCommandId, organizeImportsCommandId, refactorCommandId, sourceActionCommandId } from './codeAction.js'; import { CodeActionAutoApply, CodeActionCommandArgs, CodeActionKind } from '../common/types.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts index 5384b7a5b15..91fa641be66 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionMenu.ts @@ -7,7 +7,7 @@ import '../../../../base/browser/ui/codicons/codiconStyles.js'; // The codicon s import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { ResolvedKeybinding } from '../../../../base/common/keybindings.js'; -import { CodeAction } from '../../../common/languages.js'; +import { CodeAction } from '../../../../editor/common/language/languages.js'; import { CodeActionItem, CodeActionKind } from '../common/types.js'; import '../../symbolIcons/browser/symbolIcons.js'; // The codicon symbol colors are defined here and must be loaded to get colors import { localize } from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index d5e249add65..30a20051fe2 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -16,10 +16,10 @@ import { IMarkerService } from '../../../../platform/markers/common/markers.js'; import { IEditorProgressService, Progress } from '../../../../platform/progress/common/progress.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption, ShowLightbulbIconMode } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { CodeActionProvider, CodeActionTriggerType } from '../../../common/languages.js'; +import { CodeActionProvider, CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; import { CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from '../common/types.js'; import { getCodeActions } from './codeAction.js'; diff --git a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts index 39dd29629f5..0437c75aa8f 100644 --- a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts @@ -12,16 +12,16 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import './lightBulbWidget.css'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { GlyphMarginLane, IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { computeIndentLevel } from '../../../common/model/utils.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { GlyphMarginLane, IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { computeIndentLevel } from '../../../../editor/common/language/model/utils.js'; import { autoFixCommandId, quickFixCommandId } from './codeAction.js'; import { CodeActionSet, CodeActionTrigger } from '../common/types.js'; import * as nls from '../../../../nls.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; const GUTTER_LIGHTBULB_ICON = registerIcon('gutter-lightbulb', Codicon.lightBulb, nls.localize('gutterLightbulbWidget', 'Icon which spawns code actions menu from the gutter when there is no space in the editor.')); const GUTTER_LIGHTBULB_AUTO_FIX_ICON = registerIcon('gutter-lightbulb-auto-fix', Codicon.lightbulbAutofix, nls.localize('gutterLightbulbAutoFixWidget', 'Icon which spawns code actions menu from the gutter when there is no space in the editor and a quick fix is available.')); diff --git a/src/vs/editor/contrib/codeAction/common/types.ts b/src/vs/editor/contrib/codeAction/common/types.ts index 691a0e816c2..945e69ad103 100644 --- a/src/vs/editor/contrib/codeAction/common/types.ts +++ b/src/vs/editor/contrib/codeAction/common/types.ts @@ -6,8 +6,8 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; -import { Position } from '../../../common/core/position.js'; -import * as languages from '../../../common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { ActionSet } from '../../../../platform/actionWidget/common/actionWidget.js'; export const CodeActionKind = new class { diff --git a/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts index d3265cf48c9..c2cf0eb4e90 100644 --- a/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/browser/codeAction.test.ts @@ -8,10 +8,10 @@ import { HierarchicalKind } from '../../../../../base/common/hierarchicalKind.js import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../../common/languages.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { getCodeActions } from '../../browser/codeAction.js'; import { CodeActionItem, CodeActionKind, CodeActionTriggerSource } from '../../common/types.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; diff --git a/src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts b/src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts index ab6c174582a..490e153593d 100644 --- a/src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts +++ b/src/vs/editor/contrib/codeAction/test/browser/codeActionModel.test.ts @@ -13,8 +13,8 @@ import { MockContextKeyService } from '../../../../../platform/keybinding/test/c import { MarkerService } from '../../../../../platform/markers/common/markerService.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../../common/languages.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { CodeActionModel, CodeActionsState } from '../../browser/codeActionModel.js'; diff --git a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts index 84b8931ad4a..6ec970285c0 100644 --- a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts @@ -5,9 +5,9 @@ import { Event } from '../../../../base/common/event.js'; import { LRUCache } from '../../../../base/common/map.js'; -import { Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CodeLens, CodeLensList, CodeLensProvider } from '../../../../editor/common/language/languages.js'; import { CodeLensModel } from './codelens.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/editor/contrib/codelens/browser/codelens.ts b/src/vs/editor/contrib/codelens/browser/codelens.ts index 78aa18c690f..f8622fe2e23 100644 --- a/src/vs/editor/contrib/codelens/browser/codelens.ts +++ b/src/vs/editor/contrib/codelens/browser/codelens.ts @@ -8,12 +8,12 @@ import { illegalArgument, onUnexpectedExternalError } from '../../../../base/com import { DisposableStore, isDisposable } from '../../../../base/common/lifecycle.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextModel } from '../../../common/model.js'; -import { CodeLens, CodeLensList, CodeLensProvider } from '../../../common/languages.js'; -import { IModelService } from '../../../common/services/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CodeLens, CodeLensList, CodeLensProvider } from '../../../../editor/common/language/languages.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export interface CodeLensItem { symbol: CodeLens; diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts index bd54868ea99..3be5670eddf 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensController.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensController.ts @@ -13,8 +13,8 @@ import { EditorAction, EditorContributionInstantiation, registerEditorAction, re import { EditorOption, EDITOR_FONT_DEFAULTS } from '../../../common/config/editorOptions.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { IModelDecorationsChangeAccessor } from '../../../common/model.js'; -import { CodeLens, Command } from '../../../common/languages.js'; +import { IModelDecorationsChangeAccessor } from '../../../../editor/common/language/model.js'; +import { CodeLens, Command } from '../../../../editor/common/language/languages.js'; import { CodeLensItem, CodeLensModel, getCodeLensModel } from './codelens.js'; import { ICodeLensCache } from './codeLensCache.js'; import { CodeLensHelper, CodeLensWidget } from './codelensWidget.js'; @@ -22,8 +22,8 @@ import { localize, localize2 } from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export class CodeLensContribution implements IEditorContribution { diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts index 394c333b2e5..aeedd6babf9 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts @@ -8,10 +8,10 @@ import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/icon import { Constants } from '../../../../base/common/uint.js'; import './codelensWidget.css'; import { ContentWidgetPositionPreference, IActiveCodeEditor, IContentWidget, IContentWidgetPosition, IViewZone, IViewZoneChangeAccessor } from '../../../browser/editorBrowser.js'; -import { Range } from '../../../common/core/range.js'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { CodeLens, Command } from '../../../common/languages.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { CodeLens, Command } from '../../../../editor/common/language/languages.js'; import { CodeLensItem } from './codelens.js'; class CodeLensViewZone implements IViewZone { diff --git a/src/vs/editor/contrib/colorPicker/browser/color.ts b/src/vs/editor/contrib/colorPicker/browser/color.ts index 3118b6d2383..257c614cd7d 100644 --- a/src/vs/editor/contrib/colorPicker/browser/color.ts +++ b/src/vs/editor/contrib/colorPicker/browser/color.ts @@ -6,11 +6,11 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { illegalArgument, onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentColorProvider, IColorInformation, IColorPresentation } from '../../../common/languages.js'; -import { IModelService } from '../../../common/services/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentColorProvider, IColorInformation, IColorPresentation } from '../../../../editor/common/language/languages.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; import { DefaultDocumentColorProvider } from './defaultDocumentColorProvider.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts index e6043ebbd33..637e20bb701 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -13,13 +13,13 @@ import { noBreakWhitespace } from '../../../../base/common/strings.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { DynamicCssRules } from '../../../browser/editorDom.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; -import { IModelDecoration, IModelDeltaDecoration } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IModelDecoration, IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { getColors, IColorData } from './color.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerContribution.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerContribution.ts index 68f4e89edc8..ece24edf723 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerContribution.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerContribution.ts @@ -10,7 +10,7 @@ import { registerAction2 } from '../../../../platform/actions/common/actions.js' import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { registerEditorFeature } from '../../../common/editorFeatures.js'; -import { IColorPresentation } from '../../../common/languages.js'; +import { IColorPresentation } from '../../../../editor/common/language/languages.js'; import { HoverParticipantRegistry } from '../../hover/browser/hoverTypes.js'; import { _findColorData, _setupColorCommand, ColorPresentationsCollector, ExtColorDataCollector, IExtColorData } from './color.js'; import { ColorDetector } from './colorDetector.js'; @@ -19,7 +19,7 @@ import { HoverColorPickerContribution } from './hoverColorPicker/hoverColorPicke import { HoverColorPickerParticipant } from './hoverColorPicker/hoverColorPickerParticipant.js'; import { HideStandaloneColorPicker, InsertColorWithStandaloneColorPicker, ShowOrFocusStandaloneColorPicker } from './standaloneColorPicker/standaloneColorPickerActions.js'; import { StandaloneColorPickerController } from './standaloneColorPicker/standaloneColorPickerController.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; registerEditorAction(HideStandaloneColorPicker); registerEditorAction(InsertColorWithStandaloneColorPicker); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts index 06a0c278e27..e6beb24810c 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerModel.ts @@ -5,7 +5,7 @@ import { Color } from '../../../../base/common/color.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { IColorPresentation } from '../../../common/languages.js'; +import { IColorPresentation } from '../../../../editor/common/language/languages.js'; export class ColorPickerModel { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerParticipantUtils.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerParticipantUtils.ts index 6c0744229c7..e85afd95039 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerParticipantUtils.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerParticipantUtils.ts @@ -6,12 +6,12 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Color, RGBA } from '../../../../base/common/color.js'; import { IActiveCodeEditor } from '../../../browser/editorBrowser.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { DocumentColorProvider, IColorInformation } from '../../../common/languages.js'; -import { ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { DocumentColorProvider, IColorInformation } from '../../../../editor/common/language/languages.js'; +import { ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { getColorPresentations } from './color.js'; import { ColorPickerModel } from './colorPickerModel.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; export const enum ColorPickerWidgetType { Hover = 'hover', diff --git a/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts b/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts index 63a231f23ea..4f12a542e6a 100644 --- a/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts +++ b/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts @@ -5,11 +5,11 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Color, RGBA } from '../../../../base/common/color.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentColorProvider, IColor, IColorInformation, IColorPresentation } from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentColorProvider, IColor, IColorInformation, IColorPresentation } from '../../../../editor/common/language/languages.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; export class DefaultDocumentColorProvider implements DocumentColorProvider { diff --git a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts index bef71cbf912..1dfa97819eb 100644 --- a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts +++ b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerContribution.ts @@ -6,7 +6,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent } from '../../../../browser/editorBrowser.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../common/editorCommon.js'; import { ContentHoverController } from '../../../hover/browser/contentHoverController.js'; import { HoverStartMode, HoverStartSource } from '../../../hover/browser/hoverOperation.js'; diff --git a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts index c850d374443..9468d47451d 100644 --- a/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.ts @@ -6,9 +6,9 @@ import { AsyncIterableObject } from '../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Range } from '../../../../common/core/range.js'; -import { IModelDecoration } from '../../../../common/model.js'; -import { DocumentColorProvider } from '../../../../common/languages.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IModelDecoration } from '../../../../../editor/common/language/model.js'; +import { DocumentColorProvider } from '../../../../../editor/common/language/languages.js'; import { ColorDetector } from '../colorDetector.js'; import { ColorPickerModel } from '../colorPickerModel.js'; import { ColorPickerWidget } from '../colorPickerWidget.js'; diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerParticipant.ts index 8e66ff8b1b2..b0af35e5156 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerParticipant.ts @@ -9,14 +9,14 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { IActiveCodeEditor, ICodeEditor } from '../../../../browser/editorBrowser.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import { DocumentColorProvider, IColorInformation } from '../../../../common/languages.js'; +import { DocumentColorProvider, IColorInformation } from '../../../../../editor/common/language/languages.js'; import { IEditorHoverRenderContext } from '../../../hover/browser/hoverTypes.js'; import { getColors } from '../color.js'; import { ColorDetector } from '../colorDetector.js'; import { ColorPickerModel } from '../colorPickerModel.js'; import { BaseColor, ColorPickerWidgetType, createColorHover, updateColorPresentations, updateEditorModel } from '../colorPickerParticipantUtils.js'; import { ColorPickerWidget } from '../colorPickerWidget.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { Dimension } from '../../../../../base/browser/dom.js'; diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts index f15557441bd..3c5c2c7e74b 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPicker/standaloneColorPickerWidget.ts @@ -7,19 +7,19 @@ import '../colorPicker.css'; import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { IEditorHoverRenderContext } from '../../../hover/browser/hoverTypes.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../browser/editorBrowser.js'; -import { PositionAffinity } from '../../../../common/model.js'; -import { Position } from '../../../../common/core/position.js'; +import { PositionAffinity } from '../../../../../editor/common/language/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorHoverStatusBar } from '../../../hover/browser/contentHoverStatusBar.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { Emitter } from '../../../../../base/common/event.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { IColorInformation } from '../../../../common/languages.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { IColorInformation } from '../../../../../editor/common/language/languages.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { IContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; -import { IRange } from '../../../../common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { DefaultDocumentColorProvider } from '../defaultDocumentColorProvider.js'; -import { IEditorWorkerService } from '../../../../common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; import { StandaloneColorPickerHover, StandaloneColorPickerParticipant, StandaloneColorPickerRenderedParts } from './standaloneColorPickerParticipant.js'; import * as dom from '../../../../../base/browser/dom.js'; import { InsertButton } from '../colorPickerParts/colorPickerInsertButton.js'; diff --git a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts index 79c543e14f6..048e85959f6 100644 --- a/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/browser/blockCommentCommand.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { CharCode } from '../../../../base/common/charCode.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; export class BlockCommentCommand implements ICommand { diff --git a/src/vs/editor/contrib/comment/browser/comment.ts b/src/vs/editor/contrib/comment/browser/comment.ts index 1d225b85bcb..00d266aef52 100644 --- a/src/vs/editor/contrib/comment/browser/comment.ts +++ b/src/vs/editor/contrib/comment/browser/comment.ts @@ -7,7 +7,7 @@ import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, IActionOptions, registerEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ICommand } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; diff --git a/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts b/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts index 53875399585..fd82a49cd59 100644 --- a/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/browser/lineCommentCommand.ts @@ -6,12 +6,12 @@ import { CharCode } from '../../../../base/common/charCode.js'; import * as strings from '../../../../base/common/strings.js'; import { Constants } from '../../../../base/common/uint.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { BlockCommentCommand } from './blockCommentCommand.js'; diff --git a/src/vs/editor/contrib/comment/test/browser/blockCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/browser/blockCommentCommand.test.ts index 2b5e6eeff3d..57eb6182cd4 100644 --- a/src/vs/editor/contrib/comment/test/browser/blockCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/browser/blockCommentCommand.test.ts @@ -5,9 +5,9 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ICommand } from '../../../../common/editorCommon.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { BlockCommentCommand } from '../../browser/blockCommentCommand.js'; import { testCommand } from '../../../../test/browser/testCommand.js'; diff --git a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts index e738c1d865d..9415974105b 100644 --- a/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/browser/lineCommentCommand.test.ts @@ -6,11 +6,11 @@ import assert from 'assert'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ICommand } from '../../../../common/editorCommon.js'; -import { ColorId, MetadataConsts } from '../../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ColorId, MetadataConsts } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { CommentRule } from '../../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { NullState } from '../../../../common/languages/nullTokenize.js'; diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index 756e14ca80f..49ad4130aeb 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -18,7 +18,7 @@ import { EditorAction, EditorContributionInstantiation, registerEditorAction, re import { EditorOption } from '../../../common/config/editorOptions.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { IMenuService, MenuId, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts index a05f5f6354c..c45b94fb62a 100644 --- a/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/browser/cursorUndo.ts @@ -7,7 +7,7 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/cursorUndo/test/browser/cursorUndo.test.ts b/src/vs/editor/contrib/cursorUndo/test/browser/cursorUndo.test.ts index 786fccbad25..0d2c22489e8 100644 --- a/src/vs/editor/contrib/cursorUndo/test/browser/cursorUndo.test.ts +++ b/src/vs/editor/contrib/cursorUndo/test/browser/cursorUndo.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { CoreEditingCommands, CoreNavigationCommands } from '../../../../browser/coreCommands.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { Handler } from '../../../../common/editorCommon.js'; import { CursorUndo, CursorUndoRedoController } from '../../browser/cursorUndo.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts b/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts index 0eb179dc3b0..9ac724911d2 100644 --- a/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts +++ b/src/vs/editor/contrib/diffEditorBreadcrumbs/browser/contribution.ts @@ -7,13 +7,13 @@ import { reverseOrder, compareBy, numberComparator } from '../../../../base/comm import { observableValue, observableSignalFromEvent, autorunWithStore, IReader } from '../../../../base/common/observable.js'; import { HideUnchangedRegionsFeature, IDiffEditorBreadcrumbsSource } from '../../../browser/widget/diffEditor/features/hideUnchangedRegionsFeature.js'; import { DisposableCancellationTokenSource } from '../../../browser/widget/diffEditor/utils.js'; -import { LineRange } from '../../../common/core/lineRange.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { IOutlineModelService, OutlineModel } from '../../documentSymbols/browser/outlineModel.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { Event } from '../../../../base/common/event.js'; -import { SymbolKind } from '../../../common/languages.js'; +import { SymbolKind } from '../../../../editor/common/language/languages.js'; class DiffEditorBreadcrumbsSource extends Disposable implements IDiffEditorBreadcrumbsSource { private readonly _currentModel = observableValue(this, undefined); diff --git a/src/vs/editor/contrib/dnd/browser/dnd.ts b/src/vs/editor/contrib/dnd/browser/dnd.ts index 8ca7e29f781..b02ff1a7657 100644 --- a/src/vs/editor/contrib/dnd/browser/dnd.ts +++ b/src/vs/editor/contrib/dnd/browser/dnd.ts @@ -14,11 +14,11 @@ import { EditorContributionInstantiation, registerEditorContribution } from '../ import { CodeEditorWidget } from '../../../browser/widget/codeEditor/codeEditorWidget.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { CursorChangeReason } from '../../../common/cursorEvents.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution, IEditorDecorationsCollection, ScrollType } from '../../../common/editorCommon.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { DragAndDropCommand } from './dragAndDropCommand.js'; function hasTriggerModifier(e: IKeyboardEvent | IMouseEvent): boolean { diff --git a/src/vs/editor/contrib/dnd/browser/dragAndDropCommand.ts b/src/vs/editor/contrib/dnd/browser/dragAndDropCommand.ts index 7dd03062c6f..cc05ff2db88 100644 --- a/src/vs/editor/contrib/dnd/browser/dragAndDropCommand.ts +++ b/src/vs/editor/contrib/dnd/browser/dragAndDropCommand.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class DragAndDropCommand implements ICommand { diff --git a/src/vs/editor/contrib/documentSymbols/browser/documentSymbols.ts b/src/vs/editor/contrib/documentSymbols/browser/documentSymbols.ts index 949af74822a..d0e33046f2a 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/documentSymbols.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/documentSymbols.ts @@ -6,7 +6,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IOutlineModelService } from './outlineModel.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index b63525347fc..49931bea079 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -10,18 +10,18 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { LRUCache } from '../../../../base/common/map.js'; import { commonPrefixLength } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentSymbol, DocumentSymbolProvider } from '../../../common/languages.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentSymbol, DocumentSymbolProvider } from '../../../../editor/common/language/languages.js'; import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export abstract class TreeElement { diff --git a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts index a59d3a904dc..38536f0ba9f 100644 --- a/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts +++ b/src/vs/editor/contrib/documentSymbols/test/browser/outlineModel.test.ts @@ -7,11 +7,11 @@ import assert from 'assert'; import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Range } from '../../../../common/core/range.js'; -import { DocumentSymbol, SymbolKind } from '../../../../common/languages.js'; -import { LanguageFeatureDebounceService } from '../../../../common/services/languageFeatureDebounce.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { IModelService } from '../../../../common/services/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { DocumentSymbol, SymbolKind } from '../../../../../editor/common/language/languages.js'; +import { LanguageFeatureDebounceService } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { createModelServices, createTextModel } from '../../../../test/common/testTextModel.js'; import { NullLogService } from '../../../../../platform/log/common/log.js'; import { IMarker, MarkerSeverity } from '../../../../../platform/markers/common/markers.js'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts index 6d106bf78c4..7b2b9a26df9 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/copyPasteController.ts @@ -29,12 +29,12 @@ import { toExternalVSDataTransfer, toVSDataTransfer } from '../../../browser/dnd import { ICodeEditor, PastePayload } from '../../../browser/editorBrowser.js'; import { IBulkEditService } from '../../../browser/services/bulkEditService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { Handler, IEditorContribution } from '../../../common/editorCommon.js'; -import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteTriggerKind } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteTriggerKind } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { InlineProgressManager } from '../../inlineProgress/browser/inlineProgress.js'; import { MessageController } from '../../message/browser/messageController.js'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts index 3032d17757d..3a1ee173363 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/defaultProviders.ts @@ -14,12 +14,12 @@ import { relativePath } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { localize } from '../../../../nls.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { IPosition } from '../../../common/core/position.js'; -import { IRange } from '../../../common/core/range.js'; -import { DocumentDropEditProvider, DocumentDropEditsSession, DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteEditsSession, DocumentPasteTriggerKind } from '../../../common/languages.js'; -import { LanguageFilter } from '../../../common/languageSelector.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { DocumentDropEditProvider, DocumentDropEditsSession, DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteEditsSession, DocumentPasteTriggerKind } from '../../../../editor/common/language/languages.js'; +import { LanguageFilter } from '../../../../editor/common/language/languageSelector.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; abstract class SimplePasteAndDropProvider implements DocumentDropEditProvider, DocumentPasteEditProvider { diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts index 54a5d6d4fe4..97ae074be79 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.ts @@ -19,14 +19,14 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { toExternalVSDataTransfer } from '../../../browser/dnd.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { DocumentDropEdit, DocumentDropEditProvider } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { DraggedTreeItemsIdentifier } from '../../../common/services/treeViewsDnd.js'; -import { ITreeViewsDnDService } from '../../../common/services/treeViewsDndService.js'; +import { DocumentDropEdit, DocumentDropEditProvider } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { DraggedTreeItemsIdentifier } from '../../../../editor/common/language/services/treeViewsDnd.js'; +import { ITreeViewsDnDService } from '../../../../editor/common/language/services/treeViewsDndService.js'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { InlineProgressManager } from '../../inlineProgress/browser/inlineProgress.js'; import { PreferredDropConfiguration } from './dropIntoEditorContribution.js'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts index 0b47ad3d3e5..74da350fb65 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/edit.ts @@ -5,8 +5,8 @@ import { URI } from '../../../../base/common/uri.js'; import { ResourceTextEdit } from '../../../browser/services/bulkEditService.js'; -import { DocumentDropEdit, DocumentPasteEdit, DropYieldTo, WorkspaceEdit } from '../../../common/languages.js'; -import { Range } from '../../../common/core/range.js'; +import { DocumentDropEdit, DocumentPasteEdit, DropYieldTo, WorkspaceEdit } from '../../../../editor/common/language/languages.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { SnippetParser } from '../../snippet/browser/snippetParser.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts index 0b8a7b23edd..8f9a8802621 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts @@ -23,9 +23,9 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { IBulkEditResult, IBulkEditService } from '../../../browser/services/bulkEditService.js'; -import { Range } from '../../../common/core/range.js'; -import { DocumentDropEdit, DocumentPasteEdit } from '../../../common/languages.js'; -import { TrackedRangeStickiness } from '../../../common/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { DocumentDropEdit, DocumentPasteEdit } from '../../../../editor/common/language/languages.js'; +import { TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { createCombinedWorkspaceEdit } from './edit.js'; import './postEditWidget.css'; diff --git a/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts index c989e4ec338..0e250ccb3b0 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/test/browser/editSort.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { HierarchicalKind } from '../../../../../base/common/hierarchicalKind.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { DocumentDropEdit } from '../../../../common/languages.js'; +import { DocumentDropEdit } from '../../../../../editor/common/language/languages.js'; import { sortEditsByYieldTo } from '../../browser/edit.js'; diff --git a/src/vs/editor/contrib/editorState/browser/editorState.ts b/src/vs/editor/contrib/editorState/browser/editorState.ts index fdb296ee7f7..4e3e8c58095 100644 --- a/src/vs/editor/contrib/editorState/browser/editorState.ts +++ b/src/vs/editor/contrib/editorState/browser/editorState.ts @@ -5,11 +5,11 @@ import * as strings from '../../../../base/common/strings.js'; import { ICodeEditor, IActiveCodeEditor } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range, IRange } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range, IRange } from '../../../../editor/common/language/core/range.js'; import { CancellationTokenSource, CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { EditorKeybindingCancellationTokenSource } from './keybindingCancellation.js'; export const enum CodeEditorStateFlag { diff --git a/src/vs/editor/contrib/editorState/test/browser/editorState.test.ts b/src/vs/editor/contrib/editorState/test/browser/editorState.test.ts index c72c5a7b7e6..0911a8cd6dc 100644 --- a/src/vs/editor/contrib/editorState/test/browser/editorState.test.ts +++ b/src/vs/editor/contrib/editorState/test/browser/editorState.test.ts @@ -7,9 +7,9 @@ import assert from 'assert'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { ITextModel } from '../../../../common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { CodeEditorStateFlag, EditorState } from '../../browser/editorState.js'; interface IStubEditorState { diff --git a/src/vs/editor/contrib/find/browser/findController.ts b/src/vs/editor/contrib/find/browser/findController.ts index 9e00a1d5d47..6dfee725ab4 100644 --- a/src/vs/editor/contrib/find/browser/findController.ts +++ b/src/vs/editor/contrib/find/browser/findController.ts @@ -10,11 +10,11 @@ import * as strings from '../../../../base/common/strings.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, MultiEditorAction, registerEditorAction, registerEditorCommand, registerEditorContribution, registerMultiEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { overviewRulerRangeHighlight } from '../../../common/core/editorColorRegistry.js'; -import { IRange } from '../../../common/core/range.js'; +import { overviewRulerRangeHighlight } from '../../../../editor/common/language/core/editorColorRegistry.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { OverviewRulerLane } from '../../../common/model.js'; +import { OverviewRulerLane } from '../../../../editor/common/language/model.js'; import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, CONTEXT_REPLACE_INPUT_FOCUSED, FindModelBoundToEditorModel, FIND_IDS, ToggleCaseSensitiveKeybinding, TogglePreserveCaseKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from './findModel.js'; import { FindOptionsWidget } from './findOptionsWidget.js'; import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from './findState.js'; @@ -30,7 +30,7 @@ import { INotificationService, Severity } from '../../../../platform/notificatio import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IThemeService, themeColorFromId } from '../../../../platform/theme/common/themeService.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { FindWidgetSearchHistory } from './findWidgetSearchHistory.js'; import { ReplaceWidgetHistory } from './replaceWidgetHistory.js'; diff --git a/src/vs/editor/contrib/find/browser/findDecorations.ts b/src/vs/editor/contrib/find/browser/findDecorations.ts index cfa856aaa32..80004eea631 100644 --- a/src/vs/editor/contrib/find/browser/findDecorations.ts +++ b/src/vs/editor/contrib/find/browser/findDecorations.ts @@ -5,10 +5,10 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { IActiveCodeEditor } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { FindMatch, IModelDecorationsChangeAccessor, IModelDeltaDecoration, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { minimapFindMatch, overviewRulerFindMatchForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/editor/contrib/find/browser/findModel.ts b/src/vs/editor/contrib/find/browser/findModel.ts index bd6fa9baedc..3bd1c2621a1 100644 --- a/src/vs/editor/contrib/find/browser/findModel.ts +++ b/src/vs/editor/contrib/find/browser/findModel.ts @@ -12,12 +12,12 @@ import { IActiveCodeEditor } from '../../../browser/editorBrowser.js'; import { ReplaceCommand, ReplaceCommandThatPreservesSelection } from '../../../common/commands/replaceCommand.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ScrollType } from '../../../common/editorCommon.js'; -import { EndOfLinePreference, FindMatch, ITextModel } from '../../../common/model.js'; -import { SearchParams } from '../../../common/model/textModelSearch.js'; +import { EndOfLinePreference, FindMatch, ITextModel } from '../../../../editor/common/language/model.js'; +import { SearchParams } from '../../../../editor/common/language/model/textModelSearch.js'; import { FindDecorations } from './findDecorations.js'; import { FindReplaceState, FindReplaceStateChangedEvent } from './findState.js'; import { ReplaceAllCommand } from './replaceAllCommand.js'; diff --git a/src/vs/editor/contrib/find/browser/findState.ts b/src/vs/editor/contrib/find/browser/findState.ts index f48d60c1763..361551b30fa 100644 --- a/src/vs/editor/contrib/find/browser/findState.ts +++ b/src/vs/editor/contrib/find/browser/findState.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { MATCHES_LIMIT } from './findModel.js'; export interface FindReplaceStateChangedEvent { diff --git a/src/vs/editor/contrib/find/browser/findWidget.ts b/src/vs/editor/contrib/find/browser/findWidget.ts index e60addcd084..398daf87d18 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.ts +++ b/src/vs/editor/contrib/find/browser/findWidget.ts @@ -24,7 +24,7 @@ import * as strings from '../../../../base/common/strings.js'; import './findWidget.css'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, OverlayWidgetPositionPreference } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MATCHES_LIMIT } from './findModel.js'; import { FindReplaceState, FindReplaceStateChangedEvent } from './findState.js'; import * as nls from '../../../../nls.js'; @@ -42,7 +42,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { isHighContrast } from '../../../../platform/theme/common/theme.js'; import { assertIsDefined } from '../../../../base/common/types.js'; import { defaultInputBoxStyles, defaultToggleStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { createInstantHoverDelegate, getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; diff --git a/src/vs/editor/contrib/find/browser/replaceAllCommand.ts b/src/vs/editor/contrib/find/browser/replaceAllCommand.ts index 0926711fb3d..377dfd8998a 100644 --- a/src/vs/editor/contrib/find/browser/replaceAllCommand.ts +++ b/src/vs/editor/contrib/find/browser/replaceAllCommand.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; interface IEditOperation { range: Range; diff --git a/src/vs/editor/contrib/find/test/browser/find.test.ts b/src/vs/editor/contrib/find/test/browser/find.test.ts index 8b0cd2a87a5..960fb7be0d3 100644 --- a/src/vs/editor/contrib/find/test/browser/find.test.ts +++ b/src/vs/editor/contrib/find/test/browser/find.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { getSelectionSearchString } from '../../browser/findController.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/find/test/browser/findController.test.ts b/src/vs/editor/contrib/find/test/browser/findController.test.ts index bab641242e1..043f4f62c50 100644 --- a/src/vs/editor/contrib/find/test/browser/findController.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findController.test.ts @@ -9,10 +9,10 @@ import * as platform from '../../../../../base/common/platform.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorAction } from '../../../../browser/editorExtensions.js'; -import { EditOperation } from '../../../../common/core/editOperation.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { CommonFindController, FindStartFocusAction, IFindStartOptions, NextMatchFindAction, NextSelectionMatchFindAction, StartFindAction, StartFindReplaceAction, StartFindWithSelectionAction } from '../../browser/findController.js'; import { CONTEXT_FIND_INPUT_FOCUSED } from '../../browser/findModel.js'; import { withAsyncTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/find/test/browser/findModel.test.ts b/src/vs/editor/contrib/find/test/browser/findModel.test.ts index 09a0de5a7ae..70c981e7e1f 100644 --- a/src/vs/editor/contrib/find/test/browser/findModel.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findModel.test.ts @@ -8,10 +8,10 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { CoreNavigationCommands } from '../../../../browser/coreCommands.js'; import { IActiveCodeEditor, ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { PieceTreeTextBufferBuilder } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; import { FindModelBoundToEditorModel } from '../../browser/findModel.js'; import { FindReplaceState } from '../../browser/findState.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index dedd1ce7964..ac99751b071 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -15,14 +15,14 @@ import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js' import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, registerInstantiatedEditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { IRange } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; -import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; -import { FoldingRange, FoldingRangeKind, FoldingRangeProvider } from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; +import { FoldingRange, FoldingRangeKind, FoldingRangeProvider } from '../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { CollapseMemento, FoldingModel, getNextFoldLine, getParentFoldLine as getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateForType, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp, toggleCollapseState } from './foldingModel.js'; import { HiddenRangeModel } from './hiddenRangeModel.js'; @@ -34,13 +34,13 @@ import { FoldingDecorationProvider } from './foldingDecorations.js'; import { FoldingRegion, FoldingRegions, FoldRange, FoldSource, ILineRange } from './foldingRanges.js'; import { SyntaxRangeProvider } from './syntaxRangeProvider.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { URI } from '../../../../base/common/uri.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; const CONTEXT_FOLDING_ENABLED = new RawContextKey('foldingEnabled', false); diff --git a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts index 821badb88af..3ff57b7c457 100644 --- a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts @@ -5,8 +5,8 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { IModelDecorationOptions, IModelDecorationsChangeAccessor, MinimapPosition, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { IModelDecorationOptions, IModelDecorationsChangeAccessor, MinimapPosition, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { IDecorationProvider } from './foldingModel.js'; import { localize } from '../../../../nls.js'; import { editorSelectionBackground, iconForeground, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/editor/contrib/folding/browser/foldingModel.ts b/src/vs/editor/contrib/folding/browser/foldingModel.ts index c7721163695..4d1a46f5f1a 100644 --- a/src/vs/editor/contrib/folding/browser/foldingModel.ts +++ b/src/vs/editor/contrib/folding/browser/foldingModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Emitter, Event } from '../../../../base/common/event.js'; -import { IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../common/model.js'; +import { IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel } from '../../../../editor/common/language/model.js'; import { FoldingRegion, FoldingRegions, ILineRange, FoldRange, FoldSource } from './foldingRanges.js'; import { hash } from '../../../../base/common/hash.js'; import { SelectedLines } from './folding.js'; diff --git a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts index 9994afcb847..443ae570dfb 100644 --- a/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts +++ b/src/vs/editor/contrib/folding/browser/hiddenRangeModel.ts @@ -7,10 +7,10 @@ import { findFirstIdxMonotonousOrArrLen } from '../../../../base/common/arraysFi import { Emitter, Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; -import { countEOL } from '../../../common/core/eolCounter.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; +import { countEOL } from '../../../../editor/common/language/core/eolCounter.js'; import { FoldingModel } from './foldingModel.js'; export class HiddenRangeModel { diff --git a/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts b/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts index e1bbe8a949c..d85a57cdee3 100644 --- a/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts +++ b/src/vs/editor/contrib/folding/browser/indentRangeProvider.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { ITextModel } from '../../../common/model.js'; -import { computeIndentLevel } from '../../../common/model/utils.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { computeIndentLevel } from '../../../../editor/common/language/model/utils.js'; import { FoldingMarkers } from '../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { FoldingRegions, MAX_LINE_NUMBER } from './foldingRanges.js'; @@ -26,7 +26,7 @@ export class IndentRangeProvider implements RangeProvider { dispose() { } - compute(cancelationToken: CancellationToken,): Promise { + compute(_cancelationToken: CancellationToken,): Promise { const foldingRules = this.languageConfigurationService.getLanguageConfiguration(this.editorModel.getLanguageId()).foldingRules; const offSide = foldingRules && !!foldingRules.offSide; const markers = foldingRules && foldingRules.markers; diff --git a/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts index 2acc59de701..50c26feed85 100644 --- a/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts +++ b/src/vs/editor/contrib/folding/browser/syntaxRangeProvider.ts @@ -6,8 +6,8 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { ITextModel } from '../../../common/model.js'; -import { FoldingContext, FoldingRange, FoldingRangeProvider } from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { FoldingContext, FoldingRange, FoldingRangeProvider } from '../../../../editor/common/language/languages.js'; import { FoldingLimitReporter, RangeProvider } from './folding.js'; import { FoldingRegions, MAX_LINE_NUMBER } from './foldingRanges.js'; diff --git a/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts b/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts index 0a498e74f38..16816f70384 100644 --- a/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts +++ b/src/vs/editor/contrib/folding/test/browser/foldingModel.test.ts @@ -5,12 +5,12 @@ import assert from 'assert'; import { escapeRegExpCharacters } from '../../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { EditOperation } from '../../../../common/core/editOperation.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { IModelDecorationsChangeAccessor, ITextModel, TrackedRangeStickiness } from '../../../../common/model.js'; -import { ModelDecorationOptions } from '../../../../common/model/textModel.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { IModelDecorationsChangeAccessor, ITextModel, TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../editor/common/language/model/textModel.js'; import { toSelectedLines } from '../../browser/folding.js'; import { FoldingModel, getNextFoldLine, getParentFoldLine, getPreviousFoldLine, setCollapseStateAtLevel, setCollapseStateForMatchingLines, setCollapseStateForRest, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateUp } from '../../browser/foldingModel.js'; import { FoldingRegion } from '../../browser/foldingRanges.js'; diff --git a/src/vs/editor/contrib/folding/test/browser/hiddenRangeModel.test.ts b/src/vs/editor/contrib/folding/test/browser/hiddenRangeModel.test.ts index 6570dfe84ce..1a0597ae9ff 100644 --- a/src/vs/editor/contrib/folding/test/browser/hiddenRangeModel.test.ts +++ b/src/vs/editor/contrib/folding/test/browser/hiddenRangeModel.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { IRange } from '../../../../common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { FoldingModel } from '../../browser/foldingModel.js'; import { HiddenRangeModel } from '../../browser/hiddenRangeModel.js'; import { computeRanges } from '../../browser/indentRangeProvider.js'; diff --git a/src/vs/editor/contrib/folding/test/browser/syntaxFold.test.ts b/src/vs/editor/contrib/folding/test/browser/syntaxFold.test.ts index 22387ee2fc9..839b4d1d167 100644 --- a/src/vs/editor/contrib/folding/test/browser/syntaxFold.test.ts +++ b/src/vs/editor/contrib/folding/test/browser/syntaxFold.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { ITextModel } from '../../../../common/model.js'; -import { FoldingContext, FoldingRange, FoldingRangeProvider, ProviderResult } from '../../../../common/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { FoldingContext, FoldingRange, FoldingRangeProvider, ProviderResult } from '../../../../../editor/common/language/languages.js'; import { SyntaxRangeProvider } from '../../browser/syntaxRangeProvider.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { FoldingLimitReporter } from '../../browser/folding.js'; diff --git a/src/vs/editor/contrib/format/browser/format.ts b/src/vs/editor/contrib/format/browser/format.ts index 586663691d1..88b2a11dc31 100644 --- a/src/vs/editor/contrib/format/browser/format.ts +++ b/src/vs/editor/contrib/format/browser/format.ts @@ -14,20 +14,20 @@ import { URI } from '../../../../base/common/uri.js'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { IActiveCodeEditor, isCodeEditor } from '../../../browser/editorBrowser.js'; import { ServicesAccessor } from '../../../browser/editorExtensions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ScrollType } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, FormattingOptions, TextEdit } from '../../../common/languages.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider, FormattingOptions, TextEdit } from '../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { FormattingEdit } from './formattingEdit.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { ExtensionIdentifierSet } from '../../../../platform/extensions/common/extensions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; diff --git a/src/vs/editor/contrib/format/browser/formatActions.ts b/src/vs/editor/contrib/format/browser/formatActions.ts index f170a3e5167..082803bb22e 100644 --- a/src/vs/editor/contrib/format/browser/formatActions.ts +++ b/src/vs/editor/contrib/format/browser/formatActions.ts @@ -12,12 +12,12 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { CharacterSet } from '../../../common/core/characterClassifier.js'; -import { Range } from '../../../common/core/range.js'; +import { CharacterSet } from '../../../../editor/common/language/core/characterClassifier.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode, getOnTypeFormattingEdits } from './format.js'; import { FormattingEdit } from './formattingEdit.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/format/browser/formattingEdit.ts b/src/vs/editor/contrib/format/browser/formattingEdit.ts index e2d0dacda38..5d0824ae217 100644 --- a/src/vs/editor/contrib/format/browser/formattingEdit.ts +++ b/src/vs/editor/contrib/format/browser/formattingEdit.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { EndOfLineSequence } from '../../../common/model.js'; -import { TextEdit } from '../../../common/languages.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { EndOfLineSequence } from '../../../../editor/common/language/model.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js'; export class FormattingEdit { diff --git a/src/vs/editor/contrib/gotoError/browser/gotoError.ts b/src/vs/editor/contrib/gotoError/browser/gotoError.ts index eea7aa4931a..627ad527dff 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoError.ts @@ -10,8 +10,8 @@ import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { IMarkerNavigationService, MarkerList } from './markerNavigationService.js'; diff --git a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts index 3d2538512e7..515ac5d9b27 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -15,7 +15,7 @@ import { splitLines } from '../../../../base/common/strings.js'; import './media/gotoErrorWidget.css'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; import { peekViewTitleForeground, peekViewTitleInfoForeground, PeekViewWidget } from '../../peekView/browser/peekView.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts index bc02d0a62fa..858180888ee 100644 --- a/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts +++ b/src/vs/editor/contrib/gotoError/browser/markerNavigationService.ts @@ -9,9 +9,9 @@ import { DisposableStore, IDisposable, toDisposable } from '../../../../base/com import { LinkedList } from '../../../../base/common/linkedList.js'; import { compare } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IMarker, IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts index 15dc3bf04e6..4ab62caddaf 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToCommands.ts @@ -15,12 +15,12 @@ import { EditorAction2, ServicesAccessor } from '../../../browser/editorExtensio import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption, GoToLocationValues } from '../../../common/config/editorOptions.js'; -import * as corePosition from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import * as corePosition from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; -import { isLocationLink, Location, LocationLink } from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { isLocationLink, Location, LocationLink } from '../../../../editor/common/language/languages.js'; import { ReferencesController } from './peek/referencesController.js'; import { ReferencesModel } from './referencesModel.js'; import { ISymbolNavigationService } from './symbolNavigation.js'; @@ -36,8 +36,8 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; import { getDeclarationsAtPosition, getDefinitionsAtPosition, getImplementationsAtPosition, getReferencesAtPosition, getTypeDefinitionsAtPosition } from './goToSymbol.js'; -import { IWordAtPosition } from '../../../common/core/wordHelper.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/goToSymbol.ts b/src/vs/editor/contrib/gotoSymbol/browser/goToSymbol.ts index c76e80dbda7..3e3619adf43 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/goToSymbol.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/goToSymbol.ts @@ -8,11 +8,11 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { matchesSomeScheme, Schemas } from '../../../../base/common/network.js'; import { registerModelAndPositionCommand } from '../../../browser/editorExtensions.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { DeclarationProvider, DefinitionProvider, ImplementationProvider, LocationLink, ProviderResult, ReferenceProvider, TypeDefinitionProvider } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { DeclarationProvider, DefinitionProvider, ImplementationProvider, LocationLink, ProviderResult, ReferenceProvider, TypeDefinitionProvider } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { ReferencesModel } from './referencesModel.js'; function shouldIncludeLocationLink(sourceModel: ITextModel, loc: LocationLink): boolean { diff --git a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts index ea3d4987eeb..912e25c497c 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/link/goToDefinitionAtPosition.ts @@ -14,13 +14,13 @@ import { CodeEditorStateFlag, EditorState } from '../../../editorState/browser/e import { ICodeEditor, MouseTargetType } from '../../../../browser/editorBrowser.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../../browser/editorExtensions.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; -import { IRange, Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../../common/editorCommon.js'; -import { IModelDeltaDecoration, ITextModel } from '../../../../common/model.js'; -import { LocationLink } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; -import { ITextModelService } from '../../../../common/services/resolverService.js'; +import { IModelDeltaDecoration, ITextModel } from '../../../../../editor/common/language/model.js'; +import { LocationLink } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { ClickLinkGesture, ClickLinkKeyboardEvent, ClickLinkMouseEvent } from './clickLinkGesture.js'; import { PeekContext } from '../../../peekView/browser/peekView.js'; import * as nls from '../../../../../nls.js'; @@ -28,9 +28,9 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { DefinitionAction } from '../goToCommands.js'; import { getDefinitionsAtPosition } from '../goToSymbol.js'; -import { IWordAtPosition } from '../../../../common/core/wordHelper.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; -import { ModelDecorationInjectedTextOptions } from '../../../../common/model/textModel.js'; +import { IWordAtPosition } from '../../../../../editor/common/language/core/wordHelper.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { ModelDecorationInjectedTextOptions } from '../../../../../editor/common/language/model/textModel.js'; export class GotoDefinitionAtPositionEditorContribution implements IEditorContribution { diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts index 48ba268a699..f3fa48b24fa 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesController.ts @@ -10,10 +10,10 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../common/editorCommon.js'; -import { Location } from '../../../../common/languages.js'; +import { Location } from '../../../../../editor/common/language/languages.js'; import { PeekContext } from '../../../peekView/browser/peekView.js'; import { getOuterEditor } from '../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import * as nls from '../../../../../nls.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts index c6cda849675..b7264ed8c3c 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesTree.ts @@ -14,7 +14,7 @@ import { IAsyncDataSource, ITreeNode, ITreeRenderer } from '../../../../../base/ import { createMatches, FuzzyScore, IMatch } from '../../../../../base/common/filters.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { basename, dirname } from '../../../../../base/common/resources.js'; -import { ITextModelService } from '../../../../common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index 9581b5cf6ff..429848053d2 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -18,13 +18,13 @@ import './referencesWidget.css'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EmbeddedCodeEditorWidget } from '../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { IEditorOptions } from '../../../../common/config/editorOptions.js'; -import { IRange, Range } from '../../../../common/core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../../common/editorCommon.js'; -import { IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../common/model.js'; -import { ModelDecorationOptions, TextModel } from '../../../../common/model/textModel.js'; -import { Location } from '../../../../common/languages.js'; +import { IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions, TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { Location } from '../../../../../editor/common/language/languages.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../common/languages/modesRegistry.js'; -import { ITextEditorModel, ITextModelService } from '../../../../common/services/resolverService.js'; +import { ITextEditorModel, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { AccessibilityProvider, DataSource, Delegate, FileReferencesRenderer, IdentityProvider, OneReferenceRenderer, StringRepresentationProvider, TreeElement } from './referencesTree.js'; import * as peekView from '../../../peekView/browser/peekView.js'; import * as nls from '../../../../../nls.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/referencesModel.ts b/src/vs/editor/contrib/gotoSymbol/browser/referencesModel.ts index 6248ead0979..4b788c495a0 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/referencesModel.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/referencesModel.ts @@ -13,10 +13,10 @@ import { basename, extUri } from '../../../../base/common/resources.js'; import * as strings from '../../../../base/common/strings.js'; import { Constants } from '../../../../base/common/uint.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { Location, LocationLink } from '../../../common/languages.js'; -import { ITextEditorModel, ITextModelService } from '../../../common/services/resolverService.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Location, LocationLink } from '../../../../editor/common/language/languages.js'; +import { ITextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; export class OneReference { diff --git a/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts b/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts index e02392bdad9..a20d76f5bff 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/symbolNavigation.ts @@ -10,7 +10,7 @@ import { isEqual } from '../../../../base/common/resources.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorCommand, registerEditorCommand } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { OneReference, ReferencesModel } from './referencesModel.js'; import { localize } from '../../../../nls.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts b/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts index 58e29282327..0d18ea04724 100644 --- a/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts +++ b/src/vs/editor/contrib/gotoSymbol/test/browser/referencesModel.test.ts @@ -6,8 +6,8 @@ import assert from 'assert'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ReferencesModel } from '../../browser/referencesModel.js'; suite('references', function () { diff --git a/src/vs/editor/contrib/hover/browser/contentHoverComputer.ts b/src/vs/editor/contrib/hover/browser/contentHoverComputer.ts index 6306de9e5f7..b2c7f380f0d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverComputer.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverComputer.ts @@ -6,7 +6,7 @@ import { coalesce } from '../../../../base/common/arrays.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IActiveCodeEditor, ICodeEditor } from '../../../browser/editorBrowser.js'; -import { IModelDecoration } from '../../../common/model.js'; +import { IModelDecoration } from '../../../../editor/common/language/model.js'; import { HoverStartSource, IHoverComputer } from './hoverOperation.js'; import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IHoverPart } from './hoverTypes.js'; import { AsyncIterableObject } from '../../../../base/common/async.js'; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index c4c3bfd82ab..f8b02f5ced3 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -8,14 +8,14 @@ import { IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, IScrollEvent } from '../../../common/editorCommon.js'; import { HoverStartMode, HoverStartSource } from './hoverOperation.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { InlineSuggestionHintsContentWidget } from '../../inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; -import { HoverVerbosityAction } from '../../../common/languages.js'; +import { HoverVerbosityAction } from '../../../../editor/common/language/languages.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { isMousePositionWithinElement } from './hoverUtils.js'; import { ContentHoverWidgetWrapper } from './contentHoverWidgetWrapper.js'; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index a80bdb7966f..1fa51a3b56c 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -8,13 +8,13 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../.. import { EditorHoverStatusBar } from './contentHoverStatusBar.js'; import { HoverStartSource } from './hoverOperation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ContentHoverResult } from './contentHoverTypes.js'; import * as dom from '../../../../base/browser/dom.js'; -import { HoverVerbosityAction } from '../../../common/languages.js'; +import { HoverVerbosityAction } from '../../../../editor/common/language/languages.js'; import { MarkdownHoverParticipant } from './markdownHoverParticipant.js'; import { HoverColorPickerParticipant } from '../../colorPicker/browser/hoverColorPicker/hoverColorPickerParticipant.js'; import { localize } from '../../../../nls.js'; @@ -22,7 +22,7 @@ import { InlayHintsHover } from '../../inlayHints/browser/inlayHintsHover.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; import { HoverAction } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { IOffsetRange } from '../../../common/core/offsetRange.js'; +import { IOffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; export class RenderedContentHover extends Disposable { diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index 1b30ad9357a..12990fdb460 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -14,7 +14,7 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { getHoverAccessibleViewHint, HoverWidget } from '../../../../base/browser/ui/hover/hoverWidget.js'; -import { PositionAffinity } from '../../../common/model.js'; +import { PositionAffinity } from '../../../../editor/common/language/model.js'; import { Emitter } from '../../../../base/common/event.js'; import { RenderedContentHover } from './contentHoverRendered.js'; import { ScrollEvent } from '../../../../base/common/scrollable.js'; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts index 1be3324dffa..f251068f03b 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidgetWrapper.ts @@ -8,8 +8,8 @@ import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; -import { TokenizationRegistry } from '../../../common/languages.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { TokenizationRegistry } from '../../../../editor/common/language/languages.js'; import { HoverOperation, HoverResult, HoverStartMode, HoverStartSource } from './hoverOperation.js'; import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from './hoverTypes.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/editor/contrib/hover/browser/getHover.ts b/src/vs/editor/contrib/hover/browser/getHover.ts index 111eb72540d..8c900b43fe2 100644 --- a/src/vs/editor/contrib/hover/browser/getHover.ts +++ b/src/vs/editor/contrib/hover/browser/getHover.ts @@ -7,11 +7,11 @@ import { AsyncIterableObject } from '../../../../base/common/async.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { registerModelAndPositionCommand } from '../../../browser/editorExtensions.js'; -import { Position } from '../../../common/core/position.js'; -import { ITextModel } from '../../../common/model.js'; -import { Hover, HoverProvider } from '../../../common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { Hover, HoverProvider } from '../../../../editor/common/language/languages.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export class HoverProviderResult { constructor( diff --git a/src/vs/editor/contrib/hover/browser/glyphHoverComputer.ts b/src/vs/editor/contrib/hover/browser/glyphHoverComputer.ts index 760d00fb644..01a88647051 100644 --- a/src/vs/editor/contrib/hover/browser/glyphHoverComputer.ts +++ b/src/vs/editor/contrib/hover/browser/glyphHoverComputer.ts @@ -7,7 +7,7 @@ import { asArray } from '../../../../base/common/arrays.js'; import { IMarkdownString, isEmptyMarkdownString } from '../../../../base/common/htmlContent.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { IHoverComputer } from './hoverOperation.js'; -import { GlyphMarginLane } from '../../../common/model.js'; +import { GlyphMarginLane } from '../../../../editor/common/language/model.js'; export type LaneOrLineNumber = GlyphMarginLane | 'lineNo'; diff --git a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts index d40ff7189c5..25f16fd0f58 100644 --- a/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/glyphHoverWidget.ts @@ -8,7 +8,7 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.j import { MarkdownRenderer } from '../../../browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { ICodeEditor, IEditorMouseEvent, IOverlayWidget, IOverlayWidgetPosition, MouseTargetType } from '../../../browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../common/config/editorOptions.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { HoverOperation, HoverResult, HoverStartMode } from './hoverOperation.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { HoverWidget } from '../../../../base/browser/ui/hover/hoverWidget.js'; diff --git a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts index 0ffff8a229b..0619493ff95 100644 --- a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts +++ b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts @@ -10,7 +10,7 @@ import { IAccessibleViewImplementation } from '../../../../platform/accessibilit import { IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { HoverVerbosityAction } from '../../../common/languages.js'; +import { HoverVerbosityAction } from '../../../../editor/common/language/languages.js'; import { DECREASE_HOVER_VERBOSITY_ACCESSIBLE_ACTION_ID, DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACCESSIBLE_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID } from './hoverActionIds.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; diff --git a/src/vs/editor/contrib/hover/browser/hoverActions.ts b/src/vs/editor/contrib/hover/browser/hoverActions.ts index a2f16bc0c4c..39b35c82329 100644 --- a/src/vs/editor/contrib/hover/browser/hoverActions.ts +++ b/src/vs/editor/contrib/hover/browser/hoverActions.ts @@ -8,14 +8,14 @@ import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { GotoDefinitionAtPositionEditorContribution } from '../../gotoSymbol/browser/link/goToDefinitionAtPosition.js'; import { HoverStartMode, HoverStartSource } from './hoverOperation.js'; import { AccessibilitySupport } from '../../../../platform/accessibility/common/accessibility.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ContentHoverController } from './contentHoverController.js'; -import { HoverVerbosityAction } from '../../../common/languages.js'; +import { HoverVerbosityAction } from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import './hover.css'; diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index 82fe9eebf99..97a1cab727b 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -8,9 +8,9 @@ import { AsyncIterableObject } from '../../../../base/common/async.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { IModelDecoration } from '../../../common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IModelDecoration } from '../../../../editor/common/language/model.js'; import { BrandedService, IConstructorSignature } from '../../../../platform/instantiation/common/instantiation.js'; import { HoverStartSource } from './hoverOperation.js'; import { ScrollEvent } from '../../../../base/common/scrollable.js'; diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 03b585a99b1..352697c9c76 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -11,17 +11,17 @@ import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle import { MarkdownRenderer } from '../../../browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID } from './hoverActionIds.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { IModelDecoration, ITextModel } from '../../../common/model.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IModelDecoration, ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from './hoverTypes.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Hover, HoverContext, HoverProvider, HoverVerbosityAction } from '../../../common/languages.js'; +import { Hover, HoverContext, HoverProvider, HoverVerbosityAction } from '../../../../editor/common/language/languages.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; diff --git a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts index 986e464be99..8abb2275f9a 100644 --- a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts @@ -11,11 +11,11 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../.. import { basename } from '../../../../base/common/resources.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; -import { CodeActionTriggerType } from '../../../common/languages.js'; -import { IModelDecoration } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IMarkerDecorationsService } from '../../../common/services/markerDecorations.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; +import { IModelDecoration } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IMarkerDecorationsService } from '../../../../editor/common/language/services/markerDecorations.js'; import { ApplyCodeActionReason, getCodeActions, quickFixCommandId } from '../../codeAction/browser/codeAction.js'; import { CodeActionController } from '../../codeAction/browser/codeActionController.js'; import { CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from '../../codeAction/common/types.js'; diff --git a/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts b/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts index 7ff606a1e15..83d333c12a9 100644 --- a/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts +++ b/src/vs/editor/contrib/hover/browser/resizableContentWidget.ts @@ -7,7 +7,7 @@ import { ResizableHTMLElement } from '../../../../base/browser/ui/resizable/resi import { Disposable } from '../../../../base/common/lifecycle.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition, Position } from '../../../common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import * as dom from '../../../../base/browser/dom.js'; const TOP_HEIGHT = 30; diff --git a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts index 65337368f9c..236c9941024 100644 --- a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts +++ b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { RenderedContentHover } from '../../browser/contentHoverRendered.js'; import { IHoverPart } from '../../browser/hoverTypes.js'; import { TestCodeEditorInstantiationOptions, withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts index 2e42151eadc..8178659e0ad 100644 --- a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts +++ b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplace.ts @@ -9,13 +9,13 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { CodeEditorStateFlag, EditorState } from '../../editorState/browser/editorState.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { IInplaceReplaceSupportResult } from '../../../common/languages.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IInplaceReplaceSupportResult } from '../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import * as nls from '../../../../nls.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { InPlaceReplaceCommand } from './inPlaceReplaceCommand.js'; diff --git a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplaceCommand.ts b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplaceCommand.ts index 4bd38f64ef4..5984f69f310 100644 --- a/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplaceCommand.ts +++ b/src/vs/editor/contrib/inPlaceReplace/browser/inPlaceReplaceCommand.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class InPlaceReplaceCommand implements ICommand { diff --git a/src/vs/editor/contrib/indentation/browser/indentation.ts b/src/vs/editor/contrib/indentation/browser/indentation.ts index e467efa9ac0..be9e02451e6 100644 --- a/src/vs/editor/contrib/indentation/browser/indentation.ts +++ b/src/vs/editor/contrib/indentation/browser/indentation.ts @@ -9,24 +9,24 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ShiftCommand } from '../../../common/commands/shiftCommand.js'; import { EditorAutoIndentStrategy, EditorOption } from '../../../common/config/editorOptions.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder, IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { EndOfLineSequence, ITextModel } from '../../../common/model.js'; -import { TextEdit } from '../../../common/languages.js'; -import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; +import { EndOfLineSequence, ITextModel } from '../../../../editor/common/language/model.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { IndentConsts } from '../../../common/languages/supports/indentRules.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import * as indentUtils from '../common/indentUtils.js'; import * as nls from '../../../../nls.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { getGoodIndentForLine, getIndentMetadata } from '../../../common/languages/autoIndent.js'; import { getReindentEditOperations } from '../common/indentation.js'; -import { getStandardTokenTypeAtPosition } from '../../../common/tokens/lineTokens.js'; -import { Position } from '../../../common/core/position.js'; +import { getStandardTokenTypeAtPosition } from '../../../../editor/common/language/tokens/lineTokens.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; export class IndentationToSpacesAction extends EditorAction { public static readonly ID = 'editor.action.indentationToSpaces'; diff --git a/src/vs/editor/contrib/indentation/common/indentation.ts b/src/vs/editor/contrib/indentation/common/indentation.ts index f1046f84728..d83fec80c2d 100644 --- a/src/vs/editor/contrib/indentation/common/indentation.ts +++ b/src/vs/editor/contrib/indentation/common/indentation.ts @@ -5,13 +5,13 @@ import * as strings from '../../../../base/common/strings.js'; import { ShiftCommand } from '../../../common/commands/shiftCommand.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { normalizeIndentation } from '../../../common/core/indentation.js'; -import { Selection } from '../../../common/core/selection.js'; -import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { normalizeIndentation } from '../../../../editor/common/language/core/indentation.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { ProcessedIndentRulesSupport } from '../../../common/languages/supports/indentationLineProcessor.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export function getReindentEditOperations(model: ITextModel, languageConfigurationService: ILanguageConfigurationService, startLineNumber: number, endLineNumber: number): ISingleEditOperation[] { if (model.getLineCount() === 1 && model.getLineMaxColumn(1) === 1) { diff --git a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts index b4019cbea94..d9997e1f83c 100644 --- a/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts +++ b/src/vs/editor/contrib/indentation/test/browser/indentation.test.ts @@ -9,11 +9,11 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { MetadataConsts, StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { MetadataConsts, StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { NullState } from '../../../../common/languages/nullTokenize.js'; import { AutoIndentOnPaste, IndentationToSpacesCommand, IndentationToTabsCommand } from '../../browser/indentation.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; @@ -23,7 +23,7 @@ import { cppOnEnterRules, htmlOnEnterRules, javascriptOnEnterRules, phpOnEnterRu import { TypeOperations } from '../../../../common/cursor/cursorTypeOperations.js'; import { cppBracketRules, goBracketRules, htmlBracketRules, latexBracketRules, luaBracketRules, phpBracketRules, rubyBracketRules, typescriptBracketRules, vbBracketRules } from '../../../../test/common/modes/supports/bracketRules.js'; import { javascriptAutoClosingPairsRules, latexAutoClosingPairsRules } from '../../../../test/common/modes/supports/autoClosingPairsRules.js'; -import { LanguageService } from '../../../../common/services/languageService.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; diff --git a/src/vs/editor/contrib/indentation/test/browser/indentationLineProcessor.test.ts b/src/vs/editor/contrib/indentation/test/browser/indentationLineProcessor.test.ts index 20a08cd14b7..22c2e012ccf 100644 --- a/src/vs/editor/contrib/indentation/test/browser/indentationLineProcessor.test.ts +++ b/src/vs/editor/contrib/indentation/test/browser/indentationLineProcessor.test.ts @@ -5,17 +5,17 @@ import assert from 'assert'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { IndentationContextProcessor, ProcessedIndentRulesSupport } from '../../../../common/languages/supports/indentationLineProcessor.js'; import { Language, registerLanguage, registerLanguageConfiguration, registerTokenizationSupport, StandardTokenTypeData } from './indentation.test.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; -import { LanguageService } from '../../../../common/services/languageService.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; suite('Indentation Context Processor - TypeScript/JavaScript', () => { diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHints.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHints.ts index 8ce3830a3f2..739cddf0d19 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHints.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHints.ts @@ -6,11 +6,11 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { CancellationError, onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { InlayHint, InlayHintList, InlayHintsProvider, Command } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; +import { InlayHint, InlayHintList, InlayHintsProvider, Command } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { Schemas } from '../../../../base/common/network.js'; import { URI } from '../../../../base/common/uri.js'; diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts index 913e8075b3c..be39b083692 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsController.ts @@ -17,15 +17,15 @@ import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, MouseTargetType } fr import { ClassNameReference, CssProperties, DynamicCssRules } from '../../../browser/editorDom.js'; import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js'; import { EditorOption, EDITOR_FONT_DEFAULTS } from '../../../common/config/editorOptions.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import * as languages from '../../../common/languages.js'; -import { IModelDeltaDecoration, InjectedTextCursorStops, InjectedTextOptions, ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration, InjectedTextCursorStops, InjectedTextOptions, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationInjectedTextOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ClickLinkGesture, ClickLinkMouseEvent } from '../../gotoSymbol/browser/link/clickLinkGesture.js'; import { InlayHintAnchor, InlayHintItem, InlayHintsFragments } from './inlayHints.js'; import { goToDefinitionWithLocation, showGoToContextMenu } from './inlayHintsLocations.js'; @@ -35,7 +35,7 @@ import { createDecorator, IInstantiationService } from '../../../../platform/ins import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; import * as colors from '../../../../platform/theme/common/colorRegistry.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; // --- hint caching service (per session) diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts index 9c7320f98b5..4453d0a19b3 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts @@ -7,18 +7,18 @@ import { AsyncIterableObject } from '../../../../base/common/async.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../browser/editorBrowser.js'; -import { Position } from '../../../common/core/position.js'; -import { IModelDecoration } from '../../../common/model.js'; -import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IModelDecoration } from '../../../../editor/common/language/model.js'; +import { ModelDecorationInjectedTextOptions } from '../../../../editor/common/language/model/textModel.js'; import { HoverAnchor, HoverForeignElementAnchor, IEditorHoverParticipant } from '../../hover/browser/hoverTypes.js'; -import { ILanguageService } from '../../../common/languages/language.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { getHoverProviderResultsAsAsyncIterable } from '../../hover/browser/getHover.js'; import { MarkdownHover, MarkdownHoverParticipant } from '../../hover/browser/markdownHoverParticipant.js'; import { RenderedInlayHintLabelPart, InlayHintsController } from './inlayHintsController.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { localize } from '../../../../nls.js'; import * as platform from '../../../../base/common/platform.js'; diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts index edf82acff9a..ac532447b91 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsLocations.ts @@ -9,9 +9,9 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { IActiveCodeEditor, ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; -import { Location } from '../../../common/languages.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Location } from '../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { DefinitionAction, SymbolNavigationAction, SymbolNavigationAnchor } from '../../gotoSymbol/browser/goToCommands.js'; import { ClickLinkMouseEvent } from '../../gotoSymbol/browser/link/clickLinkGesture.js'; import { RenderedInlayHintLabelPart } from './inlayHintsController.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index 1658944cc6d..85f43ed6ebc 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -24,11 +24,11 @@ import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../browser/observableCodeEditor.js'; import { getOuterEditor } from '../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { CursorChangeReason } from '../../../../common/cursorEvents.js'; -import { ILanguageFeatureDebounceService } from '../../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { ILanguageFeatureDebounceService } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { InlineSuggestionHintsContentWidget } from '../hintsWidget/inlineCompletionsHintsWidget.js'; import { TextModelChangeRecorder } from '../model/changeRecorder.js'; import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts index 84650683bbe..f7bb6393fae 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/hoverParticipant.ts @@ -9,9 +9,9 @@ import { DisposableStore, IDisposable } from '../../../../../base/common/lifecyc import { autorun, autorunWithStore, constObservable } from '../../../../../base/common/observable.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../../browser/editorBrowser.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Range } from '../../../../common/core/range.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; -import { IModelDecoration } from '../../../../common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { IModelDecoration } from '../../../../../editor/common/language/model.js'; import { HoverAnchor, HoverAnchorType, HoverForeignElementAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from '../../../hover/browser/hoverTypes.js'; import { InlineCompletionsController } from '../controller/inlineCompletionsController.js'; import { InlineSuggestionHintsContentWidget } from './inlineCompletionsHintsWidget.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts index d5b54d82af8..1664b9d0053 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts @@ -29,9 +29,9 @@ import { ITelemetryService } from '../../../../../platform/telemetry/common/tele import { registerIcon } from '../../../../../platform/theme/common/iconRegistry.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../browser/editorBrowser.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Command, InlineCompletionTriggerKind, InlineCompletionWarning } from '../../../../common/languages.js'; -import { PositionAffinity } from '../../../../common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Command, InlineCompletionTriggerKind, InlineCompletionWarning } from '../../../../../editor/common/language/languages.js'; +import { PositionAffinity } from '../../../../../editor/common/language/model.js'; import { showNextInlineSuggestionActionId, showPreviousInlineSuggestionActionId } from '../controller/commandIds.js'; import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; import './inlineCompletionsHintsWidget.css'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts index 959a99ea366..47f8b5de95f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts @@ -14,9 +14,9 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { InlineCompletionsModel } from './model/inlineCompletionsModel.js'; -import { TextEdit } from '../../../common/core/textEdit.js'; -import { LineEdit } from '../../../common/core/lineEdit.js'; -import { TextModelText } from '../../../common/model/textModelText.js'; +import { TextEdit } from '../../../../editor/common/language/core/textEdit.js'; +import { LineEdit } from '../../../../editor/common/language/core/lineEdit.js'; +import { TextModelText } from '../../../../editor/common/language/model/textModelText.js'; import { localize } from '../../../../nls.js'; export class InlineCompletionsAccessibleView implements IAccessibleViewImplementation { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts index 2e9b53e49a1..8c8426ea4f7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/computeGhostText.ts @@ -5,10 +5,10 @@ import { IDiffChange, LcsDiff } from '../../../../../base/common/diff/diff.js'; import { getLeadingWhitespace } from '../../../../../base/common/strings.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; -import { ITextModel } from '../../../../common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { GhostText, GhostTextPart } from './ghostText.js'; import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts index 700bc76bf64..d1921378b23 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts @@ -5,9 +5,9 @@ import { equals } from '../../../../../base/common/arrays.js'; import { splitLines } from '../../../../../base/common/strings.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit, TextEdit } from '../../../../../editor/common/language/core/textEdit.js'; import { LineDecoration } from '../../../../common/viewLayout/lineDecorations.js'; import { InlineDecoration } from '../../../../common/viewModel.js'; import { ColumnRange } from '../utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 43be28ea2d5..88f9b35dcd0 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -17,21 +17,21 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { CursorColumns } from '../../../../common/core/cursorColumns.js'; -import { EditOperation } from '../../../../common/core/editOperation.js'; -import { LineRange } from '../../../../common/core/lineRange.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { SingleTextEdit, TextEdit } from '../../../../common/core/textEdit.js'; -import { TextLength } from '../../../../common/core/textLength.js'; +import { CursorColumns } from '../../../../../editor/common/language/core/cursorColumns.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { SingleTextEdit, TextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; import { ScrollType } from '../../../../common/editorCommon.js'; -import { Command, InlineCompletion, InlineCompletionContext, InlineCompletionTriggerKind, PartialAcceptTriggerKind } from '../../../../common/languages.js'; +import { Command, InlineCompletion, InlineCompletionContext, InlineCompletionTriggerKind, PartialAcceptTriggerKind } from '../../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { EndOfLinePreference, IModelDeltaDecoration, ITextModel } from '../../../../common/model.js'; -import { TextModelText } from '../../../../common/model/textModelText.js'; -import { IFeatureDebounceInformation } from '../../../../common/services/languageFeatureDebounce.js'; -import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js'; +import { EndOfLinePreference, IModelDeltaDecoration, ITextModel } from '../../../../../editor/common/language/model.js'; +import { TextModelText } from '../../../../../editor/common/language/model/textModelText.js'; +import { IFeatureDebounceInformation } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { SnippetController2 } from '../../../snippet/browser/snippetController2.js'; import { addPositions, getEndPositionsAfterApplying, getModifiedRangesAfterApplying, substringPos, subtractPositions } from '../utils.js'; import { AnimatedValue, easeOutCubic, ObservableAnimatedValue } from './animation.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index afaad779d6d..69ccaf87182 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts @@ -16,20 +16,20 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; -import { applyEditsToRanges, OffsetEdit, SingleOffsetEdit } from '../../../../common/core/offsetEdit.js'; -import { OffsetRange } from '../../../../common/core/offsetRange.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit, StringText } from '../../../../common/core/textEdit.js'; -import { TextLength } from '../../../../common/core/textLength.js'; +import { applyEditsToRanges, OffsetEdit, SingleOffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit, StringText } from '../../../../../editor/common/language/core/textEdit.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; import { linesDiffComputers } from '../../../../common/diff/linesDiffComputers.js'; -import { InlineCompletionContext, InlineCompletionTriggerKind } from '../../../../common/languages.js'; +import { InlineCompletionContext, InlineCompletionTriggerKind } from '../../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { EndOfLinePreference, ITextModel } from '../../../../common/model.js'; -import { OffsetEdits } from '../../../../common/model/textModelOffsetEdit.js'; -import { IFeatureDebounceInformation } from '../../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; -import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js'; +import { EndOfLinePreference, ITextModel } from '../../../../../editor/common/language/model.js'; +import { OffsetEdits } from '../../../../../editor/common/language/model/textModelOffsetEdit.js'; +import { IFeatureDebounceInformation } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { InlineCompletionItem, InlineCompletionProviderResult, provideInlineCompletions } from './provideInlineCompletions.js'; import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; import { StructuredLogger, IRecordableEditorLogEntry, IRecordableLogEntry, formatRecordableLogEntry } from '../structuredLogger.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts index 13e6d6edf74..29717cc0388 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; -import { Command } from '../../../../common/languages.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { Command } from '../../../../../editor/common/language/languages.js'; import { InlineCompletionItem } from './provideInlineCompletions.js'; export class InlineEdit { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEditsAdapter.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEditsAdapter.ts index f9485a16301..0b5009e6398 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEditsAdapter.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEditsAdapter.ts @@ -9,10 +9,10 @@ import { autorunWithStore, observableSignalFromEvent } from '../../../../../base import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Position } from '../../../../common/core/position.js'; -import { IInlineEdit, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineEditProvider, InlineEditTriggerKind } from '../../../../common/languages.js'; -import { ITextModel } from '../../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { IInlineEdit, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineEditProvider, InlineEditTriggerKind } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; export class InlineEditsAdapterContribution extends Disposable { public static ID = 'editor.contrib.inlineEditsAdapter'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index 49c79efeafe..1b0222cc104 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -10,18 +10,18 @@ import { onUnexpectedExternalError } from '../../../../../base/common/errors.js' import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; import { SetMap } from '../../../../../base/common/map.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; -import { ISingleEditOperation } from '../../../../common/core/editOperation.js'; -import { SingleOffsetEdit } from '../../../../common/core/offsetEdit.js'; -import { OffsetRange } from '../../../../common/core/offsetRange.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { ISingleEditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { SingleOffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import { Command, InlineCompletion, InlineCompletionContext, InlineCompletionProviderGroupId, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind } from '../../../../common/languages.js'; +import { Command, InlineCompletion, InlineCompletionContext, InlineCompletionProviderGroupId, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind } from '../../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { ITextModel } from '../../../../common/model.js'; -import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js'; -import { TextModelText } from '../../../../common/model/textModelText.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { fixBracketsInLine } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/fixBrackets.js'; +import { TextModelText } from '../../../../../editor/common/language/model/textModelText.js'; import { SnippetParser, Text } from '../../../snippet/browser/snippetParser.js'; import { getReadonlyEmptyArray } from '../utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts index 3c4d51ef8d9..aa5c515e5cf 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/singleTextEditHelpers.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { commonPrefixLength } from '../../../../../base/common/strings.js'; -import { Range } from '../../../../common/core/range.js'; -import { TextLength } from '../../../../common/core/textLength.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; -import { EndOfLinePreference, ITextModel } from '../../../../common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { EndOfLinePreference, ITextModel } from '../../../../../editor/common/language/model.js'; export function singleTextRemoveCommonPrefix(edit: SingleTextEdit, model: ITextModel, validModelRange?: Range): SingleTextEdit { const modelRange = validModelRange ? edit.range.intersectRanges(validModelRange) : edit.range; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts index 88df0a98a52..5655ef19d75 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts @@ -8,11 +8,11 @@ import { findFirstMax } from '../../../../../base/common/arraysFind.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; -import { CompletionItemInsertTextRule, CompletionItemKind, SelectedSuggestionInfo } from '../../../../common/languages.js'; -import { ITextModel } from '../../../../common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { CompletionItemInsertTextRule, CompletionItemKind, SelectedSuggestionInfo } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; import { SnippetParser } from '../../../snippet/browser/snippetParser.js'; import { SnippetSession } from '../../../snippet/browser/snippetSession.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts index 3d0da6db7e2..4d2758d50ae 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/utils.ts @@ -9,10 +9,10 @@ import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle. import { IObservable, observableValue, ISettableObservable, autorun, transaction, IReader } from '../../../../base/common/observable.js'; import { ContextKeyValue, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; -import { Position } from '../../../common/core/position.js'; -import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; -import { Range } from '../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { PositionOffsetTransformer } from '../../../../editor/common/language/core/positionToOffset.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { SingleTextEdit, TextEdit } from '../../../../editor/common/language/core/textEdit.js'; const array: ReadonlyArray = []; export function getReadonlyEmptyArray(): readonly T[] { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index d592eca09ea..34c551520bb 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -15,14 +15,14 @@ import { applyFontInfo } from '../../../../../browser/config/domFontInfo.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, IViewZoneChangeAccessor, MouseTargetType } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from '../../../../../common/config/editorOptions.js'; -import { OffsetEdit, SingleOffsetEdit } from '../../../../../common/core/offsetEdit.js'; -import { Position } from '../../../../../common/core/position.js'; -import { Range } from '../../../../../common/core/range.js'; -import { StringBuilder } from '../../../../../common/core/stringBuilder.js'; -import { IconPath } from '../../../../../common/languages.js'; -import { ILanguageService } from '../../../../../common/languages/language.js'; -import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops, PositionAffinity } from '../../../../../common/model.js'; -import { LineTokens } from '../../../../../common/tokens/lineTokens.js'; +import { OffsetEdit, SingleOffsetEdit } from '../../../../../../editor/common/language/core/offsetEdit.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { StringBuilder } from '../../../../../../editor/common/language/core/stringBuilder.js'; +import { IconPath } from '../../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; +import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops, PositionAffinity } from '../../../../../../editor/common/language/model.js'; +import { LineTokens } from '../../../../../../editor/common/language/tokens/lineTokens.js'; import { LineDecoration } from '../../../../../common/viewLayout/lineDecorations.js'; import { RenderLineInput, renderViewLine } from '../../../../../common/viewLayout/viewLineRenderer.js'; import { InlineDecorationType } from '../../../../../common/viewModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts index 4a3c4f22e56..24220d20a2c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts @@ -24,8 +24,8 @@ import { Rect } from '../../../../../../browser/rect.js'; import { HoverService } from '../../../../../../browser/services/hoverService/hoverService.js'; import { HoverWidget } from '../../../../../../browser/services/hoverService/hoverWidget.js'; import { EditorOption, RenderLineNumbersType } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; import { StickyScrollController } from '../../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineEditHost } from '../inlineEditsModel.js'; import { IInlineEditModel, InlineEditTabAction } from '../inlineEditsViewInterface.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/indicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/indicatorView.ts index a30da5e93a2..262f84caba4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/indicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/indicatorView.ts @@ -12,7 +12,7 @@ import { localize } from '../../../../../../../nls.js'; import { buttonBackground, buttonForeground, buttonSeparator } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { registerColor } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; import { InlineCompletionsModel } from '../../../model/inlineCompletionsModel.js'; export interface IInlineEditsIndicatorState { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts index 0aac963340b..80aecbdbef7 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SingleLineEdit } from '../../../../../common/core/lineEdit.js'; -import { Position } from '../../../../../common/core/position.js'; -import { AbstractText, TextEdit } from '../../../../../common/core/textEdit.js'; -import { Command } from '../../../../../common/languages.js'; +import { SingleLineEdit } from '../../../../../../editor/common/language/core/lineEdit.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { AbstractText, TextEdit } from '../../../../../../editor/common/language/core/textEdit.js'; +import { Command } from '../../../../../../editor/common/language/languages.js'; import { InlineCompletionItem } from '../../model/provideInlineCompletions.js'; export class InlineEditWithChanges { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts index 0a07f09a151..d24bc49321a 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -8,9 +8,9 @@ import { derived, IObservable } from '../../../../../../base/common/observable.j import { localize } from '../../../../../../nls.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; -import { StringText, TextEdit } from '../../../../../common/core/textEdit.js'; -import { Command } from '../../../../../common/languages.js'; +import { LineRange } from '../../../../../../editor/common/language/core/lineRange.js'; +import { StringText, TextEdit } from '../../../../../../editor/common/language/core/textEdit.js'; +import { Command } from '../../../../../../editor/common/language/languages.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineCompletionWithUpdatedRange } from '../../model/inlineCompletionsSource.js'; import { IInlineEditHost, IInlineEditModel, InlineEditTabAction } from './inlineEditsViewInterface.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts index 7931a86e959..52df75f205e 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -12,13 +12,13 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; -import { Position } from '../../../../../common/core/position.js'; -import { Range } from '../../../../../common/core/range.js'; -import { AbstractText, SingleTextEdit, StringText } from '../../../../../common/core/textEdit.js'; -import { TextLength } from '../../../../../common/core/textLength.js'; +import { LineRange } from '../../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { AbstractText, SingleTextEdit, StringText } from '../../../../../../editor/common/language/core/textEdit.js'; +import { TextLength } from '../../../../../../editor/common/language/core/textLength.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; -import { TextModel } from '../../../../../common/model/textModel.js'; +import { TextModel } from '../../../../../../editor/common/language/model/textModel.js'; import { InlineEditsGutterIndicator } from './components/gutterIndicatorView.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; import { GhostTextIndicator, InlineEditHost, InlineEditModel } from './inlineEditsModel.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts index dae493157af..69aac571397 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts @@ -6,7 +6,7 @@ import { IMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; import { Event } from '../../../../../../base/common/event.js'; import { IObservable } from '../../../../../../base/common/observable.js'; -import { Command } from '../../../../../common/languages.js'; +import { Command } from '../../../../../../editor/common/language/languages.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; export enum InlineEditTabAction { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts index 39b6acae41f..d293de3f35c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts @@ -9,10 +9,10 @@ import { derived, IObservable, ISettableObservable } from '../../../../../../bas import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; -import { LineRange } from '../../../../../common/core/lineRange.js'; -import { Range } from '../../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../../common/core/textEdit.js'; -import { TextModelText } from '../../../../../common/model/textModelText.js'; +import { LineRange } from '../../../../../../editor/common/language/core/lineRange.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit, TextEdit } from '../../../../../../editor/common/language/core/textEdit.js'; +import { TextModelText } from '../../../../../../editor/common/language/model/textModelText.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineEdit } from '../../model/inlineEdit.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts index fcd48453e74..a63c9892ae1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts @@ -13,10 +13,10 @@ import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { Rect } from '../../../../../../browser/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Position } from '../../../../../../common/core/position.js'; -import { Range } from '../../../../../../common/core/range.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { InlineEditWithChanges } from '../inlineEditWithChanges.js'; import { getOriginalBorderColor, originalBackgroundColor } from '../theme.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts index e79107bbd24..a5cfc7c46d3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts @@ -15,13 +15,13 @@ import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../br import { Rect } from '../../../../../../browser/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Position } from '../../../../../../common/core/position.js'; -import { Range } from '../../../../../../common/core/range.js'; -import { ILanguageService } from '../../../../../../common/languages/language.js'; -import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; -import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../../../editor/common/language/language.js'; +import { LineTokens } from '../../../../../../../editor/common/language/tokens/lineTokens.js'; +import { TokenArray } from '../../../../../../../editor/common/language/tokens/tokenArray.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../common/viewModel.js'; import { GhostText, GhostTextPart } from '../../../model/ghostText.js'; import { GhostTextView } from '../../ghostText/ghostTextView.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts index 7d79399b099..d66be5a601c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts @@ -18,13 +18,13 @@ import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Range } from '../../../../../../common/core/range.js'; -import { ILanguageService } from '../../../../../../common/languages/language.js'; -import { IModelDecorationOptions, TrackedRangeStickiness } from '../../../../../../common/model.js'; -import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; -import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../../../editor/common/language/language.js'; +import { IModelDecorationOptions, TrackedRangeStickiness } from '../../../../../../../editor/common/language/model.js'; +import { LineTokens } from '../../../../../../../editor/common/language/tokens/lineTokens.js'; +import { TokenArray } from '../../../../../../../editor/common/language/tokens/tokenArray.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../common/viewModel.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getEditorBlendedColor, getModifiedBorderColor, getOriginalBorderColor, modifiedChangedLineBackgroundColor, originalBackgroundColor } from '../theme.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts index 26e186b6c88..2f817250715 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts @@ -17,11 +17,11 @@ import { observableCodeEditor } from '../../../../../../browser/observableCodeEd import { Rect } from '../../../../../../browser/rect.js'; import { EmbeddedCodeEditorWidget } from '../../../../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Position } from '../../../../../../common/core/position.js'; -import { Range } from '../../../../../../common/core/range.js'; -import { ITextModel } from '../../../../../../common/model.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../../../editor/common/language/model.js'; import { StickyScrollController } from '../../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineCompletionContextKeys } from '../../../controller/inlineCompletionContextKeys.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts index 55ee6e9b87d..c2210e5d09d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts @@ -13,8 +13,8 @@ import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEd import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { SingleTextEdit } from '../../../../../../../editor/common/language/core/textEdit.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor } from '../theme.js'; import { mapOutFalsy, rectToProps } from '../utils/utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts index 2702159e1d2..ccf9903904d 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts @@ -15,12 +15,12 @@ import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { SingleOffsetEdit } from '../../../../../../common/core/offsetEdit.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; -import { ILanguageService } from '../../../../../../common/languages/language.js'; -import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; -import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; +import { SingleOffsetEdit } from '../../../../../../../editor/common/language/core/offsetEdit.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { SingleTextEdit } from '../../../../../../../editor/common/language/core/textEdit.js'; +import { ILanguageService } from '../../../../../../../editor/common/language/language.js'; +import { LineTokens } from '../../../../../../../editor/common/language/tokens/lineTokens.js'; +import { TokenArray } from '../../../../../../../editor/common/language/tokens/tokenArray.js'; import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor, getOriginalBorderColor, modifiedChangedTextOverlayColor, originalChangedTextOverlayColor } from '../theme.js'; import { mapOutFalsy, rectToProps } from '../utils/utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts index 63b438213af..c3a41757656 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts @@ -10,12 +10,12 @@ import { autorunWithStore, derived, IObservable, observableFromEvent } from '../ import { ICodeEditor, MouseTargetType } from '../../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { rangeIsSingleLine } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Range } from '../../../../../../common/core/range.js'; -import { AbstractText } from '../../../../../../common/core/textEdit.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; +import { AbstractText } from '../../../../../../../editor/common/language/core/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../../../common/diff/rangeMapping.js'; -import { EndOfLinePreference, IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from '../../../../../../common/model.js'; -import { ModelDecorationOptions } from '../../../../../../common/model/textModel.js'; +import { EndOfLinePreference, IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from '../../../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../../../editor/common/language/model/textModel.js'; import { IInlineEditsView } from '../inlineEditsViewInterface.js'; import { classNames } from '../utils/utils.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts index 8aac0066ab6..09699f0c9c6 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts @@ -18,14 +18,14 @@ import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEd import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; -import { LineRange } from '../../../../../../common/core/lineRange.js'; -import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; -import { Position } from '../../../../../../common/core/position.js'; -import { Range } from '../../../../../../common/core/range.js'; -import { SingleTextEdit, TextEdit } from '../../../../../../common/core/textEdit.js'; +import { LineRange } from '../../../../../../../editor/common/language/core/lineRange.js'; +import { OffsetRange } from '../../../../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit, TextEdit } from '../../../../../../../editor/common/language/core/textEdit.js'; import { RangeMapping } from '../../../../../../common/diff/rangeMapping.js'; -import { ITextModel } from '../../../../../../common/model.js'; -import { indentOfLine } from '../../../../../../common/model/textModel.js'; +import { ITextModel } from '../../../../../../../editor/common/language/model.js'; +import { indentOfLine } from '../../../../../../../editor/common/language/model/textModel.js'; export function maxContentWidthInRange(editor: ObservableCodeEditor, range: LineRange, reader: IReader | undefined): number { editor.layoutInfo.read(reader); diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsModel.test.ts index 6ba5f06b158..7b5b13fb41c 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsModel.test.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Position } from '../../../../common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { getSecondaryEdits } from '../../browser/model/inlineCompletionsModel.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('inlineCompletionModel', () => { diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts index 28bd5ab66a4..4508efb4064 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/inlineCompletionsProvider.test.ts @@ -8,20 +8,20 @@ import { timeout } from '../../../../../base/common/async.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; -import { InlineCompletionsProvider } from '../../../../common/languages.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { InlineCompletionsProvider } from '../../../../../editor/common/language/languages.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; import { ViewModel } from '../../../../common/viewModel/viewModelImpl.js'; import { InlineCompletionsController } from '../../browser/controller/inlineCompletionsController.js'; import { InlineCompletionsModel } from '../../browser/model/inlineCompletionsModel.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; import { GhostTextContext, MockInlineCompletionsProvider } from './utils.js'; import { ITestCodeEditor, TestCodeEditorInstantiationOptions, withAsyncTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { computeGhostText } from '../../browser/model/computeGhostText.js'; suite('Inline Completions', () => { diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts index 1b369ed62b9..75ac53b04d6 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -8,9 +8,9 @@ import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; -import { Range } from '../../../../common/core/range.js'; -import { CompletionItemKind, CompletionItemProvider } from '../../../../common/languages.js'; -import { IEditorWorkerService } from '../../../../common/services/editorWorker.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { CompletionItemKind, CompletionItemProvider } from '../../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; import { ViewModel } from '../../../../common/viewModel/viewModelImpl.js'; import { GhostTextContext } from './utils.js'; import { SnippetController2 } from '../../../snippet/browser/snippetController2.js'; @@ -28,8 +28,8 @@ import { NullTelemetryService } from '../../../../../platform/telemetry/common/t import assert from 'assert'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { InlineCompletionsModel } from '../../browser/model/inlineCompletionsModel.js'; import { InlineCompletionsController } from '../../browser/controller/inlineCompletionsController.js'; import { autorun } from '../../../../../base/common/observable.js'; diff --git a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts index 9cd0d114694..b0f6d7dd82d 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/utils.ts @@ -7,9 +7,9 @@ import { timeout } from '../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { CoreEditingCommands, CoreNavigationCommands } from '../../../../browser/coreCommands.js'; -import { Position } from '../../../../common/core/position.js'; -import { ITextModel } from '../../../../common/model.js'; -import { InlineCompletion, InlineCompletionContext, InlineCompletionsProvider } from '../../../../common/languages.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { InlineCompletion, InlineCompletionContext, InlineCompletionsProvider } from '../../../../../editor/common/language/languages.js'; import { ITestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { InlineCompletionsModel } from '../../browser/model/inlineCompletionsModel.js'; import { autorun } from '../../../../../base/common/observable.js'; diff --git a/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts b/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts index 5b238c21476..24a7d74c044 100644 --- a/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts +++ b/src/vs/editor/contrib/inlineProgress/browser/inlineProgress.ts @@ -12,11 +12,11 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import './inlineProgressWidget.css'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorDecorationsCollection } from '../../../common/editorCommon.js'; -import { TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; const inlineProgressDecoration = ModelDecorationOptions.register({ diff --git a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts index 0b1493d16b7..d3d33b083a6 100644 --- a/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts +++ b/src/vs/editor/contrib/insertFinalNewLine/browser/insertFinalNewLineCommand.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as strings from '../../../../base/common/strings.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class InsertFinalNewLineCommand implements ICommand { diff --git a/src/vs/editor/contrib/lineSelection/test/browser/lineSelection.test.ts b/src/vs/editor/contrib/lineSelection/test/browser/lineSelection.test.ts index b5c075ca416..cafac691585 100644 --- a/src/vs/editor/contrib/lineSelection/test/browser/lineSelection.test.ts +++ b/src/vs/editor/contrib/lineSelection/test/browser/lineSelection.test.ts @@ -7,8 +7,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import type { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorAction } from '../../../../browser/editorExtensions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ExpandLineSelectionAction } from '../../browser/lineSelection.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/linesOperations/browser/copyLinesCommand.ts b/src/vs/editor/contrib/linesOperations/browser/copyLinesCommand.ts index ccc16cd5360..67b2332c9d3 100644 --- a/src/vs/editor/contrib/linesOperations/browser/copyLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/browser/copyLinesCommand.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../common/core/range.js'; -import { Selection, SelectionDirection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection, SelectionDirection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class CopyLinesCommand implements ICommand { diff --git a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts index 0ca8dd2abfb..57423ae0cf3 100644 --- a/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts +++ b/src/vs/editor/contrib/linesOperations/browser/linesOperations.ts @@ -12,13 +12,13 @@ import { TrimTrailingWhitespaceCommand } from '../../../common/commands/trimTrai import { EditorOption } from '../../../common/config/editorOptions.js'; import { TypeOperations } from '../../../common/cursor/cursorTypeOperations.js'; import { EnterOperation } from '../../../common/cursor/cursorTypeEditOperations.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { CopyLinesCommand } from './copyLinesCommand.js'; import { MoveLinesCommand } from './moveLinesCommand.js'; import { SortLinesCommand } from './sortLinesCommand.js'; diff --git a/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts b/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts index 40fdd0378e1..253ecad036a 100644 --- a/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/browser/moveLinesCommand.ts @@ -6,10 +6,10 @@ import * as strings from '../../../../base/common/strings.js'; import { ShiftCommand } from '../../../common/commands/shiftCommand.js'; import { EditorAutoIndentStrategy } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { CompleteEnterAction, IndentAction } from '../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { IndentConsts } from '../../../common/languages/supports/indentRules.js'; diff --git a/src/vs/editor/contrib/linesOperations/browser/sortLinesCommand.ts b/src/vs/editor/contrib/linesOperations/browser/sortLinesCommand.ts index 4c13058d104..a382e7e2780 100644 --- a/src/vs/editor/contrib/linesOperations/browser/sortLinesCommand.ts +++ b/src/vs/editor/contrib/linesOperations/browser/sortLinesCommand.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export class SortLinesCommand implements ICommand { diff --git a/src/vs/editor/contrib/linesOperations/test/browser/copyLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/browser/copyLinesCommand.test.ts index 763389a5ddf..76358a3f134 100644 --- a/src/vs/editor/contrib/linesOperations/test/browser/copyLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/browser/copyLinesCommand.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { CopyLinesCommand } from '../../browser/copyLinesCommand.js'; import { DuplicateSelectionAction } from '../../browser/linesOperations.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts index 199b22c12ba..2a34d749825 100644 --- a/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/browser/linesOperations.test.ts @@ -7,10 +7,10 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; import type { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorAction } from '../../../../browser/editorExtensions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { Handler } from '../../../../common/editorCommon.js'; -import { ITextModel } from '../../../../common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { ViewModel } from '../../../../common/viewModel/viewModelImpl.js'; import { CamelCaseAction, PascalCaseAction, DeleteAllLeftAction, DeleteAllRightAction, DeleteDuplicateLinesAction, DeleteLinesAction, IndentLinesAction, InsertLineAfterAction, InsertLineBeforeAction, JoinLinesAction, KebabCaseAction, LowerCaseAction, SnakeCaseAction, SortLinesAscendingAction, SortLinesDescendingAction, TitleCaseAction, TransposeAction, UpperCaseAction } from '../../browser/linesOperations.js'; import { withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/linesOperations/test/browser/moveLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/browser/moveLinesCommand.test.ts index f83419cc70e..f6273b2f700 100644 --- a/src/vs/editor/contrib/linesOperations/test/browser/moveLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/browser/moveLinesCommand.test.ts @@ -5,11 +5,11 @@ import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { EditorAutoIndentStrategy } from '../../../../common/config/editorOptions.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { IndentationRule } from '../../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { LanguageService } from '../../../../common/services/languageService.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; import { MoveLinesCommand } from '../../browser/moveLinesCommand.js'; import { testCommand } from '../../../../test/browser/testCommand.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; diff --git a/src/vs/editor/contrib/linesOperations/test/browser/sortLinesCommand.test.ts b/src/vs/editor/contrib/linesOperations/test/browser/sortLinesCommand.test.ts index 3e79cb3436e..15751b10303 100644 --- a/src/vs/editor/contrib/linesOperations/test/browser/sortLinesCommand.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/browser/sortLinesCommand.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { SortLinesCommand } from '../../browser/sortLinesCommand.js'; import { testCommand } from '../../../../test/browser/testCommand.js'; diff --git a/src/vs/editor/contrib/linkedEditing/browser/linkedEditing.ts b/src/vs/editor/contrib/linkedEditing/browser/linkedEditing.ts index 0644fcfff23..7cfa4b8e147 100644 --- a/src/vs/editor/contrib/linkedEditing/browser/linkedEditing.ts +++ b/src/vs/editor/contrib/linkedEditing/browser/linkedEditing.ts @@ -17,22 +17,22 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, registerEditorAction, registerEditorCommand, registerEditorContribution, registerModelAndPositionCommand, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { LinkedEditingRangeProvider, LinkedEditingRanges } from '../../../common/languages.js'; +import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { LinkedEditingRangeProvider, LinkedEditingRanges } from '../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import * as nls from '../../../../nls.js'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { registerColor } from '../../../../platform/theme/common/colorRegistry.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; import './linkedEditing.css'; diff --git a/src/vs/editor/contrib/linkedEditing/test/browser/linkedEditing.test.ts b/src/vs/editor/contrib/linkedEditing/test/browser/linkedEditing.test.ts index df4e2720674..d00ad6fc950 100644 --- a/src/vs/editor/contrib/linkedEditing/test/browser/linkedEditing.test.ts +++ b/src/vs/editor/contrib/linkedEditing/test/browser/linkedEditing.test.ts @@ -9,13 +9,13 @@ import { URI } from '../../../../../base/common/uri.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; -import { IPosition, Position } from '../../../../common/core/position.js'; -import { IRange, Range } from '../../../../common/core/range.js'; -import { USUAL_WORD_SEPARATORS } from '../../../../common/core/wordHelper.js'; +import { IPosition, Position } from '../../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; +import { USUAL_WORD_SEPARATORS } from '../../../../../editor/common/language/core/wordHelper.js'; import { Handler } from '../../../../common/editorCommon.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { ITextModel } from '../../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { DeleteAllLeftAction } from '../../../linesOperations/browser/linesOperations.js'; import { LinkedEditingContribution } from '../../browser/linkedEditing.js'; import { DeleteWordLeft } from '../../../wordOperations/browser/wordOperations.js'; diff --git a/src/vs/editor/contrib/links/browser/getLinks.ts b/src/vs/editor/contrib/links/browser/getLinks.ts index fed24b5b1a8..d03cc57414e 100644 --- a/src/vs/editor/contrib/links/browser/getLinks.ts +++ b/src/vs/editor/contrib/links/browser/getLinks.ts @@ -9,13 +9,13 @@ import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { DisposableStore, isDisposable } from '../../../../base/common/lifecycle.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILink, ILinksList, LinkProvider } from '../../../common/languages.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILink, ILinksList, LinkProvider } from '../../../../editor/common/language/languages.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export class Link implements ILink { diff --git a/src/vs/editor/contrib/links/browser/links.ts b/src/vs/editor/contrib/links/browser/links.ts index ef1b67addd1..82d7aa9cf9f 100644 --- a/src/vs/editor/contrib/links/browser/links.ts +++ b/src/vs/editor/contrib/links/browser/links.ts @@ -17,14 +17,14 @@ import './links.css'; import { ICodeEditor, MouseTargetType } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { LinkProvider } from '../../../common/languages.js'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { LinkProvider } from '../../../../editor/common/language/languages.js'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { ClickLinkGesture, ClickLinkKeyboardEvent, ClickLinkMouseEvent } from '../../gotoSymbol/browser/link/clickLinkGesture.js'; import { getLinks, Link, LinksList } from './getLinks.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/message/browser/messageController.ts b/src/vs/editor/contrib/message/browser/messageController.ts index 8e4d1e2e9c3..88da028c6ed 100644 --- a/src/vs/editor/contrib/message/browser/messageController.ts +++ b/src/vs/editor/contrib/message/browser/messageController.ts @@ -12,10 +12,10 @@ import { DisposableStore, IDisposable, MutableDisposable } from '../../../../bas import './messageController.css'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorCommand, EditorContributionInstantiation, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; -import { PositionAffinity } from '../../../common/model.js'; +import { PositionAffinity } from '../../../../editor/common/language/model.js'; import { openLinkFromMarkdown } from '../../../browser/widget/markdownRenderer/browser/markdownRenderer.js'; import * as nls from '../../../../nls.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/contrib/multicursor/browser/multicursor.ts b/src/vs/editor/contrib/multicursor/browser/multicursor.ts index abc43c1f7d7..5b5118e24df 100644 --- a/src/vs/editor/contrib/multicursor/browser/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/browser/multicursor.ts @@ -14,18 +14,18 @@ import { EditorOption } from '../../../common/config/editorOptions.js'; import { CursorState } from '../../../common/cursorCommon.js'; import { CursorChangeReason, ICursorSelectionChangedEvent } from '../../../common/cursorEvents.js'; import { CursorMoveCommands } from '../../../common/cursor/cursorMoveCommands.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution, IEditorDecorationsCollection, ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { FindMatch, ITextModel } from '../../../common/model.js'; +import { FindMatch, ITextModel } from '../../../../editor/common/language/model.js'; import { CommonFindController } from '../../find/browser/findController.js'; import { FindOptionOverride, INewFindReplaceState } from '../../find/browser/findState.js'; import * as nls from '../../../../nls.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { getSelectionHighlightDecorationOptions } from '../../wordHighlighter/browser/highlightDecorations.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts index 06b13749659..11e9be46f9d 100644 --- a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { Handler } from '../../../../common/editorCommon.js'; -import { EndOfLineSequence } from '../../../../common/model.js'; +import { EndOfLineSequence } from '../../../../../editor/common/language/model.js'; import { CommonFindController } from '../../../find/browser/findController.js'; import { AddSelectionToNextFindMatchAction, InsertCursorAbove, InsertCursorBelow, MultiCursorSelectionController, SelectHighlightsAction } from '../../browser/multicursor.js'; import { ITestCodeEditor, withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts index ee10657bd30..649a47af004 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHints.ts @@ -10,8 +10,8 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import * as languages from '../../../common/languages.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { ParameterHintsModel, TriggerContext } from './parameterHintsModel.js'; import { Context } from './provideSignatureHelp.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts index 0c07303dc17..1b94efbb2c0 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsModel.ts @@ -9,10 +9,10 @@ import { Emitter } from '../../../../base/common/event.js'; import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { CharacterSet } from '../../../common/core/characterClassifier.js'; +import { CharacterSet } from '../../../../editor/common/language/core/characterClassifier.js'; import { ICursorSelectionChangedEvent } from '../../../common/cursorEvents.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { provideSignatureHelp } from './provideSignatureHelp.js'; export interface TriggerContext { diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index ef3cd7d78cc..2c3b38a6177 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -15,8 +15,8 @@ import { assertIsDefined } from '../../../../base/common/types.js'; import './parameterHints.css'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EDITOR_FONT_DEFAULTS, EditorOption } from '../../../common/config/editorOptions.js'; -import * as languages from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IMarkdownRenderResult, MarkdownRenderer } from '../../../browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { ParameterHintsModel } from './parameterHintsModel.js'; import { Context } from './provideSignatureHelp.js'; diff --git a/src/vs/editor/contrib/parameterHints/browser/provideSignatureHelp.ts b/src/vs/editor/contrib/parameterHints/browser/provideSignatureHelp.ts index b35d6e0e217..0865dd79a5c 100644 --- a/src/vs/editor/contrib/parameterHints/browser/provideSignatureHelp.ts +++ b/src/vs/editor/contrib/parameterHints/browser/provideSignatureHelp.ts @@ -7,12 +7,12 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts b/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts index a0138cb395a..8d79a0c13cb 100644 --- a/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts +++ b/src/vs/editor/contrib/parameterHints/test/browser/parameterHintsModel.test.ts @@ -10,11 +10,11 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { Handler } from '../../../../common/editorCommon.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import * as languages from '../../../../common/languages.js'; -import { ITextModel } from '../../../../common/model.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { ParameterHintsModel } from '../../browser/parameterHintsModel.js'; import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts index 8cb84d9463c..76baa32e12b 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts @@ -5,7 +5,7 @@ import './placeholderText.css'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js'; -import { ghostTextForeground } from '../../../common/core/editorColorRegistry.js'; +import { ghostTextForeground } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { localize } from '../../../../nls.js'; import { registerColor } from '../../../../platform/theme/common/colorUtils.js'; import { PlaceholderTextContribution } from './placeholderTextContribution.js'; diff --git a/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts index 749259c2711..f442b4605a1 100644 --- a/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/editorNavigationQuickAccess.ts @@ -8,10 +8,10 @@ import { Event } from '../../../../base/common/event.js'; import { createSingleCallFunction } from '../../../../base/common/functional.js'; import { DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { getCodeEditor, isDiffEditor } from '../../../browser/editorBrowser.js'; -import { IRange } from '../../../common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IDiffEditor, IEditor, ScrollType } from '../../../common/editorCommon.js'; -import { IModelDeltaDecoration, ITextModel, OverviewRulerLane } from '../../../common/model.js'; -import { overviewRulerRangeHighlight } from '../../../common/core/editorColorRegistry.js'; +import { IModelDeltaDecoration, ITextModel, OverviewRulerLane } from '../../../../editor/common/language/model.js'; +import { overviewRulerRangeHighlight } from '../../../../editor/common/language/core/editorColorRegistry.js'; import { IQuickAccessProvider, IQuickAccessProviderRunOptions } from '../../../../platform/quickinput/common/quickAccess.js'; import { IKeyMods, IQuickPick, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.ts index 4d720513f9f..63ea506da47 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoLineQuickAccess.ts @@ -7,8 +7,8 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { getCodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption, RenderLineNumbersType } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { IRange } from '../../../common/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IEditor, ScrollType } from '../../../common/editorCommon.js'; import { AbstractEditorNavigationQuickAccessProvider, IQuickAccessTextEditorContext } from './editorNavigationQuickAccess.js'; import { localize } from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts index b4f95f7158e..168004835c0 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -11,16 +11,16 @@ import { IMatch } from '../../../../base/common/filters.js'; import { IPreparedQuery, pieceToQuery, prepareQuery, scoreFuzzy2 } from '../../../../base/common/fuzzyScorer.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { format, trim } from '../../../../base/common/strings.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentSymbol, SymbolKind, SymbolKinds, SymbolTag, getAriaLabelForSymbol } from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentSymbol, SymbolKind, SymbolKinds, SymbolTag, getAriaLabelForSymbol } from '../../../../editor/common/language/languages.js'; import { IOutlineModelService } from '../../documentSymbols/browser/outlineModel.js'; import { AbstractEditorNavigationQuickAccessProvider, IEditorNavigationQuickAccessOptions, IQuickAccessTextEditorContext } from './editorNavigationQuickAccess.js'; import { localize } from '../../../../nls.js'; import { IQuickInputButton, IQuickPick, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { Position } from '../../../common/core/position.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { findLast } from '../../../../base/common/arraysFind.js'; import { IQuickAccessProviderRunOptions } from '../../../../platform/quickinput/common/quickAccess.js'; import { URI } from '../../../../base/common/uri.js'; diff --git a/src/vs/editor/contrib/rename/browser/rename.ts b/src/vs/editor/contrib/rename/browser/rename.ts index 2165ff1e350..4d676ea8202 100644 --- a/src/vs/editor/contrib/rename/browser/rename.ts +++ b/src/vs/editor/contrib/rename/browser/rename.ts @@ -16,15 +16,15 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution, registerModelAndPositionCommand } from '../../../browser/editorExtensions.js'; import { IBulkEditService } from '../../../browser/services/bulkEditService.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { NewSymbolNameTriggerKind, Rejection, RenameLocation, RenameProvider, WorkspaceEdit } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ITextResourceConfigurationService } from '../../../common/services/textResourceConfiguration.js'; +import { NewSymbolNameTriggerKind, Rejection, RenameLocation, RenameProvider, WorkspaceEdit } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from '../../editorState/browser/editorState.js'; import { MessageController } from '../../message/browser/messageController.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index e5a7bc71497..677010a12d9 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -24,11 +24,11 @@ import * as domFontInfo from '../../../browser/config/domFontInfo.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { FontInfo } from '../../../common/config/fontInfo.js'; -import { IDimension } from '../../../common/core/dimension.js'; -import { Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; -import { NewSymbolName, NewSymbolNameTag, NewSymbolNameTriggerKind, ProviderResult } from '../../../common/languages.js'; +import { NewSymbolName, NewSymbolNameTag, NewSymbolNameTriggerKind, ProviderResult } from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; diff --git a/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts b/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts index 8fda7ce5b66..233e72c8c44 100644 --- a/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts +++ b/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts @@ -9,12 +9,12 @@ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { EditorOption, IEditorMinimapOptions } from '../../../common/config/editorOptions.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; -import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { IModelDeltaDecoration, MinimapPosition, MinimapSectionHeaderStyle, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; -import { FindSectionHeaderOptions, SectionHeader } from '../../../common/services/findSectionHeaders.js'; +import { IModelDeltaDecoration, MinimapPosition, MinimapSectionHeaderStyle, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { FindSectionHeaderOptions, SectionHeader } from '../../../../editor/common/language/services/findSectionHeaders.js'; export class SectionHeaderDetector extends Disposable implements IEditorContribution { diff --git a/src/vs/editor/contrib/semanticTokens/browser/documentSemanticTokens.ts b/src/vs/editor/contrib/semanticTokens/browser/documentSemanticTokens.ts index 2bc7cab868a..72755a85a8a 100644 --- a/src/vs/editor/contrib/semanticTokens/browser/documentSemanticTokens.ts +++ b/src/vs/editor/contrib/semanticTokens/browser/documentSemanticTokens.ts @@ -13,14 +13,14 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { registerEditorFeature } from '../../../common/editorFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { IModelService } from '../../../common/services/model.js'; -import { SemanticTokensProviderStyling, toMultilineTokens2 } from '../../../common/services/semanticTokensProviderStyling.js'; -import { ISemanticTokensStylingService } from '../../../common/services/semanticTokensStyling.js'; -import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; +import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { SemanticTokensProviderStyling, toMultilineTokens2 } from '../../../../editor/common/language/services/semanticTokensProviderStyling.js'; +import { ISemanticTokensStylingService } from '../../../../editor/common/language/services/semanticTokensStyling.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { getDocumentSemanticTokens, hasDocumentSemanticTokensProvider, isSemanticTokens, isSemanticTokensEdits } from '../common/getSemanticTokens.js'; import { SEMANTIC_HIGHLIGHTING_SETTING_ID, isSemanticColoringEnabled } from '../common/semanticTokensConfig.js'; diff --git a/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts b/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts index 1662f1fec59..68ae70b41d7 100644 --- a/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts +++ b/src/vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens.ts @@ -7,20 +7,20 @@ import { CancelablePromise, createCancelablePromise, RunOnceScheduler } from '.. import { Disposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { getDocumentRangeSemanticTokens, hasDocumentRangeSemanticTokensProvider } from '../common/getSemanticTokens.js'; import { isSemanticColoringEnabled, SEMANTIC_HIGHLIGHTING_SETTING_ID } from '../common/semanticTokensConfig.js'; -import { toMultilineTokens2 } from '../../../common/services/semanticTokensProviderStyling.js'; +import { toMultilineTokens2 } from '../../../../editor/common/language/services/semanticTokensProviderStyling.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { DocumentRangeSemanticTokensProvider } from '../../../common/languages.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ISemanticTokensStylingService } from '../../../common/services/semanticTokensStyling.js'; +import { DocumentRangeSemanticTokensProvider } from '../../../../editor/common/language/languages.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ISemanticTokensStylingService } from '../../../../editor/common/language/services/semanticTokensStyling.js'; export class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution { diff --git a/src/vs/editor/contrib/semanticTokens/common/getSemanticTokens.ts b/src/vs/editor/contrib/semanticTokens/common/getSemanticTokens.ts index cd7cbd3e7c1..4f3971743ef 100644 --- a/src/vs/editor/contrib/semanticTokens/common/getSemanticTokens.ts +++ b/src/vs/editor/contrib/semanticTokens/common/getSemanticTokens.ts @@ -6,16 +6,16 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextModel } from '../../../common/model.js'; -import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend, DocumentRangeSemanticTokensProvider } from '../../../common/languages.js'; -import { IModelService } from '../../../common/services/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend, DocumentRangeSemanticTokensProvider } from '../../../../editor/common/language/languages.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; import { assertType } from '../../../../base/common/types.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { encodeSemanticTokensDto } from '../../../common/services/semanticTokensDto.js'; -import { Range } from '../../../common/core/range.js'; +import { encodeSemanticTokensDto } from '../../../../editor/common/language/services/semanticTokensDto.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export function isSemanticTokens(v: SemanticTokens | SemanticTokensEdits): v is SemanticTokens { return v && !!((v).data); diff --git a/src/vs/editor/contrib/semanticTokens/common/semanticTokensConfig.ts b/src/vs/editor/contrib/semanticTokens/common/semanticTokensConfig.ts index c7b39855911..4dbe2777325 100644 --- a/src/vs/editor/contrib/semanticTokens/common/semanticTokensConfig.ts +++ b/src/vs/editor/contrib/semanticTokens/common/semanticTokensConfig.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/editor/contrib/semanticTokens/test/browser/documentSemanticTokens.test.ts b/src/vs/editor/contrib/semanticTokens/test/browser/documentSemanticTokens.test.ts index ba4eb555285..c6438a19962 100644 --- a/src/vs/editor/contrib/semanticTokens/test/browser/documentSemanticTokens.test.ts +++ b/src/vs/editor/contrib/semanticTokens/test/browser/documentSemanticTokens.test.ts @@ -11,18 +11,18 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; -import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { ITextModel } from '../../../../common/model.js'; -import { LanguageFeatureDebounceService } from '../../../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { LanguageService } from '../../../../common/services/languageService.js'; -import { IModelService } from '../../../../common/services/model.js'; -import { ModelService } from '../../../../common/services/modelService.js'; -import { SemanticTokensStylingService } from '../../../../common/services/semanticTokensStylingService.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { LanguageFeatureDebounceService } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../../../editor/common/language/services/modelService.js'; +import { SemanticTokensStylingService } from '../../../../../editor/common/language/services/semanticTokensStylingService.js'; import { DocumentSemanticTokensFeature } from '../../browser/documentSemanticTokens.js'; import { getDocumentSemanticTokens, isSemanticTokens } from '../../common/getSemanticTokens.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; diff --git a/src/vs/editor/contrib/semanticTokens/test/browser/getSemanticTokens.test.ts b/src/vs/editor/contrib/semanticTokens/test/browser/getSemanticTokens.test.ts index 467fbc77132..e111480da9b 100644 --- a/src/vs/editor/contrib/semanticTokens/test/browser/getSemanticTokens.test.ts +++ b/src/vs/editor/contrib/semanticTokens/test/browser/getSemanticTokens.test.ts @@ -9,8 +9,8 @@ import { canceled } from '../../../../../base/common/errors.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import { DocumentSemanticTokensProvider, ProviderResult, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from '../../../../common/languages.js'; -import { ITextModel } from '../../../../common/model.js'; +import { DocumentSemanticTokensProvider, ProviderResult, SemanticTokens, SemanticTokensEdits, SemanticTokensLegend } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { getDocumentSemanticTokens } from '../../common/getSemanticTokens.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; diff --git a/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts b/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts index a85d4e05b17..c85e3627786 100644 --- a/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts +++ b/src/vs/editor/contrib/smartSelect/browser/bracketSelections.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { LinkedList } from '../../../../base/common/linkedList.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { SelectionRange, SelectionRangeProvider } from '../../../common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { SelectionRange, SelectionRangeProvider } from '../../../../editor/common/language/languages.js'; export class BracketSelectionRangeProvider implements SelectionRangeProvider { diff --git a/src/vs/editor/contrib/smartSelect/browser/smartSelect.ts b/src/vs/editor/contrib/smartSelect/browser/smartSelect.ts index 931f2dc52ee..5909dff0f25 100644 --- a/src/vs/editor/contrib/smartSelect/browser/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/browser/smartSelect.ts @@ -11,22 +11,22 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; -import * as languages from '../../../common/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { BracketSelectionRangeProvider } from './bracketSelections.js'; import { WordSelectionRangeProvider } from './wordSelections.js'; import * as nls from '../../../../nls.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; diff --git a/src/vs/editor/contrib/smartSelect/browser/wordSelections.ts b/src/vs/editor/contrib/smartSelect/browser/wordSelections.ts index 213cc2f5bbd..33dcbc46693 100644 --- a/src/vs/editor/contrib/smartSelect/browser/wordSelections.ts +++ b/src/vs/editor/contrib/smartSelect/browser/wordSelections.ts @@ -5,10 +5,10 @@ import { CharCode } from '../../../../base/common/charCode.js'; import { isLowerAsciiLetter, isUpperAsciiLetter } from '../../../../base/common/strings.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { ITextModel } from '../../../common/model.js'; -import { SelectionRange, SelectionRangeProvider } from '../../../common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { SelectionRange, SelectionRangeProvider } from '../../../../editor/common/language/languages.js'; export class WordSelectionRangeProvider implements SelectionRangeProvider { diff --git a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts index 3e774fc932f..a7a27a8d15e 100644 --- a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts @@ -7,18 +7,18 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Position } from '../../../../common/core/position.js'; -import { IRange, Range } from '../../../../common/core/range.js'; -import { SelectionRangeProvider } from '../../../../common/languages.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; +import { SelectionRangeProvider } from '../../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { IModelService } from '../../../../common/services/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { BracketSelectionRangeProvider } from '../../browser/bracketSelections.js'; import { provideSelectionRanges } from '../../browser/smartSelect.js'; import { WordSelectionRangeProvider } from '../../browser/wordSelections.js'; import { createModelServices } from '../../../../test/common/testTextModel.js'; import { javascriptOnEnterRules } from '../../../../test/common/modes/supports/onEnterRules.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; -import { ILanguageSelection, ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageSelection, ILanguageService } from '../../../../../editor/common/language/language.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; class StaticLanguageSelector implements ILanguageSelection { diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index a201d83b80e..0da60700dcc 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -8,14 +8,14 @@ import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle. import { assertType } from '../../../../base/common/types.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorCommand, EditorContributionInstantiation, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { CompletionItem, CompletionItemKind, CompletionItemProvider } from '../../../common/languages.js'; +import { CompletionItem, CompletionItemKind, CompletionItemProvider } from '../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { Choice } from './snippetParser.js'; import { showSimpleSuggestions } from '../../suggest/browser/suggest.js'; import { OvertypingCapturer } from '../../suggest/browser/suggestOvertypingCapturer.js'; diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index 30e42dd539a..5a9faa2340e 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -10,14 +10,14 @@ import { getLeadingWhitespace } from '../../../../base/common/strings.js'; import './snippetSession.css'; import { IActiveCodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { IPosition } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { TextChange } from '../../../common/core/textChange.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { TextChange } from '../../../../editor/common/language/core/textChange.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { IIdentifiedSingleEditOperation, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { OvertypingCapturer } from '../../suggest/browser/suggestOvertypingCapturer.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; diff --git a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts index 615e1174fca..78cd5d34d85 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetVariables.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetVariables.ts @@ -8,8 +8,8 @@ import * as path from '../../../../base/common/path.js'; import { dirname } from '../../../../base/common/resources.js'; import { commonPrefixLength, getLeadingWhitespace, isFalsyOrWhitespace, splitLines } from '../../../../base/common/strings.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { Selection } from '../../../common/core/selection.js'; -import { ITextModel } from '../../../common/model.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { Text, Variable, VariableResolver } from './snippetParser.js'; import { OvertypingCapturer } from '../../suggest/browser/suggestOvertypingCapturer.js'; diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts index 22fed982098..44faf45f030 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.old.test.ts @@ -6,9 +6,9 @@ import assert from 'assert'; import { mock } from '../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; import { SnippetController2 } from '../../browser/snippetController2.js'; import { ITestCodeEditor, withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts index a5a1addf0b0..b4dc9ecd36e 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetController2.test.ts @@ -6,10 +6,10 @@ import assert from 'assert'; import { mock } from '../../../../../base/test/common/mock.js'; import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { Range } from '../../../../common/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { Handler } from '../../../../common/editorCommon.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { SnippetController2 } from '../../browser/snippetController2.js'; import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; @@ -21,7 +21,7 @@ import { MockContextKeyService } from '../../../../../platform/keybinding/test/c import { ILabelService } from '../../../../../platform/label/common/label.js'; import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { EndOfLineSequence } from '../../../../common/model.js'; +import { EndOfLineSequence } from '../../../../../editor/common/language/model.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('SnippetController2', function () { diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts index 89914234286..b225d436851 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetSession.test.ts @@ -6,11 +6,11 @@ import assert from 'assert'; import { mock } from '../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { IActiveCodeEditor } from '../../../../browser/editorBrowser.js'; -import { IPosition, Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { IPosition, Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { SnippetParser } from '../../browser/snippetParser.js'; import { SnippetSession } from '../../browser/snippetSession.js'; import { createTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts index 276ed3376f6..beb29f8a0c7 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetVariables.test.ts @@ -11,8 +11,8 @@ import { extUriBiasedIgnorePathCase } from '../../../../../base/common/resources import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { SnippetParser, Variable, VariableResolver } from '../../browser/snippetParser.js'; import { ClipboardBasedVariableResolver, CompositeSnippetVariableResolver, ModelBasedVariableResolver, SelectionBasedVariableResolver, TimeBasedVariableResolver, WorkspaceBasedVariableResolver } from '../../browser/snippetVariables.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 0c4c59ff27e..8ec2b4bdb48 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -6,24 +6,24 @@ import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { IActiveCodeEditor, ICodeEditor, MouseTargetType } from '../../../browser/editorBrowser.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { EditorOption, RenderLineNumbersType, ConfigurationChangedEvent } from '../../../common/config/editorOptions.js'; import { StickyScrollWidget, StickyScrollWidgetState } from './stickyScrollWidget.js'; import { IStickyLineCandidateProvider, StickyLineCandidateProvider } from './stickyScrollProvider.js'; -import { IModelTokensChangedEvent } from '../../../common/textModelEvents.js'; +import { IModelTokensChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { ClickLinkGesture, ClickLinkMouseEvent } from '../../gotoSymbol/browser/link/clickLinkGesture.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { getDefinitionsAtPosition } from '../../gotoSymbol/browser/goToSymbol.js'; import { goToDefinitionWithLocation } from '../../inlayHints/browser/inlayHintsLocations.js'; -import { IPosition, Position } from '../../../common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; +import { ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; import * as dom from '../../../../base/browser/dom.js'; import { StickyRange } from './stickyScrollElement.js'; import { IMouseEvent, StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts index b313d5490df..7fc6f17f1d7 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollModelProvider.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IActiveCodeEditor } from '../../../browser/editorBrowser.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { OutlineElement, OutlineGroup, OutlineModel } from '../../documentSymbols/browser/outlineModel.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { CancelablePromise, createCancelablePromise, Delayer } from '../../../../base/common/async.js'; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 4fbe34be92f..6971a161c16 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -5,11 +5,11 @@ import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { CancellationToken, CancellationTokenSource, } from '../../../../base/common/cancellation.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { binarySearch } from '../../../../base/common/arrays.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index 9ec68fb92a8..605a09f79fe 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -13,8 +13,8 @@ import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPosit import { getColumnOfNodeOffset } from '../../../browser/viewParts/viewLines/viewLine.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorLayoutInfo, EditorOption, RenderLineNumbersType } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { StringBuilder } from '../../../common/core/stringBuilder.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js'; import { CharacterMapping, RenderLineInput, renderViewLine } from '../../../common/viewLayout/viewLineRenderer.js'; import { foldingCollapsedIcon, foldingExpandedIcon } from '../../folding/browser/foldingDecorations.js'; diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index 07493f90714..d974e21ae75 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -6,17 +6,17 @@ import assert from 'assert'; import { withAsyncTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { StickyScrollController } from '../../browser/stickyScrollController.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { DocumentSymbol, SymbolKind } from '../../../../common/languages.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { DocumentSymbol, SymbolKind } from '../../../../../editor/common/language/languages.js'; import { StickyLineCandidate, StickyLineCandidateProvider } from '../../browser/stickyScrollProvider.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../../common/services/languageFeatureDebounce.js'; +import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../../../editor/common/language/services/languageFeatureDebounce.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; import { SyncDescriptor } from '../../../../../platform/instantiation/common/descriptors.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; diff --git a/src/vs/editor/contrib/suggest/browser/completionModel.ts b/src/vs/editor/contrib/suggest/browser/completionModel.ts index ecf72cef8ed..74f553660f4 100644 --- a/src/vs/editor/contrib/suggest/browser/completionModel.ts +++ b/src/vs/editor/contrib/suggest/browser/completionModel.ts @@ -8,7 +8,7 @@ import { CharCode } from '../../../../base/common/charCode.js'; import { anyScore, fuzzyScore, FuzzyScore, fuzzyScoreGracefulAggressive, FuzzyScoreOptions, FuzzyScorer } from '../../../../base/common/filters.js'; import { compareIgnoreCase } from '../../../../base/common/strings.js'; import { InternalSuggestOptions } from '../../../common/config/editorOptions.js'; -import { CompletionItemKind, CompletionItemProvider } from '../../../common/languages.js'; +import { CompletionItemKind, CompletionItemProvider } from '../../../../editor/common/language/languages.js'; import { WordDistance } from './wordDistance.js'; import { CompletionItem } from './suggest.js'; diff --git a/src/vs/editor/contrib/suggest/browser/suggest.ts b/src/vs/editor/contrib/suggest/browser/suggest.ts index c63d3bb81b1..e3d46936934 100644 --- a/src/vs/editor/contrib/suggest/browser/suggest.ts +++ b/src/vs/editor/contrib/suggest/browser/suggest.ts @@ -11,23 +11,23 @@ import { StopWatch } from '../../../../base/common/stopwatch.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; -import * as languages from '../../../common/languages.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { SnippetParser } from '../../snippet/browser/snippetParser.js'; import { localize } from '../../../../nls.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { historyNavigationVisible } from '../../../../platform/history/browser/contextScopedHistoryWidget.js'; import { InternalQuickSuggestionsOptions, QuickSuggestionsValue } from '../../../common/config/editorOptions.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; -import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; export const Context = { Visible: historyNavigationVisible, diff --git a/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts b/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts index fb2eb635ca2..c3a85e9ea97 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestCommitCharacters.ts @@ -7,7 +7,7 @@ import { isNonEmptyArray } from '../../../../base/common/arrays.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { CharacterSet } from '../../../common/core/characterClassifier.js'; +import { CharacterSet } from '../../../../editor/common/language/core/characterClassifier.js'; import { State, SuggestModel } from './suggestModel.js'; import { ISelectedSuggestion, SuggestWidget } from './suggestWidget.js'; diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 7f871915d76..212e432124d 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -18,13 +18,13 @@ import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js' import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorCommand, EditorContributionInstantiation, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; -import { CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind } from '../../../common/languages.js'; +import { ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind } from '../../../../editor/common/language/languages.js'; import { SnippetController2 } from '../../snippet/browser/snippetController2.js'; import { SnippetParser } from '../../snippet/browser/snippetParser.js'; import { ISuggestMemoryService } from './suggestMemory.js'; @@ -45,7 +45,7 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { basename, extname } from '../../../../base/common/resources.js'; import { hash } from '../../../../base/common/hash.js'; import { WindowIdleValue, getWindow } from '../../../../base/browser/dom.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; // sticky suggest widget which doesn't disappear on focus out and such const _sticky = false diff --git a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts index 476b6c15a3a..5c12faa1d37 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestInlineCompletions.ts @@ -10,14 +10,14 @@ import { Disposable, RefCountedDisposable } from '../../../../base/common/lifecy import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { IWordAtPosition } from '../../../common/core/wordHelper.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; import { registerEditorFeature } from '../../../common/editorFeatures.js'; -import { Command, CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { Command, CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { CompletionModel, LineContext } from './completionModel.js'; import { CompletionItem, CompletionItemModel, CompletionOptions, provideSuggestionItems, QuickSuggestionsOptions } from './suggest.js'; import { ISuggestMemoryService } from './suggestMemory.js'; diff --git a/src/vs/editor/contrib/suggest/browser/suggestMemory.ts b/src/vs/editor/contrib/suggest/browser/suggestMemory.ts index 7aa97af48e1..1b04d8aa25f 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestMemory.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestMemory.ts @@ -8,9 +8,9 @@ import { RunOnceScheduler } from '../../../../base/common/async.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { LRUCache } from '../../../../base/common/map.js'; import { TernarySearchTree } from '../../../../base/common/ternarySearchTree.js'; -import { IPosition } from '../../../common/core/position.js'; -import { ITextModel } from '../../../common/model.js'; -import { CompletionItemKind, CompletionItemKinds } from '../../../common/languages.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CompletionItemKind, CompletionItemKinds } from '../../../../editor/common/language/languages.js'; import { CompletionItem } from './suggest.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index fcf97af35a6..1985cf038b7 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -12,11 +12,11 @@ import { getLeadingWhitespace, isHighSurrogate, isLowSurrogate } from '../../../ import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { CursorChangeReason, ICursorSelectionChangedEvent } from '../../../common/cursorEvents.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { Selection } from '../../../common/core/selection.js'; -import { ITextModel } from '../../../common/model.js'; -import { CompletionContext, CompletionItemKind, CompletionItemProvider, CompletionTriggerKind } from '../../../common/languages.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CompletionContext, CompletionItemKind, CompletionItemProvider, CompletionTriggerKind } from '../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { WordDistance } from './wordDistance.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; @@ -25,8 +25,8 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { CompletionModel } from './completionModel.js'; import { CompletionDurations, CompletionItem, CompletionOptions, getSnippetSuggestSupport, provideSuggestionItems, QuickSuggestionsOptions, SnippetSortOrder } from './suggest.js'; -import { IWordAtPosition } from '../../../common/core/wordHelper.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { FuzzyScoreOptions } from '../../../../base/common/filters.js'; import { assertType } from '../../../../base/common/types.js'; import { InlineCompletionContextKeys } from '../../inlineCompletions/browser/controller/inlineCompletionContextKeys.js'; diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index f22622e5ac3..c609bf5c7fe 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -18,7 +18,7 @@ import './media/suggest.css'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent } from '../../../browser/editorBrowser.js'; import { EmbeddedCodeEditorWidget } from '../../../browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; import { SuggestWidgetStatus } from './suggestWidgetStatus.js'; import '../../symbolIcons/browser/symbolIcons.js'; // The codicon symbol colors are defined here and must be loaded to get colors import * as nls from '../../../../nls.js'; @@ -35,7 +35,7 @@ import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } import { ItemRenderer } from './suggestWidgetRenderer.js'; import { getListStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { status } from '../../../../base/browser/ui/aria/aria.js'; -import { CompletionItemKinds } from '../../../common/languages.js'; +import { CompletionItemKinds } from '../../../../editor/common/language/languages.js'; /** * Suggest widget colors diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts index 3d0850da13b..0a7d79c5d9a 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetRenderer.ts @@ -14,10 +14,10 @@ import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { CompletionItemKind, CompletionItemKinds, CompletionItemTag } from '../../../common/languages.js'; -import { getIconClasses } from '../../../common/services/getIconClasses.js'; -import { IModelService } from '../../../common/services/model.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { CompletionItemKind, CompletionItemKinds, CompletionItemTag } from '../../../../editor/common/language/languages.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import * as nls from '../../../../nls.js'; import { FileKind } from '../../../../platform/files/common/files.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; diff --git a/src/vs/editor/contrib/suggest/browser/wordDistance.ts b/src/vs/editor/contrib/suggest/browser/wordDistance.ts index 3c992c6d5ed..2cedb680c7a 100644 --- a/src/vs/editor/contrib/suggest/browser/wordDistance.ts +++ b/src/vs/editor/contrib/suggest/browser/wordDistance.ts @@ -6,10 +6,10 @@ import { binarySearch, isFalsyOrEmpty } from '../../../../base/common/arrays.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { CompletionItem, CompletionItemKind } from '../../../common/languages.js'; -import { IEditorWorkerService } from '../../../common/services/editorWorker.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { CompletionItem, CompletionItemKind } from '../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { BracketSelectionRangeProvider } from '../../smartSelect/browser/bracketSelections.js'; export abstract class WordDistance { diff --git a/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts index 79b58979fe5..6b3d97146a5 100644 --- a/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/completionModel.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { EditorOptions, InternalSuggestOptions } from '../../../../common/config/editorOptions.js'; -import { IPosition } from '../../../../common/core/position.js'; -import * as languages from '../../../../common/languages.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; import { CompletionModel } from '../../browser/completionModel.js'; import { CompletionItem, getSuggestionComparator, SnippetSortOrder } from '../../browser/suggest.js'; import { WordDistance } from '../../browser/wordDistance.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts index 3b90caf5301..931c2f3f9fb 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggest.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { IDisposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { TextModel } from '../../../../common/model/textModel.js'; -import { CompletionItemKind, CompletionItemProvider } from '../../../../common/languages.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { CompletionItemKind, CompletionItemProvider } from '../../../../../editor/common/language/languages.js'; import { CompletionOptions, provideSuggestionItems, SnippetSortOrder } from '../../browser/suggest.js'; import { createTextModel } from '../../../../test/common/testTextModel.js'; import { LanguageFeatureRegistry } from '../../../../common/languageFeatureRegistry.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts index 7f83d7ecbcc..542988a5e25 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestController.test.ts @@ -9,11 +9,11 @@ import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { TextModel } from '../../../../common/model/textModel.js'; -import { CompletionItemInsertTextRule, CompletionItemKind } from '../../../../common/languages.js'; -import { IEditorWorkerService } from '../../../../common/services/editorWorker.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { CompletionItemInsertTextRule, CompletionItemKind } from '../../../../../editor/common/language/languages.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; import { SnippetController2 } from '../../../snippet/browser/snippetController2.js'; import { SuggestController } from '../../browser/suggestController.js'; import { ISuggestMemoryService } from '../../browser/suggestMemory.js'; @@ -29,8 +29,8 @@ import { InMemoryStorageService, IStorageService } from '../../../../../platform import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; import { DeleteLinesAction } from '../../../linesOperations/browser/linesOperations.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts index a4e82355ba7..6baad52ae98 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestInlineCompletions.test.ts @@ -9,12 +9,12 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, InlineCompletionContext, InlineCompletionTriggerKind, ProviderResult } from '../../../../common/languages.js'; -import { ITextModel } from '../../../../common/model.js'; -import { TextModel } from '../../../../common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, InlineCompletionContext, InlineCompletionTriggerKind, ProviderResult } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { SuggestInlineCompletions } from '../../browser/suggestInlineCompletions.js'; import { ISuggestMemoryService } from '../../browser/suggestMemory.js'; import { createCodeEditorServices, instantiateTestCodeEditor, ITestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts index 053a0fa5e99..04ed05ba15e 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestMemory.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { IPosition } from '../../../../common/core/position.js'; -import { ITextModel } from '../../../../common/model.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { CompletionItem } from '../../browser/suggest.js'; import { LRUMemory, Memory, NoMemory, PrefixMemory } from '../../browser/suggestMemory.js'; import { createSuggestItem } from './completionModel.test.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts index 51d159923bd..785b76e39e1 100644 --- a/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/suggestModel.test.ts @@ -8,18 +8,18 @@ import { Disposable, DisposableStore, toDisposable } from '../../../../../base/c import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; -import { EditOperation } from '../../../../common/core/editOperation.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { Selection } from '../../../../common/core/selection.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { Handler } from '../../../../common/editorCommon.js'; -import { ITextModel } from '../../../../common/model.js'; -import { TextModel } from '../../../../common/model/textModel.js'; -import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../common/languages.js'; -import { MetadataConsts } from '../../../../common/encodedTokenAttributes.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { CompletionItemKind, CompletionItemProvider, CompletionList, CompletionTriggerKind, EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { MetadataConsts } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { NullState } from '../../../../common/languages/nullTokenize.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { SnippetController2 } from '../../../snippet/browser/snippetController2.js'; import { SuggestController } from '../../browser/suggestController.js'; import { ISuggestMemoryService } from '../../browser/suggestMemory.js'; @@ -35,8 +35,8 @@ import { InMemoryStorageService, IStorageService } from '../../../../../platform import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { ILanguageFeaturesService } from '../../../../common/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { getSnippetSuggestSupport, setSnippetSuggestSupport } from '../../browser/suggest.js'; import { IEnvironmentService } from '../../../../../platform/environment/common/environment.js'; diff --git a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts index 5c50c04166b..50fe265565f 100644 --- a/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts +++ b/src/vs/editor/contrib/suggest/test/browser/wordDistance.test.ts @@ -8,23 +8,23 @@ import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; -import { IPosition } from '../../../../common/core/position.js'; -import { IRange } from '../../../../common/core/range.js'; -import { DEFAULT_WORD_REGEXP } from '../../../../common/core/wordHelper.js'; -import * as languages from '../../../../common/languages.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; +import { DEFAULT_WORD_REGEXP } from '../../../../../editor/common/language/core/wordHelper.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { EditorWorker } from '../../../../common/services/editorWebWorker.js'; +import { EditorWorker } from '../../../../../editor/common/language/services/editorWebWorker.js'; import { EditorWorkerService } from '../../../../browser/services/editorWorkerService.js'; -import { IModelService } from '../../../../common/services/model.js'; -import { ITextResourceConfigurationService } from '../../../../common/services/textResourceConfiguration.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { CompletionItem } from '../../browser/suggest.js'; import { WordDistance } from '../../browser/wordDistance.js'; import { createCodeEditorServices, instantiateTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; import { instantiateTextModel } from '../../../../test/common/testTextModel.js'; import { TestLanguageConfigurationService } from '../../../../test/common/modes/testLanguageConfigurationService.js'; import { NullLogService } from '../../../../../platform/log/common/log.js'; -import { LanguageFeaturesService } from '../../../../common/services/languageFeaturesService.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('suggest, word distance', function () { diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index 3a80a6d4533..dc8489993cb 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -14,13 +14,13 @@ import './unicodeHighlighter.css'; import { IActiveCodeEditor, ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorAction, EditorContributionInstantiation, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js'; import { InUntrustedWorkspace, inUntrustedWorkspace, EditorOption, InternalUnicodeHighlightOptions, unicodeHighlightConfigKeys } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { IModelDecoration, IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { UnicodeHighlighterOptions, UnicodeHighlighterReason, UnicodeHighlighterReasonKind, UnicodeTextModelHighlighter } from '../../../common/services/unicodeTextModelHighlighter.js'; -import { IEditorWorkerService, IUnicodeHighlightsResult } from '../../../common/services/editorWorker.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { IModelDecoration, IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { UnicodeHighlighterOptions, UnicodeHighlighterReason, UnicodeHighlighterReasonKind, UnicodeTextModelHighlighter } from '../../../../editor/common/language/services/unicodeTextModelHighlighter.js'; +import { IEditorWorkerService, IUnicodeHighlightsResult } from '../../../../editor/common/language/services/editorWorker.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { isModelDecorationInComment, isModelDecorationInString, isModelDecorationVisible } from '../../../common/viewModel/viewModelDecorations.js'; import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverParts } from '../../hover/browser/hoverTypes.js'; import { MarkdownHover, renderMarkdownHovers } from '../../hover/browser/markdownHoverParticipant.js'; diff --git a/src/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.ts b/src/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.ts index e4a24035103..95ac0d68484 100644 --- a/src/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.ts +++ b/src/vs/editor/contrib/unusualLineTerminators/browser/unusualLineTerminators.ts @@ -10,7 +10,7 @@ import { EditorContributionInstantiation, registerEditorContribution } from '../ import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { IConfirmationResult, IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; diff --git a/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts b/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts index 67744514340..259a1337399 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import './highlightDecorations.css'; -import { MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { DocumentHighlightKind } from '../../../common/languages.js'; +import { MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { DocumentHighlightKind } from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import { activeContrastBorder, editorSelectionHighlight, minimapSelectionOccurrenceHighlight, overviewRulerSelectionHighlightForeground, registerColor } from '../../../../platform/theme/common/colorRegistry.js'; import { registerThemingParticipant, themeColorFromId } from '../../../../platform/theme/common/themeService.js'; diff --git a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts index 075b2d6c41b..4ddc3abac97 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts @@ -3,15 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { USUAL_WORD_SEPARATORS } from '../../../common/core/wordHelper.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider, ProviderResult } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { Position } from '../../../common/core/position.js'; +import { USUAL_WORD_SEPARATORS } from '../../../../editor/common/language/core/wordHelper.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider, ProviderResult } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../base/common/map.js'; -import { LanguageFilter } from '../../../common/languageSelector.js'; +import { LanguageFilter } from '../../../../editor/common/language/languageSelector.js'; class TextualDocumentHighlightProvider implements DocumentHighlightProvider, MultiDocumentHighlightProvider { diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 118a9e66da4..386e669df33 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -23,20 +23,20 @@ import { IActiveCodeEditor, ICodeEditor, isDiffEditor } from '../../../browser/e import { EditorAction, EditorContributionInstantiation, IActionOptions, registerEditorAction, registerEditorContribution, registerModelAndPositionCommand } from '../../../browser/editorExtensions.js'; import { ICodeEditorService } from '../../../browser/services/codeEditorService.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { IWordAtPosition } from '../../../common/core/wordHelper.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; import { registerEditorFeature } from '../../../common/editorFeatures.js'; import { LanguageFeatureRegistry } from '../../../common/languageFeatureRegistry.js'; -import { DocumentHighlight, DocumentHighlightProvider, MultiDocumentHighlightProvider } from '../../../common/languages.js'; -import { score } from '../../../common/languageSelector.js'; -import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from '../../../common/model.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ITextModelService } from '../../../common/services/resolverService.js'; +import { DocumentHighlight, DocumentHighlightProvider, MultiDocumentHighlightProvider } from '../../../../editor/common/language/languages.js'; +import { score } from '../../../../editor/common/language/languageSelector.js'; +import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { getHighlightDecorationOptions } from './highlightDecorations.js'; import { TextualMultiDocumentHighlightFeature } from './textualHighlightProvider.js'; diff --git a/src/vs/editor/contrib/wordOperations/browser/wordOperations.ts b/src/vs/editor/contrib/wordOperations/browser/wordOperations.ts index 62f52402ebc..4dfe0970185 100644 --- a/src/vs/editor/contrib/wordOperations/browser/wordOperations.ts +++ b/src/vs/editor/contrib/wordOperations/browser/wordOperations.ts @@ -11,13 +11,13 @@ import { EditorOption, EditorOptions } from '../../../common/config/editorOption import { CursorState } from '../../../common/cursorCommon.js'; import { CursorChangeReason } from '../../../common/cursorEvents.js'; import { DeleteWordContext, WordNavigationType, WordOperations } from '../../../common/cursor/cursorWordOperations.js'; -import { getMapForWordSeparators, WordCharacterClassifier } from '../../../common/core/wordCharacterClassifier.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { getMapForWordSeparators, WordCharacterClassifier } from '../../../../editor/common/language/core/wordCharacterClassifier.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ScrollType } from '../../../common/editorCommon.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import * as nls from '../../../../nls.js'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts index 237945f6465..e4160c5012c 100644 --- a/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts @@ -10,9 +10,9 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes import { CoreEditingCommands } from '../../../../browser/coreCommands.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorCommand } from '../../../../browser/editorExtensions.js'; -import { Position } from '../../../../common/core/position.js'; -import { Selection } from '../../../../common/core/selection.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { ViewModel } from '../../../../common/viewModel/viewModelImpl.js'; import { CursorWordAccessibilityLeft, CursorWordAccessibilityLeftSelect, CursorWordAccessibilityRight, CursorWordAccessibilityRightSelect, CursorWordEndLeft, CursorWordEndLeftSelect, CursorWordEndRight, CursorWordEndRightSelect, CursorWordLeft, CursorWordLeftSelect, CursorWordRight, CursorWordRightSelect, CursorWordStartLeft, CursorWordStartLeftSelect, CursorWordStartRight, CursorWordStartRightSelect, DeleteInsideWord, DeleteWordEndLeft, DeleteWordEndRight, DeleteWordLeft, DeleteWordRight, DeleteWordStartLeft, DeleteWordStartRight } from '../../browser/wordOperations.js'; diff --git a/src/vs/editor/contrib/wordOperations/test/browser/wordTestUtils.ts b/src/vs/editor/contrib/wordOperations/test/browser/wordTestUtils.ts index 36ed4fa2055..385f0275ead 100644 --- a/src/vs/editor/contrib/wordOperations/test/browser/wordTestUtils.ts +++ b/src/vs/editor/contrib/wordOperations/test/browser/wordTestUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../../../../common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { ITestCodeEditor, TestCodeEditorInstantiationOptions, withTestCodeEditor } from '../../../../test/browser/testCodeEditor.js'; export function deserializePipePositions(text: string): [string, Position[]] { diff --git a/src/vs/editor/contrib/wordPartOperations/browser/wordPartOperations.ts b/src/vs/editor/contrib/wordPartOperations/browser/wordPartOperations.ts index 9bee3f0e0c2..e9648f34eac 100644 --- a/src/vs/editor/contrib/wordPartOperations/browser/wordPartOperations.ts +++ b/src/vs/editor/contrib/wordPartOperations/browser/wordPartOperations.ts @@ -6,11 +6,11 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { registerEditorCommand } from '../../../browser/editorExtensions.js'; import { DeleteWordContext, WordNavigationType, WordPartOperations } from '../../../common/cursor/cursorWordOperations.js'; -import { WordCharacterClassifier } from '../../../common/core/wordCharacterClassifier.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { WordCharacterClassifier } from '../../../../editor/common/language/core/wordCharacterClassifier.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { DeleteWordCommand, MoveWordCommand } from '../../wordOperations/browser/wordOperations.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; diff --git a/src/vs/editor/contrib/wordPartOperations/test/browser/wordPartOperations.test.ts b/src/vs/editor/contrib/wordPartOperations/test/browser/wordPartOperations.test.ts index bf9d0a504a4..6d5b26c0a41 100644 --- a/src/vs/editor/contrib/wordPartOperations/test/browser/wordPartOperations.test.ts +++ b/src/vs/editor/contrib/wordPartOperations/test/browser/wordPartOperations.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { EditorCommand } from '../../../../browser/editorExtensions.js'; -import { Position } from '../../../../common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; import { deserializePipePositions, serializePipePositions, testRepeatedActionAndExtractPositions } from '../../../wordOperations/test/browser/wordTestUtils.js'; import { CursorWordPartLeft, CursorWordPartLeftSelect, CursorWordPartRight, CursorWordPartRightSelect, DeleteWordPartLeft, DeleteWordPartRight } from '../../browser/wordPartOperations.js'; diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 2a65960d983..dd0444ecbe4 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -13,11 +13,11 @@ import * as objects from '../../../../base/common/objects.js'; import './zoneWidget.css'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, IViewZone, IViewZoneChangeAccessor } from '../../../browser/editorBrowser.js'; import { EditorLayoutInfo, EditorOption } from '../../../common/config/editorOptions.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { IEditorDecorationsCollection, ScrollType } from '../../../common/editorCommon.js'; -import { TrackedRangeStickiness } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; export interface IOptions { showFrame?: boolean; diff --git a/src/vs/editor/editor.api.ts b/src/vs/editor/editor.api.ts index 55d4fe18651..ed88edd87d5 100644 --- a/src/vs/editor/editor.api.ts +++ b/src/vs/editor/editor.api.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { EditorOptions, WrappingIndent, EditorAutoIndentStrategy } from './common/config/editorOptions.js'; -import { createMonacoBaseAPI } from './common/services/editorBaseApi.js'; +import { createMonacoBaseAPI } from '../editor/common/language/services/editorBaseApi.js'; import { createMonacoEditorAPI } from './standalone/browser/standaloneEditor.js'; import { createMonacoLanguagesAPI } from './standalone/browser/standaloneLanguages.js'; import { FormattingConflicts } from './contrib/format/browser/format.js'; diff --git a/src/vs/editor/editor.worker.start.ts b/src/vs/editor/editor.worker.start.ts index 9906249af9d..8bfd3377683 100644 --- a/src/vs/editor/editor.worker.start.ts +++ b/src/vs/editor/editor.worker.start.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { initialize } from '../base/common/worker/webWorkerBootstrap.js'; -import { EditorWorker, IWorkerContext } from './common/services/editorWebWorker.js'; -import { EditorWorkerHost } from './common/services/editorWorkerHost.js'; +import { EditorWorker, IWorkerContext } from '../editor/common/language/services/editorWebWorker.js'; +import { EditorWorkerHost } from '../editor/common/language/services/editorWorkerHost.js'; /** * Used by `monaco-editor` to hook up web worker rpc. diff --git a/src/vs/editor/standalone/browser/colorizer.ts b/src/vs/editor/standalone/browser/colorizer.ts index 0136434b1ff..9d589a280c7 100644 --- a/src/vs/editor/standalone/browser/colorizer.ts +++ b/src/vs/editor/standalone/browser/colorizer.ts @@ -5,11 +5,11 @@ import { createTrustedTypesPolicy } from '../../../base/browser/trustedTypes.js'; import * as strings from '../../../base/common/strings.js'; -import { ColorId, FontStyle, MetadataConsts } from '../../common/encodedTokenAttributes.js'; -import { ILanguageIdCodec, ITokenizationSupport, TokenizationRegistry } from '../../common/languages.js'; -import { ILanguageService } from '../../common/languages/language.js'; -import { ITextModel } from '../../common/model.js'; -import { IViewLineTokens, LineTokens } from '../../common/tokens/lineTokens.js'; +import { ColorId, FontStyle, MetadataConsts } from '../../../editor/common/language/encodedTokenAttributes.js'; +import { ILanguageIdCodec, ITokenizationSupport, TokenizationRegistry } from '../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { IViewLineTokens, LineTokens } from '../../../editor/common/language/tokens/lineTokens.js'; import { RenderLineInput, renderViewLine2 as renderViewLine } from '../../common/viewLayout/viewLineRenderer.js'; import { ViewLineRenderingData } from '../../common/viewModel.js'; import { MonarchTokenizer } from '../common/monarch/monarchLexer.js'; diff --git a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts index 2eb75568747..e73c18d9dbd 100644 --- a/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts +++ b/src/vs/editor/standalone/browser/inspectTokens/inspectTokens.ts @@ -11,13 +11,13 @@ import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../browser/editorBrowser.js'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution, EditorContributionInstantiation } from '../../../browser/editorExtensions.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { IEditorContribution } from '../../../common/editorCommon.js'; -import { ITextModel } from '../../../common/model.js'; -import { IState, ITokenizationSupport, TokenizationRegistry, ILanguageIdCodec, Token } from '../../../common/languages.js'; -import { FontStyle, StandardTokenType, TokenMetadata } from '../../../common/encodedTokenAttributes.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IState, ITokenizationSupport, TokenizationRegistry, ILanguageIdCodec, Token } from '../../../../editor/common/language/languages.js'; +import { FontStyle, StandardTokenType, TokenMetadata } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { NullState, nullTokenize, nullTokenizeEncoded } from '../../../common/languages/nullTokenize.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IStandaloneThemeService } from '../../common/standaloneTheme.js'; import { InspectTokensNLS } from '../../../common/standaloneStrings.js'; diff --git a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts index aa05b05255f..ec800eec8aa 100644 --- a/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts +++ b/src/vs/editor/standalone/browser/quickAccess/standaloneGotoSymbolQuickAccess.ts @@ -18,7 +18,7 @@ import { KeybindingWeight } from '../../../../platform/keybinding/common/keybind import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputService, ItemActivation } from '../../../../platform/quickinput/common/quickInput.js'; import { IOutlineModelService } from '../../../contrib/documentSymbols/browser/outlineModel.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export class StandaloneGotoSymbolQuickAccessProvider extends AbstractGotoSymbolQuickAccessProvider { diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index dffab646d2b..acdb751595c 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -11,7 +11,7 @@ import { CodeEditorWidget } from '../../browser/widget/codeEditor/codeEditorWidg import { IDiffEditorOptions, IEditorOptions } from '../../common/config/editorOptions.js'; import { InternalEditorAction } from '../../common/editorAction.js'; import { IModelChangedEvent } from '../../common/editorCommon.js'; -import { ITextModel } from '../../common/model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { StandaloneKeybindingService, updateConfigurationService } from './standaloneServices.js'; import { IStandaloneThemeService } from '../common/standaloneTheme.js'; import { IMenuItem, MenuId, MenuRegistry } from '../../../platform/actions/common/actions.js'; @@ -28,14 +28,14 @@ import { StandaloneCodeEditorNLS } from '../../common/standaloneStrings.js'; import { IClipboardService } from '../../../platform/clipboard/common/clipboardService.js'; import { IEditorProgressService } from '../../../platform/progress/common/progress.js'; import { StandaloneThemeService } from './standaloneThemeService.js'; -import { IModelService } from '../../common/services/model.js'; -import { ILanguageSelection, ILanguageService } from '../../common/languages/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ILanguageSelection, ILanguageService } from '../../../editor/common/language/language.js'; import { URI } from '../../../base/common/uri.js'; import { StandaloneCodeEditorService } from './standaloneCodeEditorService.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../common/languages/modesRegistry.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; import { IEditorConstructionOptions } from '../../browser/config/editorConfiguration.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; import { DiffEditorWidget } from '../../browser/widget/diffEditor/diffEditorWidget.js'; import { IAccessibilitySignalService } from '../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { mainWindow } from '../../../base/browser/window.js'; diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditorService.ts b/src/vs/editor/standalone/browser/standaloneCodeEditorService.ts index 700140df35c..7ebf1ee326a 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditorService.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditorService.ts @@ -9,9 +9,9 @@ import { URI } from '../../../base/common/uri.js'; import { ICodeEditor } from '../../browser/editorBrowser.js'; import { AbstractCodeEditorService } from '../../browser/services/abstractCodeEditorService.js'; import { ICodeEditorService } from '../../browser/services/codeEditorService.js'; -import { IRange } from '../../common/core/range.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; import { ScrollType } from '../../common/editorCommon.js'; -import { ITextModel } from '../../common/model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { IContextKey, IContextKeyService } from '../../../platform/contextkey/common/contextkey.js'; import { ITextResourceEditorInput } from '../../../platform/editor/common/editor.js'; import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; diff --git a/src/vs/editor/standalone/browser/standaloneEditor.ts b/src/vs/editor/standalone/browser/standaloneEditor.ts index 5b1232115a3..1f321f2ace6 100644 --- a/src/vs/editor/standalone/browser/standaloneEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneEditor.ts @@ -16,15 +16,15 @@ import { IInternalWebWorkerOptions, MonacoWebWorker, createWebWorker as actualCr import { ApplyUpdateResult, ConfigurationChangedEvent, EditorOptions } from '../../common/config/editorOptions.js'; import { EditorZoom } from '../../common/config/editorZoom.js'; import { BareFontInfo, FontInfo } from '../../common/config/fontInfo.js'; -import { IPosition } from '../../common/core/position.js'; -import { IRange } from '../../common/core/range.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; import { EditorType, IDiffEditor } from '../../common/editorCommon.js'; -import * as languages from '../../common/languages.js'; -import { ILanguageService } from '../../common/languages/language.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../common/languages/modesRegistry.js'; import { NullState, nullTokenize } from '../../common/languages/nullTokenize.js'; -import { FindMatch, ITextModel, TextModelResolvedOptions } from '../../common/model.js'; -import { IModelService } from '../../common/services/model.js'; +import { FindMatch, ITextModel, TextModelResolvedOptions } from '../../../editor/common/language/model.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; import * as standaloneEnums from '../../common/standalone/standaloneEnums.js'; import { Colorizer, IColorizerElementOptions, IColorizerOptions } from './colorizer.js'; import { IActionDescriptor, IStandaloneCodeEditor, IStandaloneDiffEditor, IStandaloneDiffEditorConstructionOptions, IStandaloneEditorConstructionOptions, StandaloneDiffEditor2, StandaloneEditor, createTextModel } from './standaloneCodeEditor.js'; diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index 849e4dc42d7..5930091c174 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -6,17 +6,17 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { Color } from '../../../base/common/color.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; -import { Position } from '../../common/core/position.js'; -import { Range } from '../../common/core/range.js'; -import { MetadataConsts } from '../../common/encodedTokenAttributes.js'; -import * as languages from '../../common/languages.js'; -import { ILanguageExtensionPoint, ILanguageService } from '../../common/languages/language.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { MetadataConsts } from '../../../editor/common/language/encodedTokenAttributes.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { ILanguageExtensionPoint, ILanguageService } from '../../../editor/common/language/language.js'; import { LanguageConfiguration } from '../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; import { ModesRegistry } from '../../common/languages/modesRegistry.js'; -import { LanguageSelector } from '../../common/languageSelector.js'; -import * as model from '../../common/model.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; +import { LanguageSelector } from '../../../editor/common/language/languageSelector.js'; +import * as model from '../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; import * as standaloneEnums from '../../common/standalone/standaloneEnums.js'; import { StandaloneServices } from './standaloneServices.js'; import { compile } from '../common/monarch/monarchCompile.js'; diff --git a/src/vs/editor/standalone/browser/standaloneServices.ts b/src/vs/editor/standalone/browser/standaloneServices.ts index ddf887c8b2b..50cbc337628 100644 --- a/src/vs/editor/standalone/browser/standaloneServices.ts +++ b/src/vs/editor/standalone/browser/standaloneServices.ts @@ -22,13 +22,13 @@ import Severity from '../../../base/common/severity.js'; import { URI } from '../../../base/common/uri.js'; import { IBulkEditOptions, IBulkEditResult, IBulkEditService, ResourceEdit, ResourceTextEdit } from '../../browser/services/bulkEditService.js'; import { isDiffEditorConfigurationKey, isEditorConfigurationKey } from '../../common/config/editorConfigurationSchema.js'; -import { EditOperation, ISingleEditOperation } from '../../common/core/editOperation.js'; -import { IPosition, Position as Pos } from '../../common/core/position.js'; -import { Range } from '../../common/core/range.js'; -import { ITextModel, ITextSnapshot } from '../../common/model.js'; -import { IModelService } from '../../common/services/model.js'; -import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from '../../common/services/resolverService.js'; -import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from '../../common/services/textResourceConfiguration.js'; +import { EditOperation, ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; +import { IPosition, Position as Pos } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { ITextModel, ITextSnapshot } from '../../../editor/common/language/model.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { IResolvedTextEditorModel, ITextModelContentProvider, ITextModelService } from '../../../editor/common/language/services/resolverService.js'; +import { ITextResourceConfigurationService, ITextResourcePropertiesService, ITextResourceConfigurationChangeEvent } from '../../../editor/common/language/services/textResourceConfiguration.js'; import { CommandsRegistry, ICommandEvent, ICommandHandler, ICommandService } from '../../../platform/commands/common/commands.js'; import { IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationModel, IConfigurationValue, ConfigurationTarget } from '../../../platform/configuration/common/configuration.js'; import { Configuration, ConfigurationModel, ConfigurationChangeEvent } from '../../../platform/configuration/common/configurationModels.js'; @@ -56,16 +56,16 @@ import { EditorOption } from '../../common/config/editorOptions.js'; import { ICodeEditor, IDiffEditor } from '../../browser/editorBrowser.js'; import { IContextMenuService, IContextViewDelegate, IContextViewService, IOpenContextView } from '../../../platform/contextview/browser/contextView.js'; import { ContextViewService } from '../../../platform/contextview/browser/contextViewService.js'; -import { LanguageService } from '../../common/services/languageService.js'; +import { LanguageService } from '../../../editor/common/language/services/languageService.js'; import { ContextMenuService } from '../../../platform/contextview/browser/contextMenuService.js'; import { getSingletonServiceDescriptors, InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; import { OpenerService } from '../../browser/services/openerService.js'; -import { IEditorWorkerService } from '../../common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../editor/common/language/services/editorWorker.js'; import { EditorWorkerService } from '../../browser/services/editorWorkerService.js'; -import { ILanguageService } from '../../common/languages/language.js'; -import { MarkerDecorationsService } from '../../common/services/markerDecorationsService.js'; -import { IMarkerDecorationsService } from '../../common/services/markerDecorations.js'; -import { ModelService } from '../../common/services/modelService.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { MarkerDecorationsService } from '../../../editor/common/language/services/markerDecorationsService.js'; +import { IMarkerDecorationsService } from '../../../editor/common/language/services/markerDecorations.js'; +import { ModelService } from '../../../editor/common/language/services/modelService.js'; import { StandaloneQuickInputService } from './quickInput/standaloneQuickInputService.js'; import { StandaloneThemeService } from './standaloneThemeService.js'; import { IStandaloneThemeService } from '../common/standaloneTheme.js'; @@ -86,9 +86,9 @@ import { IOpenerService } from '../../../platform/opener/common/opener.js'; import { IQuickInputService } from '../../../platform/quickinput/common/quickInput.js'; import { IStorageService, InMemoryStorageService } from '../../../platform/storage/common/storage.js'; import { DefaultConfiguration } from '../../../platform/configuration/common/configurations.js'; -import { WorkspaceEdit } from '../../common/languages.js'; +import { WorkspaceEdit } from '../../../editor/common/language/languages.js'; import { AccessibilitySignal, AccessibilityModality, IAccessibilitySignalService, Sound } from '../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; import { LogService } from '../../../platform/log/common/logService.js'; import { getEditorFeatures } from '../../common/editorFeatures.js'; @@ -96,7 +96,7 @@ import { onUnexpectedError } from '../../../base/common/errors.js'; import { ExtensionKind, IEnvironmentService, IExtensionHostDebugParams } from '../../../platform/environment/common/environment.js'; import { mainWindow } from '../../../base/browser/window.js'; import { ResourceMap } from '../../../base/common/map.js'; -import { ITreeSitterParserService } from '../../common/services/treeSitterParserService.js'; +import { ITreeSitterParserService } from '../../../editor/common/language/services/treeSitterParserService.js'; import { StandaloneTreeSitterParserService } from './standaloneTreeSitterService.js'; import { IWebWorkerDescriptor } from '../../../base/browser/webWorkerFactory.js'; diff --git a/src/vs/editor/standalone/browser/standaloneThemeService.ts b/src/vs/editor/standalone/browser/standaloneThemeService.ts index 8a697f653ff..1ad77be7da9 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeService.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeService.ts @@ -8,8 +8,8 @@ import * as domStylesheetsJs from '../../../base/browser/domStylesheets.js'; import { addMatchMediaChangeListener } from '../../../base/browser/browser.js'; import { Color } from '../../../base/common/color.js'; import { Emitter } from '../../../base/common/event.js'; -import { TokenizationRegistry } from '../../common/languages.js'; -import { FontStyle, TokenMetadata } from '../../common/encodedTokenAttributes.js'; +import { TokenizationRegistry } from '../../../editor/common/language/languages.js'; +import { FontStyle, TokenMetadata } from '../../../editor/common/language/encodedTokenAttributes.js'; import { ITokenThemeRule, TokenTheme, generateTokensCSSForColorMap } from '../../common/languages/supports/tokenization.js'; import { BuiltinTheme, IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from '../common/standaloneTheme.js'; import { hc_black, hc_light, vs, vs_dark } from '../common/themes.js'; diff --git a/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts b/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts index f9e0c0bf58b..c02e80133ad 100644 --- a/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts +++ b/src/vs/editor/standalone/browser/standaloneTreeSitterService.ts @@ -5,8 +5,8 @@ import type * as Parser from '@vscode/tree-sitter-wasm'; import { Event } from '../../../base/common/event.js'; -import { ITextModel } from '../../common/model.js'; -import { ITextModelTreeSitter, ITreeSitterParserService, TreeUpdateEvent } from '../../common/services/treeSitterParserService.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { ITextModelTreeSitter, ITreeSitterParserService, TreeUpdateEvent } from '../../../editor/common/language/services/treeSitterParserService.js'; /** * The monaco build doesn't like the dynamic import of tree sitter in the real service. diff --git a/src/vs/editor/standalone/browser/standaloneWebWorker.ts b/src/vs/editor/standalone/browser/standaloneWebWorker.ts index f8aa5966d51..95641eec969 100644 --- a/src/vs/editor/standalone/browser/standaloneWebWorker.ts +++ b/src/vs/editor/standalone/browser/standaloneWebWorker.ts @@ -5,7 +5,7 @@ import { URI } from '../../../base/common/uri.js'; import { EditorWorkerClient } from '../../browser/services/editorWorkerService.js'; -import { IModelService } from '../../common/services/model.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; /** * Create a new web worker that has model syncing capabilities built in. diff --git a/src/vs/editor/standalone/common/monarch/monarchLexer.ts b/src/vs/editor/standalone/common/monarch/monarchLexer.ts index b48d54598e5..f1f91a58ed3 100644 --- a/src/vs/editor/standalone/common/monarch/monarchLexer.ts +++ b/src/vs/editor/standalone/common/monarch/monarchLexer.ts @@ -9,14 +9,14 @@ */ import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; -import * as languages from '../../../common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { NullState, nullTokenizeEncoded, nullTokenize } from '../../../common/languages/nullTokenize.js'; import { TokenTheme } from '../../../common/languages/supports/tokenization.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import * as monarchCommon from './monarchCommon.js'; import { IStandaloneThemeService } from '../standaloneTheme.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { LanguageId, MetadataConsts } from '../../../common/encodedTokenAttributes.js'; +import { LanguageId, MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; const CACHE_STACK_DEPTH = 5; diff --git a/src/vs/editor/standalone/common/themes.ts b/src/vs/editor/standalone/common/themes.ts index 2e24698ecf0..f8c0f5a2eb1 100644 --- a/src/vs/editor/standalone/common/themes.ts +++ b/src/vs/editor/standalone/common/themes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { editorActiveIndentGuide1, editorIndentGuide1 } from '../../common/core/editorColorRegistry.js'; +import { editorActiveIndentGuide1, editorIndentGuide1 } from '../../../editor/common/language/core/editorColorRegistry.js'; import { IStandaloneThemeData } from './standaloneTheme.js'; import { editorBackground, editorForeground, editorInactiveSelection, editorSelectionHighlight } from '../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/editor/standalone/test/browser/monarch.test.ts b/src/vs/editor/standalone/test/browser/monarch.test.ts index f7048db8e35..841ab79f876 100644 --- a/src/vs/editor/standalone/test/browser/monarch.test.ts +++ b/src/vs/editor/standalone/test/browser/monarch.test.ts @@ -6,9 +6,9 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Token, TokenizationRegistry } from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; -import { LanguageService } from '../../../common/services/languageService.js'; +import { Token, TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { LanguageService } from '../../../../editor/common/language/services/languageService.js'; import { StandaloneConfigurationService } from '../../browser/standaloneServices.js'; import { compile } from '../../common/monarch/monarchCompile.js'; import { MonarchTokenizer } from '../../common/monarch/monarchLexer.js'; diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 0fa82cf782b..300df3fd59d 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -8,10 +8,10 @@ import { Color } from '../../../../base/common/color.js'; import { Emitter } from '../../../../base/common/event.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { LanguageId, MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { IState, Token } from '../../../common/languages.js'; +import { LanguageId, MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { IState, Token } from '../../../../editor/common/language/languages.js'; import { TokenTheme } from '../../../common/languages/supports/tokenization.js'; -import { LanguageService } from '../../../common/services/languageService.js'; +import { LanguageService } from '../../../../editor/common/language/services/languageService.js'; import { ILineTokens, IToken, TokenizationSupportAdapter, TokensProvider } from '../../browser/standaloneLanguages.js'; import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from '../../common/standaloneTheme.js'; import { UnthemedProductIconTheme } from '../../../../platform/theme/browser/iconsStyleSheet.js'; diff --git a/src/vs/editor/test/browser/commands/shiftCommand.test.ts b/src/vs/editor/test/browser/commands/shiftCommand.test.ts index 9e78a5568ec..4dda62e49cb 100644 --- a/src/vs/editor/test/browser/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/browser/commands/shiftCommand.test.ts @@ -8,10 +8,10 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.j import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { ShiftCommand } from '../../../common/commands/shiftCommand.js'; import { EditorAutoIndentStrategy } from '../../../common/config/editorOptions.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { getEditOperation, testCommand } from '../testCommand.js'; import { javascriptOnEnterRules } from '../../common/modes/supports/onEnterRules.js'; diff --git a/src/vs/editor/test/browser/commands/sideEditing.test.ts b/src/vs/editor/test/browser/commands/sideEditing.test.ts index db35d5e1ab6..0095bff6552 100644 --- a/src/vs/editor/test/browser/commands/sideEditing.test.ts +++ b/src/vs/editor/test/browser/commands/sideEditing.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { EditOperation, ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { withTestCodeEditor } from '../testCodeEditor.js'; function testCommand(lines: string[], selections: Selection[], edits: ISingleEditOperation[], expectedLines: string[], expectedSelections: Selection[]): void { diff --git a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts index 503dc55f875..9cb7b8cfe48 100644 --- a/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts +++ b/src/vs/editor/test/browser/commands/trimTrailingWhitespaceCommand.test.ts @@ -7,13 +7,13 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { TrimTrailingWhitespaceCommand, trimTrailingWhitespace } from '../../../common/commands/trimTrailingWhitespaceCommand.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { MetadataConsts, StandardTokenType } from '../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, ITokenizationSupport, TokenizationRegistry } from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { MetadataConsts, StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, ITokenizationSupport, TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; import { getEditOperation } from '../testCommand.js'; import { createModelServices, instantiateTextModel, withEditorModel } from '../../common/testTextModel.js'; diff --git a/src/vs/editor/test/browser/config/editorConfiguration.test.ts b/src/vs/editor/test/browser/config/editorConfiguration.test.ts index 3dcf646314f..eaf76825889 100644 --- a/src/vs/editor/test/browser/config/editorConfiguration.test.ts +++ b/src/vs/editor/test/browser/config/editorConfiguration.test.ts @@ -231,7 +231,7 @@ suite('Common Editor Config', () => { assert.deepStrictEqual(actual, { other: 'on', comments: 'off', - strings: 'off' + strings: 'on' }); config.dispose(); }); diff --git a/src/vs/editor/test/browser/controller/cursor.integrationTest.ts b/src/vs/editor/test/browser/controller/cursor.integrationTest.ts index d39b24c36fd..9abcee2b50c 100644 --- a/src/vs/editor/test/browser/controller/cursor.integrationTest.ts +++ b/src/vs/editor/test/browser/controller/cursor.integrationTest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Selection } from '../../../common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { withTestCodeEditor } from '../testCodeEditor.js'; suite('Editor Controller', () => { diff --git a/src/vs/editor/test/browser/controller/cursor.test.ts b/src/vs/editor/test/browser/controller/cursor.test.ts index fe7c5e59c46..c3437520c0e 100644 --- a/src/vs/editor/test/browser/controller/cursor.test.ts +++ b/src/vs/editor/test/browser/controller/cursor.test.ts @@ -9,20 +9,20 @@ import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { CoreEditingCommands, CoreNavigationCommands } from '../../../browser/coreCommands.js'; import { IEditorOptions } from '../../../common/config/editorOptions.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICursorPositionChangedEvent } from '../../../common/cursorEvents.js'; import { ICommand, ICursorStateComputerData, IEditOperationBuilder } from '../../../common/editorCommon.js'; -import { MetadataConsts, StandardTokenType } from '../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { MetadataConsts, StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IndentAction, IndentationRule } from '../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; -import { EndOfLinePreference, EndOfLineSequence, ITextModel } from '../../../common/model.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { EndOfLinePreference, EndOfLineSequence, ITextModel } from '../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { ViewModel } from '../../../common/viewModel/viewModelImpl.js'; import { OutgoingViewModelEventKind } from '../../../common/viewModelEventDispatcher.js'; import { ITestCodeEditor, TestCodeEditorInstantiationOptions, createCodeEditorServices, instantiateTestCodeEditor, withTestCodeEditor } from '../testCodeEditor.js'; diff --git a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts index 51184271569..639a1e059da 100644 --- a/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts +++ b/src/vs/editor/test/browser/controller/cursorMoveCommand.test.ts @@ -6,9 +6,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { CoreNavigationCommands } from '../../../browser/coreCommands.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { CursorMove } from '../../../common/cursor/cursorMoveCommands.js'; import { ViewModel } from '../../../common/viewModel/viewModelImpl.js'; import { ITestCodeEditor, withTestCodeEditor } from '../testCodeEditor.js'; diff --git a/src/vs/editor/test/browser/controller/imeTester.ts b/src/vs/editor/test/browser/controller/imeTester.ts index 25a9e6c5180..77b3c6b1a2e 100644 --- a/src/vs/editor/test/browser/controller/imeTester.ts +++ b/src/vs/editor/test/browser/controller/imeTester.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { EndOfLinePreference } from '../../../common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; import * as dom from '../../../../base/browser/dom.js'; import * as browser from '../../../../base/browser/browser.js'; import * as platform from '../../../../base/common/platform.js'; diff --git a/src/vs/editor/test/browser/controller/textAreaInput.test.ts b/src/vs/editor/test/browser/controller/textAreaInput.test.ts index a1cfa19429d..1e49fc79588 100644 --- a/src/vs/editor/test/browser/controller/textAreaInput.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaInput.test.ts @@ -8,7 +8,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { OperatingSystem } from '../../../../base/common/platform.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { IRecorded, IRecordedEvent, IRecordedTextareaState } from './imeRecordedTypes.js'; import { TestAccessibilityService } from '../../../../platform/accessibility/test/common/testAccessibilityService.js'; import { NullLogService } from '../../../../platform/log/common/log.js'; diff --git a/src/vs/editor/test/browser/controller/textAreaState.test.ts b/src/vs/editor/test/browser/controller/textAreaState.test.ts index a0f9ce82e25..d878108101d 100644 --- a/src/vs/editor/test/browser/controller/textAreaState.test.ts +++ b/src/vs/editor/test/browser/controller/textAreaState.test.ts @@ -7,8 +7,8 @@ import assert from 'assert'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { ITextAreaWrapper, TextAreaState } from '../../../browser/controller/editContext/textArea/textAreaEditContextState.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { createTextModel } from '../../common/testTextModel.js'; import { PagedScreenReaderStrategy } from '../../../browser/controller/editContext/screenReaderUtils.js'; diff --git a/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts b/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts index 7d889f5012a..b533c3b0824 100644 --- a/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts +++ b/src/vs/editor/test/browser/diff/testDiffProviderFactoryService.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { toDisposable } from '../../../../base/common/lifecycle.js'; import { IDocumentDiff, IDocumentDiffProvider, IDocumentDiffProviderOptions } from '../../../common/diff/documentDiffProvider.js'; import { linesDiffComputers } from '../../../common/diff/linesDiffComputers.js'; -import { ITextModel } from '../../../common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { Event } from '../../../../base/common/event.js'; import { IDiffProviderFactoryService } from '../../../browser/widget/diffEditor/diffProviderFactoryService.js'; diff --git a/src/vs/editor/test/browser/services/treeSitterParserService.test.ts b/src/vs/editor/test/browser/services/treeSitterParserService.test.ts index 5ebb74c6537..b8dc89af2da 100644 --- a/src/vs/editor/test/browser/services/treeSitterParserService.test.ts +++ b/src/vs/editor/test/browser/services/treeSitterParserService.test.ts @@ -11,9 +11,9 @@ import { ConsoleMainLogger, ILogService } from '../../../../platform/log/common/ import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { LogService } from '../../../../platform/log/common/logService.js'; import { mock } from '../../../../base/test/common/mock.js'; -import { ITreeSitterImporter } from '../../../common/services/treeSitterParserService.js'; -import { TextModelTreeSitter } from '../../../common/services/treeSitter/textModelTreeSitter.js'; -import { TreeSitterLanguages } from '../../../common/services/treeSitter/treeSitterLanguages.js'; +import { ITreeSitterImporter } from '../../../../editor/common/language/services/treeSitterParserService.js'; +import { TextModelTreeSitter } from '../../../../editor/common/language/services/treeSitter/textModelTreeSitter.js'; +import { TreeSitterLanguages } from '../../../../editor/common/language/services/treeSitter/treeSitterLanguages.js'; class MockParser implements Parser.Parser { language: Parser.Language | null = null; diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 72da1933953..38718cbefe2 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -12,18 +12,18 @@ import { View } from '../../browser/view.js'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../browser/widget/codeEditor/codeEditorWidget.js'; import * as editorOptions from '../../common/config/editorOptions.js'; import { IEditorContribution } from '../../common/editorCommon.js'; -import { ILanguageService } from '../../common/languages/language.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; -import { ITextBufferFactory, ITextModel } from '../../common/model.js'; -import { IEditorWorkerService } from '../../common/services/editorWorker.js'; -import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../common/services/languageFeaturesService.js'; -import { LanguageService } from '../../common/services/languageService.js'; -import { IModelService } from '../../common/services/model.js'; -import { ModelService } from '../../common/services/modelService.js'; -import { ITextResourcePropertiesService } from '../../common/services/textResourceConfiguration.js'; -import { ITreeSitterParserService } from '../../common/services/treeSitterParserService.js'; +import { ITextBufferFactory, ITextModel } from '../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../editor/common/language/services/editorWorker.js'; +import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../editor/common/language/services/languageFeaturesService.js'; +import { LanguageService } from '../../../editor/common/language/services/languageService.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../editor/common/language/services/modelService.js'; +import { ITextResourcePropertiesService } from '../../../editor/common/language/services/textResourceConfiguration.js'; +import { ITreeSitterParserService } from '../../../editor/common/language/services/treeSitterParserService.js'; import { ViewModel } from '../../common/viewModel/viewModelImpl.js'; import { TestConfiguration } from './config/testConfiguration.js'; import { TestCodeEditorService, TestCommandService } from './editorTestServices.js'; diff --git a/src/vs/editor/test/browser/testCommand.ts b/src/vs/editor/test/browser/testCommand.ts index 2d54a5f2eb4..566f15592a4 100644 --- a/src/vs/editor/test/browser/testCommand.ts +++ b/src/vs/editor/test/browser/testCommand.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { IRange } from '../../common/core/range.js'; -import { Selection, ISelection } from '../../common/core/selection.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { Selection, ISelection } from '../../../editor/common/language/core/selection.js'; import { ICommand, IEditOperationBuilder } from '../../common/editorCommon.js'; -import { ITextModel } from '../../common/model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { instantiateTestCodeEditor, createCodeEditorServices } from './testCodeEditor.js'; import { instantiateTextModel } from '../common/testTextModel.js'; import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; -import { ISingleEditOperation } from '../../common/core/editOperation.js'; +import { ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; export function testCommand( lines: string[], diff --git a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts index 20af8f3d03f..8fcaf878ea2 100644 --- a/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts +++ b/src/vs/editor/test/browser/view/minimapCharRenderer.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { MinimapCharRendererFactory } from '../../../browser/viewParts/minimap/minimapCharRendererFactory.js'; import { Constants } from '../../../browser/viewParts/minimap/minimapCharSheet.js'; -import { RGBA8 } from '../../../common/core/rgba.js'; +import { RGBA8 } from '../../../../editor/common/language/core/rgba.js'; suite('MinimapCharRenderer', () => { diff --git a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts index 417249f546b..123bdd97caa 100644 --- a/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts +++ b/src/vs/editor/test/browser/viewModel/modelLineProjection.test.ts @@ -7,15 +7,15 @@ import assert from 'assert'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; -import { Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import * as languages from '../../../common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; -import { EndOfLinePreference } from '../../../common/model.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { ModelLineProjectionData } from '../../../common/modelLineProjectionData.js'; -import { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { IViewLineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; import { ViewLineData } from '../../../common/viewModel.js'; import { IModelLineProjection, ISimpleModel, createModelLineProjection } from '../../../common/viewModel/modelLineProjection.js'; import { MonospaceLineBreaksComputerFactory } from '../../../common/viewModel/monospaceLineBreaksComputer.js'; diff --git a/src/vs/editor/test/browser/viewModel/testViewModel.ts b/src/vs/editor/test/browser/viewModel/testViewModel.ts index 1c59f3de14e..e68fa4ad0b6 100644 --- a/src/vs/editor/test/browser/viewModel/testViewModel.ts +++ b/src/vs/editor/test/browser/viewModel/testViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEditorOptions } from '../../../common/config/editorOptions.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { ViewModel } from '../../../common/viewModel/viewModelImpl.js'; import { TestConfiguration } from '../config/testConfiguration.js'; import { MonospaceLineBreaksComputerFactory } from '../../../common/viewModel/monospaceLineBreaksComputer.js'; diff --git a/src/vs/editor/test/browser/viewModel/viewModelDecorations.test.ts b/src/vs/editor/test/browser/viewModel/viewModelDecorations.test.ts index bed88ad1260..6f07c785f1d 100644 --- a/src/vs/editor/test/browser/viewModel/viewModelDecorations.test.ts +++ b/src/vs/editor/test/browser/viewModel/viewModelDecorations.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { IEditorOptions } from '../../../common/config/editorOptions.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { InlineDecoration, InlineDecorationType } from '../../../common/viewModel.js'; import { testViewModel } from './testViewModel.js'; diff --git a/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts b/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts index 8afa07a8b56..3c1342206dc 100644 --- a/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts +++ b/src/vs/editor/test/browser/viewModel/viewModelImpl.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { EndOfLineSequence, PositionAffinity } from '../../../common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { EndOfLineSequence, PositionAffinity } from '../../../../editor/common/language/model.js'; import { ViewEventHandler } from '../../../common/viewEventHandler.js'; import { ViewEvent } from '../../../common/viewEvents.js'; import { testViewModel } from './testViewModel.js'; diff --git a/src/vs/editor/test/browser/widget/codeEditorWidget.test.ts b/src/vs/editor/test/browser/widget/codeEditorWidget.test.ts index fead379bd24..6bc3558b8c5 100644 --- a/src/vs/editor/test/browser/widget/codeEditorWidget.test.ts +++ b/src/vs/editor/test/browser/widget/codeEditorWidget.test.ts @@ -6,9 +6,9 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { withTestCodeEditor } from '../testCodeEditor.js'; diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts index eeac9f526e5..187b4e975bc 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { UnchangedRegion } from '../../../browser/widget/diffEditor/diffEditorViewModel.js'; -import { LineRange } from '../../../common/core/lineRange.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; import { DetailedLineRangeMapping } from '../../../common/diff/rangeMapping.js'; suite('DiffEditorWidget2', () => { diff --git a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts index 7bb14f13871..4b3c465aa09 100644 --- a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts +++ b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts @@ -9,8 +9,8 @@ import { IObservable, derivedHandleChanges } from "../../../../base/common/obser import { ensureNoDisposablesAreLeakedInTestSuite } from "../../../../base/test/common/utils.js"; import { ICodeEditor } from "../../../browser/editorBrowser.js"; import { ObservableCodeEditor, observableCodeEditor } from "../../../browser/observableCodeEditor.js"; -import { Position } from "../../../common/core/position.js"; -import { Range } from "../../../common/core/range.js"; +import { Position } from "../../../../editor/common/language/core/position.js"; +import { Range } from "../../../../editor/common/language/core/range.js"; import { ViewModel } from "../../../common/viewModel/viewModelImpl.js"; import { withTestCodeEditor } from "../testCodeEditor.js"; diff --git a/src/vs/editor/test/common/codecs/linesDecoder.test.ts b/src/vs/editor/test/common/codecs/linesDecoder.test.ts index 019344128bc..c8fb787912a 100644 --- a/src/vs/editor/test/common/codecs/linesDecoder.test.ts +++ b/src/vs/editor/test/common/codecs/linesDecoder.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { Line } from '../../../common/codecs/linesCodec/tokens/line.js'; diff --git a/src/vs/editor/test/common/codecs/markdownDecoder.test.ts b/src/vs/editor/test/common/codecs/markdownDecoder.test.ts index d0ea73d7fa2..7f3ce9dcc20 100644 --- a/src/vs/editor/test/common/codecs/markdownDecoder.test.ts +++ b/src/vs/editor/test/common/codecs/markdownDecoder.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { TestDecoder } from '../utils/testDecoder.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { newWriteableStream } from '../../../../base/common/stream.js'; import { Tab } from '../../../common/codecs/simpleCodec/tokens/tab.js'; diff --git a/src/vs/editor/test/common/codecs/simpleDecoder.test.ts b/src/vs/editor/test/common/codecs/simpleDecoder.test.ts index 16ae699708c..73f0d6174eb 100644 --- a/src/vs/editor/test/common/codecs/simpleDecoder.test.ts +++ b/src/vs/editor/test/common/codecs/simpleDecoder.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { TestDecoder } from '../utils/testDecoder.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { newWriteableStream } from '../../../../base/common/stream.js'; import { Tab } from '../../../common/codecs/simpleCodec/tokens/tab.js'; diff --git a/src/vs/editor/test/common/core/characterClassifier.test.ts b/src/vs/editor/test/common/core/characterClassifier.test.ts index 7bd42977c34..0a99409cb34 100644 --- a/src/vs/editor/test/common/core/characterClassifier.test.ts +++ b/src/vs/editor/test/common/core/characterClassifier.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { CharCode } from '../../../../base/common/charCode.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { CharacterClassifier } from '../../../common/core/characterClassifier.js'; +import { CharacterClassifier } from '../../../../editor/common/language/core/characterClassifier.js'; suite('CharacterClassifier', () => { diff --git a/src/vs/editor/test/common/core/cursorColumns.test.ts b/src/vs/editor/test/common/core/cursorColumns.test.ts index 9e9f2728046..b497f194daf 100644 --- a/src/vs/editor/test/common/core/cursorColumns.test.ts +++ b/src/vs/editor/test/common/core/cursorColumns.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { CursorColumns } from '../../../common/core/cursorColumns.js'; +import { CursorColumns } from '../../../../editor/common/language/core/cursorColumns.js'; suite('CursorColumns', () => { diff --git a/src/vs/editor/test/common/core/lineRange.test.ts b/src/vs/editor/test/common/core/lineRange.test.ts index 30832250039..b040db8b229 100644 --- a/src/vs/editor/test/common/core/lineRange.test.ts +++ b/src/vs/editor/test/common/core/lineRange.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { LineRange, LineRangeSet } from '../../../common/core/lineRange.js'; +import { LineRange, LineRangeSet } from '../../../../editor/common/language/core/lineRange.js'; suite('LineRange', () => { diff --git a/src/vs/editor/test/common/core/lineTokens.test.ts b/src/vs/editor/test/common/core/lineTokens.test.ts index cb53c9d2be1..c02aafe56a4 100644 --- a/src/vs/editor/test/common/core/lineTokens.test.ts +++ b/src/vs/editor/test/common/core/lineTokens.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { LanguageIdCodec } from '../../../common/services/languagesRegistry.js'; -import { IViewLineTokens, LineTokens } from '../../../common/tokens/lineTokens.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { LanguageIdCodec } from '../../../../editor/common/language/services/languagesRegistry.js'; +import { IViewLineTokens, LineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; suite('LineTokens', () => { diff --git a/src/vs/editor/test/common/core/positionOffsetTransformer.test.ts b/src/vs/editor/test/common/core/positionOffsetTransformer.test.ts index 3cf6744390a..43547d9a779 100644 --- a/src/vs/editor/test/common/core/positionOffsetTransformer.test.ts +++ b/src/vs/editor/test/common/core/positionOffsetTransformer.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { PositionOffsetTransformer } from '../../../../editor/common/language/core/positionToOffset.js'; suite('PositionOffsetTransformer', () => { ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/editor/test/common/core/random.ts b/src/vs/editor/test/common/core/random.ts index cbf9a5feca6..5c640c3d83c 100644 --- a/src/vs/editor/test/common/core/random.ts +++ b/src/vs/editor/test/common/core/random.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { numberComparator } from '../../../../base/common/arrays.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { Position } from '../../../common/core/position.js'; -import { PositionOffsetTransformer } from '../../../common/core/positionToOffset.js'; -import { Range } from '../../../common/core/range.js'; -import { AbstractText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { PositionOffsetTransformer } from '../../../../editor/common/language/core/positionToOffset.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { AbstractText, SingleTextEdit, TextEdit } from '../../../../editor/common/language/core/textEdit.js'; export abstract class Random { public static basicAlphabet: string = ' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; diff --git a/src/vs/editor/test/common/core/range.test.ts b/src/vs/editor/test/common/core/range.test.ts index 5916ebe3370..70298f1b610 100644 --- a/src/vs/editor/test/common/core/range.test.ts +++ b/src/vs/editor/test/common/core/range.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; suite('Editor Core - Range', () => { diff --git a/src/vs/editor/test/common/core/stringBuilder.test.ts b/src/vs/editor/test/common/core/stringBuilder.test.ts index add3121186a..a01b693f111 100644 --- a/src/vs/editor/test/common/core/stringBuilder.test.ts +++ b/src/vs/editor/test/common/core/stringBuilder.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { writeUInt16LE } from '../../../../base/common/buffer.js'; import { CharCode } from '../../../../base/common/charCode.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { decodeUTF16LE, StringBuilder } from '../../../common/core/stringBuilder.js'; +import { decodeUTF16LE, StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; suite('decodeUTF16LE', () => { diff --git a/src/vs/editor/test/common/core/testLineToken.ts b/src/vs/editor/test/common/core/testLineToken.ts index 1d4d832d5c7..5f5d3d80800 100644 --- a/src/vs/editor/test/common/core/testLineToken.ts +++ b/src/vs/editor/test/common/core/testLineToken.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; -import { ColorId, TokenMetadata, ITokenPresentation, StandardTokenType } from '../../../common/encodedTokenAttributes.js'; -import { ILanguageIdCodec } from '../../../common/languages.js'; +import { IViewLineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; +import { ColorId, TokenMetadata, ITokenPresentation, StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { ILanguageIdCodec } from '../../../../editor/common/language/languages.js'; /** * A token on a line. diff --git a/src/vs/editor/test/common/core/textEdit.test.ts b/src/vs/editor/test/common/core/textEdit.test.ts index c048f0fbd97..fa843be166a 100644 --- a/src/vs/editor/test/common/core/textEdit.test.ts +++ b/src/vs/editor/test/common/core/textEdit.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { StringText } from '../../../common/core/textEdit.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { StringText } from '../../../../editor/common/language/core/textEdit.js'; import { Random } from './random.js'; suite('TextEdit', () => { diff --git a/src/vs/editor/test/common/diff/diffComputer.test.ts b/src/vs/editor/test/common/diff/diffComputer.test.ts index d751ff5f43d..85d3716ebbe 100644 --- a/src/vs/editor/test/common/diff/diffComputer.test.ts +++ b/src/vs/editor/test/common/diff/diffComputer.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { Constants } from '../../../../base/common/uint.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { DiffComputer, ICharChange, ILineChange } from '../../../common/diff/legacyLinesDiffComputer.js'; -import { IIdentifiedSingleEditOperation, ITextModel } from '../../../common/model.js'; +import { IIdentifiedSingleEditOperation, ITextModel } from '../../../../editor/common/language/model.js'; import { createTextModel } from '../testTextModel.js'; function assertDiff(originalLines: string[], modifiedLines: string[], expectedChanges: ILineChange[], shouldComputeCharChanges: boolean = true, shouldPostProcessCharChanges: boolean = false, shouldIgnoreTrimWhitespace: boolean = false) { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts index 47d8cd1f2d5..2a32c2a9efd 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts @@ -6,10 +6,10 @@ import assert from 'assert'; import { splitLines } from '../../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { IRange, Range } from '../../../../common/core/range.js'; -import { BeforeEditPositionMapper, TextEditInfo } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; -import { Length, lengthOfString, lengthToObj, lengthToPosition, toLength } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; +import { BeforeEditPositionMapper, TextEditInfo } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; +import { Length, lengthOfString, lengthToObj, lengthToPosition, toLength } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; suite('Bracket Pair Colorizer - BeforeEditPositionMapper', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts index d98e324eca8..f4d4d2cfc87 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts @@ -6,9 +6,9 @@ import assert from 'assert'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { LanguageAgnosticBracketTokens } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.js'; -import { SmallImmutableSet, DenseKeyProvider } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; -import { Token, TokenKind } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.js'; +import { LanguageAgnosticBracketTokens } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/brackets.js'; +import { SmallImmutableSet, DenseKeyProvider } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; +import { Token, TokenKind } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.js'; import { TestLanguageConfigurationService } from '../../modes/testLanguageConfigurationService.js'; suite('Bracket Pair Colorizer - Brackets', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts index 2d60eb41508..3eb785ea9fa 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/combineTextEditInfos.test.ts @@ -5,12 +5,12 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; -import { SingleTextEdit } from '../../../../common/core/textEdit.js'; -import { TextEditInfo } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; -import { combineTextEditInfos } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.js'; -import { lengthAdd, lengthToObj, lengthToPosition, positionToLength, toLength } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SingleTextEdit } from '../../../../../editor/common/language/core/textEdit.js'; +import { TextEditInfo } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/beforeEditPositionMapper.js'; +import { combineTextEditInfos } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/combineTextEditInfos.js'; +import { lengthAdd, lengthToObj, lengthToPosition, positionToLength, toLength } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { Random } from '../../core/random.js'; import { createTextModel } from '../../testTextModel.js'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts index fbf5a1ba33e..ed1fdcdeaf6 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { AstNode, AstNodeKind, ListAstNode, TextAstNode } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/ast.js'; -import { concat23Trees } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.js'; -import { toLength } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; +import { AstNode, AstNodeKind, ListAstNode, TextAstNode } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/ast.js'; +import { concat23Trees } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees.js'; +import { toLength } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; suite('Bracket Pair Colorizer - mergeItems', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts index c3293730e82..8360342b786 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts @@ -6,13 +6,13 @@ import assert from 'assert'; import { DisposableStore, disposeOnReturn } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; -import { TokenizationRegistry } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { BracketPairInfo } from '../../../../common/textModelBracketPairs.js'; import { TokenInfo, TokenizedDocument } from './tokenizer.test.js'; import { createModelServices, instantiateTextModel } from '../../testTextModel.js'; diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts index 174f0d85cd0..85227fa19f1 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Length, lengthAdd, lengthDiffNonNegative, lengthToObj, toLength } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; +import { Length, lengthAdd, lengthDiffNonNegative, lengthToObj, toLength } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; suite('Bracket Pair Colorizer - Length', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts index 3714c31fac6..e4347cbf3ce 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { DenseKeyProvider, SmallImmutableSet } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; +import { DenseKeyProvider, SmallImmutableSet } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; suite('Bracket Pair Colorizer - ImmutableSet', () => { diff --git a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts index 3b18e1d835c..0d43091d037 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts @@ -6,15 +6,15 @@ import assert from 'assert'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { LanguageId, MetadataConsts, StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../common/languages.js'; -import { ILanguageService } from '../../../../common/languages/language.js'; +import { LanguageId, MetadataConsts, StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js'; -import { LanguageAgnosticBracketTokens } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/brackets.js'; -import { Length, lengthAdd, lengthsToRange, lengthZero } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; -import { DenseKeyProvider } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; -import { TextBufferTokenizer, Token, Tokenizer, TokenKind } from '../../../../common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.js'; -import { TextModel } from '../../../../common/model/textModel.js'; +import { LanguageAgnosticBracketTokens } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/brackets.js'; +import { Length, lengthAdd, lengthsToRange, lengthZero } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/length.js'; +import { DenseKeyProvider } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet.js'; +import { TextBufferTokenizer, Token, Tokenizer, TokenKind } from '../../../../../editor/common/language/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { createModelServices, instantiateTextModel } from '../../testTextModel.js'; suite('Bracket Pair Colorizer - Tokenizer', () => { diff --git a/src/vs/editor/test/common/model/editStack.test.ts b/src/vs/editor/test/common/model/editStack.test.ts index 4572a71a4a7..f028e3f26ea 100644 --- a/src/vs/editor/test/common/model/editStack.test.ts +++ b/src/vs/editor/test/common/model/editStack.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Selection } from '../../../common/core/selection.js'; -import { TextChange } from '../../../common/core/textChange.js'; -import { EndOfLineSequence } from '../../../common/model.js'; -import { SingleModelEditStackData } from '../../../common/model/editStack.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { TextChange } from '../../../../editor/common/language/core/textChange.js'; +import { EndOfLineSequence } from '../../../../editor/common/language/model.js'; +import { SingleModelEditStackData } from '../../../../editor/common/language/model/editStack.js'; suite('EditStack', () => { diff --git a/src/vs/editor/test/common/model/editableTextModel.test.ts b/src/vs/editor/test/common/model/editableTextModel.test.ts index d2b73c7f536..22f373b6719 100644 --- a/src/vs/editor/test/common/model/editableTextModel.test.ts +++ b/src/vs/editor/test/common/model/editableTextModel.test.ts @@ -6,11 +6,11 @@ import assert from 'assert'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { EndOfLinePreference, EndOfLineSequence } from '../../../common/model.js'; -import { MirrorTextModel } from '../../../common/model/mirrorTextModel.js'; -import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { EndOfLinePreference, EndOfLineSequence } from '../../../../editor/common/language/model.js'; +import { MirrorTextModel } from '../../../../editor/common/language/model/mirrorTextModel.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { assertSyncedModels, testApplyEditsWithSyncedModels } from './editableTextModelTestUtils.js'; import { createTextModel } from '../testTextModel.js'; diff --git a/src/vs/editor/test/common/model/editableTextModelAuto.test.ts b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts index f8dfb21eb05..f3acc4165ac 100644 --- a/src/vs/editor/test/common/model/editableTextModelAuto.test.ts +++ b/src/vs/editor/test/common/model/editableTextModelAuto.test.ts @@ -5,9 +5,9 @@ import { CharCode } from '../../../../base/common/charCode.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { testApplyEditsWithSyncedModels } from './editableTextModelTestUtils.js'; const GENERATE_TESTS = false; diff --git a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts index 2014dc290b0..8efe4fedcc8 100644 --- a/src/vs/editor/test/common/model/editableTextModelTestUtils.ts +++ b/src/vs/editor/test/common/model/editableTextModelTestUtils.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { EndOfLinePreference, EndOfLineSequence } from '../../../common/model.js'; -import { MirrorTextModel } from '../../../common/model/mirrorTextModel.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { IModelContentChangedEvent } from '../../../common/textModelEvents.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { EndOfLinePreference, EndOfLineSequence } from '../../../../editor/common/language/model.js'; +import { MirrorTextModel } from '../../../../editor/common/language/model/mirrorTextModel.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { createTextModel } from '../testTextModel.js'; export function testApplyEditsWithSyncedModels(original: string[], edits: ISingleEditOperation[], expected: string[], inputEditsAreInvalid: boolean = false): void { diff --git a/src/vs/editor/test/common/model/intervalTree.test.ts b/src/vs/editor/test/common/model/intervalTree.test.ts index b6b09e38314..d4b1e1f915c 100644 --- a/src/vs/editor/test/common/model/intervalTree.test.ts +++ b/src/vs/editor/test/common/model/intervalTree.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { TrackedRangeStickiness } from '../../../common/model.js'; -import { IntervalNode, IntervalTree, NodeColor, SENTINEL, getNodeColor, intervalCompare, nodeAcceptEdit, setNodeStickiness } from '../../../common/model/intervalTree.js'; +import { TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { IntervalNode, IntervalTree, NodeColor, SENTINEL, getNodeColor, intervalCompare, nodeAcceptEdit, setNodeStickiness } from '../../../../editor/common/language/model/intervalTree.js'; const GENERATE_TESTS = false; const TEST_COUNT = GENERATE_TESTS ? 10000 : 0; diff --git a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts index db95c092633..46d1f09967c 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBuffer.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; -import { DefaultEndOfLine } from '../../../../common/model.js'; -import { IValidatedEditOperation, PieceTreeTextBuffer } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; -import { createTextBufferFactory } from '../../../../common/model/textModel.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { DefaultEndOfLine } from '../../../../../editor/common/language/model.js'; +import { IValidatedEditOperation, PieceTreeTextBuffer } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; +import { createTextBufferFactory } from '../../../../../editor/common/language/model/textModel.js'; suite('PieceTreeTextBuffer._getInverseEdits', () => { diff --git a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts index a855ea2ca4d..e4c9f561cd3 100644 --- a/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts +++ b/src/vs/editor/test/common/model/linesTextBuffer/linesTextBufferBuilder.test.ts @@ -6,8 +6,8 @@ import assert from 'assert'; import * as strings from '../../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { DefaultEndOfLine } from '../../../../common/model.js'; -import { createTextBufferFactory } from '../../../../common/model/textModel.js'; +import { DefaultEndOfLine } from '../../../../../editor/common/language/model.js'; +import { createTextBufferFactory } from '../../../../../editor/common/language/model/textModel.js'; function testTextBufferFactory(text: string, eol: string, mightContainNonBasicASCII: boolean, mightContainRTL: boolean): void { const { disposable, textBuffer } = createTextBufferFactory(text).create(DefaultEndOfLine.LF); diff --git a/src/vs/editor/test/common/model/model.line.test.ts b/src/vs/editor/test/common/model/model.line.test.ts index 446e7acaf42..71a1d77b9e4 100644 --- a/src/vs/editor/test/common/model/model.line.test.ts +++ b/src/vs/editor/test/common/model/model.line.test.ts @@ -5,13 +5,13 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Range } from '../../../common/core/range.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTokenizer, IState, ITokenizationSupport, TokenizationRegistry, TokenizationResult } from '../../../common/languages.js'; -import { ITextModel } from '../../../common/model.js'; -import { computeIndentLevel } from '../../../common/model/utils.js'; -import { ContiguousMultilineTokensBuilder } from '../../../common/tokens/contiguousMultilineTokensBuilder.js'; -import { LineTokens } from '../../../common/tokens/lineTokens.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTokenizer, IState, ITokenizationSupport, TokenizationRegistry, TokenizationResult } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { computeIndentLevel } from '../../../../editor/common/language/model/utils.js'; +import { ContiguousMultilineTokensBuilder } from '../../../../editor/common/language/tokens/contiguousMultilineTokensBuilder.js'; +import { LineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; import { TestLineToken, TestLineTokenFactory } from '../core/testLineToken.js'; import { createTextModel } from '../testTextModel.js'; diff --git a/src/vs/editor/test/common/model/model.modes.test.ts b/src/vs/editor/test/common/model/model.modes.test.ts index a7ad097c019..ada949bbe6a 100644 --- a/src/vs/editor/test/common/model/model.modes.test.ts +++ b/src/vs/editor/test/common/model/model.modes.test.ts @@ -6,12 +6,12 @@ import assert from 'assert'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import * as languages from '../../../common/languages.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { createTextModel } from '../testTextModel.js'; // --------- utils diff --git a/src/vs/editor/test/common/model/model.test.ts b/src/vs/editor/test/common/model/model.test.ts index e18b8438525..1a2c4395d0b 100644 --- a/src/vs/editor/test/common/model/model.test.ts +++ b/src/vs/editor/test/common/model/model.test.ts @@ -6,16 +6,16 @@ import assert from 'assert'; import { Disposable, DisposableStore, dispose } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { InternalModelContentChangeEvent, ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from '../../../common/textModelEvents.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { InternalModelContentChangeEvent, ModelRawContentChangedEvent, ModelRawFlush, ModelRawLineChanged, ModelRawLinesDeleted, ModelRawLinesInserted } from '../../../../editor/common/language/textModelEvents.js'; import { createModelServices, createTextModel, instantiateTextModel } from '../testTextModel.js'; // --------- utils diff --git a/src/vs/editor/test/common/model/modelDecorations.test.ts b/src/vs/editor/test/common/model/modelDecorations.test.ts index 142bbead17a..b42dfa216d0 100644 --- a/src/vs/editor/test/common/model/modelDecorations.test.ts +++ b/src/vs/editor/test/common/model/modelDecorations.test.ts @@ -5,11 +5,11 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { EndOfLineSequence, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../common/model.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { EndOfLineSequence, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { createTextModel } from '../testTextModel.js'; // --------- utils diff --git a/src/vs/editor/test/common/model/modelEditOperation.test.ts b/src/vs/editor/test/common/model/modelEditOperation.test.ts index fba9c92cff0..8e693c8cd88 100644 --- a/src/vs/editor/test/common/model/modelEditOperation.test.ts +++ b/src/vs/editor/test/common/model/modelEditOperation.test.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { TextModel } from '../../../common/model/textModel.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { createTextModel } from '../testTextModel.js'; suite('Editor Model - Model Edit Operation', () => { diff --git a/src/vs/editor/test/common/model/modelInjectedText.test.ts b/src/vs/editor/test/common/model/modelInjectedText.test.ts index b342e6bfb01..f712edcbf7c 100644 --- a/src/vs/editor/test/common/model/modelInjectedText.test.ts +++ b/src/vs/editor/test/common/model/modelInjectedText.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { InternalModelContentChangeEvent, LineInjectedText, ModelRawChange, RawContentChangedType } from '../../../common/textModelEvents.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { InternalModelContentChangeEvent, LineInjectedText, ModelRawChange, RawContentChangedType } from '../../../../editor/common/language/textModelEvents.js'; import { createTextModel } from '../testTextModel.js'; suite('Editor Model - Injected Text Events', () => { diff --git a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts index cb60242c20b..4a29ff2e98f 100644 --- a/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts +++ b/src/vs/editor/test/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { WordCharacterClassifier } from '../../../../common/core/wordCharacterClassifier.js'; -import { Position } from '../../../../common/core/position.js'; -import { Range } from '../../../../common/core/range.js'; -import { DefaultEndOfLine, ITextSnapshot, SearchData } from '../../../../common/model.js'; -import { PieceTreeBase } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeBase.js'; -import { PieceTreeTextBuffer } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; -import { PieceTreeTextBufferBuilder } from '../../../../common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; -import { NodeColor, SENTINEL, TreeNode } from '../../../../common/model/pieceTreeTextBuffer/rbTreeBase.js'; +import { WordCharacterClassifier } from '../../../../../editor/common/language/core/wordCharacterClassifier.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { DefaultEndOfLine, ITextSnapshot, SearchData } from '../../../../../editor/common/language/model.js'; +import { PieceTreeBase } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeBase.js'; +import { PieceTreeTextBuffer } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; +import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; +import { NodeColor, SENTINEL, TreeNode } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/rbTreeBase.js'; import { createTextModel } from '../../testTextModel.js'; import { splitLines } from '../../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; diff --git a/src/vs/editor/test/common/model/textChange.test.ts b/src/vs/editor/test/common/model/textChange.test.ts index 5c0292b4281..d77a7d8c12b 100644 --- a/src/vs/editor/test/common/model/textChange.test.ts +++ b/src/vs/editor/test/common/model/textChange.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { compressConsecutiveTextChanges, TextChange } from '../../../common/core/textChange.js'; +import { compressConsecutiveTextChanges, TextChange } from '../../../../editor/common/language/core/textChange.js'; const GENERATE_TESTS = false; diff --git a/src/vs/editor/test/common/model/textModel.test.ts b/src/vs/editor/test/common/model/textModel.test.ts index 9165d77afbf..05bd8c5c693 100644 --- a/src/vs/editor/test/common/model/textModel.test.ts +++ b/src/vs/editor/test/common/model/textModel.test.ts @@ -7,11 +7,11 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { UTF8_BOM_CHARACTER } from '../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../common/languages/modesRegistry.js'; -import { EndOfLinePreference } from '../../../common/model.js'; -import { TextModel, createTextBuffer } from '../../../common/model/textModel.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; +import { TextModel, createTextBuffer } from '../../../../editor/common/language/model/textModel.js'; import { createModelServices, createTextModel } from '../testTextModel.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/editor/test/common/model/textModelSearch.test.ts b/src/vs/editor/test/common/model/textModelSearch.test.ts index d2067f30814..f0119fba767 100644 --- a/src/vs/editor/test/common/model/textModelSearch.test.ts +++ b/src/vs/editor/test/common/model/textModelSearch.test.ts @@ -5,13 +5,13 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { getMapForWordSeparators } from '../../../common/core/wordCharacterClassifier.js'; -import { USUAL_WORD_SEPARATORS } from '../../../common/core/wordHelper.js'; -import { EndOfLineSequence, FindMatch, SearchData } from '../../../common/model.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { SearchParams, TextModelSearch, isMultilineRegexSource } from '../../../common/model/textModelSearch.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { getMapForWordSeparators } from '../../../../editor/common/language/core/wordCharacterClassifier.js'; +import { USUAL_WORD_SEPARATORS } from '../../../../editor/common/language/core/wordHelper.js'; +import { EndOfLineSequence, FindMatch, SearchData } from '../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { SearchParams, TextModelSearch, isMultilineRegexSource } from '../../../../editor/common/language/model/textModelSearch.js'; import { createTextModel } from '../testTextModel.js'; // --------- Find diff --git a/src/vs/editor/test/common/model/textModelTokens.test.ts b/src/vs/editor/test/common/model/textModelTokens.test.ts index 8850c911cfd..d40d0c04e85 100644 --- a/src/vs/editor/test/common/model/textModelTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelTokens.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { RangePriorityQueueImpl } from '../../../common/model/textModelTokens.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { RangePriorityQueueImpl } from '../../../../editor/common/language/model/textModelTokens.js'; suite('RangePriorityQueueImpl', () => { diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 3783e8e2606..56d4496ff4e 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -5,16 +5,16 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IFoundBracket } from '../../../common/textModelBracketPairs.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { ITokenizationSupport, TokenizationRegistry, EncodedTokenizationResult } from '../../../common/languages.js'; -import { StandardTokenType, MetadataConsts } from '../../../common/encodedTokenAttributes.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { ITokenizationSupport, TokenizationRegistry, EncodedTokenizationResult } from '../../../../editor/common/language/languages.js'; +import { StandardTokenType, MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { CharacterPair } from '../../../common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { NullState } from '../../../common/languages/nullTokenize.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { TestLineToken } from '../core/testLineToken.js'; import { createModelServices, createTextModel, instantiateTextModel } from '../testTextModel.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/editor/test/common/model/tokenStore.test.ts b/src/vs/editor/test/common/model/tokenStore.test.ts index 8c8cbd2d617..fae249db54e 100644 --- a/src/vs/editor/test/common/model/tokenStore.test.ts +++ b/src/vs/editor/test/common/model/tokenStore.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { TokenQuality, TokenStore } from '../../../common/model/tokenStore.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { TokenQuality, TokenStore } from '../../../../editor/common/language/model/tokenStore.js'; suite('TokenStore', () => { let textModel: TextModel; diff --git a/src/vs/editor/test/common/model/tokensStore.test.ts b/src/vs/editor/test/common/model/tokensStore.test.ts index c40695bd46f..a73b4a6f176 100644 --- a/src/vs/editor/test/common/model/tokensStore.test.ts +++ b/src/vs/editor/test/common/model/tokensStore.test.ts @@ -6,16 +6,16 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ISingleEditOperation } from '../../../common/core/editOperation.js'; -import { Position } from '../../../common/core/position.js'; -import { Range } from '../../../common/core/range.js'; -import { ColorId, FontStyle, MetadataConsts, TokenMetadata } from '../../../common/encodedTokenAttributes.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ColorId, FontStyle, MetadataConsts, TokenMetadata } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ILanguageConfigurationService, LanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; -import { TextModel } from '../../../common/model/textModel.js'; -import { LanguageIdCodec } from '../../../common/services/languagesRegistry.js'; -import { LineTokens } from '../../../common/tokens/lineTokens.js'; -import { SparseMultilineTokens } from '../../../common/tokens/sparseMultilineTokens.js'; -import { SparseTokensStore } from '../../../common/tokens/sparseTokensStore.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; +import { LanguageIdCodec } from '../../../../editor/common/language/services/languagesRegistry.js'; +import { LineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; +import { SparseMultilineTokens } from '../../../../editor/common/language/tokens/sparseMultilineTokens.js'; +import { SparseTokensStore } from '../../../../editor/common/language/tokens/sparseTokensStore.js'; import { createModelServices, createTextModel, instantiateTextModel } from '../testTextModel.js'; suite('TokensStore', () => { diff --git a/src/vs/editor/test/common/modes/languageConfiguration.test.ts b/src/vs/editor/test/common/modes/languageConfiguration.test.ts index f6e4fb34a93..689ff9ba9af 100644 --- a/src/vs/editor/test/common/modes/languageConfiguration.test.ts +++ b/src/vs/editor/test/common/modes/languageConfiguration.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { StandardAutoClosingPairConditional } from '../../../common/languages/languageConfiguration.js'; import { TestLanguageConfigurationService } from './testLanguageConfigurationService.js'; diff --git a/src/vs/editor/test/common/modes/languageSelector.test.ts b/src/vs/editor/test/common/modes/languageSelector.test.ts index 31f6c051af4..8d430b97cfd 100644 --- a/src/vs/editor/test/common/modes/languageSelector.test.ts +++ b/src/vs/editor/test/common/modes/languageSelector.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { LanguageSelector, score } from '../../../common/languageSelector.js'; +import { LanguageSelector, score } from '../../../../editor/common/language/languageSelector.js'; suite('LanguageSelector', function () { diff --git a/src/vs/editor/test/common/modes/linkComputer.test.ts b/src/vs/editor/test/common/modes/linkComputer.test.ts index 8a955f9684c..4017bd31c5f 100644 --- a/src/vs/editor/test/common/modes/linkComputer.test.ts +++ b/src/vs/editor/test/common/modes/linkComputer.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ILink } from '../../../common/languages.js'; +import { ILink } from '../../../../editor/common/language/languages.js'; import { ILinkComputerTarget, computeLinks } from '../../../common/languages/linkComputer.js'; class SimpleLinkComputerTarget implements ILinkComputerTarget { diff --git a/src/vs/editor/test/common/modes/supports/characterPair.test.ts b/src/vs/editor/test/common/modes/supports/characterPair.test.ts index 0e0f4e01922..5f3bb92d701 100644 --- a/src/vs/editor/test/common/modes/supports/characterPair.test.ts +++ b/src/vs/editor/test/common/modes/supports/characterPair.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { StandardAutoClosingPairConditional } from '../../../../common/languages/languageConfiguration.js'; import { CharacterPairSupport } from '../../../../common/languages/supports/characterPair.js'; import { TokenText, createFakeScopedLineTokens } from '../../modesTestUtils.js'; diff --git a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts index f6258de670f..b40e9a678f4 100644 --- a/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts +++ b/src/vs/editor/test/common/modes/supports/electricCharacter.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { StandardTokenType } from '../../../../common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { BracketElectricCharacterSupport, IElectricAction } from '../../../../common/languages/supports/electricCharacter.js'; import { RichEditBrackets } from '../../../../common/languages/supports/richEditBrackets.js'; import { TokenText, createFakeScopedLineTokens } from '../../modesTestUtils.js'; diff --git a/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts index 27f27e7d54e..f04dd7c7733 100644 --- a/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts +++ b/src/vs/editor/test/common/modes/supports/richEditBrackets.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { BracketsUtils } from '../../../../common/languages/supports/richEditBrackets.js'; suite('richEditBrackets', () => { diff --git a/src/vs/editor/test/common/modes/supports/tokenization.test.ts b/src/vs/editor/test/common/modes/supports/tokenization.test.ts index 69fe4beaac0..a27df1214e7 100644 --- a/src/vs/editor/test/common/modes/supports/tokenization.test.ts +++ b/src/vs/editor/test/common/modes/supports/tokenization.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { FontStyle } from '../../../../common/encodedTokenAttributes.js'; +import { FontStyle } from '../../../../../editor/common/language/encodedTokenAttributes.js'; import { ColorMap, ExternalThemeTrieElement, ParsedTokenThemeRule, ThemeTrieElementRule, TokenTheme, parseTokenTheme, strcmp } from '../../../../common/languages/supports/tokenization.js'; suite('Token theme matching', () => { diff --git a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts index 31230b13358..3f6e2ac8fc9 100644 --- a/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts +++ b/src/vs/editor/test/common/modes/textToHtmlTokenizer.test.ts @@ -6,11 +6,11 @@ import assert from 'assert'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ColorId, FontStyle, MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../common/languages.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ColorId, FontStyle, MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { EncodedTokenizationResult, IState, TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { _tokenizeToString, tokenizeLineToHTML } from '../../../common/languages/textToHtmlTokenizer.js'; -import { LanguageIdCodec } from '../../../common/services/languagesRegistry.js'; +import { LanguageIdCodec } from '../../../../editor/common/language/services/languagesRegistry.js'; import { TestLineToken, TestLineTokens } from '../core/testLineToken.js'; import { createModelServices } from '../testTextModel.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/editor/test/common/modesTestUtils.ts b/src/vs/editor/test/common/modesTestUtils.ts index 509eff0ea8a..40bf5984df2 100644 --- a/src/vs/editor/test/common/modesTestUtils.ts +++ b/src/vs/editor/test/common/modesTestUtils.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LineTokens } from '../../common/tokens/lineTokens.js'; -import { StandardTokenType, MetadataConsts } from '../../common/encodedTokenAttributes.js'; +import { LineTokens } from '../../../editor/common/language/tokens/lineTokens.js'; +import { StandardTokenType, MetadataConsts } from '../../../editor/common/language/encodedTokenAttributes.js'; import { ScopedLineTokens, createScopedLineTokens } from '../../common/languages/supports.js'; -import { LanguageIdCodec } from '../../common/services/languagesRegistry.js'; +import { LanguageIdCodec } from '../../../editor/common/language/services/languagesRegistry.js'; export interface TokenText { text: string; diff --git a/src/vs/editor/test/common/services/editorWebWorker.test.ts b/src/vs/editor/test/common/services/editorWebWorker.test.ts index f2f33476c16..f6050bc8bcf 100644 --- a/src/vs/editor/test/common/services/editorWebWorker.test.ts +++ b/src/vs/editor/test/common/services/editorWebWorker.test.ts @@ -5,11 +5,11 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Position } from '../../../common/core/position.js'; -import { IRange, Range } from '../../../common/core/range.js'; -import { TextEdit } from '../../../common/languages.js'; -import { EditorWorker } from '../../../common/services/editorWebWorker.js'; -import { ICommonModel } from '../../../common/services/textModelSync/textModelSync.impl.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; +import { EditorWorker } from '../../../../editor/common/language/services/editorWebWorker.js'; +import { ICommonModel } from '../../../../editor/common/language/services/textModelSync/textModelSync.impl.js'; suite('EditorWebWorker', () => { diff --git a/src/vs/editor/test/common/services/findSectionHeaders.test.ts b/src/vs/editor/test/common/services/findSectionHeaders.test.ts index 98231e69edf..e42370918be 100644 --- a/src/vs/editor/test/common/services/findSectionHeaders.test.ts +++ b/src/vs/editor/test/common/services/findSectionHeaders.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { FindSectionHeaderOptions, ISectionHeaderFinderTarget, findSectionHeaders } from '../../../common/services/findSectionHeaders.js'; +import { FindSectionHeaderOptions, ISectionHeaderFinderTarget, findSectionHeaders } from '../../../../editor/common/language/services/findSectionHeaders.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; class TestSectionHeaderFinderTarget implements ISectionHeaderFinderTarget { diff --git a/src/vs/editor/test/common/services/languageService.test.ts b/src/vs/editor/test/common/services/languageService.test.ts index ba3c588fa01..8ef8d9c1839 100644 --- a/src/vs/editor/test/common/services/languageService.test.ts +++ b/src/vs/editor/test/common/services/languageService.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../common/languages/modesRegistry.js'; -import { LanguageService } from '../../../common/services/languageService.js'; +import { LanguageService } from '../../../../editor/common/language/services/languageService.js'; suite('LanguageService', () => { diff --git a/src/vs/editor/test/common/services/languagesAssociations.test.ts b/src/vs/editor/test/common/services/languagesAssociations.test.ts index 891260d0fb5..90f056f32e9 100644 --- a/src/vs/editor/test/common/services/languagesAssociations.test.ts +++ b/src/vs/editor/test/common/services/languagesAssociations.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { getMimeTypes, registerPlatformLanguageAssociation, registerConfiguredLanguageAssociation } from '../../../common/services/languagesAssociations.js'; +import { getMimeTypes, registerPlatformLanguageAssociation, registerConfiguredLanguageAssociation } from '../../../../editor/common/language/services/languagesAssociations.js'; suite('LanguagesAssociations', () => { diff --git a/src/vs/editor/test/common/services/languagesRegistry.test.ts b/src/vs/editor/test/common/services/languagesRegistry.test.ts index 9789d1380aa..68bcd8e153c 100644 --- a/src/vs/editor/test/common/services/languagesRegistry.test.ts +++ b/src/vs/editor/test/common/services/languagesRegistry.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { LanguagesRegistry } from '../../../common/services/languagesRegistry.js'; +import { LanguagesRegistry } from '../../../../editor/common/language/services/languagesRegistry.js'; suite('LanguagesRegistry', () => { diff --git a/src/vs/editor/test/common/services/modelService.test.ts b/src/vs/editor/test/common/services/modelService.test.ts index ccc2fabc213..0ada5ab3578 100644 --- a/src/vs/editor/test/common/services/modelService.test.ts +++ b/src/vs/editor/test/common/services/modelService.test.ts @@ -7,17 +7,17 @@ import assert from 'assert'; import { CharCode } from '../../../../base/common/charCode.js'; import * as platform from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; -import { EditOperation } from '../../../common/core/editOperation.js'; -import { Range } from '../../../common/core/range.js'; -import { Selection } from '../../../common/core/selection.js'; -import { StringBuilder } from '../../../common/core/stringBuilder.js'; -import { DefaultEndOfLine, ITextBuffer, ITextBufferFactory, ITextSnapshot } from '../../../common/model.js'; -import { createTextBuffer } from '../../../common/model/textModel.js'; -import { ModelService } from '../../../common/services/modelService.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; +import { DefaultEndOfLine, ITextBuffer, ITextBufferFactory, ITextSnapshot } from '../../../../editor/common/language/model.js'; +import { createTextBuffer } from '../../../../editor/common/language/model/textModel.js'; +import { ModelService } from '../../../../editor/common/language/services/modelService.js'; import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js'; import { createModelServices, createTextModel } from '../testTextModel.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { IModelService } from '../../../common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; diff --git a/src/vs/editor/test/common/services/semanticTokensDto.test.ts b/src/vs/editor/test/common/services/semanticTokensDto.test.ts index 05980e011a0..303eff8c34c 100644 --- a/src/vs/editor/test/common/services/semanticTokensDto.test.ts +++ b/src/vs/editor/test/common/services/semanticTokensDto.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { IFullSemanticTokensDto, IDeltaSemanticTokensDto, encodeSemanticTokensDto, ISemanticTokensDto, decodeSemanticTokensDto } from '../../../common/services/semanticTokensDto.js'; +import { IFullSemanticTokensDto, IDeltaSemanticTokensDto, encodeSemanticTokensDto, ISemanticTokensDto, decodeSemanticTokensDto } from '../../../../editor/common/language/services/semanticTokensDto.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; diff --git a/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts b/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts index 98894403654..14e2dfe5e90 100644 --- a/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts +++ b/src/vs/editor/test/common/services/semanticTokensProviderStyling.test.ts @@ -5,13 +5,13 @@ import assert from 'assert'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { SparseMultilineTokens } from '../../../common/tokens/sparseMultilineTokens.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { SemanticTokensProviderStyling, toMultilineTokens2 } from '../../../common/services/semanticTokensProviderStyling.js'; +import { SparseMultilineTokens } from '../../../../editor/common/language/tokens/sparseMultilineTokens.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { SemanticTokensProviderStyling, toMultilineTokens2 } from '../../../../editor/common/language/services/semanticTokensProviderStyling.js'; import { createModelServices } from '../testTextModel.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IColorTheme, IThemeService, ITokenStyle } from '../../../../platform/theme/common/themeService.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; suite('ModelService', () => { diff --git a/src/vs/editor/test/common/services/testEditorWorkerService.ts b/src/vs/editor/test/common/services/testEditorWorkerService.ts index 44a9d5fffc3..87b30c7382f 100644 --- a/src/vs/editor/test/common/services/testEditorWorkerService.ts +++ b/src/vs/editor/test/common/services/testEditorWorkerService.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../common/core/range.js'; -import { DiffAlgorithmName, IEditorWorkerService, IUnicodeHighlightsResult } from '../../../common/services/editorWorker.js'; -import { TextEdit, IInplaceReplaceSupportResult, IColorInformation } from '../../../common/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { DiffAlgorithmName, IEditorWorkerService, IUnicodeHighlightsResult } from '../../../../editor/common/language/services/editorWorker.js'; +import { TextEdit, IInplaceReplaceSupportResult, IColorInformation } from '../../../../editor/common/language/languages.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../../common/diff/documentDiffProvider.js'; import { IChange } from '../../../common/diff/legacyLinesDiffComputer.js'; -import { SectionHeader } from '../../../common/services/findSectionHeaders.js'; +import { SectionHeader } from '../../../../editor/common/language/services/findSectionHeaders.js'; export class TestEditorWorkerService implements IEditorWorkerService { diff --git a/src/vs/editor/test/common/services/testTextResourcePropertiesService.ts b/src/vs/editor/test/common/services/testTextResourcePropertiesService.ts index 77a92fa0ee3..0f2b40be09f 100644 --- a/src/vs/editor/test/common/services/testTextResourcePropertiesService.ts +++ b/src/vs/editor/test/common/services/testTextResourcePropertiesService.ts @@ -5,7 +5,7 @@ import * as platform from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextResourcePropertiesService } from '../../../common/services/textResourceConfiguration.js'; +import { ITextResourcePropertiesService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; export class TestTextResourcePropertiesService implements ITextResourcePropertiesService { diff --git a/src/vs/editor/test/common/services/testTreeSitterService.ts b/src/vs/editor/test/common/services/testTreeSitterService.ts index 2d67b5abce3..32303a29d81 100644 --- a/src/vs/editor/test/common/services/testTreeSitterService.ts +++ b/src/vs/editor/test/common/services/testTreeSitterService.ts @@ -5,8 +5,8 @@ import type * as Parser from '@vscode/tree-sitter-wasm'; import { Event } from '../../../../base/common/event.js'; -import { ITextModel } from '../../../common/model.js'; -import { ITreeSitterParserService, ITextModelTreeSitter, TreeUpdateEvent } from '../../../common/services/treeSitterParserService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ITreeSitterParserService, ITextModelTreeSitter, TreeUpdateEvent } from '../../../../editor/common/language/services/treeSitterParserService.js'; export class TestTreeSitterParserService implements ITreeSitterParserService { getLanguage(languageId: string): Promise { diff --git a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts index f3299fdba92..9729356b286 100644 --- a/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts +++ b/src/vs/editor/test/common/services/textResourceConfigurationService.test.ts @@ -6,10 +6,10 @@ import assert from 'assert'; import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { IModelService } from '../../../common/services/model.js'; -import { ILanguageService } from '../../../common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IConfigurationValue, IConfigurationService, ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; -import { TextResourceConfigurationService } from '../../../common/services/textResourceConfigurationService.js'; +import { TextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfigurationService.js'; import { URI } from '../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; diff --git a/src/vs/editor/test/common/services/unicodeTextModelHighlighter.test.ts b/src/vs/editor/test/common/services/unicodeTextModelHighlighter.test.ts index 3b4b550cfad..8a6e6cb364d 100644 --- a/src/vs/editor/test/common/services/unicodeTextModelHighlighter.test.ts +++ b/src/vs/editor/test/common/services/unicodeTextModelHighlighter.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Range } from '../../../common/core/range.js'; -import { UnicodeHighlighterOptions, UnicodeTextModelHighlighter } from '../../../common/services/unicodeTextModelHighlighter.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { UnicodeHighlighterOptions, UnicodeTextModelHighlighter } from '../../../../editor/common/language/services/unicodeTextModelHighlighter.js'; import { createTextModel } from '../testTextModel.js'; suite('UnicodeTextModelHighlighter', () => { diff --git a/src/vs/editor/test/common/testTextModel.ts b/src/vs/editor/test/common/testTextModel.ts index 01732b71158..80907ce6167 100644 --- a/src/vs/editor/test/common/testTextModel.ts +++ b/src/vs/editor/test/common/testTextModel.ts @@ -5,12 +5,12 @@ import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; -import { BracketPairColorizationOptions, DefaultEndOfLine, ITextBufferFactory, ITextModelCreationOptions } from '../../common/model.js'; -import { TextModel } from '../../common/model/textModel.js'; +import { BracketPairColorizationOptions, DefaultEndOfLine, ITextBufferFactory, ITextModelCreationOptions } from '../../../editor/common/language/model.js'; +import { TextModel } from '../../../editor/common/language/model/textModel.js'; import { ILanguageConfigurationService } from '../../common/languages/languageConfigurationRegistry.js'; -import { ILanguageService } from '../../common/languages/language.js'; -import { LanguageService } from '../../common/services/languageService.js'; -import { ITextResourcePropertiesService } from '../../common/services/textResourceConfiguration.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { LanguageService } from '../../../editor/common/language/services/languageService.js'; +import { ITextResourcePropertiesService } from '../../../editor/common/language/services/textResourceConfiguration.js'; import { TestLanguageConfigurationService } from './modes/testLanguageConfigurationService.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../platform/configuration/test/common/testConfigurationService.js'; @@ -25,16 +25,16 @@ import { TestThemeService } from '../../../platform/theme/test/common/testThemeS import { IUndoRedoService } from '../../../platform/undoRedo/common/undoRedo.js'; import { UndoRedoService } from '../../../platform/undoRedo/common/undoRedoService.js'; import { TestTextResourcePropertiesService } from './services/testTextResourcePropertiesService.js'; -import { IModelService } from '../../common/services/model.js'; -import { ModelService } from '../../common/services/modelService.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../editor/common/language/services/modelService.js'; import { createServices, ServiceIdCtorPair, TestInstantiationService } from '../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../common/languages/modesRegistry.js'; -import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../common/services/languageFeaturesService.js'; +import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../editor/common/language/services/languageFeaturesService.js'; import { IEnvironmentService } from '../../../platform/environment/common/environment.js'; import { mock } from '../../../base/test/common/mock.js'; -import { ITreeSitterParserService } from '../../common/services/treeSitterParserService.js'; +import { ITreeSitterParserService } from '../../../editor/common/language/services/treeSitterParserService.js'; import { TestTreeSitterParserService } from './services/testTreeSitterService.js'; class TestTextModel extends TextModel { diff --git a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts index 59f87b95b43..46a63c8bee9 100644 --- a/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts +++ b/src/vs/editor/test/common/viewLayout/lineDecorations.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { DecorationSegment, LineDecoration, LineDecorationsNormalizer } from '../../../common/viewLayout/lineDecorations.js'; import { InlineDecoration, InlineDecorationType } from '../../../common/viewModel.js'; diff --git a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts index c35690fe93a..4a745834bc0 100644 --- a/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts +++ b/src/vs/editor/test/common/viewLayout/viewLineRenderer.test.ts @@ -7,8 +7,8 @@ import assert from 'assert'; import { CharCode } from '../../../../base/common/charCode.js'; import * as strings from '../../../../base/common/strings.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { MetadataConsts } from '../../../common/encodedTokenAttributes.js'; -import { IViewLineTokens } from '../../../common/tokens/lineTokens.js'; +import { MetadataConsts } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { IViewLineTokens } from '../../../../editor/common/language/tokens/lineTokens.js'; import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js'; import { CharacterMapping, DomPosition, LineRange, RenderLineInput, RenderLineOutput2, renderViewLine2 as renderViewLine } from '../../../common/viewLayout/viewLineRenderer.js'; import { InlineDecorationType } from '../../../common/viewModel.js'; diff --git a/src/vs/editor/test/common/viewModel/glyphLanesModel.test.ts b/src/vs/editor/test/common/viewModel/glyphLanesModel.test.ts index 39f80ae0a32..a29d031cabe 100644 --- a/src/vs/editor/test/common/viewModel/glyphLanesModel.test.ts +++ b/src/vs/editor/test/common/viewModel/glyphLanesModel.test.ts @@ -6,8 +6,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { GlyphMarginLanesModel, } from '../../../common/viewModel/glyphLanesModel.js'; -import { Range } from '../../../common/core/range.js'; -import { GlyphMarginLane } from '../../../common/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { GlyphMarginLane } from '../../../../editor/common/language/model.js'; suite('GlyphLanesModel', () => { let model: GlyphMarginLanesModel; diff --git a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts index b621632fe1d..748a196e06d 100644 --- a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts +++ b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts @@ -5,8 +5,8 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { PositionAffinity } from '../../../common/model.js'; -import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js'; +import { PositionAffinity } from '../../../../editor/common/language/model.js'; +import { ModelDecorationInjectedTextOptions } from '../../../../editor/common/language/model/textModel.js'; import { ModelLineProjectionData } from '../../../common/modelLineProjectionData.js'; suite('Editor ViewModel - LineBreakData', () => { diff --git a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts index a7f934a6247..1f16fd9a0b9 100644 --- a/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts +++ b/src/vs/editor/test/common/viewModel/prefixSumComputer.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { toUint32 } from '../../../../base/common/uint.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { PrefixSumComputer, PrefixSumIndexOfResult } from '../../../common/model/prefixSumComputer.js'; +import { PrefixSumComputer, PrefixSumIndexOfResult } from '../../../../editor/common/language/model/prefixSumComputer.js'; function toUint32Array(arr: number[]): Uint32Array { const len = arr.length; diff --git a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts index 7e93197efec..a744ef7d6c3 100644 --- a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { getLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; import { LinesSliceCharSequence } from '../../../common/diff/defaultLinesDiffComputer/linesSliceCharSequence.js'; import { MyersDiffAlgorithm } from '../../../common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.js'; import { DynamicProgrammingDiffing } from '../../../common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ArrayText } from '../../../common/core/textEdit.js'; +import { ArrayText } from '../../../../editor/common/language/core/textEdit.js'; suite('myers', () => { ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/editor/test/node/diffing/fixtures.test.ts b/src/vs/editor/test/node/diffing/fixtures.test.ts index e85d69f1260..78be4360aef 100644 --- a/src/vs/editor/test/node/diffing/fixtures.test.ts +++ b/src/vs/editor/test/node/diffing/fixtures.test.ts @@ -11,9 +11,9 @@ import { FileAccess } from '../../../../base/common/network.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; import { LegacyLinesDiffComputer } from '../../../common/diff/legacyLinesDiffComputer.js'; import { DefaultLinesDiffComputer } from '../../../common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; -import { Range } from '../../../common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; +import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from '../../../../editor/common/language/core/textEdit.js'; import { LinesDiff } from '../../../common/diff/linesDiffComputer.js'; suite('diffing fixtures', () => { diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx index 693ebcc1e12..ae4881f18b7 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/1.tsx @@ -1,5 +1,21 @@ -import { Stack, Text } from '@fluentui/react'; -import { View } from '../../layout/layout'; +import * as React from 'react'; +import type { ReactElement, ReactNode } from 'react'; +import { View } from '../../layout/layout.js'; + +const Stack = Object.assign( + ({ children }: { children?: ReactNode; grow?: boolean; verticalFill?: boolean }): ReactElement => ( +
{children}
+ ), + { + Item: ({ children }: { children?: ReactNode }): ReactElement => ( +
{children}
+ ) + } +); + +const Text = ({ children }: { children?: ReactNode }): ReactElement => ( + {children} +); export const WelcomeView = () => { return ( diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx index cb911e799d4..4ee4f2ede4c 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/2.tsx @@ -1,5 +1,26 @@ -import { Nav } from '@fluentui/react'; -import { View } from '../../layout/layout'; +import * as React from 'react'; +import type { ReactElement, ReactNode } from 'react'; +import { View } from '../../layout/layout.js'; + +const Nav = ({ + groups +}: { + groups: { links: { name: string; url: string; icon: string; target: string }[] }[]; +}): ReactElement => ( + +); export const WelcomeView = () => { return ( diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json index 66e738ab041..95e01cddcfa 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/advanced.expected.diff.json @@ -1,34 +1,38 @@ { "original": { - "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "content": "import * as React from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { View } from '../../layout/layout.js';\n\nconst Stack = Object.assign(\n ({ children }: { children?: ReactNode; grow?: boolean; verticalFill?: boolean }): ReactElement => (\n\t\t
{children}
\n\t),\n\t{\n Item: ({ children }: { children?: ReactNode }): ReactElement => (\n\t\t\t
{children}
\n\t\t)\n\t}\n);\n\nconst Text = ({ children }: { children?: ReactNode }): ReactElement => (\n\t{children}\n);\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", "fileName": "./1.tsx" }, "modified": { - "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "content": "import * as React from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { View } from '../../layout/layout.js';\n\nconst Nav = ({\n\tgroups\n}: {\n groups: { links: { name: string; url: string; icon: string; target: string }[] }[];\n}): ReactElement => (\n\t\n);\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", "fileName": "./2.tsx" }, "diffs": [ { - "originalRange": "[1,2)", - "modifiedRange": "[1,2)", + "originalRange": "[5,18)", + "modifiedRange": "[5,23)", "innerChanges": [ { - "originalRange": "[1,10 -> 1,21]", - "modifiedRange": "[1,10 -> 1,13]" + "originalRange": "[5,7 -> 6,83]", + "modifiedRange": "[5,7 -> 9,1]" + }, + { + "originalRange": "[7,1 -> 18,1]", + "modifiedRange": "[10,1 -> 23,1]" } ] }, { - "originalRange": "[7,14)", - "modifiedRange": "[7,18)", + "originalRange": "[23,30)", + "modifiedRange": "[28,39)", "innerChanges": [ { - "originalRange": "[7,1 -> 13,1]", - "modifiedRange": "[7,1 -> 17,1]" + "originalRange": "[23,1 -> 29,1]", + "modifiedRange": "[28,1 -> 38,1]" }, { - "originalRange": "[13,6 -> 13,11]", - "modifiedRange": "[17,6 -> 17,9]" + "originalRange": "[29,6 -> 29,11]", + "modifiedRange": "[38,6 -> 38,9]" } ] } diff --git a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json index 0fff59a0214..957a3619de5 100644 --- a/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/ws-alignment/legacy.expected.diff.json @@ -1,62 +1,90 @@ { "original": { - "content": "import { Stack, Text } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "content": "import * as React from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { View } from '../../layout/layout.js';\n\nconst Stack = Object.assign(\n ({ children }: { children?: ReactNode; grow?: boolean; verticalFill?: boolean }): ReactElement => (\n\t\t
{children}
\n\t),\n\t{\n Item: ({ children }: { children?: ReactNode }): ReactElement => (\n\t\t\t
{children}
\n\t\t)\n\t}\n);\n\nconst Text = ({ children }: { children?: ReactNode }): ReactElement => (\n\t{children}\n);\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\tWelcome to the VS Code Tools application.\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", "fileName": "./1.tsx" }, "modified": { - "content": "import { Nav } from '@fluentui/react';\nimport { View } from '../../layout/layout';\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", + "content": "import * as React from 'react';\nimport type { ReactElement, ReactNode } from 'react';\nimport { View } from '../../layout/layout.js';\n\nconst Nav = ({\n\tgroups\n}: {\n groups: { links: { name: string; url: string; icon: string; target: string }[] }[];\n}): ReactElement => (\n\t\n);\n\nexport const WelcomeView = () => {\n\treturn (\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t);\n}\n", "fileName": "./2.tsx" }, "diffs": [ { - "originalRange": "[1,2)", - "modifiedRange": "[1,2)", + "originalRange": "[5,18)", + "modifiedRange": "[5,23)", "innerChanges": [ { - "originalRange": "[1,10 -> 1,21]", - "modifiedRange": "[1,10 -> 1,13]" + "originalRange": "[5,7 -> 5,12]", + "modifiedRange": "[5,7 -> 5,10]" + }, + { + "originalRange": "[5,15 -> 6,2]", + "modifiedRange": "[5,13 -> 8,1]" + }, + { + "originalRange": "[6,5 -> 6,18]", + "modifiedRange": "[8,4 -> 8,11]" + }, + { + "originalRange": "[6,22 -> 6,84]", + "modifiedRange": "[8,15 -> 9,2]" + }, + { + "originalRange": "[7,2 -> 7,6]", + "modifiedRange": "[10,2 -> 10,5]" + }, + { + "originalRange": "[7,19 -> 10,69]", + "modifiedRange": "[10,18 -> 13,28]" + }, + { + "originalRange": "[11,4 -> 11,47]", + "modifiedRange": "[14,4 -> 14,24]" + }, + { + "originalRange": "[12,3 -> 17,24]", + "modifiedRange": "[15,3 -> 22,7]" } ] }, { - "originalRange": "[7,14)", - "modifiedRange": "[7,18)", + "originalRange": "[23,30)", + "modifiedRange": "[28,39)", "innerChanges": [ { - "originalRange": "[7,5 -> 7,11]", - "modifiedRange": "[7,5 -> 8,5]" + "originalRange": "[23,5 -> 23,11]", + "modifiedRange": "[28,5 -> 29,5]" }, { - "originalRange": "[7,14 -> 7,43 EOL]", - "modifiedRange": "[8,8 -> 11,140 EOL]" + "originalRange": "[23,14 -> 23,43 EOL]", + "modifiedRange": "[29,8 -> 32,140 EOL]" }, { - "originalRange": "[8,5 -> 8,6]", - "modifiedRange": "[12,5 -> 12,25]" + "originalRange": "[24,5 -> 24,6]", + "modifiedRange": "[33,5 -> 33,25]" }, { - "originalRange": "[8,9 -> 9,12 EOL]", - "modifiedRange": "[12,28 -> 12,131 EOL]" + "originalRange": "[24,9 -> 25,12 EOL]", + "modifiedRange": "[33,28 -> 33,131 EOL]" }, { - "originalRange": "[10,7 -> 10,22]", - "modifiedRange": "[13,7 -> 13,17]" + "originalRange": "[26,7 -> 26,22]", + "modifiedRange": "[34,7 -> 34,17]" }, { - "originalRange": "[10,30 -> 10,48 EOL]", - "modifiedRange": "[13,25 -> 14,8 EOL]" + "originalRange": "[26,30 -> 26,48 EOL]", + "modifiedRange": "[34,25 -> 35,8 EOL]" }, { - "originalRange": "[11,6 -> 11,13 EOL]", - "modifiedRange": "[15,6 -> 15,7 EOL]" + "originalRange": "[27,6 -> 27,13 EOL]", + "modifiedRange": "[36,6 -> 36,7 EOL]" }, { - "originalRange": "[12,5 -> 12,17]", - "modifiedRange": "[16,5 -> 16,7]" + "originalRange": "[28,5 -> 28,17]", + "modifiedRange": "[37,5 -> 37,7]" }, { - "originalRange": "[13,6 -> 13,11]", - "modifiedRange": "[17,6 -> 17,9]" + "originalRange": "[29,6 -> 29,11]", + "modifiedRange": "[38,6 -> 38,9]" } ] } diff --git a/src/vs/editor/test/node/diffing/layout/layout.tsx b/src/vs/editor/test/node/diffing/layout/layout.tsx new file mode 100644 index 00000000000..6ead1975756 --- /dev/null +++ b/src/vs/editor/test/node/diffing/layout/layout.tsx @@ -0,0 +1,15 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as React from 'react'; + +export interface ViewProps { + title: string; + children?: React.ReactNode; +} + +export const View: React.FC = ({ title, children }) => ( +
{children}
+); diff --git a/src/vs/platform/acp/common/acpIpc.ts b/src/vs/platform/acp/common/acpIpc.ts new file mode 100644 index 00000000000..d43574f7aaa --- /dev/null +++ b/src/vs/platform/acp/common/acpIpc.ts @@ -0,0 +1,190 @@ + +import { Event } from '../../../base/common/event.js'; +import { IChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { + IAcpService, + IAcpStream, + IAcpUserMessage, + IAcpMessageChunk, + IAcpSendOptions, + IAcpChatMessage +} from './iAcpService.js'; + +export const AcpChannelName = 'acp'; +export type AcpRequestId = string; + + +export type AcpHostCallbackKind = + | 'requestPermission' + | 'createTerminal' + | 'terminalOutput' + | 'waitForTerminalExit' + | 'killTerminal' + | 'releaseTerminal' + | 'readTextFile' + | 'writeTextFile' + | 'extMethod'; + +export interface AcpHostCallbackRequest { + requestId: string; + kind: AcpHostCallbackKind; + + sessionId?: string; + threadId?: string; + + params?: any; +} + +export interface AcpHostCallbackResponse { + requestId: string; + result?: any; + error?: string; +} + + +export interface IAcpMainServiceForChannel { + + connect(opts?: IAcpSendOptions): Promise; + disconnect(): Promise; + isConnected(): boolean; + + + sendChatMessage(args: { + threadId: string; + history: IAcpChatMessage[]; + message: IAcpChatMessage; + opts?: IAcpSendOptions; + }): Promise; + + cancel(args: { requestId: AcpRequestId }): Promise; + + + onData(requestId: AcpRequestId): Event; + + + onHostCallback: Event; + hostCallbackResult(resp: AcpHostCallbackResponse): Promise; +} + + +function unwrapWindowHandshake(name: string, arg: any, extraArg?: any): { name: string; arg: any } { + const extractPayload = (container: any): any => { + if (!container || typeof container !== 'object') + return undefined; + if ('arg' in container) return (container as any).arg; + + if ('args' in container) { + const a = (container as any).args; + return Array.isArray(a) ? a[0] : a; + } + + if ('payload' in container) return (container as any).payload; + if ('data' in container) return (container as any).data; + + return undefined; + }; + + if (typeof name === 'string' && name.startsWith('window:')) { + if (arg && typeof arg === 'object') { + if (typeof (arg as any).event === 'string') { + return { name: String((arg as any).event), arg: extractPayload(arg) }; + } + if (typeof (arg as any).command === 'string') { + return { name: String((arg as any).command), arg: extractPayload(arg) }; + } + if (Array.isArray(arg) && arg.length >= 1) { + return { name: String(arg[0]), arg: arg.length > 1 ? arg[1] : undefined }; + } + } + + if (typeof arg === 'string') { + return { name: arg, arg: typeof extraArg === 'undefined' ? undefined : extraArg }; + } + + return { name: '', arg: undefined }; + } + return { name, arg }; +} + + +export class AcpChannel implements IChannel { + constructor(private readonly service: IAcpMainServiceForChannel) { } + + listen(event: string, arg?: any): Event { + const extra = (arguments as any)[2]; + const { name, arg: realArg } = unwrapWindowHandshake(event, arg, extra); + + switch (name) { + case 'onData': + return this.service.onData(realArg.requestId) as Event; + case 'onHostCallback': + return this.service.onHostCallback as Event; + } + throw new Error(`AcpChannel: unknown event ${name}`); + } + + + call(command: string, arg?: any): Promise { + const extra = (arguments as any)[2]; + const { name, arg: realArg } = unwrapWindowHandshake(command, arg, extra); + + switch (name) { + case 'connect': + return this.service.connect(realArg) as any; + case 'disconnect': + return this.service.disconnect() as any; + case 'isConnected': + return Promise.resolve(this.service.isConnected()) as any; + case 'sendChatMessage': + return this.service.sendChatMessage(realArg) as any; + case 'cancel': + return this.service.cancel(realArg) as any; + case 'hostCallbackResult': + return this.service.hostCallbackResult(realArg) as any; + } + return Promise.reject(new Error(`AcpChannel: unknown command ${name}`)); + } +} + +export class AcpChannelClient implements IAcpService { + declare readonly _serviceBrand: undefined; + + private _connected = false; + + constructor(private readonly channel: IChannel) { } + + isConnected(): boolean { + + return this._connected; + } + + async connect(opts?: IAcpSendOptions): Promise { + await this.channel.call('connect', opts); + this._connected = true; + } + + async disconnect(): Promise { + try { + await this.channel.call('disconnect'); + } finally { + this._connected = false; + } + } + + async sendChatMessage( + threadId: string, + history: IAcpChatMessage[], + message: IAcpUserMessage, + opts?: IAcpSendOptions + ): Promise { + + const requestId = await this.channel.call('sendChatMessage', { threadId, history, message, opts }); + const onData = this.channel.listen('onData', { requestId }); + + return { + onData, + cancel: () => { void this.channel.call('cancel', { requestId }); } + }; + } +} + diff --git a/src/vs/platform/acp/common/acpLogSanitizer.ts b/src/vs/platform/acp/common/acpLogSanitizer.ts new file mode 100644 index 00000000000..48dbc12181a --- /dev/null +++ b/src/vs/platform/acp/common/acpLogSanitizer.ts @@ -0,0 +1,15 @@ +export const SENSITIVE_KEY_RE = /(^|_)(key|token|secret|password|passwd|pwd|authorization|bearer|cookie|session)(_|$)/i; + +export function redactEnvForLog(env: any): any { + if (!env || typeof env !== 'object') return env; + const out: Record = {}; + for (const [k, v] of Object.entries(env)) { + out[k] = SENSITIVE_KEY_RE.test(k) ? '' : v; + } + return out; +} + +export function sanitizeAcpSendOptionsForLog(opts?: T): T | undefined { + if (!opts) return opts; + return { ...(opts as any), env: redactEnvForLog((opts as any).env) }; +} diff --git a/src/vs/platform/acp/common/iAcpService.ts b/src/vs/platform/acp/common/iAcpService.ts new file mode 100644 index 00000000000..0668b83eb93 --- /dev/null +++ b/src/vs/platform/acp/common/iAcpService.ts @@ -0,0 +1,90 @@ +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { Event } from '../../../base/common/event.js'; +import type { LLMTokenUsage } from '../../void/common/sendLLMMessageTypes.js'; + +export type IAcpMessageChunk = + | { type: 'text'; text: string } + | { type: 'reasoning'; reasoning: string } + | { type: 'plan'; plan: { title?: string; items: Array<{ id?: string; text: string; state?: string }> } } + | { + type: 'tool_call'; + toolCall: { + id: string; + name: string; + args: Record; + }; + } + | { + type: 'tool_result'; + toolResult: { + id: string; + name: string; + result: any; + error?: string; + }; + } + | { + type: 'tool_progress'; + toolProgress: { + id: string; + name: string; + terminalId?: string; + output: string; + truncated?: boolean; + exitStatus?: { exitCode: number | null; signal: string | null }; + }; + } + | { type: 'error'; error: string } + | { type: 'done'; tokenUsageSnapshot?: LLMTokenUsage }; + +export interface IAcpSendOptions { + mode?: 'builtin' | 'websocket' | 'process'; + // For websocket mode + agentUrl?: string; + + // For process mode + command?: string; + args?: string[]; + env?: Record; + + // Common + model?: string | null; + system?: string | null; + featureName?: 'Chat' | 'Ctrl+K'; + maxToolOutputLength?: number; + readFileChunkLines?: number; +} + +export interface IAcpUserMessage { + role: 'user'; + content: string; +} + +export interface IAcpAssistantMessage { + role: 'assistant'; + content: string; +} + +export type IAcpChatMessage = IAcpUserMessage | IAcpAssistantMessage; + +export interface IAcpStream { + onData: Event; + cancel(): void; +} + +export const IAcpService = createDecorator('acpService'); + +export interface IAcpService { + readonly _serviceBrand: undefined; + + isConnected(): boolean; + connect(opts?: IAcpSendOptions): Promise; + disconnect(): Promise; + + sendChatMessage( + threadId: string, + history: IAcpChatMessage[], + message: IAcpUserMessage, + opts?: IAcpSendOptions + ): Promise; +} diff --git a/src/vs/platform/acp/electron-main/acpBuiltinAgent.ts b/src/vs/platform/acp/electron-main/acpBuiltinAgent.ts new file mode 100644 index 00000000000..3ba80cd2be6 --- /dev/null +++ b/src/vs/platform/acp/electron-main/acpBuiltinAgent.ts @@ -0,0 +1,1882 @@ +import { WebSocketServer } from 'ws'; +import { + AgentSideConnection, + ndJsonStream, + PROTOCOL_VERSION, + type Agent, + type InitializeRequest, + type InitializeResponse, + type AuthenticateRequest, + type AuthenticateResponse, + type NewSessionRequest, + type NewSessionResponse, + type CancelNotification, + type PromptRequest, + type PromptResponse, +} from '@agentclientprotocol/sdk'; +import type { ILogService } from '../../log/common/log.js'; +import type { INotificationService } from '../../notification/common/notification.js'; +import type { IInstantiationService, ServicesAccessor } from '../../instantiation/common/instantiation.js'; +import { IVoidSettingsService } from '../../void/common/voidSettingsService.js'; +import { sendChatRouter as sendChatRouterOriginal } from '../../void/electron-main/llmMessage/sendLLMMessage.impl.js'; +import { ProviderName, SettingsOfProvider, ModelSelectionOptions, OverridesOfModel, ChatMode, defaultGlobalSettings } from '../../void/common/voidSettingsTypes.js'; +import { LLMChatMessage, type DynamicRequestConfig, type RequestParamsConfig, type ProviderRouting, type AdditionalToolInfo, LLMPlan, LLMTokenUsage } from '../../void/common/sendLLMMessageTypes.js'; +import { getModelApiConfiguration } from '../../void/common/modelInference.js'; +import { LLMLoopDetector, LOOP_DETECTED_MESSAGE } from '../../void/common/loopGuard.js'; +import { computeTruncatedToolOutput } from '../../void/common/toolOutputTruncation.js'; +import { stableToolOutputsRelPath } from '../../void/common/toolOutputFileNames.js'; + +type Stream = ConstructorParameters[1]; + +// Allow tests to override sendChatRouter while keeping the default implementation for runtime. +let sendChatRouterImpl = sendChatRouterOriginal; + +let started = false; + +function wsNdjsonStream(ws: any): Stream { + const readable = new ReadableStream({ + start(controller) { + ws.on('message', (data: any) => { + try { + if (typeof data === 'string') { + controller.enqueue(new TextEncoder().encode(data)); + } else if (data instanceof Buffer) { + controller.enqueue(new Uint8Array(data)); + } else if (data instanceof ArrayBuffer) { + controller.enqueue(new Uint8Array(data)); + + } else if (ArrayBuffer.isView(data)) { + const view = data as ArrayBufferView; + controller.enqueue(new Uint8Array(view.buffer, view.byteOffset, view.byteLength)); + } + } catch (e) { + controller.error(e); + } + }); + ws.on('close', () => controller.close()); + ws.on('error', (e: any) => controller.error(e)); + } + }); + const writable = new WritableStream({ + write(chunk) { ws.send(Buffer.from(chunk)); }, + close() { try { ws.close(); } catch { } }, + abort() { try { ws.close(); } catch { } } + }); + return ndJsonStream(writable, readable); +} + +export function startBuiltinAcpAgent(log?: ILogService, notificationService?: INotificationService, instantiationService?: IInstantiationService): void { + if (started) return; + started = true; + + const PORT = Number(process.env.VOID_ACP_AGENT_PORT || 8719); + const HOST = process.env.VOID_ACP_AGENT_HOST || '127.0.0.1'; + + let wss: WebSocketServer | null = null; + try { + wss = new WebSocketServer({ host: HOST, port: PORT }); + const HEARTBEAT_MS = 30_000; + const heartbeatTimer = setInterval(() => { + if (!wss) return; + for (const ws of wss.clients as any) { + if (ws.isAlive === false) { + try { ws.terminate(); } catch { /* noop */ } + continue; + } + ws.isAlive = false; + try { ws.ping(); } catch { /* noop */ } + } + }, HEARTBEAT_MS); + wss.on('close', () => clearInterval(heartbeatTimer)); + } catch (e) { + log?.warn?.('[ACP Agent] failed to start ws server', e); + started = false; + return; + } + + wss.on('connection', (ws) => { + (ws as any).isAlive = true; + ws.on('pong', () => { (ws as any).isAlive = true; }); + const stream = wsNdjsonStream(ws); + new AgentSideConnection((conn) => new VoidPipelineAcpAgent(conn, log, notificationService, instantiationService), stream); + log?.trace?.('[ACP Agent] client connected'); + }); + + wss.on('listening', () => log?.info?.(`[ACP Agent] listening on ws://${HOST}:${PORT}`)); + wss.on('error', (e) => log?.warn?.('[ACP Agent] error', e)); +} + +// ---- Local types to reduce any ---- + +type ToolCall = { + id: string; + name: string; + args?: Record; +}; + +type ToolCallUpdate = { + toolCallId: string; + status: 'pending' | 'in_progress' | 'completed' | 'failed'; + title: string; + kind?: string; + content?: string | Record; +}; + +type ProviderNameStr = string; +type SettingsOfProviderLike = unknown; +type ModelSelectionOptionsLike = unknown; +type OverridesOfModelLike = unknown; +type ChatModeLike = string | null; + +interface LoopGuardConfig { + maxTurnsPerPrompt?: number; + maxSameAssistantPrefix?: number; + maxSameToolCall?: number; +} + +interface GetLLMConfigResponse { + providerName: ProviderNameStr | null; + modelName: string | null; + settingsOfProvider: SettingsOfProviderLike; + modelSelectionOptions: ModelSelectionOptionsLike | null; + overridesOfModel: OverridesOfModelLike | null; + separateSystemMessage: string | null; + chatMode: ChatModeLike; + loopGuard?: LoopGuardConfig | null; + requestParams: RequestParamsConfig | null; + providerRouting?: ProviderRouting | null; + dynamicRequestConfig?: DynamicRequestConfig | null; + additionalTools?: AdditionalToolInfo[] | null; + disabledStaticTools?: string[] | null; + disabledDynamicTools?: string[] | null; +} + +interface ExecuteWithTextResponse { + ok: boolean; + result: unknown; + text: string; +} + +const ACP_PLAN_TOOL: AdditionalToolInfo = { + name: 'acp_plan', + description: 'Report/update the execution plan to the client UI via ACP. Use instead of printing a plan in text.', + params: { + entries: { + description: 'Complete list of plan entries (client replaces the plan on each update).', + type: 'array', + items: { + type: 'object', + description: 'Plan entry', + properties: { + content: { type: 'string', description: 'Human-readable task description' }, + priority: { type: 'string', enum: ['high', 'medium', 'low'] }, + status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'failed'] }, + }, + required: ['content', 'priority', 'status'], + }, + }, + }, +}; + +interface ToolCallLike { + id?: string; + name?: string; + rawParams?: Record; + isDone?: boolean; +} + +interface OnTextChunk { + fullText?: string; + fullReasoning?: string; + toolCall?: ToolCallLike; + plan?: LLMPlan; +} + +interface OnFinalMessagePayload { + fullText?: string; + fullReasoning?: string; + toolCall?: ToolCallLike; + plan?: LLMPlan; + tokenUsage?: LLMTokenUsage; +} + +type OAIFunctionCall = { id: string; name: string; args: Record }; + +type LLMMessage = { + role: 'system' | 'user' | 'assistant' | 'tool'; + content: string; + tool_call_id?: string; + tool_calls?: Array<{ + id: string; + type: 'function'; + function: { + name: string; + arguments: string; + }; + }>; +}; + +type SessionState = { + + cancelled?: boolean; + aborter?: (() => void) | null; + // Track the most recent tool call that is awaiting a tool result. + // This lets us handle UI "skip" that arrives as a separate user message + // without scanning message history. + pendingToolCall?: { id: string; name: string } | null; + messages: LLMMessage[]; + // Last LLM token usage snapshot for the most recent sendChatRouter turn in this session. + // Used to aggregate per‑prompt usage and send it back to the host via PromptResponse._meta. + llmTokenUsageLast?: LLMTokenUsage | undefined; + threadId?: string; + llmCfg: { + providerName: ProviderNameStr; + settingsOfProvider: SettingsOfProviderLike; + modelSelectionOptions?: ModelSelectionOptionsLike; + overridesOfModel?: OverridesOfModelLike; + modelName: string; + separateSystemMessage?: string | null; + chatMode: ChatModeLike; + requestParams?: RequestParamsConfig | null; + dynamicRequestConfig?: DynamicRequestConfig | null; + providerRouting?: ProviderRouting | null; + loopGuard?: LoopGuardConfig | null; + additionalTools?: AdditionalToolInfo[] | null; + disabledStaticTools?: string[] | null; + disabledDynamicTools?: string[] | null; + }; +}; + +type StreamDeltaState = { + totalLength: number; + prefix: string; +}; + +const STREAM_PREFIX_PROBE_LEN = 96; +const emptyStreamDeltaState = (): StreamDeltaState => ({ totalLength: 0, prefix: '' }); +const makePrefixProbe = (s: string): string => s.slice(0, STREAM_PREFIX_PROBE_LEN); + +const toDeltaChunk = ( + incomingRaw: unknown, + prev: StreamDeltaState +): { chunk: string; next: StreamDeltaState } => { + const incoming = typeof incomingRaw === 'string' ? incomingRaw : ''; + if (!incoming) return { chunk: '', next: prev }; + + if (prev.totalLength <= 0) { + return { + chunk: incoming, + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + const probeLen = Math.min(prev.prefix.length, incoming.length); + const prevProbe = probeLen > 0 ? prev.prefix.slice(0, probeLen) : ''; + const incomingProbe = probeLen > 0 ? incoming.slice(0, probeLen) : ''; + const hasSamePrefix = probeLen > 0 && prevProbe === incomingProbe; + + if (incoming.length > prev.totalLength && hasSamePrefix) { + return { + chunk: incoming.slice(prev.totalLength), + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + if (incoming.length === prev.totalLength && hasSamePrefix) { + return { + chunk: '', + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + if (incoming.length < prev.totalLength && hasSamePrefix) { + // Ignore regressive snapshots to keep stream monotonic for UI. + return { + chunk: '', + next: prev, + }; + } + + // Fallback: treat incoming as plain delta chunk. + return { + chunk: incoming, + next: { + totalLength: prev.totalLength + incoming.length, + prefix: prev.prefix || makePrefixProbe(incoming), + }, + }; +}; + +class VoidPipelineAcpAgent implements Agent { + private sessions = new Map(); + private _updateChainBySession = new Map>(); + private _textStreamStateBySession = new Map(); + private _reasoningStreamStateBySession = new Map(); + private _lastPlanSigBySession = new Map(); + + constructor( + private readonly conn: AgentSideConnection, + private readonly log?: ILogService, + private readonly notificationService?: INotificationService, + private readonly instantiationService?: IInstantiationService + ) { } + + private _getReadFileChunkLines(): number { + try { + const vss = this.instantiationService?.invokeFunction((a: ServicesAccessor) => a.get(IVoidSettingsService)); + const raw = (vss?.state as any)?.globalSettings?.readFileChunkLines; + const n = typeof raw === 'number' ? raw : (typeof raw === 'string' ? Number(raw) : NaN); + if (Number.isFinite(n) && n > 0) return n; + } catch { /* ignore */ } + return defaultGlobalSettings.readFileChunkLines; + } + + async initialize(_params: InitializeRequest): Promise { + return { + protocolVersion: PROTOCOL_VERSION, + agentCapabilities: { + loadSession: false, + promptCapabilities: { image: false, audio: false, embeddedContext: false } + }, + authMethods: [] + }; + } + + async authenticate(_params: AuthenticateRequest): Promise { + + return {}; + } + + async newSession(_params: NewSessionRequest): Promise { + const sessionId = 'sess_' + Math.random().toString(36).slice(2); + + const meta = (_params as any)._meta; + const threadIdFromMeta = + (typeof meta?.threadId === 'string' && meta.threadId.trim()) + ? String(meta.threadId).trim() + : undefined; + + // IMPORTANT: include routing hints so renderer window routing is correct even during newSession + const rawCfg = await this.conn.extMethod('void/settings/getLLMConfig', { + featureName: 'Chat', + sessionId, + ...(threadIdFromMeta ? { threadId: threadIdFromMeta } : {}) + }) as unknown; + + const cfg = rawCfg as GetLLMConfigResponse; + + + const providerName: string = + (typeof cfg?.providerName === 'string' && cfg.providerName) ? cfg.providerName : 'openAI'; + const modelName: string = + (typeof cfg?.modelName === 'string' && cfg.modelName) ? cfg.modelName : (process.env.VOID_DEFAULT_MODEL || 'gpt-4o-mini'); + + const messages: LLMMessage[] = []; + // Restore history if provided in _meta (from AcpMainService) + if (meta?.history && Array.isArray(meta.history)) { + for (const m of meta.history) { + // Filter only user/assistant messages to avoid clutter or duplicates + if ((m.role === 'user' || m.role === 'assistant') && typeof m.content === 'string') { + messages.push({ role: m.role, content: m.content }); + } + } + } + + this.sessions.set(sessionId, { + cancelled: false, + pendingToolCall: null, + messages, + threadId: threadIdFromMeta, + llmCfg: { + providerName, + settingsOfProvider: cfg?.settingsOfProvider, + modelSelectionOptions: cfg?.modelSelectionOptions ?? undefined, + overridesOfModel: cfg?.overridesOfModel ?? undefined, + modelName, + separateSystemMessage: (typeof cfg?.separateSystemMessage === 'string' || cfg?.separateSystemMessage === null) ? cfg.separateSystemMessage : null, + chatMode: cfg?.chatMode ?? null, + requestParams: cfg?.requestParams ?? null, + dynamicRequestConfig: cfg?.dynamicRequestConfig ?? null, + providerRouting: cfg?.providerRouting ?? null, + loopGuard: cfg?.loopGuard ?? null, + additionalTools: cfg?.additionalTools ?? null, + disabledStaticTools: Array.isArray(cfg?.disabledStaticTools) + ? cfg.disabledStaticTools.map(v => String(v ?? '').trim()).filter(Boolean) + : null, + disabledDynamicTools: Array.isArray(cfg?.disabledDynamicTools) + ? cfg.disabledDynamicTools.map(v => String(v ?? '').trim()).filter(Boolean) + : null, + } + }); + + return { sessionId }; + } + + async cancel(params: CancelNotification): Promise { + const sid = params?.sessionId; + const s = sid ? this.sessions.get(sid) : undefined; + + if (s) { + s.cancelled = true; + + try { s.aborter?.(); } catch { /* noop */ } + s.aborter = null; + + this.log?.debug?.('[ACP Agent][cancel] session cancelled', { + sessionId: sid, + threadId: s.threadId, + messagesInHistory: s.messages.length, + pendingToolCall: s.pendingToolCall, + }); + } else { + this.log?.debug?.('[ACP Agent][cancel] unknown session', { sessionId: sid }); + } + } + + + async prompt(params: PromptRequest): Promise { + const sid: string | undefined = params?.sessionId as any; + const state = sid ? this.sessions.get(sid) : undefined; + if (!sid || !state) throw new Error('No session'); + + // IMPORTANT: + + + state.cancelled = false; + + this.log?.debug?.('[ACP Agent][prompt] START', { + sessionId: sid, + threadId: state.threadId, + messageCount: state.messages.length, + provider: state.llmCfg.providerName, + model: state.llmCfg.modelName, + chatMode: state.llmCfg.chatMode, + }); + + // Aggregate token usage for this ACP prompt across all underlying LLM turns + // (including tool-induced follow-up calls). This is sent back via PromptResponse._meta + // and later forwarded to the renderer as IAcpMessageChunk.tokenUsageSnapshot. + const accumulateUsage = (a: LLMTokenUsage | undefined, b: LLMTokenUsage | undefined): LLMTokenUsage | undefined => { + if (!b) return a; + if (!a) return { ...b }; + return { + input: a.input + b.input, + cacheCreation: a.cacheCreation + b.cacheCreation, + cacheRead: a.cacheRead + b.cacheRead, + output: a.output + b.output, + }; + }; + + const rollbackDanglingToolCall = (toolCallId: string, assistantText?: string) => { + if (!toolCallId) return; + const last = state.messages[state.messages.length - 1] as any; + const toolCalls = last?.role === 'assistant' ? last?.tool_calls : undefined; + if (!Array.isArray(toolCalls)) return; + const hasThisId = toolCalls.some((tc: any) => String(tc?.id ?? '') === String(toolCallId)); + if (!hasThisId) return; + const existingText = typeof last.content === 'string' ? last.content : ''; + const t = (assistantText ?? existingText ?? '').trim(); + delete last.tool_calls; + if (t) last.content = t; + }; + + //const skipUiText = (toolName: string) => `Skip ${toolName}. Continue with next steps.`; + const skipModelText = (toolName: string) => + `Tool execution was skipped by the user.\n` + + `Skip ${toolName}. Continue with next steps.\n` + + `Do NOT call the same tool again in this prompt with the same arguments.\n` + + `If you require the output, ask the user to run it manually and paste the result.`; + + + let usageForThisPrompt: LLMTokenUsage | undefined = undefined; + + // refresh cfg + try { + // Update threadId from prompt meta (best-effort) + const metaWrapper = params as PromptRequest & { _meta?: any }; + const tidFromPrompt = + (typeof metaWrapper._meta?.threadId === 'string' && metaWrapper._meta.threadId.trim()) + ? String(metaWrapper._meta.threadId).trim() + : undefined; + if (tidFromPrompt) state.threadId = tidFromPrompt; + + const rawCfg = await this.conn.extMethod('void/settings/getLLMConfig', { + featureName: 'Chat', + sessionId: sid, + ...(state.threadId ? { threadId: state.threadId } : {}) + }) as unknown; + + const cfg = rawCfg as GetLLMConfigResponse; + + if (cfg && typeof cfg.providerName === 'string' && typeof cfg.modelName === 'string' + && cfg.providerName && cfg.modelName) { + const old = state.llmCfg; + state.llmCfg = { + providerName: cfg.providerName, + modelName: cfg.modelName, + settingsOfProvider: cfg.settingsOfProvider ?? old.settingsOfProvider, + modelSelectionOptions: cfg.modelSelectionOptions ?? old.modelSelectionOptions, + overridesOfModel: cfg.overridesOfModel ?? old.overridesOfModel, + separateSystemMessage: (typeof cfg.separateSystemMessage === 'string' || cfg.separateSystemMessage === null) + ? cfg.separateSystemMessage + : old.separateSystemMessage ?? null, + chatMode: cfg.chatMode ?? old.chatMode ?? null, + requestParams: cfg.requestParams ?? old.requestParams ?? null, + dynamicRequestConfig: cfg.dynamicRequestConfig ?? old.dynamicRequestConfig ?? null, + providerRouting: cfg.providerRouting ?? old.providerRouting ?? null, + loopGuard: cfg.loopGuard ?? old.loopGuard ?? null, + additionalTools: cfg.additionalTools ?? old.additionalTools ?? null, + disabledStaticTools: Array.isArray(cfg.disabledStaticTools) + ? cfg.disabledStaticTools.map(v => String(v ?? '').trim()).filter(Boolean) + : old.disabledStaticTools ?? null, + disabledDynamicTools: Array.isArray(cfg.disabledDynamicTools) + ? cfg.disabledDynamicTools.map(v => String(v ?? '').trim()).filter(Boolean) + : old.disabledDynamicTools ?? null, + }; + this.log?.debug?.(`[ACP Agent] refreshed llmCfg from settings`, JSON.stringify({ + oldProvider: old.providerName, + oldModel: old.modelName, + newProvider: state.llmCfg.providerName, + newModel: state.llmCfg.modelName, + })); + } + } catch (e) { + this.log?.warn?.('[ACP Agent] failed to refresh llmCfg from settings, keeping previous config', e); + } + + // Resolve maxToolOutputLength from global defaults for ACP truncation. + let maxToolOutputLength = defaultGlobalSettings.maxToolOutputLength; + const metaWrapper = params as PromptRequest & { _meta?: unknown }; + const meta = metaWrapper._meta; + if (meta && typeof meta === 'object') { + const maybeLen = (meta as { maxToolOutputLength?: unknown }).maxToolOutputLength; + if (typeof maybeLen === 'number' && maybeLen > 0) { + maxToolOutputLength = maybeLen; + } + } + + // Resolve readFileChunkLines (prefer prompt _meta; fallback to settings service; then defaults). + const parsePositiveInt = (v: unknown): number | undefined => { + const n = typeof v === 'number' ? v : (typeof v === 'string' ? Number(v) : NaN); + return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined; + }; + + const readFileChunkLinesFromMeta = (meta && typeof meta === 'object') + ? (() => { + const m = meta as { readFileChunkLines?: unknown; globalSettings?: { readFileChunkLines?: unknown } }; + return parsePositiveInt(m.readFileChunkLines ?? m.globalSettings?.readFileChunkLines); + })() + : undefined; + + let readFileChunkLines = readFileChunkLinesFromMeta ?? this._getReadFileChunkLines(); + if (!Number.isFinite(readFileChunkLines) || readFileChunkLines <= 0) { + readFileChunkLines = defaultGlobalSettings.readFileChunkLines; + } + + const lg = state.llmCfg.loopGuard; + const loopDetector = new LLMLoopDetector(lg ? { + maxTurnsPerPrompt: lg.maxTurnsPerPrompt, + maxSameAssistantPrefix: lg.maxSameAssistantPrefix, + maxSameToolCall: lg.maxSameToolCall, + } : undefined); + + const promptBlocks = params?.prompt as any[] | undefined; + const userText = extractTextFromPrompt(promptBlocks as any); + + if (!userText && !(promptBlocks && promptBlocks.length)) { + this.log?.debug?.('[ACP Agent][prompt] EMPTY PROMPT - returning early', { + sessionId: sid, + promptBlocksLength: promptBlocks?.length + }); + await this.emitText(sid, 'Empty prompt.'); + return { stopReason: 'end_turn' }; + } + + // If UI "Skip" comes as a separate user message "skip", + // convert it into a tool-result for the currently pending tool call, + // then continue normally (do not break the thread). + let consumedAsSkip = false; + const normalizedUserText = (userText ?? '').trim().toLowerCase(); + + if (normalizedUserText === 'skip' && state.pendingToolCall?.id) { + consumedAsSkip = true; + const { id: pendingId, name: pendingName } = state.pendingToolCall; + + // Mark tool call as finished in ACP UI (best effort) + try { + await this.conn.sessionUpdate({ + sessionId: sid, + update: { + sessionUpdate: 'tool_call_update', + toolCallId: pendingId, + status: 'completed', + title: pendingName || 'tool', + content: [{ type: 'content', content: { type: 'text', text: '' } }], + rawOutput: { _skipped: true } + } + } as any); + } catch (e) { + this.log?.warn?.('[ACP Agent] failed to mark tool_call as skipped', e); + } + + // Provide tool result to the model so the loop can continue + state.messages.push({ + role: 'tool', + tool_call_id: pendingId, + content: skipModelText(pendingName || 'tool') + }); + state.pendingToolCall = null; + } + + // Normal path: push user message into model history + if (!consumedAsSkip) { + const userMsg: any = { role: 'user', content: userText }; + if (Array.isArray(promptBlocks) && promptBlocks.length) { + userMsg.contentBlocks = promptBlocks; + } + state.messages.push(userMsg); + } + + const maxTurns = state.llmCfg.loopGuard?.maxTurnsPerPrompt; + let safeguard = Math.max(25, typeof maxTurns === 'number' ? maxTurns : 0); + this.log?.debug?.('[ACP Agent] safeguard', safeguard); + + let turnCount = 0; + while (safeguard-- > 0) { + turnCount++; + this.log?.debug?.('[ACP Agent][prompt] loop iteration', { + sessionId: sid, + turn: turnCount, + safeguardRemaining: safeguard, + messagesInHistory: state.messages.length, + cancelled: state.cancelled, + }); + + if (state.cancelled) { + this.log?.debug?.('[ACP Agent][prompt] CANCELLED - returning', { + sessionId: sid, + turn: turnCount, + }); + return { stopReason: 'cancelled' }; + } + + let toolCall: OAIFunctionCall | null = null; + let assistantText = ''; + + try { + this.log?.debug?.('[ACP Agent][prompt] calling runOneTurnWithSendLLM', { + sessionId: sid, + turn: turnCount, + messagesCount: state.messages.length, + }); + + const turn = await this.runOneTurnWithSendLLM(state, sid); + toolCall = turn.toolCall; + assistantText = turn.assistantText; + + this.log?.debug?.('[ACP Agent][prompt] runOneTurnWithSendLLM completed', { + sessionId: sid, + turn: turnCount, + hasToolCall: !!toolCall, + toolName: toolCall?.name, + assistantTextLength: assistantText?.length, + }); + + const loopAfterAssistant = loopDetector.registerAssistantTurn(assistantText); + if (loopAfterAssistant.isLoop) { + this.log?.debug?.('[ACP Agent][prompt] LOOP DETECTED after assistant turn', { + sessionId: sid, + turn: turnCount, + reason: loopAfterAssistant.reason, + }); + if (toolCall?.id) { + rollbackDanglingToolCall(String(toolCall.id), assistantText); + } + this.emitError(LOOP_DETECTED_MESSAGE); + } + + if (state.llmTokenUsageLast) { + usageForThisPrompt = accumulateUsage(usageForThisPrompt, state.llmTokenUsageLast); + state.llmTokenUsageLast = undefined; + } + } catch (e: any) { + // Preserve rich error info produced by emitError (e.data.details / e.details) + // so the renderer can show the real details. + this.log?.debug?.('[ACP Agent][prompt] runOneTurnWithSendLLM threw error', { + sessionId: sid, + turn: turnCount, + error: e instanceof Error ? e.message : String(e), + }); + if (e instanceof Error) { + throw e; + } + const msg = typeof e?.message === 'string' ? e.message : String(e); + throw new Error(msg); + } + + if (!toolCall) { + this.log?.debug?.('[ACP Agent][prompt] NO TOOL CALL - ending turn', { + sessionId: sid, + turn: turnCount, + stopReason: 'end_turn', + }); + const resp: any = { stopReason: 'end_turn' as const }; + if (usageForThisPrompt) resp._meta = { ...(resp._meta || {}), llmTokenUsage: usageForThisPrompt }; + return resp as PromptResponse; + } + + if (toolCall.name === 'acp_plan') { + const rawEntries = (toolCall.args as any)?.entries; + const entries = + Array.isArray(rawEntries) + ? rawEntries.map((e: any) => { + const content = String(e?.content ?? '').trim(); + const priority = + (e?.priority === 'high' || e?.priority === 'low' || e?.priority === 'medium') + ? e.priority + : 'medium'; + const status = + (e?.status === 'pending' || e?.status === 'in_progress' || e?.status === 'completed' || e?.status === 'failed') + ? e.status + : 'pending'; + return { content, priority, status }; + }).filter((e: any) => e.content.length > 0) + : []; + + if (entries.length) { + await this.conn.sessionUpdate({ + sessionId: sid, + update: { sessionUpdate: 'plan', entries } as any + } as any); + } + + state.messages.push({ + role: 'tool', + tool_call_id: String(toolCall.id || 'acp_plan'), + content: 'ok' + }); + continue; + } + + const loopAfterTool = loopDetector.registerToolCall(toolCall.name, toolCall.args); + if (loopAfterTool.isLoop) { + if (toolCall?.id) { + rollbackDanglingToolCall(String(toolCall.id), assistantText); + } + + this.emitError(LOOP_DETECTED_MESSAGE); + } + + this.log?.debug?.('[ACP Agent][prompt] tool_call detected', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + toolName: toolCall.name, + args: toolCall.args, + }); + + // Track pending tool call BEFORE awaiting any UI action + state.pendingToolCall = { id: String(toolCall.id), name: String(toolCall.name) }; + + // ACP tool_call + await this.conn.sessionUpdate({ + sessionId: sid, + update: { + sessionUpdate: 'tool_call', + toolCallId: toolCall.id, + title: toolCall.name, + kind: 'other', + status: 'pending', + rawInput: { name: toolCall.name, args: toolCall.args } + } + } as any); + + // Request permission + this.log?.debug?.('[ACP Agent][prompt] requesting permission', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + toolName: toolCall.name, + }); + + const perm = await this.conn.requestPermission({ + sessionId: sid, + toolCall: { + toolCallId: toolCall.id, + rawInput: { name: toolCall.name, args: toolCall.args ?? {} }, + title: toolCall.name + }, + options: [ + { optionId: 'allow_once', name: 'Allow once', kind: 'allow_once' }, + { optionId: 'reject_once', name: 'Reject', kind: 'reject_once' } + ] + } as any); + + if (state.cancelled) { + this.log?.debug?.('[ACP Agent][prompt] CANCELLED after permission request', { + sessionId: sid, + turn: turnCount, + }); + return { stopReason: 'cancelled' }; + } + + const outcome = (perm as any)?.outcome; + const selected = outcome?.outcome === 'selected'; + const optionId = selected ? String(outcome?.optionId ?? '') : ''; + const isAllow = optionId === 'allow_once' || optionId === 'allow_always'; + + this.log?.debug?.('[ACP Agent][prompt] permission result', { + sessionId: sid, + turn: turnCount, + optionId, + isAllow, + outcome: outcome, + }); + + if (!isAllow) { + // Treat non-allow as "skipped" (this is how ACP Skip is implemented via rejectLatestToolRequest) + const toolName = String(toolCall.name || 'tool'); + await this.conn.sessionUpdate({ + sessionId: sid, + update: { + sessionUpdate: 'tool_call_update', + toolCallId: toolCall.id, + status: 'completed', + title: toolName, + content: [{ type: 'content', content: { type: 'text', text: '' } }], + rawOutput: { _skipped: true } + } + } as any); + + state.messages.push({ + role: 'tool', + tool_call_id: String(toolCall.id), + content: skipModelText(toolName) + }); + state.pendingToolCall = null; + continue; + } + + // in_progress + await this.conn.sessionUpdate({ + sessionId: sid, + update: { + sessionUpdate: 'tool_call_update', + toolCallId: toolCall.id, + status: 'in_progress', + title: toolCall.name, + content: [{ type: 'content', content: { type: 'text', text: 'Running...' } }] + } + } as any); + + this.log?.debug?.('[ACP Agent][prompt] executing tool', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + toolName: toolCall.name, + }); + + // Execute tool on host + let textOut = ''; + let rawOut: any = undefined; + let status: 'completed' | 'failed' | 'pending' | 'in_progress' = 'completed'; + + try { + // Special handling for terminal commands with streaming + if (toolCall.name === 'run_command') { + this.log?.debug?.('[ACP Agent][prompt] executing terminal command', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + }); + const terminalResult = await this.executeTerminalCommandWithStreaming(toolCall); + + textOut = typeof terminalResult.content === 'string' + ? terminalResult.content + : JSON.stringify(terminalResult.content || ''); + + status = terminalResult.status || 'completed'; + + const terminalId = + typeof (terminalResult as any)?.terminalId === 'string' + ? (terminalResult as any).terminalId + : undefined; + + // IMPORTANT: + // Put final text into rawOut.output (not _output), so your truncation code + // can overwrite rawOut.output with the *truncated-from-start* textOut. + rawOut = { + _type: 'terminal', + _status: status, + ...(terminalId ? { terminalId } : {}), + output: textOut, + }; + } else { + const rawExec = await this.conn.extMethod('void/tools/execute_with_text', { + name: toolCall.name, + params: toolCall.args ?? {}, + // IMPORTANT: routing hints so extMethod is handled by the correct window (workspace) + sessionId: sid, + ...(state.threadId ? { threadId: state.threadId } : {}) + }) as unknown; + + const out = rawExec as ExecuteWithTextResponse; + const originalResult = (out as any)?.result; + + // normalize + rawOut = (() => { + if (originalResult === undefined || originalResult === null) return {}; + if (typeof originalResult === 'object') return originalResult; + if (typeof originalResult === 'string') { + try { return JSON.parse(originalResult); } catch { + return { _type: 'text', content: originalResult, _originalLength: originalResult.length }; + } + } + return { _type: typeof originalResult, value: originalResult }; + })(); + + textOut = typeof out?.text === 'string' + ? out.text + : (typeof originalResult === 'string' ? originalResult : JSON.stringify(rawOut)); + } + } catch (e: any) { + textOut = `Tool error: ${String(e?.message ?? e)}`; + status = 'failed'; + rawOut = { _error: true, _message: e?.message ?? String(e), _stack: e?.stack ? e.stack.substring(0, 500) : undefined }; + this.log?.debug?.('[ACP Agent][prompt] tool execution error', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + toolName: toolCall.name, + error: e?.message ?? String(e), + }); + } + + // Truncate tool output + const originalTextOut = textOut; + if (typeof textOut === 'string' && textOut.length > maxToolOutputLength) { + const originalLength = textOut.length; + const { truncatedBody, lineAfterTruncation } = computeTruncatedToolOutput(textOut, maxToolOutputLength); + const startLineExclusive = lineAfterTruncation > 0 ? lineAfterTruncation : 0; + + const headerLines = [ + `[VOID] TOOL OUTPUT TRUNCATED, SEE TRUNCATION_META BELOW.`, + `Only the first ${maxToolOutputLength} characters are included in this message.`, + `Display limit: maxToolOutputLength = ${maxToolOutputLength} characters.`, + ]; + + const args = toolCall.args ?? {}; + const isReadFileTool = String(toolCall.name) === 'read_file'; + + + const uriArg = (args as any).uri; + const filePathFromArgs = + typeof uriArg === 'string' ? uriArg.trim() : + (uriArg && typeof uriArg === 'object' && !Array.isArray(uriArg) && typeof (uriArg as any).fsPath === 'string') + ? String((uriArg as any).fsPath).trim() + : ''; + + const requestedStartLine = (() => { + const v = (args as any).startLine; + const n = Number(v); + return Number.isFinite(n) && n > 0 ? n : 1; + })(); + + let metaObj: any; + let instructionsLines: string[]; + + if (isReadFileTool && filePathFromArgs) { + + const nextStartLine = requestedStartLine + startLineExclusive; + const fileTotalLines = parsePositiveInt( + (rawOut && typeof rawOut === 'object') ? (rawOut as any).totalNumLines : undefined + ); + + const CHUNK = readFileChunkLines; + const suggestedEndLine = nextStartLine + CHUNK - 1; + + metaObj = { + tool: 'read_file', + uri: filePathFromArgs, + requestedStartLine, + nextStartLine, + suggested: { + startLine: nextStartLine, + endLine: suggestedEndLine, + chunkLines: CHUNK, + endLineIsFileEnd: false, + }, + ...(fileTotalLines !== undefined ? { fileTotalLines } : {}), + maxChars: maxToolOutputLength, + originalLength, + }; + + instructionsLines = [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. Continue by calling read_file on the ORIGINAL uri (NOT on a tool-output log):`, + ` read_file({ uri: ${JSON.stringify(filePathFromArgs)}, startLine: ${nextStartLine}, endLine: ${suggestedEndLine} })`, + ` 3. IMPORTANT: endLine above is a chunk boundary, NOT the end of file.`, + ` 4. Recommended next chunk size: readFileChunkLines = ${CHUNK}.`, + ...(fileTotalLines !== undefined + ? [` Known total file lines (from tool): ${fileTotalLines}.`] + : []), + ` 5. If still truncated, increase startLine by about ${CHUNK} and repeat.`, + ]; + } else { + const logFilePathForLLM = stableToolOutputsRelPath({ + toolName: toolCall.name, + toolCallId: toolCall.id, + fullText: originalTextOut + }); + + metaObj = { logFilePath: logFilePathForLLM, startLineExclusive, maxChars: maxToolOutputLength, originalLength }; + instructionsLines = [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. To see the rest of this tool output, call your file-reading tool (e.g. read_file)`, + ` on logFilePath, starting from line startLineExclusive + 1.`, + ]; + } + + const metaLine = `TRUNCATION_META: ${JSON.stringify(metaObj)}`; + textOut = `${truncatedBody}...\n\n${headerLines.join('\n')}\n${instructionsLines.join('\n')}\n${metaLine}`; + + const base = (rawOut && typeof rawOut === 'object') ? rawOut : {}; + + rawOut = { + ...base, + output: (typeof (base as any).output === 'string') ? textOut : (base as any).output, + content: (typeof (base as any).content === 'string') ? textOut : (base as any).content, + text: textOut, + ...(isReadFileTool ? {} : { fileContents: originalTextOut }), + _voidTruncationMeta: metaObj, + }; + } + + if (state.cancelled) return { stopReason: 'cancelled' }; + + await this.conn.sessionUpdate({ + sessionId: sid, + update: { + sessionUpdate: 'tool_call_update', + toolCallId: toolCall.id, + status, + title: toolCall.name, + content: [{ type: 'content', content: { type: 'text', text: textOut } }], + rawOutput: rawOut + } + } as any); + + this.log?.debug?.('[ACP Agent][prompt] tool execution completed', { + sessionId: sid, + turn: turnCount, + toolCallId: toolCall.id, + toolName: toolCall.name, + status, + outputLength: textOut.length, + }); + + // Append tool result into LLM history + state.messages.push({ + role: 'tool', + tool_call_id: String(toolCall.id), + content: textOut + }); + state.pendingToolCall = null; + + this.log?.debug?.('[ACP Agent][prompt] continuing loop after tool result', { + sessionId: sid, + turn: turnCount, + totalMessages: state.messages.length, + }); + } + + // safeguard exhausted + this.log?.debug?.('[ACP Agent][prompt] SAFEGUARD EXHAUSTED - stopping', { + sessionId: sid, + totalTurns: turnCount, + messagesInHistory: state.messages.length, + }); + const safeguardMsg = 'Reached ACP safeguard limit; stopping tool loop to avoid infinite run.'; + this.emitError(safeguardMsg); + } + + private async executeTerminalCommandWithStreaming(toolCall: ToolCall): Promise { + const argsObj = (toolCall.args ?? {}) as Record; + + const rawCommand = typeof argsObj.command === 'string' ? argsObj.command.trim() : ''; + if (!rawCommand) { + this.log?.debug?.('[ACP Agent][terminal] missing command', { + toolCallId: toolCall.id, + toolName: toolCall.name, + args: argsObj, + }); + throw new Error('Command is required for terminal execution'); + } + + this.log?.debug?.('[ACP Agent][terminal] starting', { + toolCallId: toolCall.id, + toolName: toolCall.name, + command: rawCommand, + argsCount: Object.keys(argsObj).length, + }); + + const rawArgs: string[] = Array.isArray(argsObj.args) ? argsObj.args.map((a: any) => String(a ?? '')) : []; + const rawCwd = typeof argsObj.cwd === 'string' ? argsObj.cwd.trim() : ''; + const env = (argsObj.env && typeof argsObj.env === 'object') ? argsObj.env : undefined; + + const getTitle = () => { + const t = argsObj.title; + return typeof t === 'string' && t.trim() ? t : `Running: ${rawCommand}`; + }; + + // Resolve ACP sessionId (best effort) + let sessionId = + Array.from(this.sessions.entries()) + .find(([, s]) => String(s?.pendingToolCall?.id ?? '') === String(toolCall.id))?.[0] + ?? Array.from(this.sessions.keys())[0]; + if (!sessionId) sessionId = 'unknown_session'; + + const terminalId = 'void_agent_' + Math.random().toString(36).slice(2) + Date.now().toString(36); + + const commandLine = + `$ ${rawCommand}${rawArgs.length ? ' ' + rawArgs.join(' ') : ''}` + + (rawCwd ? `\n(cwd=${rawCwd})` : '') + + `\n`; + + // Stream only tail while running (UI responsiveness). + const PROGRESS_TAIL_LIMIT = Math.max(4000, defaultGlobalSettings.maxToolOutputLength || 16000); + let lastSentTail = ''; + let progressSeq = 0; + + const logProgress = (tag: string, obj: any) => { + try { + this.log?.debug?.( + `[ACP Agent][terminal_stream][${tag}]`, + JSON.stringify({ + sessionId, + toolCallId: toolCall.id, + terminalId, + ...obj + }) + ); + } catch { /* noop */ } + }; + + const postProgressTail = async (fullDisplayOutput: string, meta?: { truncated?: boolean; exitStatus?: any }) => { + const tail = + typeof fullDisplayOutput === 'string' && fullDisplayOutput.length > PROGRESS_TAIL_LIMIT + ? fullDisplayOutput.slice(fullDisplayOutput.length - PROGRESS_TAIL_LIMIT) + : (fullDisplayOutput ?? ''); + + if (tail === lastSentTail) { + logProgress('skip_same_tail', { seq: progressSeq, tailLen: tail.length }); + return; + } + lastSentTail = tail; + progressSeq++; + + logProgress('send_tail', { + seq: progressSeq, + tailLen: tail.length, + meta: meta ? { hasExitStatus: !!meta.exitStatus, truncated: !!meta.truncated } : null, + tailPreview: tail.slice(0, 120) + }); + + await this._enqueue(sessionId, async () => { + await this.conn.sessionUpdate({ + sessionId, + update: { + sessionUpdate: 'tool_call_update', + toolCallId: toolCall.id, + status: 'in_progress', + title: getTitle(), + kind: 'execute', + content: [{ type: 'content', content: { type: 'text', text: tail } }], + rawOutput: { + _type: 'terminal', + _phase: 'progress', + terminalId, + output: tail, + text: tail, + ...(typeof meta?.truncated === 'boolean' ? { truncated: meta.truncated } : {}), + ...(meta?.exitStatus ? { exitStatus: meta.exitStatus } : {}), + _voidAcpDebug: { seq: progressSeq, ts: Date.now(), tailLen: tail.length } + } + } + } as any); + }); + }; + + const makeProgressText = (snapshotOutput: string): string => { + const out = typeof snapshotOutput === 'string' ? snapshotOutput : ''; + if (commandLine.length + out.length <= PROGRESS_TAIL_LIMIT) return commandLine + out; + + const room = Math.max(0, PROGRESS_TAIL_LIMIT - commandLine.length); + if (room > 0) return commandLine + out.slice(Math.max(0, out.length - room)); + + return out.slice(Math.max(0, out.length - PROGRESS_TAIL_LIMIT)); + }; + + const fetchOutput = async (opts?: { + full?: boolean; + }): Promise<{ + output: string; + truncated: boolean; + exitStatus?: { exitCode: number | null; signal: string | null }; + }> => { + const wantFull = !!opts?.full; + const res = await this.conn.extMethod('terminal/output', { sessionId, terminalId, full: wantFull }) as any; + + const output = + typeof res === 'string' + ? res + : (res && typeof res.output === 'string' ? res.output : ''); + + const truncated = !!(res && typeof res.truncated === 'boolean' ? res.truncated : false); + + const es = res?.exitStatus; + if (es) { + return { + output, + truncated, + exitStatus: { + exitCode: (typeof es.exitCode === 'number' || es.exitCode === null) ? es.exitCode : null, + signal: (typeof es.signal === 'string' || es.signal === null) ? es.signal : null, + } + }; + } + + return { output, truncated }; + }; + + const OUTPUT_BYTE_LIMIT = 16 * 1024 * 1024; // 16MB host buffer (terminal infra) + let exitStatus: { exitCode: number | null; signal: string | null } | undefined; + + logProgress('start', { + command: rawCommand, + argsCount: rawArgs.length, + cwd: rawCwd || null, + hasEnv: !!env + }); + + try { + const createParams: any = { + sessionId, + command: rawCommand, + type: 'ephemeral', + terminalId, + outputByteLimit: OUTPUT_BYTE_LIMIT, + }; + if (rawArgs.length) createParams.args = rawArgs; + if (env) createParams.env = env; + if (rawCwd) createParams.cwd = rawCwd; + + await this.conn.extMethod('terminal/create', createParams); + + // Make spoiler non-empty immediately + await postProgressTail(commandLine); + + // Poll terminal/output until it reports exitStatus + while (true) { + const s = this.sessions.get(sessionId); + if (s?.cancelled) { + this.log?.debug?.('[ACP Agent][terminal] cancelled', { + sessionId, + toolCallId: toolCall.id, + terminalId, + }); + logProgress('cancelled', {}); + + // Best effort: capture FULL output BEFORE killing (kill deletes the run state in renderer) + let outputSoFar = ''; + try { + const o = await fetchOutput({ full: true }); + outputSoFar = o.output ?? ''; + } catch { + try { + const o2 = await fetchOutput({ full: false }); + outputSoFar = o2.output ?? ''; + } catch { /* noop */ } + } + + try { await this.conn.extMethod('terminal/kill', { sessionId, terminalId }); } catch { /* noop */ } + try { await this.conn.extMethod('terminal/release', { sessionId, terminalId }); } catch { /* noop */ } + + return { + toolCallId: toolCall.id, + status: 'completed', + title: getTitle(), + kind: 'execute', + content: `${commandLine}${outputSoFar}(Cancelled)\n`, + terminalId + } as any; + } + + const out = await fetchOutput({ full: false }).catch((e) => { + logProgress('fetch_output_error', { message: String((e as any)?.message ?? e) }); + return ({ output: '', truncated: false } as any); + }); + + // Progress UI: only tail (bounded) + await postProgressTail( + makeProgressText(out.output), + { truncated: out.truncated, exitStatus: out.exitStatus } + ); + + if (out.exitStatus) { + exitStatus = out.exitStatus; + this.log?.debug?.('[ACP Agent][terminal] exit detected', { + sessionId, + toolCallId: toolCall.id, + terminalId, + exitStatus, + }); + logProgress('exit_detected', { exitStatus }); + break; + } + + await new Promise(r => setTimeout(r, 250)); + } + + // Final FULL read (single source of truth for "full output from start") + let fullOutput = ''; + try { + await new Promise(r => setTimeout(r, 100)); + const finFull = await fetchOutput({ full: true }); + fullOutput = finFull.output ?? ''; + if (finFull.exitStatus) exitStatus = finFull.exitStatus; + logProgress('final_full_read', { fullLen: fullOutput.length, exitStatus, fullTruncated: finFull.truncated }); + } catch (e: any) { + logProgress('final_full_read_error', { message: String(e?.message ?? e) }); + // Fallback: last tail + try { + const finTail = await fetchOutput({ full: false }); + fullOutput = finTail.output ?? ''; + } catch { /* noop */ } + } + + try { await this.conn.extMethod('terminal/release', { sessionId, terminalId }); } catch { /* noop */ } + + const suffix = exitStatus + ? `\n(exitCode=${exitStatus.exitCode ?? 0}${exitStatus.signal ? `, signal=${exitStatus.signal}` : ''})` + : ''; + + // IMPORTANT: finalText is FULL from start (commandLine + full output) + const finalText = `${commandLine}${fullOutput}${suffix}`; + logProgress('done', { finalLen: finalText.length }); + + this.log?.debug?.('[ACP Agent][terminal] completed', { + sessionId, + toolCallId: toolCall.id, + terminalId, + exitStatus, + finalLength: finalText.length, + }); + + return { + toolCallId: toolCall.id, + status: 'completed', + title: getTitle(), + kind: 'execute', + content: finalText, + terminalId + } as any; + } catch (e: any) { + try { await this.conn.extMethod('terminal/release', { sessionId, terminalId }); } catch { /* noop */ } + const msg = typeof e?.message === 'string' ? e.message : String(e); + logProgress('failed', { message: msg }); + + this.log?.debug?.('[ACP Agent][terminal] failed', { + sessionId, + toolCallId: toolCall.id, + terminalId, + error: msg, + }); + + return { + toolCallId: toolCall.id, + status: 'failed', + title: getTitle(), + kind: 'execute', + content: `Terminal tool infrastructure error: ${msg}`, + terminalId + } as any; + } + } + + private async runOneTurnWithSendLLM(state: SessionState, sid: string): Promise<{ toolCall: OAIFunctionCall | null; assistantText: string }> { + const { + providerName, + settingsOfProvider, + modelSelectionOptions, + overridesOfModel, + modelName, + separateSystemMessage, + chatMode, + requestParams, + } = state.llmCfg; + + // [{ type: 'text' }, { type: 'image_url', image_url: { url: 'data:...' } }]. + const toLLMChatMessages = (arr: any[], apiStyle: DynamicRequestConfig['apiStyle']): LLMChatMessage[] => { + return (arr || []).map((m: any) => { + if (m?.role === 'tool') { + const tool_call_id = String(m.tool_call_id ?? m.id ?? ''); + const content = typeof m.content === 'string' + ? m.content + : JSON.stringify(m.args ?? m.rawParams ?? m.content ?? {}); + return { role: 'tool', tool_call_id, content }; + } + if (m?.role === 'user' && Array.isArray(m.contentBlocks) && apiStyle === 'openai-compatible') { + const parts: any[] = []; + for (const b of m.contentBlocks) { + if (b && typeof b === 'object') { + if (b.type === 'text' && typeof b.text === 'string') { + parts.push({ type: 'text', text: b.text }); + } else if (b.type === 'image' && typeof b.data === 'string' && typeof b.mimeType === 'string') { + const url = `data:${b.mimeType};base64,${b.data}`; + parts.push({ type: 'image_url', image_url: { url } }); + } + } + } + if (parts.length) { + return { role: 'user', content: parts } as LLMChatMessage; + } + } + + return m as LLMChatMessage; + }); + }; + + const providerNameForSend = providerName as ProviderName; + const settingsForSend = settingsOfProvider as SettingsOfProvider; + const selOptsForSend = modelSelectionOptions as ModelSelectionOptions | undefined; + const overridesForSend = overridesOfModel as OverridesOfModel | undefined; + const chatModeForSend: ChatMode | null = (chatMode as unknown as ChatMode) ?? null; + const requestParamsForSend: RequestParamsConfig | undefined = (requestParams ?? undefined) as RequestParamsConfig | undefined; + const providerRoutingForSend: ProviderRouting | undefined = (state.llmCfg.providerRouting ?? undefined) as ProviderRouting | undefined; + const disabledStaticToolsForSend: string[] | undefined = Array.isArray(state.llmCfg.disabledStaticTools) + ? state.llmCfg.disabledStaticTools.map(v => String(v ?? '').trim()).filter(Boolean) + : undefined; + const disabledDynamicToolsForSend: string[] | undefined = Array.isArray(state.llmCfg.disabledDynamicTools) + ? state.llmCfg.disabledDynamicTools.map(v => String(v ?? '').trim()).filter(Boolean) + : undefined; + const disabledDynamicToolSet = new Set((disabledDynamicToolsForSend ?? []).map(name => String(name ?? '').trim()).filter(Boolean)); + + const baseAdditionalTools: AdditionalToolInfo[] = Array.isArray(state.llmCfg.additionalTools) + ? (state.llmCfg.additionalTools as AdditionalToolInfo[]) + : []; + const additionalToolsBeforeDisable: AdditionalToolInfo[] = + (chatModeForSend === 'agent') + ? [...baseAdditionalTools, ACP_PLAN_TOOL] + : baseAdditionalTools; + const additionalToolsForSend: AdditionalToolInfo[] = + disabledDynamicToolSet.size === 0 + ? additionalToolsBeforeDisable + : additionalToolsBeforeDisable.filter(tool => { + const name = String(tool?.name ?? '').trim(); + return !!name && !disabledDynamicToolSet.has(name); + }); + + this._textStreamStateBySession.set(sid, emptyStreamDeltaState()); + this._reasoningStreamStateBySession.set(sid, emptyStreamDeltaState()); + + return new Promise<{ toolCall: OAIFunctionCall | null; assistantText: string }>((resolve, reject) => { + state.aborter = null; + let finalTool: OAIFunctionCall | null = null; + let lastAssistantText = ''; + + const originalOnText = (chunk: OnTextChunk) => { + const fullText = typeof chunk?.fullText === 'string' ? chunk.fullText : ''; + const fullReasoning = typeof chunk?.fullReasoning === 'string' ? chunk.fullReasoning : ''; + const plan: LLMPlan | undefined = chunk.plan; + + this.log?.debug?.('[ACP Agent][runOneTurn] onText', { + sessionId: sid, + fullTextLength: fullText.length, + fullReasoningLength: fullReasoning.length, + hasPlan: !!plan, + }); + + // Optional: if sendChatRouter provides structured plan, forward it. + if (plan) { + this.emitPlan(sid, plan); + } + if (fullReasoning) { + this.emitThought(sid, fullReasoning); + } + this.emitText(sid, fullText); + }; + + const originalOnFinalMessage = async (res: OnFinalMessagePayload) => { + const fullText = typeof res?.fullText === 'string' ? res.fullText : ''; + const fullReasoning = typeof res?.fullReasoning === 'string' ? res.fullReasoning : ''; + const tool = res?.toolCall; + const plan: LLMPlan | undefined = res.plan; + const tokenUsage = res.tokenUsage; + + this.log?.debug?.('[ACP Agent][runOneTurn] onFinalMessage', { + sessionId: sid, + fullTextLength: fullText.length, + fullReasoningLength: fullReasoning.length, + hasToolCall: !!tool, + toolName: tool?.name, + hasPlan: !!plan, + hasTokenUsage: !!tokenUsage, + }); + + if (plan) this.emitPlan(sid, plan); + if (fullReasoning) this.emitThought(sid, fullReasoning); + this.emitText(sid, fullText); + + if (tokenUsage) { + state.llmTokenUsageLast = tokenUsage; + try { await this.emitTokenUsage(sid, tokenUsage); } catch (e) { + this.log?.warn?.('[ACP Agent] Failed to emit token usage snapshot', e); + } + } + + if (tool && typeof tool?.name === 'string' && tool.name.trim() !== '') { + try { + const id = String(tool.id || ''); + const name = String(tool.name || ''); + const args = + tool.isDone && tool.rawParams && typeof tool.rawParams === 'object' + ? (tool.rawParams as Record) + : {}; + + finalTool = { id, name, args }; + state.messages.push({ + role: 'assistant', + content: fullText || '', + tool_calls: [{ + id, + type: 'function', + function: { name, arguments: JSON.stringify(args) } + }] + }); + } catch { + finalTool = null; + if (fullText) state.messages.push({ role: 'assistant', content: fullText }); + } + } else if (fullText) { + state.messages.push({ role: 'assistant', content: fullText }); + } + state.aborter = null; + lastAssistantText = fullText; + await this._drainSessionUpdates(sid); + resolve({ toolCall: finalTool, assistantText: lastAssistantText }); + }; + + const originalOnError = (err: unknown) => { + state.aborter = null; + const message = + (typeof (err as any)?.message === 'string' && (err as any).message) + ? String((err as any).message) + : String(err); + this.log?.debug?.('[ACP Agent][runOneTurn] onError', { + sessionId: sid, + error: message, + hasStack: !!(err as any)?.stack, + }); + // Use emitError so we preserve details/stack for the host/UI. + try { + this.emitError(message, err); + } catch (e: any) { + reject(e); + } + }; + + // Compute dynamicRequestConfig for ACP. + + const thisConfig = (settingsOfProvider as SettingsOfProvider)[providerNameForSend] as any; + const apiKey = typeof thisConfig?.apiKey === 'string' ? thisConfig.apiKey.trim() : ''; + const isCustomProvider = !!thisConfig && thisConfig._didFillInProviderSettings === true; + + let dynamicRequestConfig: DynamicRequestConfig; + + try { + // Prefer dynamicRequestConfig precomputed in renderer (DynamicProviderRegistryService) + // so ACP uses the same endpoint/headers/capabilities as the main chat pipeline. + const precomputed = state.llmCfg.dynamicRequestConfig as DynamicRequestConfig | null | undefined; + if (precomputed) { + dynamicRequestConfig = precomputed; + this.log?.debug?.('[ACP Agent] dynamicRequestConfig (from settings) OK', { + providerName: providerNameForSend, + endpoint: dynamicRequestConfig.endpoint, + hasApiKey: !!(dynamicRequestConfig.headers?.Authorization || dynamicRequestConfig.headers?.authorization), + }); + } else if (isCustomProvider) { + const apiStyle = (thisConfig.apiStyle || 'openai-compatible') as DynamicRequestConfig['apiStyle']; + const supportsSystemMessage = (thisConfig.supportsSystemMessage + || (apiStyle === 'anthropic-style' || apiStyle === 'gemini-style' ? 'separated' : 'system-role')) as DynamicRequestConfig['supportsSystemMessage']; + const inferredToolFormat = apiStyle === 'anthropic-style' + ? 'anthropic-style' + : apiStyle === 'gemini-style' + ? 'gemini-style' + : 'openai-style'; + const specialToolFormat = (thisConfig.specialToolFormat || inferredToolFormat) as DynamicRequestConfig['specialToolFormat']; + const endpoint = (thisConfig.endpoint || '').toString().trim(); + + const headers: Record = { ...(thisConfig.additionalHeaders || {}) }; + if (apiKey) { + const authHeader = (thisConfig.auth?.header || 'Authorization') as string; + const authFormat = (thisConfig.auth?.format || 'Bearer') as 'Bearer' | 'direct'; + headers[authHeader] = authFormat === 'Bearer' ? `Bearer ${apiKey}` : apiKey; + } + + dynamicRequestConfig = { + apiStyle, + endpoint: endpoint || 'https://openrouter.ai/api/v1', + headers, + specialToolFormat, + supportsSystemMessage, + }; + this.log?.debug?.('[ACP Agent] dynamicRequestConfig (custom provider fallback) OK', { + providerName: providerNameForSend, + endpoint: dynamicRequestConfig.endpoint, + hasApiKey: !!apiKey, + }); + } else { + + const modelIdForConfig = modelName.includes('/') + ? modelName + : `${providerNameForSend}/${modelName}`; + const apiCfg = getModelApiConfiguration(modelIdForConfig); + const headers: Record = {}; + if (apiKey) { + const authHeader = thisConfig?.auth?.header || apiCfg.auth?.header || 'Authorization'; + const authFormat = (thisConfig?.auth?.format || apiCfg.auth?.format || 'Bearer') as 'Bearer' | 'direct'; + headers[authHeader] = authFormat === 'Bearer' ? `Bearer ${apiKey}` : apiKey; + } + dynamicRequestConfig = { + apiStyle: apiCfg.apiStyle, + endpoint: apiCfg.endpoint, + headers, + specialToolFormat: apiCfg.specialToolFormat, + supportsSystemMessage: apiCfg.supportsSystemMessage, + }; + this.log?.debug?.('[ACP Agent] dynamicRequestConfig (builtin fallback) OK', { + providerName: providerNameForSend, + endpoint: apiCfg.endpoint, + hasApiKey: !!apiKey, + }); + } + } catch (e) { + this.log?.warn?.('[ACP Agent] Failed dynamicRequestConfig, using safe defaults:', e); + dynamicRequestConfig = { + apiStyle: 'openai-compatible', + endpoint: '', + headers: {}, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'system-role', + }; + } + + const messagesForSend: LLMChatMessage[] = toLLMChatMessages(state.messages || [], dynamicRequestConfig.apiStyle); + + this.log?.debug?.('[ACP Agent][runOneTurn] calling sendChatRouter', { + sessionId: sid, + providerName: providerNameForSend, + modelName, + messagesCount: messagesForSend.length, + additionalToolsCount: additionalToolsForSend.length, + chatMode: chatModeForSend, + disabledStaticToolsCount: disabledStaticToolsForSend?.length ?? 0, + disabledDynamicToolsCount: disabledDynamicToolsForSend?.length ?? 0, + }); + + try { + const ret = void sendChatRouterImpl({ + logService: this.log, + messages: messagesForSend, + separateSystemMessage: separateSystemMessage ?? undefined, + providerName: providerNameForSend, + settingsOfProvider: settingsForSend, + modelSelectionOptions: selOptsForSend, + overridesOfModel: overridesForSend, + modelName, + dynamicRequestConfig, + _setAborter: (fn: any) => { + state.aborter = (typeof fn === 'function') ? fn : null; + }, + onText: originalOnText, + onFinalMessage: originalOnFinalMessage, + onError: originalOnError, + chatMode: chatModeForSend, + tool_choice: 'auto', + additionalTools: additionalToolsForSend, + disabledStaticTools: disabledStaticToolsForSend, + disabledDynamicTools: disabledDynamicToolsForSend, + requestParams: requestParamsForSend, + providerRouting: providerRoutingForSend, + notificationService: this.notificationService, + }); + + if (ret && typeof (ret as any).catch === 'function') { + (ret as Promise).catch(originalOnError); + } + } catch (e) { + originalOnError(e); + } + + }).finally(() => { + this._textStreamStateBySession.delete(sid); + this._reasoningStreamStateBySession.delete(sid); + }); + } + + private _enqueue(sessionId: string, op: () => Promise): Promise { + const prev = this._updateChainBySession.get(sessionId) ?? Promise.resolve(); + + + const next = prev + .then(op, op) + .catch((e) => { + this.log?.warn?.('[ACP Agent] sessionUpdate failed (swallowed)', e); + }); + this._updateChainBySession.set(sessionId, next); + return next; + } + + private _drainSessionUpdates(sessionId: string): Promise { + return this._updateChainBySession.get(sessionId) ?? Promise.resolve(); + } + + private emitPlan(sessionId: string, plan: LLMPlan) { + if (!plan?.items?.length) return Promise.resolve(); + const mapStateToAcp = (s: LLMPlan['items'][number]['state'] | undefined): 'pending' | 'in_progress' | 'completed' | 'failed' => { + switch (s) { + case 'running': return 'in_progress'; + case 'done': return 'completed'; + case 'error': return 'failed'; + case 'pending': + default: return 'pending'; + } + }; + + const cleaned = plan.items + .map(it => ({ + text: (typeof it.text === 'string' ? it.text.trim() : ''), + state: it.state ?? 'pending' + })) + .filter(it => it.text.length > 0); + + if (!cleaned.length) return Promise.resolve(); + + const sig = cleaned.map(it => `${it.state}::${it.text}`).join('\n'); + const prevSig = this._lastPlanSigBySession.get(sessionId); + if (prevSig === sig) { + this.log?.debug?.('[ACP Agent][emitPlan] skipped (unchanged)', { + sessionId, + itemsCount: cleaned.length, + }); + return Promise.resolve(); + } + + this._lastPlanSigBySession.set(sessionId, sig); + const entries = cleaned.map(it => ({ + content: it.text, + status: mapStateToAcp(it.state), + priority: 'medium' as const, + })); + + this.log?.debug?.('[ACP Agent][emitPlan] sending plan', { + sessionId, + itemsCount: entries.length, + entries: entries.map(e => ({ content: e.content.substring(0, 50), status: e.status })), + }); + + return this._enqueue(sessionId, async () => { + await this.conn.sessionUpdate({ + sessionId, + update: { sessionUpdate: 'plan', entries } as any + }); + }); + } + + private _formatErrorDetails(err?: unknown): string { + if (err instanceof Error) { + return (typeof err.stack === 'string' && err.stack) ? err.stack : err.message; + } + return err ? String(err) : ''; + } + + private emitError(message: string, err?: unknown): never { + const msg = (message ?? '').toString().trim(); + if (!msg) throw new Error('ACP Agent error'); + + this.log?.warn?.('[ACP Agent][prompt error]', msg, err); + + const details = this._formatErrorDetails(err) || msg; + const e: any = new Error(msg); + + e.data = { details }; + e.details = details; + + throw e; + } + + private emitText(sessionId: string, fullText: string) { + const prev = this._textStreamStateBySession.get(sessionId) ?? emptyStreamDeltaState(); + const { chunk, next } = toDeltaChunk(fullText, prev); + this._textStreamStateBySession.set(sessionId, next); + + if (!chunk) return Promise.resolve(); + + return this._enqueue(sessionId, async () => { + await this.conn.sessionUpdate({ + sessionId, + update: { sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: chunk } } + } as any); + }); + } + + private emitThought(sessionId: string, reasoning: string) { + const prev = this._reasoningStreamStateBySession.get(sessionId) ?? emptyStreamDeltaState(); + const { chunk, next } = toDeltaChunk(reasoning, prev); + this._reasoningStreamStateBySession.set(sessionId, next); + + if (!chunk) return Promise.resolve(); + + return this._enqueue(sessionId, async () => { + await this.conn.sessionUpdate({ + sessionId, + update: { + sessionUpdate: 'agent_thought_chunk', + content: { type: 'text', text: chunk } + } + } as any); + }); + } + + private async emitTokenUsage(_sessionId: string, _usage: LLMTokenUsage) { + // ACP schema on the client side does not accept sessionUpdate: 'llm_usage_snapshot' + // (Invalid params). Usage is still aggregated and returned via PromptResponse._meta, + // and then passed as IAcpMessageChunk.tokenUsageSnapshot on done. + return; + } +} + +function extractTextFromPrompt(prompt: Array<{ type: string; text?: string }> | undefined): string { + if (!Array.isArray(prompt)) return ''; + let out = ''; + for (const b of prompt) { + if (b && typeof b === 'object' && b.type === 'text' && typeof b.text === 'string') { + out += (out ? ' ' : '') + b.text; + } + } + return out.trim(); +} + +export const __test = { + setSendChatRouter(fn: typeof sendChatRouterOriginal) { + // Allow tests to stub the chat router while keeping runtime default intact. + sendChatRouterImpl = fn; + }, + reset() { + sendChatRouterImpl = sendChatRouterOriginal; + }, + VoidPipelineAcpAgent, +}; diff --git a/src/vs/platform/acp/electron-main/acpMainService.ts b/src/vs/platform/acp/electron-main/acpMainService.ts new file mode 100644 index 00000000000..d60f3e9b090 --- /dev/null +++ b/src/vs/platform/acp/electron-main/acpMainService.ts @@ -0,0 +1,1597 @@ + +import { Emitter, Event } from '../../../base/common/event.js'; +import { generateUuid } from '../../../base/common/uuid.js'; +import { IAcpMessageChunk, IAcpSendOptions, IAcpChatMessage } from '../common/iAcpService.js'; +import type { LLMTokenUsage } from '../../void/common/sendLLMMessageTypes.js'; +import { IAcpMainServiceForChannel, AcpHostCallbackRequest, AcpHostCallbackResponse } from '../common/acpIpc.js'; +import { ILogService } from '../../log/common/log.js'; +import { sanitizeAcpSendOptionsForLog } from '../common/acpLogSanitizer.js' +import { homedir } from 'os'; +import * as sdk from './vendor/acp-sdk.vendored.js'; +import { WebSocket } from './vendor/ws.vendored.js'; +import { spawn, ChildProcess } from 'child_process'; +import type { + SessionNotification, + ContentBlock, + ToolCallContent, + PlanEntry, + AvailableCommand, + SessionModeId +} from '@agentclientprotocol/sdk'; +import * as path from 'path'; + +type AcpSessionUpdateNotification = SessionNotification['update'] | { + sessionUpdate: string; + content?: ContentBlock | ToolCallContent[] | PlanEntry[] | AvailableCommand[] | SessionModeId; +}; + +type Stream = ConstructorParameters[1]; + +export class AcpMainService implements IAcpMainServiceForChannel { + + constructor( + @ILogService private readonly logService: ILogService + ) { } + + private conn: sdk.ClientSideConnection | null = null; + private connected = false; + private childProcess: ChildProcess | null = null; + + private lastConnectParams: { + mode?: 'builtin' | 'websocket' | 'process'; + url?: string; + command?: string; + args?: string[]; + env?: Record; + } | undefined; + + private readonly onDataEmitterByRequest = new Map>(); + + // thread<->session + private readonly threadBySession = new Map(); + private readonly sessionByThread = new Map(); + private readonly requestSessionByRequestId = new Map(); + private readonly sessionCwdBySession = new Map(); + + // callbacks to renderer + private readonly _onHostCallback = new Emitter(); + readonly onHostCallback = this._onHostCallback.event; + + // awaiting host callback result + private readonly pendingHostCallbacks = new Map void; reject: (e: any) => void }>(); + + // per-session streaming + private readonly emitterBySession = new Map>(); + private readonly tokenUsageBySession = new Map(); + + // tool normalization caches (UI-friendly, does NOT change ACP wire protocol) + private readonly toolNameBySessionToolCallId = new Map(); + private readonly toolKindBySessionToolCallId = new Map(); + private readonly toolArgsBySessionToolCallId = new Map>(); + + // terminal caches (UI-friendly) + private readonly activeToolCallIdBySession = new Map(); + private readonly lastTerminalIdBySession = new Map(); + private readonly terminalInfoById = new Map(); + private readonly terminalIdsBySessionToolCall = new Map(); + + private readonly terminalPollBySessionToolCall = new Map(); + + private _pollKey(sessionId: string, toolCallId: string): string { + return `${sessionId}:${toolCallId}`; + } + + private _stopTerminalPoll(sessionId: string, toolCallId: string): void { + const key = this._pollKey(sessionId, toolCallId); + const st = this.terminalPollBySessionToolCall.get(key); + if (!st) return; + try { clearInterval(st.timer); } catch { /* ignore */ } + this.terminalPollBySessionToolCall.delete(key); + } + + private _stopAllTerminalPollsForSession(sessionId: string): void { + for (const k of Array.from(this.terminalPollBySessionToolCall.keys())) { + if (k.startsWith(`${sessionId}:`)) { + const st = this.terminalPollBySessionToolCall.get(k); + if (st) { + try { clearInterval(st.timer); } catch { /* ignore */ } + this.terminalPollBySessionToolCall.delete(k); + } + } + } + } + + private _startTerminalPoll(sessionId: string, toolCallId: string, terminalId: string): void { + if (!sessionId || !toolCallId || !terminalId) return; + + // IMPORTANT: + // In builtin mode the builtin ACP agent already streams terminal output via tool_call_update. + // Polling terminalOutput here is redundant and can cause duplicate/empty progress updates that + // overwrite UI with "(waiting for output...)". + if (this.lastConnectParams?.mode === 'builtin') { + this._logJson('terminalPoll SKIP (builtin mode)', { sessionId, toolCallId, terminalId }); + return; + } + + const emitter = this.emitterBySession.get(sessionId); + if (!emitter) return; + + const key = this._pollKey(sessionId, toolCallId); + const existing = this.terminalPollBySessionToolCall.get(key); + + // already polling same terminal -> no-op + if (existing && existing.terminalId === terminalId) return; + + // terminal changed -> restart + if (existing) this._stopTerminalPoll(sessionId, toolCallId); + + this._logJson('terminalPoll START', { sessionId, toolCallId, terminalId }); + + const pollState = { + terminalId, + runningTick: false, + lastLen: -1, + lastTail: '', + timer: setInterval(async () => { + const st = this.terminalPollBySessionToolCall.get(key); + if (!st) return; + if (st.runningTick) return; + st.runningTick = true; + + try { + const resp = await this._hostRequest('terminalOutput', { sessionId, terminalId }); + + const output = typeof resp?.output === 'string' ? resp.output : ''; + const truncated = !!resp?.truncated; + const exitStatus = resp?.exitStatus; + + const isDone = + !!exitStatus && ( + typeof exitStatus.exitCode === 'number' || exitStatus.exitCode === null || + typeof exitStatus.signal === 'string' || exitStatus.signal === null + ); + + // CRITICAL: never emit empty output while still running. + // This prevents UI from being overwritten with empty content. + if (!isDone && output.length === 0) { + this._logJson('terminalPoll TICK skip empty', { sessionId, toolCallId, terminalId }); + return; + } + + const tail = output.slice(-256); + + // de-spam identical output + if (output.length === st.lastLen && tail === st.lastTail) { + if (isDone) { + this._logJson('terminalPoll STOP (done + no changes)', { sessionId, toolCallId, terminalId }); + this._stopTerminalPoll(sessionId, toolCallId); + } + return; + } + + st.lastLen = output.length; + st.lastTail = tail; + + this._logJson('terminalPoll EMIT tool_progress', { + sessionId, + toolCallId, + terminalId, + outputLen: output.length, + truncated, + isDone + }); + + emitter.fire({ + type: 'tool_progress', + toolProgress: { + id: toolCallId, + name: 'run_command', + terminalId, + output, + truncated, + ...(exitStatus ? { exitStatus } : {}) + } + }); + + if (isDone) { + this._logJson('terminalPoll STOP (done)', { sessionId, toolCallId, terminalId }); + this._stopTerminalPoll(sessionId, toolCallId); + } + } catch (e: any) { + // ignore transient errors; keep polling + this._logJson('terminalPoll TICK error (ignored)', { + sessionId, + toolCallId, + terminalId, + error: e?.message ?? String(e) + }); + } finally { + const st2 = this.terminalPollBySessionToolCall.get(key); + if (st2) st2.runningTick = false; + } + }, 350) + }; + + this.terminalPollBySessionToolCall.set(key, pollState); + } + + private _safeStringify(v: any, maxLen = 50_000): string { + try { + const s = JSON.stringify(v, null, 2); + return s.length > maxLen ? (s.slice(0, maxLen) + '\n…(truncated)') : s; + } catch { + try { + const s = String(v); + return s.length > maxLen ? (s.slice(0, maxLen) + '\n…(truncated)') : s; + } catch { + return ''; + } + } + } + + private _logJson(label: string, payload: any) { + this.logService.debug(`[ACP Main] ${label}: ${this._safeStringify(payload)}`); + } + + private _toSessionId(v: any): string | undefined { + if (v === undefined || v === null) return undefined; + const s = String(v); + return s.length ? s : undefined; + } + + private getDefaultCwd(): string { + this.logService.debug(`[ACP Main] getDefaultCwd: ${process.cwd()}`); + try { return process.cwd(); } catch { } + try { return homedir(); } catch { } + return '/'; + } + + private _hasActiveRequestForSession(sessionId: string): boolean { + for (const [, sid] of this.requestSessionByRequestId) { + if (sid === sessionId) return true; + } + return false; + } + + private _toolKey(sessionId: string, toolCallId: string): string { + return `${sessionId}:${toolCallId}`; + } + + private _buildCommandLine(command: unknown, args: unknown): string | undefined { + const cmd = typeof command === 'string' ? command.trim() : ''; + if (!cmd) return undefined; + const arr = Array.isArray(args) ? args.map(a => String(a ?? '')).filter(Boolean) : []; + return arr.length ? `${cmd} ${arr.join(' ')}` : cmd; + } + + private _asObject(v: any): Record | null { + return (v && typeof v === 'object' && !Array.isArray(v)) ? v as any : null; + } + + private _resolvePathForSession(sessionId: string, p: unknown): string { + const s = String(p ?? '').trim(); + if (!s) return ''; + + const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s) && !/^[A-Za-z]:[\\/]/.test(s); + if (hasScheme) return s; + + if (path.isAbsolute(s)) return s; + + const base = this.sessionCwdBySession.get(sessionId) || this.getDefaultCwd(); + return path.resolve(base, s); + } + + private _addTerminalIdForToolCall(sessionId: string, toolCallId: string, terminalId: string) { + if (!sessionId || !toolCallId || !terminalId) return; + const key = this._toolKey(sessionId, toolCallId); + const prev = this.terminalIdsBySessionToolCall.get(key) ?? []; + if (!prev.includes(terminalId)) { + this.terminalIdsBySessionToolCall.set(key, [...prev, terminalId]); + } + } + + private _getTerminalIdForToolCall(sessionId: string, toolCallId: string): string | undefined { + const key = this._toolKey(sessionId, toolCallId); + const arr = this.terminalIdsBySessionToolCall.get(key); + return arr && arr.length ? arr[arr.length - 1] : undefined; + } + + private _isValidDiffItem(item: any): item is { path: string; oldText?: string; newText: string } { + return !!item + && typeof item.path === 'string' + && item.path.trim().length > 0 + && typeof item.newText === 'string'; + } + + private _canonicalToolName(args: { + kind?: string; + hasDiff?: boolean; + hasTerminal?: boolean; + rawName?: string; + title?: string; + }): string { + const kind = (args.kind ?? '').toLowerCase(); + if (kind === 'edit' || args.hasDiff) return 'edit_file'; + if (kind === 'execute' || args.hasTerminal) return 'run_command'; + if (kind === 'read') return 'read_file'; + + const raw = String(args.rawName ?? '').trim(); + if (raw) return raw; + + const t = String(args.title ?? '').trim(); + if (t) return t; + + return 'tool'; + } + + private _hostRequest(kind: AcpHostCallbackRequest['kind'], params: any): Promise { + const requestId = generateUuid(); + + const promise = new Promise((resolve, reject) => { + this.pendingHostCallbacks.set(requestId, { resolve, reject }); + }); + + const sid = this._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? this.threadBySession.get(sid) : params?.threadId; + + this._onHostCallback.fire({ requestId, kind, params, sessionId: sid, threadId }); + return promise; + } + + private async _captureTerminalOutputIntoCache(sessionId: string | undefined, terminalId: string | undefined): Promise { + if (!sessionId || !terminalId) return; + + try { + this._logJson('capture terminal/output -> hostRequest', { sessionId, terminalId }); + + const resp = await this._hostRequest('terminalOutput', { sessionId, terminalId }); + + const nextOutput = typeof resp?.output === 'string' ? resp.output : ''; + const nextTruncated = !!resp?.truncated; + + const exitStatus = resp?.exitStatus; + const exitCode = (typeof exitStatus?.exitCode === 'number' || exitStatus?.exitCode === null) ? exitStatus.exitCode : undefined; + const signal = (typeof exitStatus?.signal === 'string' || exitStatus?.signal === null) ? exitStatus.signal : undefined; + + const prev = this.terminalInfoById.get(terminalId) ?? {}; + const prevOutput = typeof prev.output === 'string' ? prev.output : ''; + + // IMPORTANT: keep the longest output we have ever seen for this terminalId + const mergedOutput = nextOutput.length >= prevOutput.length ? nextOutput : prevOutput; + + this.terminalInfoById.set(terminalId, { + ...prev, + sessionId, + output: mergedOutput, + truncated: (typeof prev.truncated === 'boolean' ? prev.truncated : false) || nextTruncated, + ...(exitCode !== undefined ? { exitCode } : {}), + ...(signal !== undefined ? { signal } : {}) + }); + + this._logJson('capture terminal/output -> cached', { + sessionId, + terminalId, + outputLen: mergedOutput.length, + truncated: ((typeof prev.truncated === 'boolean' ? prev.truncated : false) || nextTruncated), + exitCode: exitCode ?? null, + signal: signal ?? null + }); + } catch (e: any) { + this._logJson('capture terminal/output FAILED', { + sessionId, + terminalId, + error: e?.message ?? String(e) + }); + } + } + + // ------------------------- + // public + // ------------------------- + isConnected(): boolean { return this.connected; } + + async connect(opts?: IAcpSendOptions): Promise { + this.logService.debug('[ACP Main] connect(opts):', sanitizeAcpSendOptionsForLog(opts)); + const mode = opts?.mode || 'builtin'; + + if (this.connected && this.conn && this.lastConnectParams && this.lastConnectParams.mode === mode) { + if (mode === 'websocket' || mode === 'builtin') { + const targetUrl = mode === 'builtin' ? 'ws://127.0.0.1:8719' : (opts?.agentUrl || ''); + const url = targetUrl.trim().replace(/^http(s?):/, 'ws$1:'); + if (this.lastConnectParams.url === url) return; + } else { + const cmd = (opts?.command || '').trim(); + const args = opts?.args || []; + const env = opts?.env; + const isSame = cmd === this.lastConnectParams.command && + JSON.stringify(args) === JSON.stringify(this.lastConnectParams.args) && + JSON.stringify(env) === JSON.stringify(this.lastConnectParams.env); + + if (isSame && this.childProcess && !this.childProcess.killed) return; + } + } + + if (this.connected) await this.disconnect(); + + let stream: Stream; + + if (mode === 'process') { + const cmd = (opts?.command || '').trim(); + if (!cmd) throw new Error('ACP: command is required for process mode'); + const args = opts?.args || []; + const env = { ...process.env, ...opts?.env }; + + this.logService.debug(` Spawning process: ${cmd} ${args.join(' ')}`); + + const cp = spawn(cmd, args, { env, stdio: ['pipe', 'pipe', 'pipe'] }); + this.childProcess = cp; + + const disconnectIfStillCurrent = () => { + if (this.childProcess !== cp) return; + this.disconnect().catch(() => { }); + }; + + cp.on('error', (err) => { + this.logService.error('[ACP Main] Process error:', err); + disconnectIfStillCurrent(); + }); + + cp.on('exit', (code, signal) => { + this.logService.debug(`[ACP Main] Process exited with code ${code} signal ${signal}`); + disconnectIfStillCurrent(); + }); + + if (!cp.stdin || !cp.stdout) { + throw new Error('Failed to open stdio pipes for ACP process'); + } + + cp.stderr?.on('data', (data) => { + const str = data.toString(); + this.logService.warn('[ACP Agent Stderr]:', str); + }); + + stream = this._stdioStream(cp.stdin, cp.stdout); + this.lastConnectParams = { mode: 'process', command: cmd, args, env: opts?.env }; + } else { + let urlFromOpts = ''; + if (mode === 'builtin') urlFromOpts = 'ws://127.0.0.1:8719'; + else { + urlFromOpts = (opts?.agentUrl || '').trim(); + if (!urlFromOpts) throw new Error('ACP: agentUrl is required'); + } + const wsUrl = urlFromOpts.replace(/^http(s?):/, 'ws$1:'); + + stream = await this._wsNdjsonStream(wsUrl); + this.lastConnectParams = { mode: mode as 'builtin' | 'websocket', url: wsUrl }; + } + + this.conn = new sdk.ClientSideConnection((_agent) => { + const service = this; + + const client: sdk.Client = { + + async requestPermission(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + return service._hostRequest('requestPermission', { ...params, sessionId: sid, threadId }); + }, + + async createTerminal(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + const acpMode = service.lastConnectParams?.mode ?? 'builtin'; + + if (sid && typeof params?.cwd === 'string' && params.cwd) { + service.sessionCwdBySession.set(sid, String(params.cwd)); + service._logJson('createTerminal updated session cwd', { sessionId: sid, cwd: String(params.cwd) }); + } + + service._logJson('client.createTerminal called', { + sessionId: sid, + threadId, + command: params?.command, + args: params?.args, + cwd: params?.cwd, + outputByteLimit: params?.outputByteLimit + }); + + const cmdLine = service._buildCommandLine(params?.command, params?.args); + + // ------------------------- + // DEDUP across retries: + // If agent retries tool with a new toolCallId, but is actually trying to run the same command, + // reuse the last running terminalId for this session. + // ------------------------- + if (sid && !((typeof params?.terminalId === 'string') && params.terminalId.trim().length > 0) && cmdLine) { + const lastTid = service.lastTerminalIdBySession.get(sid); + if (lastTid) { + const info = service.terminalInfoById.get(lastTid); + const isDone = !!info && (info.exitCode !== undefined || info.signal !== undefined); + if (!isDone && info?.command === cmdLine) { + const activeToolCallId = service.activeToolCallIdBySession.get(sid); + if (activeToolCallId) { + service._addTerminalIdForToolCall(sid, activeToolCallId, lastTid); + service._startTerminalPoll(sid, activeToolCallId, lastTid); + } + service._logJson('client.createTerminal DEDUP (session-level): reuse last running terminal', { + sessionId: sid, + terminalId: lastTid, + commandLine: cmdLine + }); + return { terminalId: lastTid }; + } + } + } + + // Existing per-toolCall dedup (fix isDone logic) + if (sid) { + const activeToolCallId = service.activeToolCallIdBySession.get(sid); + const explicitTerminalId = (typeof params?.terminalId === 'string' && params.terminalId.trim().length > 0); + + if (!explicitTerminalId && activeToolCallId) { + const existing = service._getTerminalIdForToolCall(sid, activeToolCallId); + if (existing) { + const info = service.terminalInfoById.get(existing); + const isDone = !!info && (info.exitCode !== undefined || info.signal !== undefined); + if (!isDone) { + service._logJson('client.createTerminal DEDUP: reuse existing terminal', { + sessionId: sid, + toolCallId: activeToolCallId, + terminalId: existing + }); + return { terminalId: existing }; + } + } + } + } + + const resp = await service._hostRequest('createTerminal', { ...params, sessionId: sid, threadId, acpMode }); + const terminalId = String(resp?.terminalId ?? ''); + + if (sid && terminalId) { + service.lastTerminalIdBySession.set(sid, terminalId); + + const argv = Array.isArray(params?.args) + ? params.args.map((a: any) => String(a ?? '')) + : undefined; + + const cwd = (typeof params?.cwd === 'string' && params.cwd.trim().length) + ? String(params.cwd) + : undefined; + + const prev = service.terminalInfoById.get(terminalId) ?? {}; + service.terminalInfoById.set(terminalId, { + ...prev, + sessionId: sid, + command: cmdLine ?? prev.command, + argv: argv ?? prev.argv, + cwd: cwd ?? prev.cwd + }); + + const activeToolCallId = service.activeToolCallIdBySession.get(sid); + if (activeToolCallId) { + service._addTerminalIdForToolCall(sid, activeToolCallId, terminalId); + + const k = service._toolKey(sid, activeToolCallId); + const prevName = service.toolNameBySessionToolCallId.get(k); + if (!prevName || prevName === 'tool') service.toolNameBySessionToolCallId.set(k, 'run_command'); + + const prevArgs = service.toolArgsBySessionToolCallId.get(k) ?? {}; + service.toolArgsBySessionToolCallId.set(k, { + ...prevArgs, + commandLine: (cmdLine ?? prevArgs.commandLine), + command: (typeof prevArgs.command === 'string' ? prevArgs.command : (cmdLine ?? prevArgs.command)), + args: (Array.isArray(prevArgs.args) ? prevArgs.args : (argv ?? prevArgs.args)), + cwd: (typeof prevArgs.cwd === 'string' ? prevArgs.cwd : (cwd ?? prevArgs.cwd)) + }); + + service._logJson('createTerminal bound to active toolCallId', { + sessionId: sid, + toolCallId: activeToolCallId, + terminalId + }); + service._startTerminalPoll(sid, activeToolCallId, terminalId); + } + } + + return { terminalId }; + }, + + // terminal/output (SPEC): returns { output, truncated, exitStatus? } + async terminalOutput(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + const acpMode = service.lastConnectParams?.mode ?? 'builtin'; + + const terminalId = String(params?.terminalId ?? ''); + const resp = await service._hostRequest('terminalOutput', { ...params, sessionId: sid, threadId, acpMode }); + + if (sid && terminalId) { + service.lastTerminalIdBySession.set(sid, terminalId); + const prev = service.terminalInfoById.get(terminalId) ?? {}; + service.terminalInfoById.set(terminalId, { + ...prev, + sessionId: sid, + output: typeof resp?.output === 'string' ? resp.output : prev.output, + truncated: typeof resp?.truncated === 'boolean' ? resp.truncated : prev.truncated, + exitCode: (typeof resp?.exitStatus?.exitCode === 'number' || resp?.exitStatus?.exitCode === null) ? resp.exitStatus.exitCode : prev.exitCode, + signal: (typeof resp?.exitStatus?.signal === 'string' || resp?.exitStatus?.signal === null) ? resp.exitStatus.signal : prev.signal, + }); + } + + return { + output: typeof resp?.output === 'string' ? resp.output : '', + truncated: !!resp?.truncated, + ...(resp?.exitStatus ? { exitStatus: resp.exitStatus } : {}) + }; + }, + + // terminal/wait_for_exit (SPEC): returns { exitCode, signal } + async waitForTerminalExit(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + const acpMode = service.lastConnectParams?.mode ?? 'builtin'; + const terminalId = String(params?.terminalId ?? ''); + + const resp = await service._hostRequest('waitForTerminalExit', { ...params, sessionId: sid, threadId, acpMode }); + + // capture output best-effort (does not create files now; renderer decides finalization) + await service._captureTerminalOutputIntoCache(sid, terminalId); + + if (sid && terminalId) { + service.lastTerminalIdBySession.set(sid, terminalId); + const prev = service.terminalInfoById.get(terminalId) ?? {}; + service.terminalInfoById.set(terminalId, { + ...prev, + sessionId: sid, + exitCode: (typeof resp?.exitCode === 'number' || resp?.exitCode === null) ? resp.exitCode : prev.exitCode, + signal: (typeof resp?.signal === 'string' || resp?.signal === null) ? resp.signal : prev.signal, + }); + } + + return { + exitCode: (typeof resp?.exitCode === 'number' || resp?.exitCode === null) ? resp.exitCode : null, + signal: (typeof resp?.signal === 'string' || resp?.signal === null) ? resp.signal : null, + // extras harmless + ...(typeof resp?.isRunning === 'boolean' ? { isRunning: resp.isRunning } : {}) + }; + }, + + async killTerminal(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + const acpMode = service.lastConnectParams?.mode ?? 'builtin'; + return service._hostRequest('killTerminal', { ...params, sessionId: sid, threadId, acpMode }); + }, + + async releaseTerminal(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const threadId = sid ? service.threadBySession.get(sid) : undefined; + const acpMode = service.lastConnectParams?.mode ?? 'builtin'; + const terminalId = String(params?.terminalId ?? ''); + + await service._captureTerminalOutputIntoCache(sid, terminalId); + + await service._hostRequest('releaseTerminal', { ...params, sessionId: sid, threadId, acpMode }); + return null; + }, + + async readTextFile(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const p = params?.path ?? params?.uri; + if (sid && typeof p === 'string' && path.isAbsolute(p)) { + service._maybeUpdateSessionCwdFromAbsolutePath(sid, p); + } + return service._hostRequest('readTextFile', params); + }, + + async writeTextFile(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id); + const p = params?.path ?? params?.uri; + if (sid && typeof p === 'string' && path.isAbsolute(p)) { + service._maybeUpdateSessionCwdFromAbsolutePath(sid, p); + } + return service._hostRequest('writeTextFile', params); + }, + + async extMethod(method: string, params: Record): Promise> { + // extMethod may be called during newSession BEFORE thread<->session mapping exists, + // so we must forward routing hints to renderer. + const pAny: any = params as any; + + const threadId = + (typeof pAny?.threadId === 'string' && pAny.threadId.trim()) + ? String(pAny.threadId).trim() + : undefined; + + const sessionId = + (typeof pAny?.sessionId === 'string' && pAny.sessionId.trim()) + ? String(pAny.sessionId).trim() + : undefined; + + return service._hostRequest('extMethod', { + method, + params, + ...(threadId ? { threadId } : {}), + ...(sessionId ? { sessionId } : {}), + }); + }, + + async sessionUpdate(params: any): Promise { + const sid = service._toSessionId(params?.sessionId ?? params?.session_id ?? params?.session?.id ?? params?.session?.sessionId); + if (!sid) return; + + const emitter = service.emitterBySession.get(sid); + if (!emitter) return; + + const update = (params && typeof params === 'object' && 'update' in params) ? (params as any).update : params; + + try { + service._emitChunksFromSessionUpdate(emitter, update, sid); + } catch (err: any) { + emitter.fire({ type: 'error', error: err?.message ?? String(err) }); + } + } + }; + + return client; + }, stream); + + this.connected = true; + + try { + await this.conn.initialize({ + protocolVersion: 1, + clientInfo: { name: 'Void', version: 'dev' }, + clientCapabilities: { + fs: { readTextFile: true, writeTextFile: true }, + terminal: true + } + } as any); + } catch (e) { + this.logService.error('ACP initialize failed', e); + try { await this.disconnect(); } catch { /* ignore */ } + throw e; + } + } + + async disconnect(): Promise { + this.conn = null; + this.connected = false; + this.lastConnectParams = undefined; + + if (this.childProcess) { + if (!this.childProcess.killed) { + try { this.childProcess.kill(); } catch { } + } + this.childProcess = null; + } + + for (const [, em] of this.emitterBySession) em.dispose(); + this.emitterBySession.clear(); + + for (const [, em] of this.onDataEmitterByRequest) em.dispose?.(); + this.onDataEmitterByRequest.clear(); + + for (const [, p] of this.pendingHostCallbacks) p.reject(new Error('Disconnected')); + this.pendingHostCallbacks.clear(); + + this.requestSessionByRequestId.clear(); + this.sessionByThread.clear(); + this.threadBySession.clear(); + this.sessionCwdBySession.clear(); + + this.tokenUsageBySession.clear(); + + this.toolNameBySessionToolCallId.clear(); + this.toolKindBySessionToolCallId.clear(); + this.toolArgsBySessionToolCallId.clear(); + + this.activeToolCallIdBySession.clear(); + this.lastTerminalIdBySession.clear(); + this.terminalInfoById.clear(); + this.terminalIdsBySessionToolCall.clear(); + for (const [, st] of this.terminalPollBySessionToolCall) { + try { clearInterval(st.timer); } catch { /* ignore */ } + } + this.terminalPollBySessionToolCall.clear(); + } + + async sendChatMessage(args: { threadId: string; history: IAcpChatMessage[]; message: IAcpChatMessage; opts?: IAcpSendOptions; }): Promise { + this.logService.debug('[ACP Main] sendChatMessage called with args:', JSON.stringify({ + threadId: args.threadId, + hasHistory: args.history.length > 0, + message: args.message, + opts: sanitizeAcpSendOptionsForLog(args.opts), + }, null, 2)); + + await this.connect(args.opts); + if (!this.conn) throw new Error('ACP not connected'); + + let sessionId = this.sessionByThread.get(args.threadId); + + if (!sessionId) { + const sessionParams: any = { + cwd: this.getDefaultCwd(), + mcpServers: [], + // IMPORTANT: threadId must be present so builtin agent can route extMethod during newSession. + _meta: { history: args.history, threadId: args.threadId } + }; + + if (args?.opts?.system) { + sessionParams.systemPrompt = args.opts.system; + } + + const resp = await this.conn.newSession(sessionParams as any); + + const sidRaw = (resp as any)?.sessionId ?? (resp as any)?.session_id; + const sid = this._toSessionId(sidRaw); + if (!sid) throw new Error('ACP: agent did not return sessionId'); + + sessionId = sid; + this.sessionByThread.set(args.threadId, sessionId); + this.threadBySession.set(sessionId, args.threadId); + + if (typeof sessionParams?.cwd === 'string' && sessionParams.cwd) { + this.sessionCwdBySession.set(sessionId, sessionParams.cwd); + this._logJson('newSession stored cwd', { sessionId, cwd: sessionParams.cwd }); + } + } + + let emitter = this.emitterBySession.get(sessionId); + if (!emitter) { + emitter = new Emitter(); + this.emitterBySession.set(sessionId, emitter); + } + + const requestId = generateUuid(); + this.onDataEmitterByRequest.set(requestId, emitter); + this.requestSessionByRequestId.set(requestId, sessionId); + + let prompt: any[]; + const msgAny: any = args.message as any; + if (Array.isArray(msgAny.contentBlocks) && msgAny.contentBlocks.length) { + prompt = msgAny.contentBlocks; + } else { + prompt = [{ type: 'text', text: args.message.content }]; + } + + if (args?.opts?.model && this.conn.setSessionModel) { + try { + await this.conn.setSessionModel({ sessionId, modelId: args.opts.model } as any); + } catch { + try { + await this.conn.setSessionModel({ sessionId, model: args.opts.model } as any); + } catch { /* ignore */ } + } + } + + const parsePositiveInt = (v: unknown): number | undefined => { + const n = typeof v === 'number' ? v : (typeof v === 'string' ? Number(v) : NaN); + return Number.isFinite(n) && n > 0 ? Math.floor(n) : undefined; + }; + + const promptMaxToolOutputLength = parsePositiveInt(args?.opts?.maxToolOutputLength); + const promptReadFileChunkLines = parsePositiveInt(args?.opts?.readFileChunkLines); + + // IMPORTANT: attach threadId and truncation knobs to prompt _meta for builtin ACP agent. + const promptMeta: Record = { threadId: args.threadId }; + if (promptMaxToolOutputLength !== undefined) promptMeta.maxToolOutputLength = promptMaxToolOutputLength; + if (promptReadFileChunkLines !== undefined) promptMeta.readFileChunkLines = promptReadFileChunkLines; + if (promptMaxToolOutputLength !== undefined || promptReadFileChunkLines !== undefined) { + promptMeta.globalSettings = { + ...(promptMaxToolOutputLength !== undefined ? { maxToolOutputLength: promptMaxToolOutputLength } : {}), + ...(promptReadFileChunkLines !== undefined ? { readFileChunkLines: promptReadFileChunkLines } : {}), + }; + } + + this.conn.prompt({ sessionId, prompt, _meta: promptMeta } as any) + .then((resp: any) => { + const usageFromMeta: LLMTokenUsage | undefined = resp?._meta?.llmTokenUsage; + const usageFromSession = this.tokenUsageBySession.get(sessionId); + const usage = usageFromMeta ?? usageFromSession; + this.tokenUsageBySession.delete(sessionId); + emitter?.fire({ type: 'done', ...(usage ? { tokenUsageSnapshot: usage } : {}) }); + }) + .catch((err: any) => { + const baseMsg = typeof err?.message === 'string' ? err.message : String(err); + const details = typeof err?.data?.details === 'string' ? err.data.details + : (typeof err?.details === 'string' ? err.details : ''); + const combined = details && !baseMsg.includes(details) + ? `${baseMsg}: ${details}` + : baseMsg; + + const usage = this.tokenUsageBySession.get(sessionId); + this.tokenUsageBySession.delete(sessionId); + + emitter?.fire({ type: 'error', error: combined }); + emitter?.fire({ type: 'done', ...(usage ? { tokenUsageSnapshot: usage } : {}) }); + }) + .finally(() => { + this.onDataEmitterByRequest.delete(requestId); + this.requestSessionByRequestId.delete(requestId); + }); + + return requestId; + } + + async cancel({ requestId }: { requestId: string }): Promise { + this.logService.debug('[ACP Main] cancel(requestId):', requestId); + + const sid = this._toSessionId(this.requestSessionByRequestId.get(requestId)); + + try { + if (sid) { + await this.conn?.cancel({ sessionId: sid } as any); + } + } catch { /* ignore */ } + + // This makes "Skip/Abort" actually stop long-running commands even if the agent doesn't. + if (sid) { + const terminalIdsToKill = new Set(); + + const activeToolCallId = this.activeToolCallIdBySession.get(sid); + if (activeToolCallId) { + const tid = this._getTerminalIdForToolCall(sid, activeToolCallId); + if (tid) terminalIdsToKill.add(tid); + } + + const lastTid = this.lastTerminalIdBySession.get(sid); + if (lastTid) terminalIdsToKill.add(lastTid); + + for (const terminalId of terminalIdsToKill) { + try { await this._hostRequest('killTerminal', { sessionId: sid, terminalId }); } catch { /* ignore */ } + try { await this._hostRequest('releaseTerminal', { sessionId: sid, terminalId }); } catch { /* ignore */ } + + // update cache for UI (best-effort) + const prev = this.terminalInfoById.get(terminalId) ?? {}; + this.terminalInfoById.set(terminalId, { + ...prev, + exitCode: (typeof prev.exitCode === 'number' || prev.exitCode === null) ? prev.exitCode : null, + signal: (typeof prev.signal === 'string' || prev.signal === null) ? prev.signal : 'SIGTERM' + }); + } + this._stopAllTerminalPollsForSession(sid); + } + + const em = this.onDataEmitterByRequest.get(requestId); + if (em) { + const usage = sid ? this.tokenUsageBySession.get(sid) : undefined; + if (sid) this.tokenUsageBySession.delete(sid); + em.fire({ type: 'done', ...(usage ? { tokenUsageSnapshot: usage } : {}) }); + } + + this.requestSessionByRequestId.delete(requestId); + this.onDataEmitterByRequest.delete(requestId); + + if (sid && !this._hasActiveRequestForSession(sid)) { + const threadId = this.threadBySession.get(sid); + this.emitterBySession.get(sid)?.dispose(); + this.emitterBySession.delete(sid); + if (threadId) { + this.sessionByThread.delete(threadId); + this.threadBySession.delete(sid); + } + } + } + + onData(requestId: string): Event { + let em = this.onDataEmitterByRequest.get(requestId); + if (!em) { + em = new Emitter(); + this.onDataEmitterByRequest.set(requestId, em); + } + return em.event; + } + + private async _wsNdjsonStream(url: string): Promise { + const ws = new WebSocket(url); + + await new Promise((resolve, reject) => { + ws.once('open', () => resolve()); + ws.once('error', (e) => reject(e)); + }); + + const readable = new ReadableStream({ + start(controller) { + ws.on('message', (data) => { + try { + if (typeof data === 'string') controller.enqueue(new TextEncoder().encode(data)); + else if (data instanceof Buffer) controller.enqueue(new Uint8Array(data)); + else if (data instanceof ArrayBuffer) controller.enqueue(new Uint8Array(data)); + else if (Array.isArray(data)) controller.enqueue(new Uint8Array(Buffer.concat(data))); + } catch (e) { + controller.error(e); + } + }); + ws.on('close', () => controller.close()); + ws.on('error', (e) => controller.error(e)); + } + }); + + const writable = new WritableStream({ + write(chunk) { ws.send(Buffer.from(chunk)); }, + close() { try { ws.close(); } catch { } }, + abort() { try { ws.close(); } catch { } }, + }); + + return sdk.ndJsonStream(writable, readable); + } + + private _stdioStream(stdin: import('stream').Writable, stdout: import('stream').Readable): Stream { + const readable = new ReadableStream({ + start(controller) { + stdout.on('data', (chunk: Buffer) => { + try { + controller.enqueue(new Uint8Array(chunk)); + } catch (e) { + controller.error(e); + } + }); + stdout.on('error', (err) => controller.error(err)); + stdout.on('end', () => controller.close()); + } + }); + + const writable = new WritableStream({ + write(chunk) { + if (!stdin.writable) return; + stdin.write(chunk); + }, + close() { try { stdin.end(); } catch { } }, + abort() { try { stdin.destroy(); } catch { } }, + }); + + return sdk.ndJsonStream(writable, readable); + } + + private _maybeUpdateSessionCwdFromAbsolutePath(sessionId: string, absPath: string) { + const p = String(absPath ?? ''); + if (!sessionId || !p || !path.isAbsolute(p)) return; + + // heuristic: infer workspace root by cutting before "/src/" or "/.void/" + const norm = p.replace(/\\/g, '/'); + let root: string | undefined; + + const idxSrc = norm.indexOf('/src/'); + if (idxSrc > 0) root = norm.slice(0, idxSrc); + + const idxVoid = norm.indexOf('/.void/'); + if (!root && idxVoid > 0) root = norm.slice(0, idxVoid); + + if (root && root.trim()) { + this.sessionCwdBySession.set(sessionId, root); + this._logJson('inferred session cwd from fs path', { sessionId, cwd: root, from: absPath }); + } + } + + private _emitChunksFromSessionUpdate(emitter: Emitter, notif: AcpSessionUpdateNotification, sessionId?: string) { + if (sessionId && !this._hasActiveRequestForSession(sessionId)) return; + + const u: any = notif as any; + if (!u) return; + + const emitText = (text?: string) => { + if (typeof text === 'string') emitter.fire({ type: 'text', text }); + }; + + if (u.sessionUpdate === 'agent_message_chunk' && typeof u.content === 'object' && u.content && 'type' in u.content) { + if ((u.content as any).type === 'text') emitText((u.content as any).text); + return; + } + + if (u.sessionUpdate === 'llm_usage_snapshot' && sessionId) { + const usage: LLMTokenUsage | undefined = (u as any).usage; + if (usage) this.tokenUsageBySession.set(sessionId, usage); + return; + } + + if (u.sessionUpdate === 'agent_thought_chunk') { + if (typeof u.content === 'object' && u.content && 'type' in u.content && (u.content as any).type === 'text') { + emitter.fire({ type: 'reasoning', reasoning: (u.content as any).text }); + } + return; + } + + if ((u.sessionUpdate === 'plan' || u.sessionUpdate === 'todo' || u.sessionUpdate === 'todos') + && Array.isArray((u as any).entries)) { + + const items = (u as any).entries.map((entry: PlanEntry, i: number) => { + const rawStatus = (entry as any)?.status; + const st = typeof rawStatus === 'string' ? rawStatus : ''; + + const state: 'pending' | 'running' | 'done' | 'error' = + st === 'in_progress' ? 'running' + : st === 'completed' ? 'done' + : st === 'failed' ? 'error' + : 'pending'; + + return { + id: String(i), + text: String((entry as any)?.content ?? ''), + state, + }; + }); + + emitter.fire({ type: 'plan', plan: { items } }); + return; + } + + // ------------------------- + // tool_call + // ------------------------- + if (u.sessionUpdate === 'tool_call') { + const sid = sessionId; + const toolCallId = String(u?.toolCallId ?? ''); + const kind = typeof u?.kind === 'string' ? String(u.kind) : undefined; + const title = typeof u?.title === 'string' ? String(u.title) : undefined; + + const contentAny = Array.isArray(u?.content) ? u.content : undefined; + const hasTerminal = !!contentAny?.some((it: any) => it?.type === 'terminal' && typeof it?.terminalId === 'string' && it.terminalId); + const hasDiff = !!contentAny?.some((it: any) => it?.type === 'diff' && typeof it?.path === 'string' && it.path && typeof it?.newText === 'string'); + + const rawInAny = u?.rawInput; + const rawInObj: Record | undefined = + (rawInAny && typeof rawInAny === 'object' && !Array.isArray(rawInAny)) ? rawInAny : undefined; + + const rawName = typeof (rawInObj as any)?.name === 'string' ? String((rawInObj as any).name) : undefined; + + const canonicalName = this._canonicalToolName({ kind, hasDiff, hasTerminal, rawName, title }); + + // args: prefer rawInput.args, fallback rawInput object + let argsCandidate: unknown = undefined; + if (rawInObj) argsCandidate = ('args' in rawInObj) ? (rawInObj as any).args : rawInObj; + const args: Record = + (argsCandidate && typeof argsCandidate === 'object' && !Array.isArray(argsCandidate)) ? { ...(argsCandidate as any) } : {}; + + // Normalize args for Void UI: + try { + if (sid && canonicalName === 'read_file') { + let p: string | undefined = + (typeof (rawInObj as any)?.path === 'string' ? String((rawInObj as any).path) : undefined) + ?? (typeof (rawInObj as any)?.uri === 'string' ? String((rawInObj as any).uri) : undefined); + + if (!p && title) { + const m = title.match(/read_file:\s*(.+)$/i); + if (m?.[1]) p = m[1].trim(); + } + + if (p) { + const abs = this._resolvePathForSession(sid, p); + args.uri = abs; + } + + const line = (rawInObj as any)?.line; + const limit = (rawInObj as any)?.limit; + if (typeof line === 'number' && Number.isFinite(line)) args.startLine = line; + if (typeof limit === 'number' && Number.isFinite(limit)) args.linesCount = limit; + } + + if (sid && canonicalName === 'edit_file') { + let diffPath: string | undefined; + + if (Array.isArray(contentAny)) { + for (const it of contentAny) { + if (it?.type === 'diff' && typeof it?.path === 'string' && it.path.trim()) { + diffPath = String(it.path).trim(); + break; + } + } + } + + if (!diffPath && typeof title === 'string' && title.trim()) { + const m = title.match(/Patching\s+(.+?)(?:\s*\(|\s*$)/i); + if (m?.[1]) diffPath = m[1].trim(); + } + + const p = + diffPath + ?? (typeof (rawInObj as any)?.path === 'string' ? String((rawInObj as any).path) : undefined) + ?? (typeof (rawInObj as any)?.uri === 'string' ? String((rawInObj as any).uri) : undefined); + + if (p) { + const abs = this._resolvePathForSession(sid, p); + args.uri = abs; + args.path = abs; + } + } + + if (canonicalName === 'run_command') { + try { + if (Array.isArray(contentAny)) { + for (const it of contentAny) { + if (it?.type === 'terminal' && typeof it?.terminalId === 'string' && it.terminalId) { + args.terminalId = String(it.terminalId); + break; + } + } + } + } catch { /* ignore */ } + + const cmd = (typeof (rawInObj as any)?.command === 'string') + ? String((rawInObj as any).command).trim() + : ''; + + const argv = Array.isArray((rawInObj as any)?.args) + ? (rawInObj as any).args.map((a: any) => String(a ?? '')).filter((s: string) => s.length > 0) + : []; + + const fromPieces = cmd ? (argv.length ? `${cmd} ${argv.join(' ')}` : cmd) : ''; + const fromTitle = (typeof title === 'string') ? title.trim() : ''; + const cmdLine = (fromPieces || fromTitle || '').trim(); + + if (cmdLine) { + args.command = cmdLine; + } + } + } catch (e: any) { + this._logJson('tool_call args normalization FAILED', { + sessionId: sid, + toolCallId, + canonicalName, + error: e?.message ?? String(e) + }); + } + + if (sid && toolCallId) { + const k = this._toolKey(sid, toolCallId); + this.toolNameBySessionToolCallId.set(k, canonicalName); + this.toolKindBySessionToolCallId.set(k, kind); + this.toolArgsBySessionToolCallId.set(k, args); + + this.activeToolCallIdBySession.set(sid, toolCallId); + + if (Array.isArray(contentAny)) { + for (const item of contentAny) { + if (item?.type === 'terminal' && typeof item?.terminalId === 'string' && item.terminalId) { + this._addTerminalIdForToolCall(sid, toolCallId, String(item.terminalId)); + } + } + } + } + + this._logJson('session/update tool_call', { + sessionId: sid, + toolCallId, + kind, + title, + canonicalName, + args, + contentSummary: Array.isArray(contentAny) ? contentAny.map((x: any) => ({ type: x?.type, terminalId: x?.terminalId, path: x?.path })) : undefined + }); + + emitter.fire({ type: 'tool_call', toolCall: { id: toolCallId, name: canonicalName, args } }); + return; + } + + // ------------------------- + // tool_call_update + // ------------------------- + if (u.sessionUpdate === 'tool_call_update') { + const sid = sessionId; + if (!sid) return; + + const status = u?.status ?? null; + const toolCallId = String(u?.toolCallId ?? ''); + const key = toolCallId ? this._toolKey(sid, toolCallId) : ''; + + const updKind = typeof u?.kind === 'string' ? String(u.kind) : undefined; + if (key && updKind) this.toolKindBySessionToolCallId.set(key, updKind); + + const kind = updKind ?? (key ? this.toolKindBySessionToolCallId.get(key) : undefined); + const cachedName = key ? this.toolNameBySessionToolCallId.get(key) : undefined; + + const contentAny = Array.isArray(u?.content) ? u.content : undefined; + + const derivedDiffs: Array<{ path: string; oldText?: string; newText: string }> = []; + const derivedTerminals: Array<{ terminalId: string }> = []; + const derivedTexts: string[] = []; + + if (Array.isArray(contentAny) && contentAny.length) { + for (const item of contentAny) { + if (item?.type === 'content' && item?.content?.type === 'text' && typeof item?.content?.text === 'string') { + derivedTexts.push(item.content.text); + } else if (item?.type === 'diff') { + const cand = { + path: item?.path, + oldText: (typeof item?.oldText === 'string') ? item.oldText : '', + newText: item?.newText + }; + if (this._isValidDiffItem(cand)) { + derivedDiffs.push({ path: String(cand.path), oldText: cand.oldText, newText: String(cand.newText) }); + } + } else if (item?.type === 'terminal') { + if (typeof item?.terminalId === 'string' && item.terminalId) { + derivedTerminals.push({ terminalId: String(item.terminalId) }); + } + } + } + } + + if (toolCallId && derivedTerminals.length) { + for (const t of derivedTerminals) this._addTerminalIdForToolCall(sid, toolCallId, t.terminalId); + } + + const hasTerminal = !!(derivedTerminals.length || (toolCallId && this._getTerminalIdForToolCall(sid, toolCallId))); + const hasDiff = derivedDiffs.length > 0; + + // rawOutput from agent + let result: any = u?.rawOutput; + if (typeof result === 'string') { + const t = result.trim(); + if ((t.startsWith('{') && t.endsWith('}')) || (t.startsWith('[') && t.endsWith(']'))) { + try { result = JSON.parse(t); } catch { result = { rawOutput: result }; } + } else { + result = { rawOutput: result }; + } + } else if (result !== undefined && result !== null) { + if (typeof result !== 'object' || Array.isArray(result)) result = { value: result }; + } + if (result === undefined || result === null) result = {}; + const rObj = this._asObject(result) ?? {}; + + const canonicalName = this._canonicalToolName({ + kind, + hasDiff, + hasTerminal, + rawName: cachedName, + title: typeof u?.title === 'string' ? String(u.title) : undefined + }); + + if (key) this.toolNameBySessionToolCallId.set(key, canonicalName); + + // --- NEW: emit tool_progress for in-flight tool_call_update --- + if (status !== 'completed' && status !== 'failed') { + const acpMode = this.lastConnectParams?.mode ?? 'builtin'; + const shouldEmitProgress = !(canonicalName === 'run_command' && acpMode !== 'builtin'); + const terminalIdForProgress = + (derivedTerminals.length ? String(derivedTerminals[0].terminalId) : '') + || (typeof (rObj as any).terminalId === 'string' ? String((rObj as any).terminalId) : '') + || (toolCallId ? (this._getTerminalIdForToolCall(sid, toolCallId) ?? '') : ''); + + const rawOutStr = + (typeof (rObj as any).output === 'string' && (rObj as any).output.length > 0) ? String((rObj as any).output) + : (typeof (rObj as any).text === 'string' && (rObj as any).text.length > 0) ? String((rObj as any).text) + : (typeof (rObj as any).content === 'string' && (rObj as any).content.length > 0) ? String((rObj as any).content) + : ''; + + const textFromContent = derivedTexts.length ? derivedTexts.join('\n') : ''; + const progressText = (rawOutStr || textFromContent || '').toString(); + + const truncated = + (typeof (rObj as any).truncated === 'boolean') ? (rObj as any).truncated : undefined; + + const exitStatus = (rObj as any)?.exitStatus; + + if (!shouldEmitProgress) { + this._logJson('SKIP tool_progress (run_command via terminal poll)', { + sessionId: sid, + toolCallId, + canonicalName, + status, + acpMode, + terminalId: terminalIdForProgress || null, + derivedTextLen: textFromContent.length, + rawOutLen: rawOutStr.length, + rObjKeys: Object.keys(rObj) + }); + } else if (toolCallId && progressText) { + this._logJson('EMIT tool_progress (from tool_call_update)', { + sessionId: sid, + toolCallId, + canonicalName, + status, + terminalId: terminalIdForProgress || null, + progressLen: progressText.length, + hasDerivedTexts: derivedTexts.length, + rObjKeys: Object.keys(rObj), + hasExitStatus: !!exitStatus, + truncated: truncated ?? null, + preview: progressText.slice(0, 160) + }); + + emitter.fire({ + type: 'tool_progress', + toolProgress: { + id: toolCallId, + name: canonicalName, + ...(terminalIdForProgress ? { terminalId: terminalIdForProgress } : {}), + output: progressText, + ...(typeof truncated === 'boolean' ? { truncated } : {}), + ...(exitStatus ? { exitStatus } : {}) + } as any + }); + } else { + this._logJson('SKIP tool_progress (empty)', { + sessionId: sid, + toolCallId, + canonicalName, + status, + terminalId: terminalIdForProgress || null, + derivedTextLen: textFromContent.length, + rawOutLen: rawOutStr.length, + rObjKeys: Object.keys(rObj) + }); + } + } + + this._logJson('session/update tool_call_update', { + sessionId: sid, + toolCallId, + status, + kind, + cachedName, + canonicalName, + derivedSummary: { textLen: derivedTexts.join('\n').length, diffs: derivedDiffs.length, terminals: derivedTerminals.length }, + resultKeys: Object.keys(rObj), + contentSummary: Array.isArray(contentAny) ? contentAny.map((x: any) => ({ type: x?.type, terminalId: x?.terminalId, path: x?.path })) : undefined + }); + + if (sid && toolCallId) { + const terminalIdForPoll = + (derivedTerminals.length ? String(derivedTerminals[0].terminalId) : '') + || (typeof (rObj as any).terminalId === 'string' ? String((rObj as any).terminalId) : '') + || (this._getTerminalIdForToolCall(sid, toolCallId) ?? ''); + + // NOTE: this poll is for "SPEC terminal/* callbacks". + // Builtin agent uses extMethod('terminal/output') and does NOT need this poll, + // but keep it for websocket/process agents. + if (terminalIdForPoll && canonicalName === 'run_command' && status !== 'completed' && status !== 'failed') { + this._startTerminalPoll(sid, toolCallId, terminalIdForPoll); + } + } + + if (status === 'completed' || status === 'failed') { + if (sid && toolCallId) { + this._stopTerminalPoll(sid, toolCallId); + } + const errorText = status === 'failed' ? 'Tool failed' : undefined; + + // clear active tool call + { + const active = this.activeToolCallIdBySession.get(sid); + if (active && active === toolCallId) this.activeToolCallIdBySession.delete(sid); + } + + if (canonicalName === 'edit_file') { + const safeDiffs = derivedDiffs.filter(d => this._isValidDiffItem(d)); + emitter.fire({ + type: 'tool_result', + toolResult: { + id: toolCallId, + name: 'edit_file', + result: { ...rObj, diffs: safeDiffs }, + error: errorText + } + }); + return; + } + + if (canonicalName === 'read_file') { + const contentStr = + (typeof (rObj as any).content === 'string') ? String((rObj as any).content) + : (typeof (rObj as any).text === 'string' ? String((rObj as any).text) : ''); + + emitter.fire({ + type: 'tool_result', + toolResult: { + id: toolCallId, + name: 'read_file', + result: { ...rObj, content: String(contentStr ?? '') }, + error: errorText + } + }); + return; + } + + // run_command + if (canonicalName === 'run_command') { + const terminalId = + (derivedTerminals.length ? String(derivedTerminals[0].terminalId) : '') + || (typeof (rObj as any).terminalId === 'string' ? String((rObj as any).terminalId) : '') + || (toolCallId ? (this._getTerminalIdForToolCall(sid, toolCallId) ?? '') : ''); + + const emitRunCommandResult = () => { + const info = terminalId ? this.terminalInfoById.get(terminalId) : undefined; + const cachedOutput = (typeof info?.output === 'string') ? info.output : ''; + const fromAgentOutput = (typeof (rObj as any).output === 'string') ? String((rObj as any).output) : ''; + const output = cachedOutput.length >= fromAgentOutput.length ? cachedOutput : fromAgentOutput; + + const outResult: any = { + ...rObj, + toolCallId, + terminalId: terminalId || undefined, + output: String(output ?? '') + }; + + if (typeof info?.truncated === 'boolean') outResult.truncated = info.truncated; + if (typeof info?.command === 'string') outResult.command = info.command; + if (typeof info?.exitCode === 'number' || info?.exitCode === null) outResult.exitCode = info.exitCode; + if (typeof info?.signal === 'string' || info?.signal === null) outResult.signal = info.signal; + + emitter.fire({ + type: 'tool_result', + toolResult: { + id: toolCallId, + name: 'run_command', + result: outResult, + error: errorText + } + }); + }; + + if (terminalId) { + this._captureTerminalOutputIntoCache(sid, terminalId).then( + () => emitRunCommandResult(), + () => emitRunCommandResult() + ); + return; + } + + emitRunCommandResult(); + return; + } + + emitter.fire({ + type: 'tool_result', + toolResult: { + id: toolCallId, + name: canonicalName, + result: rObj ?? {}, + error: errorText + } + }); + return; + } + + return; + } + + if (u.sessionUpdate === 'available_commands_update' && Array.isArray((u as any).availableCommands)) { + const items = (u as any).availableCommands.map((cmd: AvailableCommand, i: number) => ({ + id: String(i), + text: `${cmd?.name ?? ''}${cmd?.description ? ` — ${cmd.description}` : ''}`.trim(), + state: 'pending' as const + })); + if (items.length) emitter.fire({ type: 'plan', plan: { items } }); + return; + } + + if (u.sessionUpdate === 'current_mode_update' && typeof (u as any).currentModeId === 'string') { + emitText(`Mode: ${(u as any).currentModeId}`); + return; + } + } + + async hostCallbackResult(resp: AcpHostCallbackResponse): Promise { + const p = this.pendingHostCallbacks.get(resp.requestId); + if (!p) return; + this.pendingHostCallbacks.delete(resp.requestId); + if (resp.error) p.reject(new Error(resp.error)); + else p.resolve(resp.result); + } +} diff --git a/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.loopError.test.ts b/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.loopError.test.ts new file mode 100644 index 00000000000..22c5bf7f93f --- /dev/null +++ b/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.loopError.test.ts @@ -0,0 +1,84 @@ +import assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { NullLogService } from '../../../../platform/log/common/log.js'; +import { LOOP_DETECTED_MESSAGE } from '../../../../platform/void/common/loopGuard.js'; +import { __test } from '../acpBuiltinAgent.js'; + +suite('acpBuiltinAgent loop error', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + teardown(() => { + __test.reset(); + }); + + test('Loop detected surfaces as error.message (not sessionId) and details', async () => { + let callNo = 0; + + __test.setSendChatRouter(async (opts: any) => { + callNo++; + if (callNo === 1) { + await opts.onFinalMessage({ + fullText: 'same', + toolCall: { id: 'tc1', name: 'fake_tool', rawParams: {}, isDone: true } + }); + return; + } + + await opts.onFinalMessage({ fullText: 'same' }); + }); + + const conn: any = { + async extMethod(method: string) { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: {}, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + loopGuard: { maxTurnsPerPrompt: 1, maxSameAssistantPrefix: 1 }, + requestParams: null, + providerRouting: null, + dynamicRequestConfig: { + apiStyle: 'openai-compatible', + endpoint: 'https://example.invalid', + headers: {}, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'system-role', + }, + additionalTools: null, + }; + } + throw new Error(`Unexpected extMethod: ${method}`); + }, + + async sessionUpdate() { /* noop */ }, + + async requestPermission() { + // Reject -> treated as skip -> get 2nd LLM turn + return { outcome: { outcome: 'selected', optionId: 'reject_once' } }; + }, + }; + + const agent = new __test.VoidPipelineAcpAgent(conn, new NullLogService()); + const { sessionId } = await agent.newSession({} as any); + + await assert.rejects( + () => agent.prompt({ sessionId, prompt: [{ type: 'text', text: 'hi' }] } as any), + (e: any) => { + assert.ok(e instanceof Error); + assert.strictEqual(e.message, LOOP_DETECTED_MESSAGE); + + const anyErr = e as any; + const details = anyErr?.data?.details ?? anyErr?.details ?? ''; + assert.ok(typeof details === 'string'); + assert.ok(details.includes(LOOP_DETECTED_MESSAGE)); + + return true; + } + ); + }); +}); diff --git a/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.test.ts b/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.test.ts new file mode 100644 index 00000000000..35df7cbdc25 --- /dev/null +++ b/src/vs/platform/acp/electron-main/test/acpBuiltinAgent.test.ts @@ -0,0 +1,926 @@ +import assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; +// eslint-disable-next-line local/code-import-patterns +import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import type { DynamicRequestConfig, ProviderRouting, RequestParamsConfig } from '../../../../platform/void/common/sendLLMMessageTypes.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { IDynamicProviderRegistryService } from '../../../../platform/void/common/providerReg.js'; +import { NullLogService } from '../../../../platform/log/common/log.js'; +import { computeTruncatedToolOutput } from '../../../../platform/void/common/toolOutputTruncation.js'; + +function pickMethod(obj: T, names: string[]): (...args: any[]) => any { + for (const n of names) { + const fn = (obj as any)?.[n]; + if (typeof fn === 'function') return fn.bind(obj); + } + throw new Error(`None of the methods exist on object: ${names.join(', ')}`); +} + +suite('AcpInternalExtMethodService.getLLMConfig (node)', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + let AcpInternalExtMethodServiceCtor: any; + + suiteSetup(async () => { + (globalThis as any).MouseEvent ??= class MouseEvent { }; + (globalThis as any).PointerEvent ??= class PointerEvent extends (globalThis as any).MouseEvent { }; + (globalThis as any).KeyboardEvent ??= class KeyboardEvent { }; + + const mod = await import('../../../../workbench/contrib/acp/browser/AcpInternalExtMethodService.js'); + AcpInternalExtMethodServiceCtor = (mod as any).AcpInternalExtMethodService; + }); + + const makeServices = (opts: { vss: any; registry?: any }) => { + const services = new ServiceCollection(); + const inst = new TestInstantiationService(services); + + services.set(IVoidSettingsService, opts.vss); + services.set(IDynamicProviderRegistryService, opts.registry ?? { + async initialize() { /* noop */ }, + getRequestConfigForModel() { return null; }, + async getEffectiveModelCapabilities() { return {}; }, + }); + + return { services, inst }; + }; + + const callGetLLMConfig = async (svc: any, featureName: string) => { + const fn = pickMethod(svc, ['handle', 'handleExtMethod', '_handle', '_handleExtMethod', 'call', '_call', 'execute', '_execute']); + return await fn({ + method: 'void/settings/getLLMConfig', + params: { featureName }, + }); + }; + + test('includes per-model requestParams from customProviders', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'openrouter/test-model' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { Chat: {}, 'Ctrl+K': {}, Autocomplete: {}, Apply: {} }, + overridesOfModel: {}, + globalSettings: { acpSystemPrompt: '', chatMode: null }, + customProviders: { + openrouter: { + perModel: { + 'openrouter/test-model': { + requestParams: { + mode: 'override', + params: { max_tokens: 99 }, + } satisfies RequestParamsConfig, + }, + }, + }, + }, + }; + + const vss = { state: fakeState } as any; + + const { inst } = makeServices({ vss }); + const logService = new NullLogService(); + + const svc: any = new AcpInternalExtMethodServiceCtor(inst as any, logService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + const rp = res.requestParams as RequestParamsConfig | null; + assert.ok(rp, 'requestParams should be present'); + assert.strictEqual(rp!.mode, 'override'); + assert.strictEqual((rp!.params as any).max_tokens, 99); + } finally { + try { svc.dispose?.(); } catch { } + inst.dispose(); + } + }); + + test('includes per-model providerRouting from customProviders when present', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'openrouter/test-model' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { Chat: {}, 'Ctrl+K': {}, Autocomplete: {}, Apply: {} }, + overridesOfModel: {}, + globalSettings: { acpSystemPrompt: '', chatMode: null }, + customProviders: { + openrouter: { + perModel: { + 'openrouter/test-model': { + providerRouting: { + order: ['openai'], + allow_fallbacks: false, + } satisfies ProviderRouting, + }, + }, + }, + }, + }; + + const vss = { state: fakeState } as any; + + const { inst } = makeServices({ vss }); + const logService = new NullLogService(); + + const svc: any = new AcpInternalExtMethodServiceCtor(inst as any, logService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + const pr = res.providerRouting as ProviderRouting | null; + assert.ok(pr, 'providerRouting should be present'); + assert.deepStrictEqual(pr, { order: ['openai'], allow_fallbacks: false }); + } finally { + try { svc.dispose?.(); } catch { } + inst.dispose(); + } + }); + + test('includes dynamicRequestConfig from dynamic provider registry when available', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'tngtech/deepseek-r1t-chimera:free' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { Chat: {}, 'Ctrl+K': {}, Autocomplete: {}, Apply: {} }, + overridesOfModel: {}, + globalSettings: { acpSystemPrompt: '', chatMode: null }, + customProviders: {}, + }; + + const vss = { state: fakeState } as any; + + const dynamicCfg: DynamicRequestConfig = { + endpoint: 'https://openrouter.ai/api/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: false, + specialToolFormat: 'disabled', + headers: { + Accept: 'application/json', + Authorization: 'Bearer test-key', + }, + }; + + const registry: any = { + async initialize() { /* noop */ }, + getRequestConfigForModel(modelId: string, slug?: string) { + assert.strictEqual(slug, 'openrouter'); + assert.strictEqual(modelId, 'tngtech/deepseek-r1t-chimera:free'); + return dynamicCfg; + }, + async getEffectiveModelCapabilities() { + return {}; + }, + }; + + const { inst } = makeServices({ vss, registry }); + const logService = new NullLogService(); + + const svc: any = new AcpInternalExtMethodServiceCtor(inst as any, logService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + const drc = res.dynamicRequestConfig as DynamicRequestConfig | null; + assert.ok(drc, 'dynamicRequestConfig should be present'); + assert.strictEqual(drc!.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(drc!.supportsSystemMessage, false); + assert.strictEqual(drc!.specialToolFormat, 'disabled'); + assert.strictEqual(drc!.headers['Authorization'], 'Bearer test-key'); + } finally { + try { svc.dispose?.(); } catch { } + inst.dispose(); + } + }); +}); + +suite('acpBuiltinAgent read_file truncation', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + let __test: any; + + suiteSetup(async () => { + const mod = await import('../acpBuiltinAgent.js'); + __test = mod.__test; + }); + + teardown(() => { + try { __test?.reset?.(); } catch { /* ignore */ } + }); + + test('read_file truncation uses uri + nextStartLine and does NOT emit logFilePath', async () => { + const maxToolOutputLength = 200; + + // big multi-line content to force truncation + const big = Array.from({ length: 300 }, (_, i) => `LINE_${i + 1}`).join('\n'); + + const startLine = 10; + const { lineAfterTruncation } = computeTruncatedToolOutput(big, maxToolOutputLength); + const expectedNextStartLine = startLine + (lineAfterTruncation > 0 ? lineAfterTruncation : 0); + + let sendCount = 0; + + __test.setSendChatRouter((opts: any) => { + sendCount++; + + // 1st call -> ask for read_file tool + if (sendCount === 1) { + opts.onFinalMessage({ + fullText: '', + toolCall: { + id: 'tc1', + name: 'read_file', + isDone: true, + // IMPORTANT: args are plain JSON, uri is a STRING (this is the scenario that broke your code) + rawParams: { uri: '/abs/path/file.ts', startLine, endLine: startLine + 999 } + } + }); + return; + } + + // 2nd call -> tool result should be in messages + const msgs = opts.messages as any[]; + const toolMsg = msgs.find(m => m.role === 'tool' && String(m.tool_call_id) === 'tc1'); + assert.ok(toolMsg, 'tool message must be present in history'); + assert.ok(typeof toolMsg.content === 'string', 'tool message content must be string'); + + const text = toolMsg.content as string; + + const metaMatch = text.match(/TRUNCATION_META:\s*(\{.*\})\s*$/); + assert.ok(metaMatch, 'TRUNCATION_META must exist'); + + const meta = JSON.parse(metaMatch![1]); + + assert.strictEqual(meta.tool, 'read_file'); + assert.strictEqual(meta.uri, '/abs/path/file.ts'); + assert.strictEqual(meta.requestedStartLine, startLine); + assert.strictEqual(meta.nextStartLine, expectedNextStartLine); + assert.ok(meta.suggested, 'suggested should be present'); + assert.strictEqual(meta.suggested.startLine, expectedNextStartLine); + assert.strictEqual(meta.suggested.endLineIsFileEnd, false); + assert.ok(typeof meta.suggested.chunkLines === 'number' && meta.suggested.chunkLines > 0); + assert.strictEqual(meta.fileTotalLines, 300); + + assert.ok(!meta.logFilePath, 'read_file must not provide logFilePath'); + assert.ok(text.includes('ORIGINAL uri') || text.includes('call read_file'), 'must instruct to continue reading original file'); + assert.ok(text.includes('chunk boundary, NOT the end of file'), 'must explain endLine semantics'); + + // finish + opts.onFinalMessage({ fullText: 'done' }); + }); + + const updates: any[] = []; + + const fakeConn: any = { + extMethod: async (method: string, params: any) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + + if (method === 'void/tools/execute_with_text') { + assert.strictEqual(params?.name, 'read_file'); + + // emulate renderer tool result: rawOut contains real URI object + startLine + const resultObj = { + uri: { fsPath: '/abs/path/file.ts' }, + startLine, + endLine: startLine + 999, + totalNumLines: 300, + fileContents: big, + text: big, + output: big, + }; + + return { + ok: true, + result: resultObj, + text: big, + }; + } + + throw new Error(`unexpected extMethod: ${method}`); + }, + + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + + sessionUpdate: async (p: any) => { + updates.push(p); + }, + }; + + const agent = new __test.VoidPipelineAcpAgent(fakeConn, new NullLogService() as any, undefined); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: { maxToolOutputLength } + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + }); + + test('read_file truncation uses readFileChunkLines setting (700) for suggested range', async () => { + const maxToolOutputLength = 200; + const readFileChunkLines = 700; + + // big multi-line content to force truncation + const big = Array.from({ length: 300 }, (_, i) => `LINE_${i + 1}`).join('\n'); + + const startLine = 10; + const { lineAfterTruncation } = computeTruncatedToolOutput(big, maxToolOutputLength); + const expectedNextStartLine = startLine + (lineAfterTruncation > 0 ? lineAfterTruncation : 0); + const expectedSuggestedEndLine = expectedNextStartLine + readFileChunkLines - 1; + + let sendCount = 0; + + __test.setSendChatRouter((opts: any) => { + sendCount++; + + // 1st call -> ask for read_file tool + if (sendCount === 1) { + opts.onFinalMessage({ + fullText: '', + toolCall: { + id: 'tc1', + name: 'read_file', + isDone: true, + rawParams: { uri: '/abs/path/file.ts', startLine, endLine: startLine + 999 } + } + }); + return; + } + + // 2nd call -> tool result should be in messages + const msgs = opts.messages as any[]; + const toolMsg = msgs.find(m => m.role === 'tool' && String(m.tool_call_id) === 'tc1'); + assert.ok(toolMsg, 'tool message must be present in history'); + assert.ok(typeof toolMsg.content === 'string', 'tool message content must be string'); + + const text = toolMsg.content as string; + + const metaMatch = text.match(/TRUNCATION_META:\s*(\{.*\})\s*$/); + assert.ok(metaMatch, 'TRUNCATION_META must exist'); + + const meta = JSON.parse(metaMatch![1]); + + assert.strictEqual(meta.tool, 'read_file'); + assert.strictEqual(meta.uri, '/abs/path/file.ts'); + assert.strictEqual(meta.requestedStartLine, startLine); + assert.strictEqual(meta.nextStartLine, expectedNextStartLine); + + // Key assertion: suggested.endLine should use readFileChunkLines (700) + assert.ok(meta.suggested, 'suggested should be present'); + assert.strictEqual(meta.suggested.startLine, expectedNextStartLine); + assert.strictEqual(meta.suggested.endLine, expectedSuggestedEndLine); + assert.strictEqual(meta.suggested.chunkLines, readFileChunkLines); + assert.strictEqual(meta.suggested.endLineIsFileEnd, false); + assert.strictEqual(meta.fileTotalLines, 300); + + assert.ok(!meta.logFilePath, 'read_file must not provide logFilePath'); + assert.ok(text.includes('ORIGINAL uri') || text.includes('call read_file'), 'must instruct to continue reading original file'); + assert.ok(text.includes(`readFileChunkLines = ${readFileChunkLines}`), 'must include configured chunk size'); + + // finish + opts.onFinalMessage({ fullText: 'done' }); + }); + + const updates: any[] = []; + + // Create a settings service that returns readFileChunkLines = 700 + const settingsService: any = { + state: { + globalSettings: { + readFileChunkLines: readFileChunkLines, + maxToolOutputLength + } + } + }; + + const services = new ServiceCollection(); + const instantiationService = new TestInstantiationService(services); + services.set(IVoidSettingsService, settingsService); + + const fakeConn: any = { + extMethod: async (method: string, params: any) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + + if (method === 'void/tools/execute_with_text') { + assert.strictEqual(params?.name, 'read_file'); + + // emulate renderer tool result: rawOut contains real URI object + startLine + const resultObj = { + uri: { fsPath: '/abs/path/file.ts' }, + startLine, + endLine: startLine + 999, + totalNumLines: 300, + fileContents: big, + text: big, + output: big, + }; + + return { + ok: true, + result: resultObj, + text: big, + }; + } + + throw new Error(`unexpected extMethod: ${method}`); + }, + + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + + sessionUpdate: async (p: any) => { + updates.push(p); + }, + }; + + // Create agent with instantiation service that provides the settings + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined, + instantiationService as any + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: { maxToolOutputLength } + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + }); + + test('read_file truncation uses readFileChunkLines from prompt _meta when provided', async () => { + const maxToolOutputLength = 200; + const readFileChunkLines = 333; + + const big = Array.from({ length: 300 }, (_, i) => `LINE_${i + 1}`).join('\n'); + const startLine = 10; + + const { lineAfterTruncation } = computeTruncatedToolOutput(big, maxToolOutputLength); + const expectedNextStartLine = startLine + (lineAfterTruncation > 0 ? lineAfterTruncation : 0); + const expectedSuggestedEndLine = expectedNextStartLine + readFileChunkLines - 1; + + let sendCount = 0; + __test.setSendChatRouter((opts: any) => { + sendCount++; + + if (sendCount === 1) { + opts.onFinalMessage({ + fullText: '', + toolCall: { + id: 'tc1', + name: 'read_file', + isDone: true, + rawParams: { uri: '/abs/path/file.ts', startLine, endLine: startLine + 999 } + } + }); + return; + } + + const msgs = opts.messages as any[]; + const toolMsg = msgs.find(m => m.role === 'tool' && String(m.tool_call_id) === 'tc1'); + assert.ok(toolMsg, 'tool message must be present in history'); + assert.ok(typeof toolMsg.content === 'string', 'tool message content must be string'); + + const text = toolMsg.content as string; + const metaMatch = text.match(/TRUNCATION_META:\s*(\{.*\})\s*$/); + assert.ok(metaMatch, 'TRUNCATION_META must exist'); + + const meta = JSON.parse(metaMatch![1]); + assert.strictEqual(meta.tool, 'read_file'); + assert.strictEqual(meta.uri, '/abs/path/file.ts'); + assert.strictEqual(meta.requestedStartLine, startLine); + assert.strictEqual(meta.nextStartLine, expectedNextStartLine); + assert.ok(meta.suggested, 'suggested should be present'); + assert.strictEqual(meta.suggested.startLine, expectedNextStartLine); + assert.strictEqual(meta.suggested.endLine, expectedSuggestedEndLine); + assert.strictEqual(meta.suggested.chunkLines, readFileChunkLines); + assert.strictEqual(meta.suggested.endLineIsFileEnd, false); + assert.strictEqual(meta.fileTotalLines, 300); + + opts.onFinalMessage({ fullText: 'done' }); + }); + + const fakeConn: any = { + extMethod: async (method: string, params: any) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + + if (method === 'void/tools/execute_with_text') { + assert.strictEqual(params?.name, 'read_file'); + const resultObj = { + uri: { fsPath: '/abs/path/file.ts' }, + startLine, + endLine: startLine + 999, + totalNumLines: 300, + fileContents: big, + text: big, + output: big, + }; + return { + ok: true, + result: resultObj, + text: big, + }; + } + + throw new Error(`unexpected extMethod: ${method}`); + }, + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + sessionUpdate: async () => { /* noop */ }, + }; + + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: { maxToolOutputLength, readFileChunkLines } + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + }); + + test('emits reasoning deltas instead of full cumulative reasoning on each chunk', async () => { + let sendCount = 0; + __test.setSendChatRouter((opts: any) => { + sendCount++; + assert.strictEqual(sendCount, 1); + + opts.onText({ fullText: '', fullReasoning: 'abc' }); + opts.onText({ fullText: '', fullReasoning: 'abcdef' }); + opts.onFinalMessage({ fullText: 'done', fullReasoning: 'abcdef' }); + }); + + const updates: any[] = []; + + const fakeConn: any = { + extMethod: async (method: string) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + throw new Error(`unexpected extMethod: ${method}`); + }, + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + sessionUpdate: async (p: any) => { + updates.push(p); + }, + }; + + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: {} + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + + const reasoningUpdates = updates + .filter(u => u?.update?.sessionUpdate === 'agent_thought_chunk') + .map(u => u.update?.content?.text); + + assert.deepStrictEqual(reasoningUpdates, ['abc', 'def']); + }); + + test('emits only incremental reasoning chunk for large cumulative updates', async () => { + const r1 = 'x'.repeat(12000); + const r2 = r1 + 'y'.repeat(8000); + const r3 = r2 + 'z'.repeat(4000); + + let sendCount = 0; + __test.setSendChatRouter((opts: any) => { + sendCount++; + assert.strictEqual(sendCount, 1); + + opts.onText({ fullText: '', fullReasoning: r1 }); + opts.onText({ fullText: '', fullReasoning: r2 }); + opts.onText({ fullText: '', fullReasoning: r3 }); + opts.onFinalMessage({ fullText: 'done', fullReasoning: r3 }); + }); + + const updates: any[] = []; + + const fakeConn: any = { + extMethod: async (method: string) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + throw new Error(`unexpected extMethod: ${method}`); + }, + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + sessionUpdate: async (p: any) => { + updates.push(p); + }, + }; + + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: {} + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + + const reasoningUpdates = updates + .filter(u => u?.update?.sessionUpdate === 'agent_thought_chunk') + .map(u => String(u.update?.content?.text ?? '')); + + assert.strictEqual(reasoningUpdates.length, 3); + assert.strictEqual(reasoningUpdates[0].length, 12000); + assert.strictEqual(reasoningUpdates[1], 'y'.repeat(8000)); + assert.strictEqual(reasoningUpdates[2], 'z'.repeat(4000)); + }); + + test('ignores regressive cumulative reasoning snapshot to avoid UI shrink flicker', async () => { + let sendCount = 0; + __test.setSendChatRouter((opts: any) => { + sendCount++; + assert.strictEqual(sendCount, 1); + + opts.onText({ fullText: '', fullReasoning: 'The quick' }); + opts.onText({ fullText: '', fullReasoning: 'The' }); // regressive snapshot + opts.onText({ fullText: '', fullReasoning: 'The quick brown' }); + opts.onFinalMessage({ fullText: 'done', fullReasoning: 'The quick brown' }); + }); + + const updates: any[] = []; + + const fakeConn: any = { + extMethod: async (method: string) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + }; + } + throw new Error(`unexpected extMethod: ${method}`); + }, + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + sessionUpdate: async (p: any) => { + updates.push(p); + }, + }; + + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: {} + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + + const reasoningUpdates = updates + .filter(u => u?.update?.sessionUpdate === 'agent_thought_chunk') + .map(u => String(u.update?.content?.text ?? '')); + + assert.deepStrictEqual(reasoningUpdates, ['The quick', ' brown']); + }); + + test('safeguard exhaustion raises ACP error instead of plain assistant text', async () => { + let sendCount = 0; + __test.setSendChatRouter((opts: any) => { + sendCount++; + + opts.onFinalMessage({ + fullText: `turn ${sendCount}`, + toolCall: { + id: `tc${sendCount}`, + name: 'read_file', + isDone: true, + rawParams: { uri: `/abs/path/file${sendCount}.ts`, startLine: 1, endLine: 1 } + } + }); + }); + + const fakeConn: any = { + extMethod: async (method: string) => { + if (method === 'void/settings/getLLMConfig') { + return { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: {} }, + modelSelectionOptions: null, + overridesOfModel: null, + separateSystemMessage: null, + chatMode: null, + requestParams: null, + dynamicRequestConfig: { + endpoint: '', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + headers: {}, + }, + providerRouting: null, + additionalTools: null, + // safeguard floor is 25; keep explicit for clarity in this test + loopGuard: { maxTurnsPerPrompt: 25 } + }; + } + + if (method === 'void/tools/execute_with_text') { + return { + ok: true, + result: { + uri: { fsPath: '/abs/path/file.ts' }, + startLine: 1, + endLine: 1, + totalNumLines: 1, + fileContents: 'x', + text: 'x', + output: 'x', + }, + text: 'x', + }; + } + + throw new Error(`unexpected extMethod: ${method}`); + }, + requestPermission: async () => ({ + outcome: { outcome: 'selected', optionId: 'allow_once' } + }), + sessionUpdate: async (_p: any) => { /* noop */ }, + }; + + const agent = new __test.VoidPipelineAcpAgent( + fakeConn, + new NullLogService() as any, + undefined + ); + + const { sessionId } = await agent.newSession({ _meta: {} } as any); + + await assert.rejects( + () => agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'go' }], + _meta: {} + } as any), + (err: any) => { + assert.ok(String(err?.message ?? '').includes('Reached ACP safeguard limit')); + return true; + } + ); + + assert.ok(sendCount >= 25, 'expected prompt loop to run until safeguard floor'); + }); +}); diff --git a/src/vs/platform/acp/electron-main/test/acpMainService.test.ts b/src/vs/platform/acp/electron-main/test/acpMainService.test.ts new file mode 100644 index 00000000000..ceb5ad6e175 --- /dev/null +++ b/src/vs/platform/acp/electron-main/test/acpMainService.test.ts @@ -0,0 +1,70 @@ +import assert from 'assert'; +import { AcpMainService } from '../acpMainService.js'; +import { ILogService } from '../../../log/common/log.js'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; + +suite('AcpMainService', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + let service: AcpMainService; + let logService: ILogService; + + setup(() => { + logService = { + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { }, + trace: () => { }, + } as any; + service = new AcpMainService(logService); + }); + + teardown(async () => { + await service.disconnect(); + }); + + test('connect defaults to websocket mode', async () => { + try { + // Expect failure due to invalid URL, but verify mode logic + await service.connect({ mode: 'websocket', agentUrl: 'ws://invalid' }); + } catch { } + // We can't inspect private state directly, but we verified the call flow doesn't crash + }); + + test('connect builtin defaults URL', async () => { + try { + await service.connect({ mode: 'builtin' }); + } catch { } + }); + + test('connect in process mode requires command', async () => { + await assert.rejects(async () => { + await service.connect({ mode: 'process', args: [] }); + }, /command is required/); + }); + + test('disconnect clears connection state', async () => { + // Manually set state to simulate connection (since we can't easily connect to real things) + (service as any).connected = true; + (service as any).lastConnectParams = { mode: 'builtin' }; + + await service.disconnect(); + + assert.strictEqual((service as any).connected, false); + assert.strictEqual((service as any).lastConnectParams, undefined); + }); + + test('sendChatMessage throws if connection fails', async () => { + await assert.rejects(async () => { + await service.sendChatMessage({ + threadId: 't1', + history: [], + message: { role: 'user', content: 'hi' }, + // Use a local non-existent port to ensure fast failure (ECONNREFUSED) instead of DNS timeout + opts: { mode: 'websocket', agentUrl: 'ws://127.0.0.1:54321' } + }); + }); + }); +}); diff --git a/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.d.ts b/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.d.ts new file mode 100644 index 00000000000..a9f4d5fc05a --- /dev/null +++ b/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.d.ts @@ -0,0 +1,18 @@ +export type Stream = any; +export function ndJsonStream(writable: any, readable: any): Stream; + +export type Client = any; + +export class ClientSideConnection { + constructor(createClient: (agent: any) => any, stream: Stream); + initialize(opts: any): Promise; + newSession(params: any): Promise; + prompt(params: any): Promise; + cancel(params: any): Promise; + setSessionModel?(params: any): Promise; +} + +export class AgentSideConnection { + constructor(createAgent: (conn: any) => any, stream: Stream); +} + diff --git a/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.js b/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.js new file mode 100644 index 00000000000..144c530f18f --- /dev/null +++ b/src/vs/platform/acp/electron-main/vendor/acp-sdk.vendored.js @@ -0,0 +1 @@ +export * from '@agentclientprotocol/sdk'; diff --git a/src/vs/platform/acp/electron-main/vendor/ws.vendored.d.ts b/src/vs/platform/acp/electron-main/vendor/ws.vendored.d.ts new file mode 100644 index 00000000000..6bd856f9e3e --- /dev/null +++ b/src/vs/platform/acp/electron-main/vendor/ws.vendored.d.ts @@ -0,0 +1,13 @@ +export class WebSocket { + constructor(url: string, protocols?: string | string[]); + send(data: any): void; + close(): void; + on(event: 'open' | 'message' | 'close' | 'error', listener: (...args: any[]) => void): void; + once(event: 'open' | 'message' | 'close' | 'error', listener: (...args: any[]) => void): void; +} + +export class WebSocketServer { + constructor(opts: any); + on(event: 'connection' | 'listening' | 'error' | 'close', listener: (...args: any[]) => void): void; + close(cb?: (err?: Error) => void): void; +} diff --git a/src/vs/platform/acp/electron-main/vendor/ws.vendored.js b/src/vs/platform/acp/electron-main/vendor/ws.vendored.js new file mode 100644 index 00000000000..b533ca7f5ea --- /dev/null +++ b/src/vs/platform/acp/electron-main/vendor/ws.vendored.js @@ -0,0 +1,7 @@ +import { createRequire as __createRequire } from 'node:module'; + +const require = __createRequire(import.meta.url); +const ws = require('ws'); + +export const WebSocket = ws.WebSocket || ws; +export const WebSocketServer = ws.WebSocketServer || ws.Server; diff --git a/src/vs/platform/acp/test/common/acpLogSanitizer.test.ts b/src/vs/platform/acp/test/common/acpLogSanitizer.test.ts new file mode 100644 index 00000000000..fd37e7ae292 --- /dev/null +++ b/src/vs/platform/acp/test/common/acpLogSanitizer.test.ts @@ -0,0 +1,33 @@ +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { redactEnvForLog } from '../../common/acpLogSanitizer.js'; + +suite('acpLogSanitizer.redactEnvForLog', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('redacts *_KEY / *_TOKEN / password-like fields', () => { + const env = { + MISTRAL_API_KEY: 'secret1', + OPENAI_TOKEN: 'secret2', + password: 'secret3', + PATH: '/bin', + NORMAL: 'ok', + }; + + const out = redactEnvForLog(env); + + assert.deepStrictEqual(out, { + MISTRAL_API_KEY: '', + OPENAI_TOKEN: '', + password: '', + PATH: '/bin', + NORMAL: 'ok', + }); + }); + + test('returns non-object as-is', () => { + assert.strictEqual(redactEnvForLog(null), null); + assert.strictEqual(redactEnvForLog(undefined), undefined); + assert.strictEqual(redactEnvForLog('x' as any), 'x'); + }); +}); diff --git a/src/vs/platform/acp/test/node/acpBuiltinAgent.refreshConfig.test.ts b/src/vs/platform/acp/test/node/acpBuiltinAgent.refreshConfig.test.ts new file mode 100644 index 00000000000..97b246bd092 --- /dev/null +++ b/src/vs/platform/acp/test/node/acpBuiltinAgent.refreshConfig.test.ts @@ -0,0 +1,280 @@ +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { NullLogService } from '../../../log/common/log.js'; +// eslint-disable-next-line local/code-layering, local/code-import-patterns +import { __test as acpTest } from '../../electron-main/acpBuiltinAgent.js'; +// eslint-disable-next-line local/code-layering, local/code-import-patterns +import { __test as llmImplTest } from '../../../void/electron-main/llmMessage/sendLLMMessage.impl.js'; + +suite('ACP builtin agent - refresh config on prompt', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('prompt() refreshes void/settings/getLLMConfig and uses new dynamicRequestConfig', async () => { + const log = new NullLogService(); + + const captured: any[] = []; + + acpTest.setSendChatRouter(async (params: any) => { + captured.push(JSON.parse(JSON.stringify(params))); + + params.onFinalMessage?.({ + fullText: 'ok', + fullReasoning: '', + anthropicReasoning: null, + toolCall: undefined, + tokenUsage: { input: 1, output: 1, cacheCreation: 0, cacheRead: 0 }, + }); + }); + + + + + let getCfgCall = 0; + + const cfgA = { + providerName: 'provA', + modelName: 'provA/modelA', + settingsOfProvider: { provA: { endpoint: 'https://provider-a.example/v1', apiKey: 'provKeyA' } }, + modelSelectionOptions: {}, + overridesOfModel: {}, + separateSystemMessage: 'SYS', + chatMode: 'normal', + requestParams: null, + providerRouting: null, + dynamicRequestConfig: { + apiStyle: 'openai-compatible', + endpoint: 'https://api-a.example/v1', + headers: { Authorization: 'Bearer keyA' }, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }, + additionalTools: null, + loopGuard: { maxTurnsPerPrompt: 25, maxSameAssistantPrefix: 10, maxSameToolCall: 10 }, + }; + + const cfgB = { + ...cfgA, + providerName: 'provB', + modelName: 'provB/modelB', + chatMode: 'agent', + separateSystemMessage: 'SYS2', + settingsOfProvider: { provB: { endpoint: 'https://provider-b.example/v1', apiKey: 'provKeyB' } }, + dynamicRequestConfig: { + apiStyle: 'openai-compatible', + endpoint: 'https://api-b.example/v1', + headers: { Authorization: 'Bearer keyB' }, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }, + additionalTools: [ + { name: 'mcp__allowed', description: 'allowed', params: {} }, + { name: 'mcp__disabled', description: 'disabled', params: {} }, + ], + disabledStaticTools: ['read_file', 'edit_file'], + disabledDynamicTools: ['mcp__disabled'], + }; + + const conn: any = { + extMethod: async (method: string, _params: any) => { + if (method === 'void/settings/getLLMConfig') { + getCfgCall++; + return (getCfgCall === 1) ? cfgA : cfgB; + } + throw new Error('Unexpected extMethod: ' + method); + }, + sessionUpdate: async (_u: any) => { /* ignore */ }, + requestPermission: async () => { throw new Error('requestPermission should not be called (no tool)'); }, + }; + + try { + const agent = new acpTest.VoidPipelineAcpAgent(conn, log); + + const { sessionId } = await agent.newSession({} as any); + + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'hello' }], + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + assert.ok(getCfgCall >= 2, 'expected getLLMConfig called at least twice (newSession + prompt refresh)'); + + assert.strictEqual(captured.length, 1); + assert.strictEqual(captured[0].dynamicRequestConfig.endpoint, 'https://api-b.example/v1'); + assert.strictEqual(captured[0].dynamicRequestConfig.headers.Authorization, 'Bearer keyB'); + assert.strictEqual(captured[0].providerName, 'provB'); + assert.strictEqual(captured[0].modelName, 'provB/modelB'); + assert.strictEqual(captured[0].chatMode, 'agent'); + assert.deepStrictEqual(captured[0].disabledStaticTools, ['read_file', 'edit_file']); + assert.deepStrictEqual(captured[0].disabledDynamicTools, ['mcp__disabled']); + + const toolNames = Array.isArray(captured[0].additionalTools) + ? captured[0].additionalTools.map((t: any) => String(t?.name ?? '')) + : []; + assert.ok(toolNames.includes('mcp__allowed'), 'enabled dynamic tool must be passed'); + assert.ok(!toolNames.includes('mcp__disabled'), 'disabled dynamic tool must not be passed'); + assert.ok(toolNames.includes('acp_plan'), 'ACP plan tool should be present in agent mode'); + } finally { + acpTest.reset(); + } + }); + + test('builtin ACP: disabled static tools are excluded from provider payload tools', async () => { + const log = new NullLogService(); + acpTest.reset(); + + let capturedOpenAIOptions: any = null; + + class FakeOpenAI { + chat = { + completions: { + create: async (opts: any) => { + capturedOpenAIOptions = opts; + return { + choices: [{ message: { content: 'ok', tool_calls: undefined } }], + }; + }, + }, + }; + } + + llmImplTest.setOpenAIModule?.({ + default: FakeOpenAI, + APIError: class extends Error { }, + } as any); + + const cfg = { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: { apiKey: 'k' } }, + modelSelectionOptions: {}, + overridesOfModel: {}, + separateSystemMessage: 'SYS', + chatMode: 'agent', + requestParams: null, + providerRouting: null, + dynamicRequestConfig: { + apiStyle: 'openai-compatible', + endpoint: 'https://api.openai.com/v1', + headers: { Authorization: 'Bearer k' }, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }, + additionalTools: null, + disabledStaticTools: ['read_file', 'edit_file'], + disabledDynamicTools: [], + loopGuard: { maxTurnsPerPrompt: 25, maxSameAssistantPrefix: 10, maxSameToolCall: 10 }, + }; + + const conn: any = { + extMethod: async (method: string, _params: any) => { + if (method === 'void/settings/getLLMConfig') return cfg; + throw new Error('Unexpected extMethod: ' + method); + }, + sessionUpdate: async (_u: any) => { /* ignore */ }, + requestPermission: async () => { throw new Error('requestPermission should not be called (no tool)'); }, + }; + + try { + const agent = new acpTest.VoidPipelineAcpAgent(conn, log); + const { sessionId } = await agent.newSession({} as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'hello' }], + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + assert.ok(capturedOpenAIOptions, 'OpenAI payload should be captured'); + assert.ok(Array.isArray(capturedOpenAIOptions.tools), 'tools must be present in agent mode'); + + const toolNames = capturedOpenAIOptions.tools + .map((t: any) => String(t?.function?.name ?? '')) + .filter(Boolean); + + assert.ok(toolNames.length > 0, 'there should be at least one tool in payload'); + assert.ok(!toolNames.includes('read_file'), 'disabled static tool read_file must be excluded'); + assert.ok(!toolNames.includes('edit_file'), 'disabled static tool edit_file must be excluded'); + assert.ok(toolNames.includes('run_command'), 'enabled static tools should remain available'); + } finally { + llmImplTest.reset?.(); + acpTest.reset(); + } + }); + + test('builtin ACP: when specialToolFormat=disabled, provider payload must not include tools', async () => { + const log = new NullLogService(); + acpTest.reset(); + + let capturedOpenAIOptions: any = null; + + class FakeOpenAI { + chat = { + completions: { + create: async (opts: any) => { + capturedOpenAIOptions = opts; + return { + choices: [{ message: { content: 'ok', tool_calls: undefined } }], + }; + }, + }, + }; + } + + llmImplTest.setOpenAIModule?.({ + default: FakeOpenAI, + APIError: class extends Error { }, + } as any); + + const cfg = { + providerName: 'openAI', + modelName: 'gpt-4o-mini', + settingsOfProvider: { openAI: { apiKey: 'k' } }, + modelSelectionOptions: {}, + overridesOfModel: {}, + separateSystemMessage: 'SYS', + chatMode: 'agent', + requestParams: null, + providerRouting: null, + dynamicRequestConfig: { + apiStyle: 'openai-compatible', + endpoint: 'https://api.openai.com/v1', + headers: { Authorization: 'Bearer k' }, + specialToolFormat: 'disabled', + supportsSystemMessage: 'developer-role', + }, + additionalTools: null, + disabledStaticTools: [], + disabledDynamicTools: [], + loopGuard: { maxTurnsPerPrompt: 25, maxSameAssistantPrefix: 10, maxSameToolCall: 10 }, + }; + + const conn: any = { + extMethod: async (method: string, _params: any) => { + if (method === 'void/settings/getLLMConfig') return cfg; + throw new Error('Unexpected extMethod: ' + method); + }, + sessionUpdate: async (_u: any) => { /* ignore */ }, + requestPermission: async () => { throw new Error('requestPermission should not be called (no tool)'); }, + }; + + try { + const agent = new acpTest.VoidPipelineAcpAgent(conn, log); + const { sessionId } = await agent.newSession({} as any); + const resp = await agent.prompt({ + sessionId, + prompt: [{ type: 'text', text: 'hello' }], + } as any); + + assert.strictEqual(resp.stopReason, 'end_turn'); + assert.ok(capturedOpenAIOptions, 'OpenAI payload should be captured'); + assert.strictEqual( + capturedOpenAIOptions.tools, + undefined, + 'tools must be omitted when specialToolFormat is disabled in ACP mode' + ); + } finally { + llmImplTest.reset?.(); + acpTest.reset(); + } + }); +}); diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index a205483c48c..1fa6438c786 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -279,7 +279,6 @@ export function parseArgs(args: string[], options: OptionDescriptions, err const newArgs = args.filter(a => a !== firstArg); const reporter = errorReporter.getSubcommandReporter ? errorReporter.getSubcommandReporter(firstArg) : undefined; const subcommandOptions = parseArgs(newArgs, options, reporter); - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { [firstArg]: subcommandOptions, _: [] diff --git a/src/vs/platform/keybinding/common/abstractKeybindingService.ts b/src/vs/platform/keybinding/common/abstractKeybindingService.ts index f3e1e2b854d..45c440f2974 100644 --- a/src/vs/platform/keybinding/common/abstractKeybindingService.ts +++ b/src/vs/platform/keybinding/common/abstractKeybindingService.ts @@ -179,6 +179,13 @@ export abstract class AbstractKeybindingService extends Disposable implements IK private _expectAnotherChord(firstChord: string, keypressLabel: string | null): void { + // Dispose any previous chord status notification before showing a new one + // so that the disposable is correctly marked as disposed for leak tracking. + if (this._currentChordStatusMessage) { + this._currentChordStatusMessage.dispose(); + this._currentChordStatusMessage = null; + } + this._currentChords.push({ keypress: firstChord, label: keypressLabel }); switch (this._currentChords.length) { @@ -186,11 +193,15 @@ export abstract class AbstractKeybindingService extends Disposable implements IK throw illegalState('impossible'); case 1: // TODO@ulugbekna: revise this message and the one below (at least, fix terminology) - this._currentChordStatusMessage = this._notificationService.status(nls.localize('first.chord', "({0}) was pressed. Waiting for second key of chord...", keypressLabel)); + this._currentChordStatusMessage = this._notificationService.status( + nls.localize('first.chord', "({0}) was pressed. Waiting for second key of chord...", keypressLabel) + ); break; default: { const fullKeypressLabel = this._currentChords.map(({ label }) => label).join(', '); - this._currentChordStatusMessage = this._notificationService.status(nls.localize('next.chord', "({0}) was pressed. Waiting for next key of chord...", fullKeypressLabel)); + this._currentChordStatusMessage = this._notificationService.status( + nls.localize('next.chord', "({0}) was pressed. Waiting for next key of chord...", fullKeypressLabel) + ); } } @@ -201,8 +212,8 @@ export abstract class AbstractKeybindingService extends Disposable implements IK } } - private _leaveChordMode(): void { - if (this._currentChordStatusMessage) { + private _leaveChordMode(clearStatusMessage: boolean = true): void { + if (clearStatusMessage && this._currentChordStatusMessage) { this._currentChordStatusMessage.dispose(); this._currentChordStatusMessage = null; } @@ -322,8 +333,16 @@ export abstract class AbstractKeybindingService extends Disposable implements IK if (this.inChordMode) { const currentChordsLabel = this._currentChords.map(({ label }) => label).join(', '); this._log(`+ Leaving multi-chord mode: Nothing bound to "${currentChordsLabel}, ${keypressLabel}".`); - this._notificationService.status(nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), { hideAfter: 10 * 1000 /* 10s */ }); - this._leaveChordMode(); + // Dispose any previous chord status message and remember this one so it + // can be disposed when leaving chord mode. + if (this._currentChordStatusMessage) { + this._currentChordStatusMessage.dispose(); + } + this._currentChordStatusMessage = this._notificationService.status( + nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), + { hideAfter: 10 * 1000 /* 10s */ } + ); + this._leaveChordMode(false); shouldPreventDefault = true; } @@ -349,8 +368,14 @@ export abstract class AbstractKeybindingService extends Disposable implements IK if (this.inChordMode) { const currentChordsLabel = this._currentChords.map(({ label }) => label).join(', '); this._log(`+ Leaving chord mode: Nothing bound to "${currentChordsLabel}, ${keypressLabel}".`); - this._notificationService.status(nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), { hideAfter: 10 * 1000 /* 10s */ }); - this._leaveChordMode(); + if (this._currentChordStatusMessage) { + this._currentChordStatusMessage.dispose(); + } + this._currentChordStatusMessage = this._notificationService.status( + nls.localize('missing.chord', "The key combination ({0}, {1}) is not a command.", currentChordsLabel, keypressLabel), + { hideAfter: 10 * 1000 /* 10s */ } + ); + this._leaveChordMode(false); shouldPreventDefault = true; } diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 94c2acead57..78cc2e7e994 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1147,7 +1147,6 @@ function workbenchTreeDataPreamble; envFile?: string; + /** Optional list of MCP tool names to exclude for this server. */ + excludeTools?: readonly string[]; } export interface IMcpConfigurationSSE { type: 'sse'; url: string; headers?: Record; + /** Optional list of MCP tool names to exclude for this server. */ + excludeTools?: readonly string[]; } diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/terminalCommand.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/terminalCommand.ts index fc8c7655d5e..8bc2b442bdf 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/terminalCommand.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/terminalCommand.ts @@ -115,25 +115,31 @@ export class TerminalCommand implements ITerminalCommand { } getOutput(): string | undefined { - if (!this.executedMarker || !this.endMarker) { - return undefined; - } + if (!this.executedMarker || !this.endMarker) return undefined; + const startLine = this.executedMarker.line; const endLine = this.endMarker.line; + if (startLine === endLine) return undefined; - if (startLine === endLine) { - return undefined; - } let output = ''; - let line: IBufferLine | undefined; + let logicalLine = ''; + for (let i = startLine; i < endLine; i++) { - line = this._xterm.buffer.active.getLine(i); - if (!line) { - continue; + const line = this._xterm.buffer.active.getLine(i); + if (!line) continue; + + logicalLine += line.translateToString(true); + const nextLine = this._xterm.buffer.active.getLine(i + 1); + const nextIsWrapped = nextLine?.isWrapped === true; + + if (!nextIsWrapped) { + output += logicalLine + '\n'; + logicalLine = ''; } - output += line.translateToString(!line.isWrapped) + (line.isWrapped ? '' : '\n'); } - return output === '' ? undefined : output; + + if (output === '') return undefined; + return output; } getOutputMatch(outputMatcher: ITerminalOutputMatcher): ITerminalOutputMatch | undefined { diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 41efe327cfa..6b9eb732e4f 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -436,7 +436,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe this._currentCommand.commandStartX = e.startX; this._currentCommand.promptStartMarker = e.promptStartLine !== undefined ? this._terminal.registerMarker(e.promptStartLine - (buffer.baseY + buffer.cursorY)) : undefined; this._cwd = e.cwd; - // eslint-disable-next-line local/code-no-dangerous-type-assertions this._onCommandStarted.fire({ marker } as ITerminalCommand); continue; } @@ -515,7 +514,6 @@ class UnixPtyHeuristics extends Disposable { } this._hooks.commandMarkers.length = 0; - // eslint-disable-next-line local/code-no-dangerous-type-assertions this._hooks.onCommandStartedEmitter.fire({ marker: options?.marker || currentCommand.commandStartMarker, markProperties: options?.markProperties } as ITerminalCommand); this._logService.debug('CommandDetectionCapability#handleCommandStart', currentCommand.commandStartX, currentCommand.commandStartMarker?.line); } @@ -773,7 +771,6 @@ class WindowsPtyHeuristics extends Disposable { this._capability.currentCommand.commandStartLineContent = line.translateToString(true); } } - // eslint-disable-next-line local/code-no-dangerous-type-assertions this._hooks.onCommandStartedEmitter.fire({ marker: this._capability.currentCommand.commandStartMarker } as ITerminalCommand); this._logService.debug('CommandDetectionCapability#_handleCommandStartWindows', this._capability.currentCommand.commandStartX, this._capability.currentCommand.commandStartMarker?.line); } diff --git a/src/vs/platform/void/common/acpArgs.ts b/src/vs/platform/void/common/acpArgs.ts new file mode 100644 index 00000000000..43233fae073 --- /dev/null +++ b/src/vs/platform/void/common/acpArgs.ts @@ -0,0 +1,40 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// Parse a shell-like arguments string into an array suitable for process spawn. +// Behaviour is intentionally close to typical POSIX shell splitting: +// - Whitespace outside quotes splits arguments +// - Double quotes are removed and everything inside is kept verbatim +// (so "my cfg.json" -> "my cfg.json") +// - Flags with equals are preserved as-is, including combined forms like +// --config=my_cfg_json or --config="my_cfg_json" (quotes stripped) +export const parseAcpProcessArgs = (raw: string): string[] => { + const out: string[] = []; + let current = ''; + let inQuotes = false; + + for (let i = 0; i < raw.length; i++) { + const ch = raw[i]; + if (ch === '"') { + // Toggle quote state, but do not include the quote character itself. + inQuotes = !inQuotes; + continue; + } + if (!inQuotes && /\s/.test(ch)) { + if (current.length > 0) { + out.push(current); + current = ''; + } + continue; + } + current += ch; + } + + if (current.length > 0) { + out.push(current); + } + + return out; +}; diff --git a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts b/src/vs/platform/void/common/chatThreadServiceTypes.ts similarity index 53% rename from src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts rename to src/vs/platform/void/common/chatThreadServiceTypes.ts index 44dc307e790..777fc2abd25 100644 --- a/src/vs/workbench/contrib/void/common/chatThreadServiceTypes.ts +++ b/src/vs/platform/void/common/chatThreadServiceTypes.ts @@ -3,34 +3,48 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { URI } from '../../../../base/common/uri.js'; +import { URI } from '../../../base/common/uri.js'; import { VoidFileSnapshot } from './editCodeServiceTypes.js'; +// Allow dynamic tool names (MCP/runtime tools) +export type AnyToolName = ToolName | string; import { AnthropicReasoning, RawToolParamsObj } from './sendLLMMessageTypes.js'; -import { ToolCallParams, ToolName, ToolResult } from './toolsServiceTypes.js'; +import { ToolCallParams, ToolName, ToolResultType } from './toolsServiceTypes.js'; -export type ToolMessage = { +// Attachments that can be associated with a user chat message +export type ChatImageAttachment = { + kind: 'image'; + uri: URI; + mimeType: string; + name: string; +}; + +export type ChatAttachment = ChatImageAttachment; + +// ToolMessage supports both known static tools (with typed params/results) +// and dynamic/unknown tools where params/results are untyped. +export type ToolMessage = { role: 'tool'; content: string; // give this result to LLM (string of value) + displayContent?: string; // for UI (cleaned content without path and tags) id: string; rawParams: RawToolParamsObj; - mcpServerName: string | undefined; // the server name at the time of the call } & ( - // in order of events: - | { type: 'invalid_params', result: null, name: T, } + // in order of events: for known static tools we keep strong typing + | (T extends ToolName ? { type: 'invalid_params', result: null, name: T, } : { type: 'invalid_params', result: null, name: AnyToolName }) - | { type: 'tool_request', result: null, name: T, params: ToolCallParams, } // params were validated, awaiting user + | (T extends ToolName ? { type: 'tool_request', result: null, name: T, params: ToolCallParams[T], } : { type: 'tool_request', result: null, name: AnyToolName, params: Record }) // params were validated, awaiting user - | { type: 'running_now', result: null, name: T, params: ToolCallParams, } + | (T extends ToolName ? { type: 'running_now', result: null, name: T, params: ToolCallParams[T], } : { type: 'running_now', result: null, name: AnyToolName, params: Record }) - | { type: 'tool_error', result: string, name: T, params: ToolCallParams, } // error when tool was running - | { type: 'success', result: Awaited>, name: T, params: ToolCallParams, } - | { type: 'rejected', result: null, name: T, params: ToolCallParams } + | (T extends ToolName ? { type: 'tool_error', result: string, name: T, params: ToolCallParams[T], } : { type: 'tool_error', result: string, name: AnyToolName, params: Record }) // error when tool was running + | (T extends ToolName ? { type: 'success', result: Awaited, name: T, params: ToolCallParams[T], } : { type: 'success', result: any, name: AnyToolName, params: Record }) + | (T extends ToolName ? { type: 'rejected', result: null, name: T, params: ToolCallParams[T] } : { type: 'rejected', result: null, name: AnyToolName, params: Record }) + | (T extends ToolName ? { type: 'skipped', result: null, name: T, params: ToolCallParams[T] } : { type: 'skipped', result: null, name: AnyToolName, params: Record }) ) // user rejected export type DecorativeCanceledTool = { role: 'interrupted_streaming_tool'; - name: ToolName; - mcpServerName: string | undefined; // the server name at the time of the call + name: AnyToolName; } @@ -53,10 +67,12 @@ export type ChatMessage = content: string; // content displayed to the LLM on future calls - allowed to be '', will be replaced with (empty) displayContent: string; // content displayed to user - allowed to be '', will be ignored selections: StagingSelectionItem[] | null; // the user's selection + attachments?: ChatAttachment[] | null; // optional explicit attachments (e.g. images) state: { stagingSelections: StagingSelectionItem[]; isBeingEdited: boolean; } + hidden?: boolean; // whether the message should be hidden from UI } | { role: 'assistant'; displayContent: string; // content received from LLM - allowed to be '', will be replaced with (empty) @@ -64,7 +80,7 @@ export type ChatMessage = anthropicReasoning: AnthropicReasoning[] | null; // anthropic reasoning } - | ToolMessage + | ToolMessage | DecorativeCanceledTool | CheckpointEntry diff --git a/src/vs/workbench/contrib/void/common/directoryStrService.ts b/src/vs/platform/void/common/directoryStrService.ts similarity index 92% rename from src/vs/workbench/contrib/void/common/directoryStrService.ts rename to src/vs/platform/void/common/directoryStrService.ts index d9d0a319cec..cc60a5f526a 100644 --- a/src/vs/workbench/contrib/void/common/directoryStrService.ts +++ b/src/vs/platform/void/common/directoryStrService.ts @@ -3,21 +3,21 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { URI } from '../../../../base/common/uri.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IFileService, IFileStat } from '../../../../platform/files/common/files.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { ShallowDirectoryItem, BuiltinToolCallParams, BuiltinToolResultType } from './toolsServiceTypes.js'; -import { MAX_CHILDREN_URIs_PAGE, MAX_DIRSTR_CHARS_TOTAL_BEGINNING, MAX_DIRSTR_CHARS_TOTAL_TOOL } from './prompt/prompts.js'; +import { URI } from '../../../base/common/uri.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { registerSingleton, InstantiationType } from '../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { IFileService, IFileStat } from '../../../platform/files/common/files.js'; +import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js'; +import { ShallowDirectoryItem, ToolCallParams, ToolResultType } from './toolsServiceTypes.js'; +import { MAX_CHILDREN_URIs_PAGE, MAX_DIRSTR_CHARS_TOTAL_BEGINNING, MAX_DIRSTR_CHARS_TOTAL_TOOL } from './prompt/constants.js'; -const MAX_FILES_TOTAL = 1000; +const MAX_FILES_TOTAL = 1_000; const START_MAX_DEPTH = Infinity; -const START_MAX_ITEMS_PER_DIR = Infinity; // Add start value as Infinity +const START_MAX_ITEMS_PER_DIR = Infinity; const DEFAULT_MAX_DEPTH = 3; const DEFAULT_MAX_ITEMS_PER_DIR = 3; @@ -76,7 +76,7 @@ export const computeDirectoryTree1Deep = async ( fileService: IFileService, rootURI: URI, pageNumber: number = 1, -): Promise => { +): Promise => { const stat = await fileService.resolve(rootURI, { resolveMetadata: false }); if (!stat.isDirectory) { return { children: null, hasNextPage: false, hasPrevPage: false, itemsRemaining: 0 }; @@ -107,7 +107,7 @@ export const computeDirectoryTree1Deep = async ( }; }; -export const stringifyDirectoryTree1Deep = (params: BuiltinToolCallParams['ls_dir'], result: BuiltinToolResultType['ls_dir']): string => { +export const stringifyDirectoryTree1Deep = (params: ToolCallParams['ls_dir'], result: ToolResultType['ls_dir']): string => { if (!result.children) { return `Error: ${params.uri} is not a directory`; } @@ -122,12 +122,14 @@ export const stringifyDirectoryTree1Deep = (params: BuiltinToolCallParams['ls_di for (let i = 0; i < entries.length; i++) { const entry = entries[i]; const isLast = i === entries.length - 1 && !result.hasNextPage; + // allow-any-unicode-next-line const prefix = isLast ? '└── ' : '├── '; output += `${prefix}${entry.name}${entry.isDirectory ? '/' : ''}${entry.isSymbolicLink ? ' (symbolic link)' : ''}\n`; } if (result.hasNextPage) { + // allow-any-unicode-next-line output += `└── (${result.itemsRemaining} results remaining...)\n`; } @@ -253,7 +255,9 @@ const renderChildrenCombined = async ( const isLast = (i === itemsToProcess.length - 1) && !hasMoreItems; // Create the tree branch symbols + // allow-any-unicode-next-line const branchSymbol = isLast ? '└── ' : '├── '; + // allow-any-unicode-next-line const childLine = `${parentPrefix}${branchSymbol}${child.name}${child.isDirectory ? '/' : ''}${child.isSymbolicLink ? ' (symbolic link)' : ''}\n`; // Check if adding this line would exceed the limit @@ -266,6 +270,7 @@ const renderChildrenCombined = async ( remainingChars -= childLine.length; fileCount.count++; + // allow-any-unicode-next-line const nextLevelPrefix = parentPrefix + (isLast ? ' ' : '│ '); // Skip processing children for git ignored directories @@ -304,6 +309,7 @@ const renderChildrenCombined = async ( // Add a message if we truncated the items due to maxItemsPerDir if (hasMoreItems) { const remainingCount = children.length - itemsToProcess.length; + // allow-any-unicode-next-line const truncatedLine = `${parentPrefix}└── (${remainingCount} more items not shown...)\n`; if (truncatedLine.length <= remainingChars) { @@ -427,9 +433,9 @@ class DirectoryStrService extends Disposable implements IDirectoryStrService { wasCutOff = initialCutOff; } - let c = content.substring(0, MAX_DIRSTR_CHARS_TOTAL_TOOL) + let c = content // return full content (truncation happens in chatThreadService) c = `Directory of ${uri.fsPath}:\n${content}` - if (wasCutOff) c = `${c}\n...Result was truncated...` + // if (wasCutOff) c = `${c}\n...Result was truncated...` return c } diff --git a/src/vs/platform/void/common/dynamicModelService.ts b/src/vs/platform/void/common/dynamicModelService.ts new file mode 100644 index 00000000000..3e0aba9e602 --- /dev/null +++ b/src/vs/platform/void/common/dynamicModelService.ts @@ -0,0 +1,389 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ +import { ProviderName } from './voidSettingsTypes.js'; +import { ILogService } from '../../log/common/log.js'; +import { + VoidStaticModelInfo, ModelOverrides, + inferCapabilitiesFromOpenRouterModel, + OpenRouterModel, + ModelApiConfig +} from './modelInference.js'; +import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { IRemoteModelsService } from './remoteModelsService.js'; + +export interface IDynamicModelService { + readonly _serviceBrand: undefined; + initialize(): Promise; + getDynamicCapabilities(modelName: string): VoidStaticModelInfo | null; + getAllDynamicCapabilities(): Record; + getSupportedParameters(modelId: string): string[] | null; + getDefaultParameters(modelId: string): Record | null; + getModelCapabilitiesWithFallback( + providerName: ProviderName, + modelName: string, + overridesOfModel?: ModelOverrides | undefined + ): Promise; +} + +export const IDynamicModelService = createDecorator('dynamicModelService'); + +type ModelsCache = { ts: number; data: OpenRouterModel[] }; +const MODELS_CACHE_KEY = 'void.openrouter.models.cache.v1'; +const MODELS_TTL_MS = 24 * 60 * 60 * 1000; + +function isModelsResponse(x: unknown): x is { data: OpenRouterModel[] } { + return !!x && typeof x === 'object' && Array.isArray((x as any).data); +} + +export class DynamicModelService implements IDynamicModelService { + declare readonly _serviceBrand: undefined; + + private dynamicCapabilities: Map = new Map(); + private supportedParams: Map = new Map(); + private defaultParams: Map> = new Map(); + // Alias indices to resolve models not referenced by OpenRouter id + private aliasByCanonical: Map = new Map(); // canonical_slug -> id + private aliasByHF: Map = new Map(); // hugging_face_id -> id + private isInitialized = false; + + constructor( + @IRemoteModelsService private readonly remoteModelsService: IRemoteModelsService, + @ILogService private readonly logService: ILogService + ) { } + + async initialize(): Promise { + if (this.isInitialized) return; + + this.safeDebug('[DynamicModelService] initialize() start'); + + try { + const cache = this.readCache(); + const now = Date.now(); + const cacheFresh = cache && (now - cache.ts) < MODELS_TTL_MS; + + if (cacheFresh) { + this.safeDebug( + '[DynamicModelService] cache hit (age=%dms, models=%d)', + now - cache!.ts, + cache!.data.length + ); + + this.setFromModels(cache!.data); + this.isInitialized = true; + return; + } + + if (cache) { + this.safeDebug( + '[DynamicModelService] cache stale (age=%dms, models=%d) -> refreshing', + now - cache.ts, + cache.data.length + ); + } else { + this.safeDebug('[DynamicModelService] cache miss -> fetching OpenRouter models'); + } + + const models = await this.fetchOpenRouterModels(); + + this.safeDebug( + '[DynamicModelService] fetched OpenRouter models: %d; sample: %s', + models.data.length, + this.summarizeModelIds(models.data, 30) + ); + + this.setFromModels(models.data); + this.writeCache({ ts: now, data: models.data }); + this.isInitialized = true; + + this.safeDebug('[DynamicModelService] initialize() done (models=%d)', models.data.length); + } catch (error) { + // Fallback to any cached data + const cache = this.readCache(); + if (cache) { + this.safeWarn( + '[DynamicModelService] initialize() failed, falling back to cache (models=%d)', + cache.data.length, + error + ); + this.setFromModels(cache.data); + this.isInitialized = true; + return; + } + + this.safeError('[DynamicModelService] Failed to initialize (no cache fallback)', error); + } + } + + + private safeDebug(message: string, ...args: any[]) { + try { this.logService.debug(message, ...args); } catch { /* ignore */ } + } + + private safeWarn(message: string, ...args: any[]) { + try { this.logService.warn(message, ...args); } catch { /* ignore */ } + } + + private safeError(message: string, ...args: any[]) { + try { this.logService.error(message, ...args); } catch { /* ignore */ } + } + + private summarizeModelIds(models: OpenRouterModel[], limit = 50): string { + const ids = models.map(m => m?.id).filter(Boolean) as string[]; + const head = ids.slice(0, limit).join(', '); + return ids.length > limit ? `${head} …(+${ids.length - limit})` : head; + } + + private setFromModels(models: OpenRouterModel[]) { + this.dynamicCapabilities.clear(); + this.supportedParams.clear(); + this.defaultParams.clear(); + this.aliasByCanonical.clear(); + this.aliasByHF.clear(); + for (const model of models) { + const capabilities = inferCapabilitiesFromOpenRouterModel(model); + const modelInfo: VoidStaticModelInfo = { + ...capabilities, + modelName: model.id, + recognizedModelName: model.canonical_slug, + isUnrecognizedModel: false, + _apiConfig: this.getApiConfigForModel(model) + } as VoidStaticModelInfo & { + modelName: string, + recognizedModelName: string, + isUnrecognizedModel: false, + _apiConfig: ModelApiConfig + }; + this.dynamicCapabilities.set(model.id, modelInfo); + this.supportedParams.set(model.id, Array.isArray(model.supported_parameters) ? model.supported_parameters.slice() : []); + this.defaultParams.set(model.id, model.default_parameters && typeof model.default_parameters === 'object' ? { ...model.default_parameters } : {}); + + // Build alias indices + const norm = (s: string) => s.toLowerCase(); + if (model.canonical_slug) { + this.aliasByCanonical.set(norm(model.canonical_slug), model.id); + } + if (model.hugging_face_id) { + this.aliasByHF.set(norm(model.hugging_face_id), model.id); + } + } + } + + private async fetchOpenRouterModels(): Promise<{ data: OpenRouterModel[] }> { + const headers = { + 'HTTP-Referer': 'https://voideditor.com', + 'X-Title': 'Void', + 'Accept': 'application/json' + }; + + this.safeDebug('[DynamicModelService] GET https://openrouter.ai/api/v1/models'); + + // Prefer fetchModels; some tests/mocks rely on request() counting + let json: any; + + if (typeof (this.remoteModelsService as any).fetchModels === 'function') { + this.safeDebug('[DynamicModelService] using remoteModelsService.fetchModels()'); + json = await (this.remoteModelsService as any).fetchModels('https://openrouter.ai/api/v1/models', headers); + } else if (typeof (this.remoteModelsService as any).request === 'function') { + this.safeDebug('[DynamicModelService] using remoteModelsService.request() fallback path'); + + const res = await (this.remoteModelsService as any).request( + { url: 'https://openrouter.ai/api/v1/models', headers }, + undefined + ); + + if (res?.res?.statusCode && res.res.statusCode >= 400) { + throw new Error(`OpenRouter /models HTTP ${res.res.statusCode}`); + } + + const chunks: string[] = []; + await new Promise((resolve) => { + res.stream.on('data', (d: any) => chunks.push(String(d))); + res.stream.on('end', () => resolve()); + }); + + try { json = JSON.parse(chunks.join('')); } catch { json = null; } + } else { + throw new Error('IRemoteModelsService has neither fetchModels() nor request()'); + } + + if (!isModelsResponse(json)) { + this.safeWarn('[DynamicModelService] invalid response format from OpenRouter /models'); + throw new Error('Invalid response format from OpenRouter API'); + } + + this.safeDebug( + '[DynamicModelService] /models ok: %d models; sample: %s', + json.data.length, + this.summarizeModelIds(json.data, 25) + ); + + return json; + } + + private getApiConfigForModel(_model: OpenRouterModel): ModelApiConfig { + return { + apiStyle: 'openai-compatible', + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + endpoint: 'https://openrouter.ai/api/v1', + auth: { header: 'Authorization', format: 'Bearer' } + }; + } + + private resolveModelId(query: string): string | null { + if (!query) return null; + // 1) exact id + if (this.dynamicCapabilities.has(query)) return query; + const q = query.toLowerCase(); + // 2) alias by canonical_slug + const byCanon = this.aliasByCanonical.get(q); + if (byCanon && this.dynamicCapabilities.has(byCanon)) return byCanon; + // 3) alias by hugging_face_id + const byHF = this.aliasByHF.get(q); + if (byHF && this.dynamicCapabilities.has(byHF)) return byHF; + // 4) try short-id resolution: match any known id whose suffix after '/' equals query + // (common for local/custom providers storing short model names) + for (const id of this.dynamicCapabilities.keys()) { + const i = id.indexOf('/'); + if (i > 0 && id.slice(i + 1).toLowerCase() === q) return id; + } + // 5) try normalized exact id + const normalized = q; + if (this.dynamicCapabilities.has(normalized)) return normalized; + return null; + } + + getDynamicCapabilities(modelName: string): VoidStaticModelInfo | null { + const resolved = this.resolveModelId(modelName); + if (resolved) return this.dynamicCapabilities.get(resolved) || null; + return null; + } + + getAllDynamicCapabilities(): Record { + const result: Record = {}; + for (const [name, capabilities] of this.dynamicCapabilities) { + result[name] = capabilities; + } + return result; + } + + getSupportedParameters(modelId: string): string[] | null { + const resolved = this.resolveModelId(modelId) || modelId; + return this.supportedParams.get(resolved) || null; + } + + getDefaultParameters(modelId: string): Record | null { + const resolved = this.resolveModelId(modelId) || modelId; + return this.defaultParams.get(resolved) || null; + } + + async getModelCapabilitiesWithFallback( + providerName: ProviderName, + modelName: string, + overridesOfModel?: ModelOverrides | undefined + ): Promise< + VoidStaticModelInfo & + ( + | { modelName: string; recognizedModelName: string; isUnrecognizedModel: false } + | { modelName: string; recognizedModelName?: undefined; isUnrecognizedModel: true } + ) + > { + + const dynamicCapabilities = this.getDynamicCapabilities(modelName); + if (dynamicCapabilities) { + // providerName — case-insensitive + let providerOverridesAny: any | undefined; + if (overridesOfModel) { + const providerLower = String(providerName).toLowerCase(); + for (const key of Object.keys(overridesOfModel as any)) { + if (key.toLowerCase() === providerLower) { + providerOverridesAny = (overridesOfModel as any)[key]; + break; + } + } + } + const modelOverrides = providerOverridesAny ? providerOverridesAny[modelName] : undefined; + + return { + ...dynamicCapabilities, + ...(modelOverrides || {}), + modelName, + recognizedModelName: modelName, + isUnrecognizedModel: false + }; + } + + // 2. TODO: fallback static capabillity + + + return { + modelName, + contextWindow: 4096, + reservedOutputTokenSpace: 4096, + cost: { input: 0, output: 0 }, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + supportCacheControl: false, + reasoningCapabilities: false, + isUnrecognizedModel: true + }; + } + + + private getStorage(): { getItem(key: string): string | null; setItem(key: string, value: string): void } | null { + try { + const g: any = globalThis as any; + + if (g.__voidDynamicModelStorage__) { + return g.__voidDynamicModelStorage__; + } + if (typeof g.localStorage === 'undefined') { + return null; + } + const storage = g.localStorage; + if (!storage || typeof storage.getItem !== 'function' || typeof storage.setItem !== 'function') { + return null; + } + return storage; + } catch { + return null; + } + } + + private readCache(): ModelsCache | null { + try { + const storage = this.getStorage(); + if (!storage) { + return null; + } + const raw = storage.getItem(MODELS_CACHE_KEY); + if (!raw) return null; + const obj = JSON.parse(raw) as ModelsCache; + if (!obj || typeof obj.ts !== 'number' || !Array.isArray(obj.data)) return null; + return obj; + } catch { + return null; + } + } + + private writeCache(v: ModelsCache) { + try { + const storage = this.getStorage(); + if (!storage) { + return; + } + storage.setItem(MODELS_CACHE_KEY, JSON.stringify(v)); + } catch { + // ignore + } + } +} + +registerSingleton(IDynamicModelService, DynamicModelService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/void/common/editCodeServiceTypes.ts b/src/vs/platform/void/common/editCodeServiceTypes.ts similarity index 92% rename from src/vs/workbench/contrib/void/common/editCodeServiceTypes.ts rename to src/vs/platform/void/common/editCodeServiceTypes.ts index 4aa09de332e..1dd993bc898 100644 --- a/src/vs/workbench/contrib/void/common/editCodeServiceTypes.ts +++ b/src/vs/platform/void/common/editCodeServiceTypes.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { URI } from '../../../../base/common/uri.js'; +import { URI } from '../../../base/common/uri.js'; export type ComputedDiff = { type: 'edit'; @@ -37,12 +37,9 @@ export type CommonZoneProps = { diffareaid: number; startLine: number; endLine: number; - _URI: URI; // typically we get the URI from model - } - export type CtrlKZone = { type: 'CtrlKZone'; originalCode?: undefined; @@ -94,7 +91,8 @@ export type DiffZone = { }; editorId?: undefined; linkedStreamingDiffZone?: undefined; - _removeStylesFns: Set // these don't remove diffs or this diffArea, only their styles + _removeStylesFns: Set; // these don't remove diffs or this diffArea, only their styles + applyBoxId?: string; // Optional applyBoxId to associate with the diff zone } & CommonZoneProps @@ -108,8 +106,6 @@ export const diffAreaSnapshotKeys = [ ] as const satisfies (keyof DiffArea)[] - - export type DiffAreaSnapshotEntry = Pick export type VoidFileSnapshot = { diff --git a/src/vs/platform/void/common/helpers/colors.ts b/src/vs/platform/void/common/helpers/colors.ts new file mode 100644 index 00000000000..8e25a98b4c8 --- /dev/null +++ b/src/vs/platform/void/common/helpers/colors.ts @@ -0,0 +1,49 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { Color, RGBA } from '../../../../base/common/color.js'; +import { registerColor } from '../../../../platform/theme/common/colorUtils.js'; + +// editCodeService colors +const sweepBG = new Color(new RGBA(100, 100, 100, .2)); +const highlightBG = new Color(new RGBA(100, 100, 100, .1)); +const sweepIdxBG = new Color(new RGBA(100, 100, 100, .5)); + +const acceptBGDark = new Color(new RGBA(155, 185, 85, .14)); +const acceptBGLight = new Color(new RGBA(46, 160, 67, .2)); +const acceptBorderDark = new Color(new RGBA(155, 185, 85, .8)); +const acceptBorderLight = new Color(new RGBA(46, 160, 67, .9)); + +const rejectBGDark = new Color(new RGBA(255, 70, 70, .14)); +const rejectBGLight = new Color(new RGBA(220, 38, 38, .2)); +const rejectBorderDark = new Color(new RGBA(255, 120, 120, .8)); +const rejectBorderLight = new Color(new RGBA(220, 38, 38, .9)); + +// Widget colors +export const acceptAllBg = 'rgb(30, 133, 56)' +export const acceptBg = 'rgb(26, 116, 48)' +export const acceptBorder = '1px solid rgb(20, 86, 38)' + +export const rejectAllBg = 'rgb(207, 40, 56)' +export const rejectBg = 'rgb(180, 35, 49)' +export const rejectBorder = '1px solid rgb(142, 28, 39)' + +export const buttonFontSize = '11px' +export const buttonTextColor = 'white' + + + +const configOfTheme = ({ dark, light }: { dark: Color; light: Color }) => { + return { dark, light, hcDark: dark, hcLight: light, } +} + +// gets converted to --vscode-void-greenBG, see void.css, asCssVariable +registerColor('void.greenBG', configOfTheme({ dark: acceptBGDark, light: acceptBGLight }), '', true); +registerColor('void.redBG', configOfTheme({ dark: rejectBGDark, light: rejectBGLight }), '', true); +registerColor('void.greenBorder', configOfTheme({ dark: acceptBorderDark, light: acceptBorderLight }), '', true); +registerColor('void.redBorder', configOfTheme({ dark: rejectBorderDark, light: rejectBorderLight }), '', true); +registerColor('void.sweepBG', configOfTheme({ dark: sweepBG, light: sweepBG }), '', true); +registerColor('void.highlightBG', configOfTheme({ dark: highlightBG, light: highlightBG }), '', true); +registerColor('void.sweepIdxBG', configOfTheme({ dark: sweepIdxBG, light: sweepIdxBG }), '', true); diff --git a/src/vs/platform/void/common/helpers/extractCodeFromResult.ts b/src/vs/platform/void/common/helpers/extractCodeFromResult.ts new file mode 100644 index 00000000000..9552d61869c --- /dev/null +++ b/src/vs/platform/void/common/helpers/extractCodeFromResult.ts @@ -0,0 +1,172 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export class SurroundingsRemover { + readonly originalS: string + i: number + j: number + + // string is s[i...j] + + constructor(s: string) { + this.originalS = s + this.i = 0 + this.j = s.length - 1 + } + value() { + return this.originalS.substring(this.i, this.j + 1) + } + + // returns whether it removed the whole prefix + removePrefix = (prefix: string): boolean => { + let offset = 0 + // console.log('prefix', prefix, Math.min(this.j, prefix.length - 1)) + while (this.i <= this.j && offset <= prefix.length - 1) { + if (this.originalS.charAt(this.i) !== prefix.charAt(offset)) + break + offset += 1 + this.i += 1 + } + return offset === prefix.length + } + + // // removes suffix from right to left + removeSuffix = (suffix: string): boolean => { + // e.g. suffix =
, the string is 
hi

= 1; len -= 1) { + if (s.endsWith(suffix.substring(0, len))) { // the end of the string equals a prefix + this.j -= len + return len === suffix.length + } + } + return false + } + // removeSuffix = (suffix: string): boolean => { + // let offset = 0 + + // while (this.j >= Math.max(this.i, 0)) { + // if (this.originalS.charAt(this.j) !== suffix.charAt(suffix.length - 1 - offset)) + // break + // offset += 1 + // this.j -= 1 + // } + // return offset === suffix.length + // } + + // either removes all or nothing + removeFromStartUntilFullMatch = (until: string, alsoRemoveUntilStr: boolean) => { + const index = this.originalS.indexOf(until, this.i) + + if (index === -1) { + // this.i = this.j + 1 + return false + } + // console.log('index', index, until.length) + + if (alsoRemoveUntilStr) + this.i = index + until.length + else + this.i = index + + return true + } + + + removeCodeBlock = () => { + // Match either: + // 1. ```language\n\n```\n? + // 2. ```\n```\n? + + const pm = this + const foundCodeBlock = pm.removePrefix('```') + if (!foundCodeBlock) return false + + pm.removeFromStartUntilFullMatch('\n', true) // language + + const j = pm.j + let foundCodeBlockEnd = pm.removeSuffix('```') + + if (pm.j === j) foundCodeBlockEnd = pm.removeSuffix('```\n') // if no change, try again with \n after ``` + + if (!foundCodeBlockEnd) return false + + pm.removeSuffix('\n') // remove the newline before ``` + return true + } + + + deltaInfo = (recentlyAddedTextLen: number) => { + // aaaaaatextaaaaaa{recentlyAdded} + // ^ i j len + // | + // recentyAddedIdx + const recentlyAddedIdx = this.originalS.length - recentlyAddedTextLen + const actualDelta = this.originalS.substring(Math.max(this.i, recentlyAddedIdx), this.j + 1) + const ignoredSuffix = this.originalS.substring(Math.max(this.j + 1, recentlyAddedIdx), Infinity) + return [actualDelta, ignoredSuffix] as const + } +} + + + +export const extractCodeFromRegular = ({ text, recentlyAddedTextLen }: { text: string, recentlyAddedTextLen: number }): [string, string, string] => { + + const pm = new SurroundingsRemover(text) + + pm.removeCodeBlock() + + const s = pm.value() + const [delta, ignoredSuffix] = pm.deltaInfo(recentlyAddedTextLen) + + return [s, delta, ignoredSuffix] +} + + +// Ollama has its own FIM, we should not use this if we use that +export const extractCodeFromFIM = ({ text, recentlyAddedTextLen, midTag, }: { text: string, recentlyAddedTextLen: number, midTag: string }): [string, string, string] => { + + /* ------------- summary of the regex ------------- + [optional ` | `` | ```] + (match optional_language_name) + [optional strings here] + [required tag] + (match the stuff between mid tags) + [optional tag] + [optional ` | `` | ```] + */ + + const pm = new SurroundingsRemover(text) + + pm.removeCodeBlock() + + const foundMid = pm.removePrefix(`<${midTag}>`) + + if (foundMid) { + pm.removeSuffix(`\n`) // sometimes outputs \n + pm.removeSuffix(``) + } + const s = pm.value() + const [delta, ignoredSuffix] = pm.deltaInfo(recentlyAddedTextLen) + + return [s, delta, ignoredSuffix] +} + +export type ExtractedSearchReplaceBlock = { + state: 'writingOriginal' | 'writingFinal' | 'done', + orig: string, + final: string, +} + + +export const endsWithAnyPrefixOf = (str: string, anyPrefix: string) => { + // for each prefix + for (let i = anyPrefix.length; i >= 1; i--) { // i >= 1 because must not be empty string + const prefix = anyPrefix.slice(0, i) + if (str.endsWith(prefix)) return prefix + } + return null +} diff --git a/src/vs/workbench/contrib/void/common/helpers/systemInfo.ts b/src/vs/platform/void/common/helpers/systemInfo.ts similarity index 63% rename from src/vs/workbench/contrib/void/common/helpers/systemInfo.ts rename to src/vs/platform/void/common/helpers/systemInfo.ts index 85b909b23b3..9b92f5b43c9 100644 --- a/src/vs/workbench/contrib/void/common/helpers/systemInfo.ts +++ b/src/vs/platform/void/common/helpers/systemInfo.ts @@ -3,12 +3,6 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { isLinux, isMacintosh, isWindows } from '../../../../../base/common/platform.js'; - -// import { OS, OperatingSystem } from '../../../../../base/common/platform.js'; -// alternatively could use ^ and OS === OperatingSystem.Windows ? ... - - - +import { isLinux, isMacintosh, isWindows } from '../../../../base/common/platform.js'; export const os = isWindows ? 'windows' : isMacintosh ? 'mac' : isLinux ? 'linux' : null diff --git a/src/vs/workbench/contrib/void/common/helpers/util.ts b/src/vs/platform/void/common/helpers/util.ts similarity index 60% rename from src/vs/workbench/contrib/void/common/helpers/util.ts rename to src/vs/platform/void/common/helpers/util.ts index b2309a3ff7f..f46f81e9d59 100644 --- a/src/vs/workbench/contrib/void/common/helpers/util.ts +++ b/src/vs/platform/void/common/helpers/util.ts @@ -1,4 +1,7 @@ - +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ export const separateOutFirstLine = (content: string): [string, string] | [string, undefined] => { const newLineIdx = content.indexOf('\r\n') if (newLineIdx !== -1) { diff --git a/src/vs/platform/void/common/jsonTypes.ts b/src/vs/platform/void/common/jsonTypes.ts new file mode 100644 index 00000000000..b7325642566 --- /dev/null +++ b/src/vs/platform/void/common/jsonTypes.ts @@ -0,0 +1,24 @@ +export type JsonPrimitive = string | number | boolean | null; +export type JsonValue = JsonPrimitive | JsonObject | JsonValue[]; +export type JsonObject = { [key: string]: JsonValue | undefined }; + +export type ToolOutputInput = string | JsonValue; + +export function isJsonObject(v: unknown): v is JsonObject { + return typeof v === 'object' && v !== null && !Array.isArray(v); +} + +export function toJsonObject(v: unknown): JsonObject { + return isJsonObject(v) ? v : {}; +} + +export function getStringField(o: JsonObject | null | undefined, key: string): string | undefined { + if (!o) return undefined; + const v = o[key]; + return typeof v === 'string' ? v : undefined; +} + +export function stringifyUnknown(e: unknown): string { + if (e instanceof Error) return e.message; + return String(e); +} diff --git a/src/vs/platform/void/common/loopGuard.ts b/src/vs/platform/void/common/loopGuard.ts new file mode 100644 index 00000000000..1a4378dc93a --- /dev/null +++ b/src/vs/platform/void/common/loopGuard.ts @@ -0,0 +1,178 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// Shared lightweight heuristics to detect potential infinite loops in agent-style +// LLM orchestrations (Void chat + ACP). The goals are: +// - keep behaviour deterministic and cheap (no heavy NLP) +// - be conservative to avoid false positives +// - provide a single place to tune thresholds for both ACP / non-ACP flows. + +export const LOOP_DETECTED_MESSAGE = 'Loop detected, stop stream'; + +export type LoopDetectionReason = 'max_turns' | 'assistant_repeat' | 'tool_repeat'; + +export type LoopDetectionResult = + | { isLoop: false } + | { isLoop: true; reason: LoopDetectionReason; details?: string }; + +export interface LoopDetectorOptions { + /** Maximum number of assistant turns per single user prompt (LLM calls). */ + maxTurnsPerPrompt: number; + /** How many times the same assistant first-line prefix may repeat. */ + maxSameAssistantPrefix: number; + /** How many times the same tool(name+args) may be invoked in one prompt. */ + maxSameToolCall: number; + /** Prefix length (in chars) used for assistant repetition fingerprinting. */ + assistantPrefixLength: number; +} + +const DEFAULT_OPTIONS: LoopDetectorOptions = { + maxTurnsPerPrompt: 12, + maxSameAssistantPrefix: 3, + maxSameToolCall: 3, + assistantPrefixLength: 120, +}; + +export class LLMLoopDetector { + private readonly opts: LoopDetectorOptions; + private assistantTurns = 0; + private readonly assistantPrefixCounts = new Map(); + private readonly toolSignatureCounts = new Map(); + + constructor(options?: Partial) { + this.opts = { ...DEFAULT_OPTIONS, ...(options ?? {}) }; + } + + /** + * Register a completed assistant turn (one LLM response). Returns a loop + * signal if any of the assistant-based heuristics trigger. + */ + registerAssistantTurn(text: string | undefined | null): LoopDetectionResult { + this.assistantTurns++; + + // Hard cap on number of assistant responses per prompt. + if (this.assistantTurns > this.opts.maxTurnsPerPrompt) { + return { + isLoop: true, + reason: 'max_turns', + details: `assistantTurns=${this.assistantTurns} > maxTurnsPerPrompt=${this.opts.maxTurnsPerPrompt}`, + }; + } + + if (!text) { + return { isLoop: false }; + } + + const prefix = this._normalizedAssistantPrefix(text); + if (!prefix) { + return { isLoop: false }; + } + + const prev = this.assistantPrefixCounts.get(prefix) ?? 0; + const next = prev + 1; + this.assistantPrefixCounts.set(prefix, next); + + if (next > this.opts.maxSameAssistantPrefix) { + return { + isLoop: true, + reason: 'assistant_repeat', + details: `assistant first-line prefix repeated ${next} times`, + }; + } + + return { isLoop: false }; + } + + /** + * Register a tool call candidate (name+args). Called before actually + * executing the tool so we can short-circuit potentially useless loops. + */ + registerToolCall(name: string | undefined | null, args: unknown): LoopDetectionResult { + const n = (name ?? '').trim(); + if (!n) { + return { isLoop: false }; + } + + const sig = this._signatureForTool(n, args); + const prev = this.toolSignatureCounts.get(sig) ?? 0; + const next = prev + 1; + this.toolSignatureCounts.set(sig, next); + + if (next > this.opts.maxSameToolCall) { + return { + isLoop: true, + reason: 'tool_repeat', + details: `tool ${n} with same arguments called ${next} times`, + }; + } + + return { isLoop: false }; + } + + private _normalizedAssistantPrefix(text: string): string | null { + const trimmed = text.trim(); + if (!trimmed) return null; + + const firstLine = trimmed.split(/\r?\n/, 1)[0] ?? ''; + let normalized = firstLine + .toLowerCase() + .replace(/\s+/g, ' ') // collapse whitespace + .trim(); + + if (!normalized) return null; + + // Use only the first couple of words as the canonical "prefix" so that + // small trailing variations like "Repeat me again" vs "Repeat me" still + // map to the same fingerprint. + const words = normalized.split(' '); + const maxWords = 2; + normalized = words.slice(0, maxWords).join(' '); + + // Still cap by assistantPrefixLength to avoid overly long keys. + normalized = normalized.slice(0, this.opts.assistantPrefixLength).trim(); + + return normalized || null; + } + + private _signatureForTool(name: string, args: unknown): string { + const n = name.trim().toLowerCase(); + const argsJson = this._stableStringify(args); + return `${n}::${argsJson}`; + } + + private _stableStringify(value: any): string { + const seen = new Set(); + + const helper = (v: any): any => { + if (v === null || typeof v !== 'object') { + return v; + } + if (seen.has(v)) { + return '[Circular]'; + } + seen.add(v); + + if (Array.isArray(v)) { + return v.map(helper); + } + + const out: any = {}; + for (const key of Object.keys(v).sort()) { + out[key] = helper(v[key]); + } + return out; + }; + + try { + return JSON.stringify(helper(value)); + } catch { + try { + return JSON.stringify(String(value)); + } catch { + return '"[Unserializable]"'; + } + } + } +} diff --git a/src/vs/platform/void/common/mcpServiceTypes.ts b/src/vs/platform/void/common/mcpServiceTypes.ts new file mode 100644 index 00000000000..28ab8a2991d --- /dev/null +++ b/src/vs/platform/void/common/mcpServiceTypes.ts @@ -0,0 +1,153 @@ + +export interface MCPTool { + /** Unique tool identifier */ + name: string; + /** Human‑readable description */ + description?: string; + /** JSON schema describing expected arguments */ + inputSchema?: Record; + /** Free‑form annotations describing behaviour, security, etc. */ + annotations?: Record; +} + +export interface MCPConfigFileEntryJSON { + // Command-based server properties + command?: string; + args?: string[]; + env?: Record; + /** Optional list of MCP tool names to exclude for this server. */ + excludeTools?: string[]; + + // URL-based server properties + url?: URL; + headers?: Record; +} + +export interface MCPConfigFileJSON { + mcpServers: Record; +} + + +export type MCPServer = { + // Command-based server properties + tools: MCPTool[], + status: 'loading' | 'success' | 'offline', + command?: string, + error?: string, +} | { + tools?: undefined, + status: 'error', + command?: string, + error: string, +} + +export interface MCPServerOfName { + [serverName: string]: MCPServer; +} + +export type MCPServerEvent = { + name: string; + prevServer?: MCPServer; + newServer?: MCPServer; +} +export type MCPServerEventResponse = { response: MCPServerEvent } + +export interface MCPConfigFileParseErrorResponse { + response: { + type: 'config-file-error'; + error: string | null; + } +} + + +type MCPToolResponseType = 'text' | 'image' | 'audio' | 'resource' | 'error'; + +type ResponseImageTypes = 'image/png' | 'image/jpeg' | 'image/gif' | 'image/webp' | 'image/svg+xml' | 'image/bmp' | 'image/tiff' | 'image/vnd.microsoft.icon'; + +interface ImageData { + data: string; + mimeType: ResponseImageTypes; +} + +interface MCPToolResponseBase { + toolName: string; + serverName?: string; + event: MCPToolResponseType; + text?: string; + image?: ImageData; +} + +type MCPToolResponseConstraints = { + 'text': { + image?: never; + text: string; + }; + 'error': { + image?: never; + text: string; + }; + 'image': { + text?: never; + image: ImageData; + }; + 'audio': { + text?: never; + image?: never; + }; + 'resource': { + text?: never; + image?: never; + } +} + +type MCPToolEventResponse = Omit & MCPToolResponseConstraints[T] & { event: T }; + +// Response types +export type MCPToolTextResponse = MCPToolEventResponse<'text'>; +export type MCPToolErrorResponse = MCPToolEventResponse<'error'>; +export type MCPToolImageResponse = MCPToolEventResponse<'image'>; +export type MCPToolAudioResponse = MCPToolEventResponse<'audio'>; +export type MCPToolResourceResponse = MCPToolEventResponse<'resource'>; +export type RawMCPToolCall = MCPToolTextResponse | MCPToolErrorResponse | MCPToolImageResponse | MCPToolAudioResponse | MCPToolResourceResponse; + +export interface MCPToolCallParams { + serverName: string; + toolName: string; + params: Record; +} + + + +const _sanitizeMcpToolPrefix = (serverName: string): string => { + // Avoid delimiter collisions and invalid chars in tool names + let s = (serverName ?? '').trim(); + + // Prevent "__" inside prefix (since it's our delimiter) + s = s.replace(/__+/g, '_'); + + // OpenAI-compatible function names are typically limited to [a-zA-Z0-9_-] + s = s.replace(/[^a-zA-Z0-9_-]/g, '_'); + + // Ensure it starts with a letter or underscore (more broadly compatible) + if (!/^[a-zA-Z_]/.test(s)) { + s = `mcp_${s}`; + } + + // Never return empty + return s || 'mcp'; +}; + +export const addMCPToolNamePrefix = (serverName: string, toolName: string) => { + // Format: "server_name__tool_name" + return `${_sanitizeMcpToolPrefix(serverName)}__${toolName}`; +}; + +export const removeMCPToolNamePrefix = (name: string) => { + // Remove server name prefix with __ separator + // Format: "server_name__tool_name" -> "tool_name" + const parts = name.split('__'); + if (parts.length > 1) { + return parts.slice(1).join('__'); + } + return name; +}; diff --git a/src/vs/workbench/contrib/void/common/metricsService.ts b/src/vs/platform/void/common/metricsService.ts similarity index 72% rename from src/vs/workbench/contrib/void/common/metricsService.ts rename to src/vs/platform/void/common/metricsService.ts index 853ca95534d..74248402d61 100644 --- a/src/vs/workbench/contrib/void/common/metricsService.ts +++ b/src/vs/platform/void/common/metricsService.ts @@ -3,24 +3,22 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; -import { localize2 } from '../../../../nls.js'; -import { registerAction2, Action2 } from '../../../../platform/actions/common/actions.js'; -import { INotificationService } from '../../../../platform/notification/common/notification.js'; +import { createDecorator, ServicesAccessor } from '../../instantiation/common/instantiation.js'; +import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js'; +import { IMainProcessService } from '../../ipc/common/mainProcessService.js'; +import { localize2 } from '../../../nls.js'; +import { registerAction2, Action2 } from '../../actions/common/actions.js'; +import { INotificationService } from '../../notification/common/notification.js'; export interface IMetricsService { readonly _serviceBrand: undefined; capture(event: string, params: Record): void; - setOptOut(val: boolean): void; getDebuggingProperties(): Promise; } export const IMetricsService = createDecorator('metricsService'); - // implemented by calling channel export class MetricsService implements IMetricsService { @@ -39,11 +37,6 @@ export class MetricsService implements IMetricsService { this.metricsService.capture(...params); } - setOptOut(...params: Parameters) { - this.metricsService.setOptOut(...params); - } - - // anything transmitted over a channel must be async even if it looks like it doesn't have to be async getDebuggingProperties(): Promise { return this.metricsService.getDebuggingProperties() diff --git a/src/vs/platform/void/common/modelInference.ts b/src/vs/platform/void/common/modelInference.ts new file mode 100644 index 00000000000..b20a6c399a6 --- /dev/null +++ b/src/vs/platform/void/common/modelInference.ts @@ -0,0 +1,684 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { IDynamicModelService } from './dynamicModelService.js'; +import { specialToolFormat, supportsSystemMessage, ProviderName, OverridesOfModel, FeatureName, ModelSelectionOptions } from './voidSettingsTypes.js'; + +export type VoidStaticModelInfo = { + contextWindow: number; + reservedOutputTokenSpace: number | null + + supportsSystemMessage: supportsSystemMessage; + specialToolFormat?: specialToolFormat; + supportsFIM: boolean; + fimTransport?: 'openai-compatible' | 'mistral-native' | 'ollama-native' | 'emulated'; + // Whether this model is allowed to use provider-specific prompt caching via + // cache_control breakpoints (Anthropic / OpenRouter / Gemini-on-OpenRouter, etc.). + // Default is false and can be opted into per model via overrides. + supportCacheControl?: boolean; + // Input modalities supported by the model (e.g. ["text", "image", "audio"]) as reported by OpenRouter/underlying provider + inputModalities?: string[]; + reasoningCapabilities: false | { + readonly supportsReasoning: true; + readonly canTurnOffReasoning: boolean; + readonly canIOReasoning: boolean; + readonly reasoningReservedOutputTokenSpace?: number; + readonly reasoningSlider?: + | undefined + | { type: 'budget_slider'; min: number; max: number; default: number } + | { type: 'effort_slider'; values: string[]; default: string } + readonly openSourceThinkTags?: [string, string]; + readonly hideEncryptedReasoning?: boolean; + }; + + cost: { + input: number; + output: number; + cache_read?: number; + cache_write?: number; + } +} + +export type ModelOverrides = Pick + +let __dynamicModelService: IDynamicModelService | null = null; + +export const setDynamicModelService = (svc: IDynamicModelService) => { + __dynamicModelService = svc; +}; + +const defaultModelOptions = { + contextWindow: 1000_000, + reservedOutputTokenSpace: 4_096, + cost: { input: 0, output: 0 }, + supportsSystemMessage: 'system-role', + supportsFIM: false, + supportCacheControl: false, + reasoningCapabilities: false, +} as const satisfies VoidStaticModelInfo; + +export const getModelCapabilities = ( + providerName: ProviderName, + modelName: string, + overridesOfModel: OverridesOfModel | undefined +): VoidStaticModelInfo & ( + | { modelName: string; recognizedModelName: string; isUnrecognizedModel: false } + | { modelName: string; recognizedModelName?: undefined; isUnrecognizedModel: true } +) => { + + const findOverrides = (overrides: OverridesOfModel | undefined, prov: ProviderName, model: string): Partial | undefined => { + if (!overrides) return undefined; + const provKey = Object.keys(overrides).find(k => k.toLowerCase() === String(prov).toLowerCase()); + if (!provKey) return undefined; + const byModel = (overrides as any)[provKey] as Record | undefined>; + let o = byModel?.[model]; + if (o === undefined && model.includes('/')) { + const afterSlash = model.slice(model.indexOf('/') + 1); + o = byModel?.[afterSlash]; + } + return o; + }; + + try { + const dynamicCaps = __dynamicModelService?.getDynamicCapabilities(modelName); + if (dynamicCaps) { + const overrides = findOverrides(overridesOfModel, providerName, modelName); + const merged: any = { + ...dynamicCaps, + ...(overrides || {}), + modelName, + recognizedModelName: modelName, + isUnrecognizedModel: false, + }; + + + + const rc = merged.reasoningCapabilities; + if (rc && typeof rc === 'object' && (rc as any).hideEncryptedReasoning === undefined) { + merged.reasoningCapabilities = { ...(rc as any), hideEncryptedReasoning: true }; + } + + return merged as VoidStaticModelInfo & { modelName: string; recognizedModelName: string; isUnrecognizedModel: false }; + } + } catch (error) { + console.warn('[getModelCapabilities] Dynamic lookup failed:', error); + } + + + const overrides = findOverrides(overridesOfModel, providerName, modelName); + const base: any = { + ...defaultModelOptions, + ...(overrides || {}), + modelName, + isUnrecognizedModel: true, + }; + + const rc = base.reasoningCapabilities; + if (rc && typeof rc === 'object' && (rc as any).hideEncryptedReasoning === undefined) { + base.reasoningCapabilities = { ...(rc as any), hideEncryptedReasoning: true }; + } + + return base as VoidStaticModelInfo & { modelName: string; isUnrecognizedModel: true }; +}; + +export const getReservedOutputTokenSpace = ( + providerName: ProviderName, + modelName: string, + opts: { isReasoningEnabled: boolean, overridesOfModel: OverridesOfModel | undefined } +) => { + const capabilities = getModelCapabilities(providerName, modelName, opts.overridesOfModel); + const { + reasoningCapabilities, + reservedOutputTokenSpace, + } = capabilities; + return opts.isReasoningEnabled && reasoningCapabilities ? reasoningCapabilities.reasoningReservedOutputTokenSpace : reservedOutputTokenSpace; +}; + +export const getIsReasoningEnabledState = ( + featureName: FeatureName, + providerName: string, + modelName: string, + modelSelectionOptions: ModelSelectionOptions | undefined, + overridesOfModel: OverridesOfModel | undefined, +) => { + const capabilities = getModelCapabilities(providerName as ProviderName, modelName, overridesOfModel); + const rc = capabilities.reasoningCapabilities as (false | { + supportsReasoning?: boolean; + canTurnOffReasoning?: boolean; + }); + + // No reasoning support at all + if (!rc || !rc || (typeof rc === 'object' && rc.supportsReasoning === false)) { + return false; + } + + // If the model cannot turn off reasoning, it must always be enabled, + // regardless of any previously persisted user option. + if (typeof rc === 'object' && rc.canTurnOffReasoning === false) { + return true; + } + + // Otherwise (toggle allowed), respect the stored value if present, + // falling back to feature defaults (Chat => enabled by default). + const defaultEnabledVal = featureName === 'Chat'; + return modelSelectionOptions?.reasoningEnabled ?? defaultEnabledVal; +}; + +// Reasoning IO wiring per provider/api style +export type SendableReasoningInfo = + | { type: 'budget_slider_value'; isReasoningEnabled: true; reasoningBudget: number } + | { type: 'effort_slider_value'; isReasoningEnabled: true; reasoningEffort: string } + | { type: 'enabled_only'; isReasoningEnabled: true } + | null; + +type ProviderReasoningIOSettings = { + input?: { + includeInPayload?: (reasoningState: SendableReasoningInfo) => null | { [key: string]: any }; + }; + output?: + | { nameOfFieldInDelta?: string; needsManualParse?: undefined } + | { nameOfFieldInDelta?: undefined; needsManualParse?: true }; +}; + +export const getSendableReasoningInfo = ( + featureName: FeatureName, + providerName: ProviderName, + modelName: string, + modelSelectionOptions: ModelSelectionOptions | undefined, + overridesOfModel: OverridesOfModel | undefined +): SendableReasoningInfo => { + const capabilities = getModelCapabilities(providerName, modelName, overridesOfModel); + const reasoning = capabilities.reasoningCapabilities; + const isEnabled = getIsReasoningEnabledState( + featureName, + providerName, + modelName, + modelSelectionOptions, + overridesOfModel + ); + if (!isEnabled) return null; + + const budget = + typeof reasoning === 'object' && reasoning?.reasoningSlider?.type === 'budget_slider' + ? modelSelectionOptions?.reasoningBudget ?? reasoning.reasoningSlider.default + : undefined; + if (budget !== undefined) { + return { type: 'budget_slider_value', isReasoningEnabled: true, reasoningBudget: budget }; + } + + const effort = + typeof reasoning === 'object' && reasoning?.reasoningSlider?.type === 'effort_slider' + ? modelSelectionOptions?.reasoningEffort ?? reasoning.reasoningSlider.default + : undefined; + if (effort !== undefined) { + return { type: 'effort_slider_value', isReasoningEnabled: true, reasoningEffort: effort }; + } + + return { type: 'enabled_only', isReasoningEnabled: true }; +}; + +function toSlugFromProviderName(p: string): string { + const s = String(p).toLowerCase(); + if (s === 'openai') return 'openai'; + if (s === 'anthropic') return 'anthropic'; + if (s === 'gemini' || s === 'google') return 'google'; + if (s === 'google-vertex' || s === 'vertex' || s === 'googlevertex') return 'google-vertex'; + if (s === 'groq') return 'groq'; + if (s === 'mistral') return 'mistral'; + if (s === 'cohere') return 'cohere'; + if (s === 'zhipuai' || s === 'zhipu' || s === 'glm') return 'zhipuai'; + if (s === 'ollama') return 'ollama'; + if (s === 'lmstudio' || s === 'lm-studio') return 'lmstudio'; + if (s === 'vllm' || s === 'vllm-server') return 'vllm'; + if (s === 'openrouter' || s === 'open-router') return 'openrouter'; + return s; +} + +export function getProviderCapabilities( + providerName: ProviderName, + modelName?: string, + _overridesOfModel?: OverridesOfModel +): { providerReasoningIOSettings: ProviderReasoningIOSettings } { + // Prefer explicit provider slug from providerName; do not override with model prefix + const slug = toSlugFromProviderName(providerName); + + // Infer via API style where possible + let apiStyle: ModelApiConfig['apiStyle'] = 'openai-compatible'; + try { + apiStyle = getModelApiConfiguration(modelName || '')?.apiStyle ?? 'openai-compatible'; + } catch { /* ignore */ } + + // Anthropic-style + if (apiStyle === 'anthropic-style' || slug === 'anthropic') { + return { + providerReasoningIOSettings: { + input: { + includeInPayload: (reasoning) => { + if (!reasoning) return null; + if (reasoning.type === 'budget_slider_value') { + return { thinking: { type: 'enabled', budget_tokens: reasoning.reasoningBudget } }; + } + return null; + } + } + } + }; + } + + // Gemini-style handled natively in sendGeminiChat; no special IO hints + if (apiStyle === 'gemini-style' || slug === 'google' || slug === 'google-vertex') { + return { providerReasoningIOSettings: {} }; + } + + // OpenRouter specifics (OpenAI-compatible transport with reasoning field) + if (slug === 'openrouter') { + return { + providerReasoningIOSettings: { + input: { + includeInPayload: (reasoning) => { + if (!reasoning) return null; + if (reasoning.type === 'budget_slider_value') { + return { reasoning: { max_tokens: reasoning.reasoningBudget } }; + } + if (reasoning.type === 'effort_slider_value') { + return { reasoning: { effort: reasoning.reasoningEffort } }; + } + return { reasoning: { enabled: true } }; + } + }, + output: { nameOfFieldInDelta: 'reasoning' } + } + }; + } + + // Groq reasoning + if (slug === 'groq') { + return { + providerReasoningIOSettings: { + input: { + includeInPayload: (reasoning) => (reasoning ? { reasoning_format: 'parsed' } : null) + }, + output: { nameOfFieldInDelta: 'reasoning' } + } + }; + } + + // vLLM server exposes reasoning_content + if (slug === 'vllm') { + return { providerReasoningIOSettings: { output: { nameOfFieldInDelta: 'reasoning_content' } } }; + } + + // Zhipu GLM-4.5 style + if (slug === 'zhipuai') { + return { + providerReasoningIOSettings: { + input: { + includeInPayload: (reasoning) => { + if (reasoning && reasoning.type === 'effort_slider_value') { + return { reasoning: { effort: reasoning.reasoningEffort } }; + } + return null; + } + }, + output: { nameOfFieldInDelta: 'reasoning_content' } + } + }; + } + + // Local runtimes (ollama, lmstudio) may render reasoning in text; ask wrapper to manually parse think tags + if (slug === 'ollama' || slug === 'lmstudio') { + return { providerReasoningIOSettings: { output: { needsManualParse: true } } }; + } + + // Default: no special handling + return { providerReasoningIOSettings: {} }; +} + + +export interface OpenRouterProvider { + name: string; + slug: string; + privacy_policy_url?: string; + terms_of_service_url?: string; + status_page_url?: string; +} + +export interface OpenRouterModel { + id: string; + canonical_slug: string; + hugging_face_id?: string; + name: string; + created: number; + description?: string; + context_length?: number; + architecture: { + modality: string; + input_modalities: string[]; + output_modalities: string[]; + tokenizer: string; + instruct_type?: string; + }; + pricing: { + prompt: string; + completion: string; + request?: string; + image?: string; + audio?: string; + web_search?: string; + internal_reasoning?: string; + input_cache_read?: string; + input_cache_write?: string; + }; + top_provider: { + context_length?: number; + max_completion_tokens?: number; + is_moderated: boolean; + }; + supported_parameters: string[]; + default_parameters?: Record; +} + +export interface ModelApiConfig { + apiStyle: 'openai-compatible' | 'anthropic-style' | 'gemini-style' | 'disabled'; + supportsSystemMessage: supportsSystemMessage; + specialToolFormat: specialToolFormat; + endpoint: string; + auth: { + header: string; + format: 'Bearer' | 'direct'; + }; +} + + +type ProviderDefaults = { + baseEndpoint?: string; + apiStyle: 'openai-compatible' | 'anthropic-style' | 'gemini-style'; + supportsSystemMessage: supportsSystemMessage; +}; + +export const WELL_KNOWN_PROVIDER_DEFAULTS: Record = { + openai: { + baseEndpoint: 'https://api.openai.com/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'developer-role' + }, + anthropic: { + baseEndpoint: 'https://api.anthropic.com/v1', + apiStyle: 'anthropic-style', + supportsSystemMessage: 'separated' + }, + google: { + baseEndpoint: 'https://generativelanguage.googleapis.com/v1', + apiStyle: 'gemini-style', + supportsSystemMessage: 'separated' + }, + 'google-vertex': { + baseEndpoint: 'https://generativelanguage.googleapis.com/v1', + apiStyle: 'gemini-style', + supportsSystemMessage: 'separated' + }, + mistral: { + baseEndpoint: 'https://api.mistral.ai/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + }, + groq: { + baseEndpoint: 'https://api.groq.com/openai/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + }, + cohere: { + baseEndpoint: 'https://api.cohere.ai/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + }, + deepseek: { + baseEndpoint: 'https://api.deepseek.com/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + }, + minimax: { + baseEndpoint: 'https://api.minimax.io/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + }, + _default: { + apiStyle: 'openai-compatible', + supportsSystemMessage: 'system-role' + } +}; + + +export type ProviderConfigResolver = (providerSlug: string, modelId?: string) => Partial | null; +export type UserModelApiConfigGetter = (modelId: string) => ModelApiConfig | null; + +let _providerResolver: ProviderConfigResolver | null = null; +let _userModelGetter: UserModelApiConfigGetter | null = null; + +export function registerProviderConfigResolver(resolver: ProviderConfigResolver | null) { + _providerResolver = resolver; +} + +export function registerUserModelApiConfigGetter(getter: UserModelApiConfigGetter | null) { + _userModelGetter = getter; +} + + +export function __dangerouslyResetApiResolversForTests() { + _providerResolver = null; + _userModelGetter = null; +} + + +export function getProviderSlug(modelId: string): string { + const parts = modelId.split('/'); + const result = parts.length > 1 ? parts[0] : '_unknown'; + return result; +} + +function apiStyleToToolFormat(style: ModelApiConfig['apiStyle']): ModelApiConfig['specialToolFormat'] { + if (style === 'anthropic-style') return 'anthropic-style'; + if (style === 'gemini-style') return 'gemini-style'; + return 'openai-style'; +} + + +export function getModelApiConfiguration(modelId: string): ModelApiConfig { + + + if (_userModelGetter) { + const userCfg = _userModelGetter(modelId); + if (userCfg) { + return userCfg; + } + } + + const providerSlug = getProviderSlug(modelId); + + + if (_providerResolver) { + const p = _providerResolver(providerSlug, modelId); + if (p) { + const apiStyle = p.apiStyle ?? 'openai-compatible'; + const supportsSystemMessage = + p.supportsSystemMessage ?? + (apiStyle === 'anthropic-style' || apiStyle === 'gemini-style' ? 'separated' : 'system-role'); + + const result = { + apiStyle, + supportsSystemMessage, + specialToolFormat: p.specialToolFormat ?? apiStyleToToolFormat(apiStyle), + endpoint: p.endpoint ?? 'https://openrouter.ai/api/v1', + auth: p.auth ?? { header: 'Authorization', format: 'Bearer' } + }; + return result; + } + } + + + const known = WELL_KNOWN_PROVIDER_DEFAULTS[providerSlug] || WELL_KNOWN_PROVIDER_DEFAULTS._default; + const apiStyle = known.apiStyle; + const result: ModelApiConfig = { + apiStyle, + supportsSystemMessage: known.supportsSystemMessage, + specialToolFormat: apiStyleToToolFormat(apiStyle), + endpoint: known.baseEndpoint || 'https://openrouter.ai/api/v1', + auth: { header: 'Authorization', format: 'Bearer' } + }; + return result; +} + + +export function inferCapabilitiesFromOpenRouterModel(model: OpenRouterModel): Partial { + const params = model.supported_parameters || []; + + const capabilities: Partial = { + contextWindow: model.context_length || 4096, + reservedOutputTokenSpace: model.top_provider?.max_completion_tokens || 4096, + cost: { + input: parseFloat(model.pricing?.prompt) || 0, + output: parseFloat(model.pricing?.completion) || 0 + } + }; + + // System message support depends on tool support; when tools unsupported, set false + const apiConfig = getModelApiConfiguration(model.id); + const hasTools = params.includes('tools') && params.includes('tool_choice'); + capabilities.supportsSystemMessage = hasTools ? apiConfig.supportsSystemMessage : false; + + + if (hasTools) { + capabilities.specialToolFormat = apiConfig.specialToolFormat; + } else { + capabilities.specialToolFormat = 'disabled'; + } + + // Inference reasoning capabilities + capabilities.reasoningCapabilities = inferReasoningCapabilities(params, model); + + // Input modalities (text, image, audio, etc.) + if (Array.isArray(model.architecture?.input_modalities) && model.architecture.input_modalities.length > 0) { + capabilities.inputModalities = model.architecture.input_modalities.slice(); + } + + + const description = model.description?.toLowerCase() || ''; + if (description.includes('fill-in-middle') || + description.includes('autocomplete') || + model.architecture?.instruct_type === 'fim') { + capabilities.supportsFIM = true; + } else { + capabilities.supportsFIM = false; + } + + + if (capabilities.supportsFIM) { + if (model.id.includes('codellama') || model.id.includes('ollama')) { + capabilities.fimTransport = 'ollama-native'; + } else if (model.id.includes('mistral')) { + capabilities.fimTransport = 'mistral-native'; + } else { + capabilities.fimTransport = 'openai-compatible'; + } + } + + return capabilities; +} + + +export function inferReasoningCapabilities(params: string[], model: OpenRouterModel): false | any { + const hasParams = params.includes('reasoning') && params.includes('include_reasoning'); + if (!hasParams) return false; + + const modelName = model.name.toLowerCase(); + + // Anthropic/Claude patterns + if (modelName.includes('anthropic') || modelName.includes('claude')) { + return { + supportsReasoning: true, + canTurnOffReasoning: false, + canIOReasoning: true, + reasoningReservedOutputTokenSpace: model.top_provider?.max_completion_tokens || 8192, + reasoningSlider: { + type: 'budget_slider', + min: 1024, + max: 8192, + default: 1024 + } + }; + } + + + if (isThinkingOnlyModel(model)) { + return { + supportsReasoning: true, + canTurnOffReasoning: false, + canIOReasoning: true, + openSourceThinkTags: ['', ''] + }; + } + + // OpenAI-style reasoning models + if (modelName.includes('openai') || modelName.includes('gpt')) { + return { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low' + } + }; + } + + // Default reasoning capabilities + return { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low' + } + }; +} + + +function isThinkingOnlyModel(model: OpenRouterModel): boolean { + const searchText = [model.name, model.canonical_slug, model.description] + .filter(Boolean) + .join(' ') + .toLowerCase(); + + const patterns = { + nonThinking: ['non-thinking', 'non thinking'], + thinkingOnly: ['thinking only', 'thinking-only', 'thinking', '', 'code reasoning'] + }; + + + if (patterns.nonThinking.some(pattern => searchText.includes(pattern))) { + return false; + } + + + + return patterns.thinkingOnly.some(pattern => searchText.includes(pattern)); +} + + +export function getSystemMessageType(providerSlug: string): 'system-role' | 'developer-role' | 'separated' { + const config = WELL_KNOWN_PROVIDER_DEFAULTS[providerSlug]; + return config?.supportsSystemMessage || 'system-role'; +} + +export function inferApiStyle(providerSlug: string): 'openai-compatible' | 'anthropic-style' | 'gemini-style' { + const config = WELL_KNOWN_PROVIDER_DEFAULTS[providerSlug]; + return config?.apiStyle || 'openai-compatible'; +} + diff --git a/src/vs/platform/void/common/prompt/constants.ts b/src/vs/platform/void/common/prompt/constants.ts new file mode 100644 index 00000000000..38b216e847a --- /dev/null +++ b/src/vs/platform/void/common/prompt/constants.ts @@ -0,0 +1,20 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export const MAX_DIRSTR_CHARS_TOTAL_BEGINNING = 20_000; +export const MAX_DIRSTR_CHARS_TOTAL_TOOL = 20_000; +export const MAX_DIRSTR_RESULTS_TOTAL_BEGINNING = 100; +export const MAX_DIRSTR_RESULTS_TOTAL_TOOL = 100; + +export const MAX_FILE_CHARS_PAGE = 500_000; +export const MAX_CHILDREN_URIs_PAGE = 500; + +export const MAX_TERMINAL_CHARS = 100_000; +export const MAX_TERMINAL_INACTIVE_TIME = 8; +export const MAX_TERMINAL_BG_COMMAND_TIME = 20; + +export const MAX_PREFIX_SUFFIX_CHARS = 20_000; + + diff --git a/src/vs/platform/void/common/prompt/prompt_helper.ts b/src/vs/platform/void/common/prompt/prompt_helper.ts new file mode 100644 index 00000000000..13f7666dee6 --- /dev/null +++ b/src/vs/platform/void/common/prompt/prompt_helper.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export function toolFormatNativeHelp(format: 'openai-style' | 'anthropic-style' | 'gemini-style' | 'disabled' | undefined) { + if (format === 'disabled' || format === undefined) { + throw new Error(`Unsupported tool format: ${String(format)}. This helper should not handle 'disabled' or undefined formats.`); + } + + switch (format) { + case 'openai-style': + return `Provider format: OpenAI function-calling. +- Call tools by returning a function call with 'name' and JSON 'arguments'. +- Use snake_case keys in arguments; omit optional args unless needed. +- Avoid free-form patches; use tools to make changes.` + case 'anthropic-style': + return `Provider format: Anthropic tool use. +- Invoke tools with the native tool invocation object (name + input JSON). +- Keep inputs minimal; avoid free-form patches; use tools to make changes.` + case 'gemini-style': + return `Provider format: Gemini function calling. +- Call tools via functionCall object (name + JSON args). +- Use snake_case keys; avoid free-form patches.` + default: + return `Tools are available via native function calling. Use snake_case keys; omit optional args unless needed. Avoid free-form patches; apply changes via tools.` + } +} + diff --git a/src/vs/platform/void/common/prompt/systemPromptNativeTemplate.ts b/src/vs/platform/void/common/prompt/systemPromptNativeTemplate.ts new file mode 100644 index 00000000000..6bc62bc1ece --- /dev/null +++ b/src/vs/platform/void/common/prompt/systemPromptNativeTemplate.ts @@ -0,0 +1,45 @@ +export const SYSTEM_PROMPT_NATIVE_TEMPLATE = ` + +{{CRITICAL_RULES}} + +GUIDELINES + +Context: +- This is a fork of the VSCode repo called Void. +- Explore the repo as needed; prefer existing services and built-in functions. +- OS: {{OS}} +{{SHELL_LINE}} +- Workspaces: {{WORKSPACES}} + +Role & Objective: +- You are an expert coding agent that helps the user develop, run, and modify their codebase with minimal, correct changes. + +Priorities (in order): +1) Tool rules & safety +2) Correctness and minimal deltas (respect repo conventions) +3) Helpfulness & brevity +4) Style consistent with the codebase + +{{CORE_EXECUTION_RULES}} + +HALLUCINATION PREVENTION RULES: +- If you're "assuming" what code looks like → STOP +- When in doubt → ALWAYS read first, edit second +- NEVER trust your "knowledge" of file contents — only trust what you read this session + +{{SELECTIONS_SECTION}} + +{{EDITS_SECTION}} + +{{STRICT_EDIT_SPEC}} + +{{SAFETY_SCOPE_SECTION}} + +Language & formatting: +- Match the user's language. Use concise Markdown; avoid tables. + +Follow the provider-specific invocation rules: +{{TOOL_FORMAT_HELP}} + +Now Date: {{NOW_DATE}} +` diff --git a/src/vs/platform/void/common/prompt/systemPromptXMLTemplate.ts b/src/vs/platform/void/common/prompt/systemPromptXMLTemplate.ts new file mode 100644 index 00000000000..b20f314e9ae --- /dev/null +++ b/src/vs/platform/void/common/prompt/systemPromptXMLTemplate.ts @@ -0,0 +1,39 @@ +export const SYSTEM_PROMPT_XML_TEMPLATE = `{{ROLE_AND_OBJECTIVE}} + +{{CRITICAL_SECTIONS}} + +{{ABSOLUTE_PRIORITY}} + +{{FORBIDDEN_SECTION}} + +{{PRIMARY_RESPONSE_FORMAT}} + +{{XML_TOOL_SHAPE}} + +{{MANDATORY_RULES}} + +{{WHEN_TO_USE_TOOLS}} + +{{WHEN_NOT_TO_USE_TOOLS}} + +{{TOKEN_OPTIMIZATION}} + +{{VERIFICATION_WORKFLOW}} + +{{STOP_CONDITION}} + +Context: +- OS: {{OS}} +{{SHELL_LINE}} +- Workspace: {{WORKSPACES}} +- This is a VSCode fork called Void + +Available tools: + +{{XML_TOOLS_LIST}} + +{{EFFICIENT_TASK_APPROACH}} + +{{REMEMBER_SECTION}} + +Now Date: {{NOW_DATE}}`; diff --git a/src/vs/platform/void/common/providerReg.ts b/src/vs/platform/void/common/providerReg.ts new file mode 100644 index 00000000000..74066aed19f --- /dev/null +++ b/src/vs/platform/void/common/providerReg.ts @@ -0,0 +1,864 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { InstantiationType, registerSingleton } from '../../instantiation/common/extensions.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { + OpenRouterProvider, + ModelApiConfig, + registerProviderConfigResolver, + registerUserModelApiConfigGetter, + getModelApiConfiguration, + VoidStaticModelInfo, + getProviderSlug +} from './modelInference.js'; +import { IVoidSettingsService, CustomProviderSettings, ModelCapabilityOverride } from './voidSettingsService.js'; +import { IDynamicModelService } from './dynamicModelService.js'; +import { IRemoteModelsService } from './remoteModelsService.js'; +import { ILogService } from '../../log/common/log.js'; +import { specialToolFormat, supportsSystemMessage } from './voidSettingsTypes.js'; + +export type ProviderMeta = { + title: string; + subTextMd?: string; + defaultModels?: string[]; + apiKeyPlaceholder?: string; + endpointPlaceholder?: string; +} + +export interface IUserProviderSettings extends CustomProviderSettings { } + +export interface IDynamicProviderRegistryService { + readonly _serviceBrand: undefined; + initialize(): Promise; + refreshProviders(force?: boolean): Promise; + getProviders(): OpenRouterProvider[]; + + // events + onDidChangeProviders: Event; + onDidChangeProviderModels: Event<{ slug: string }>; + + + getUserProviderSettings(slug: string): IUserProviderSettings | undefined; + getConfiguredProviderSlugs(): string[]; + setUserProviderSettings(slug: string, settings: IUserProviderSettings): Promise; + deleteUserProviderSettings(slug: string): Promise; + + + refreshModelsForProvider(slug: string): Promise; + getProviderModels(slug: string): string[]; + setProviderModels(slug: string, models: string[], modelsCapabilities?: Record>): Promise; // updated signature + + + setPerModelOverride(modelId: string, cfg: ModelApiConfig | null): Promise; + getPerModelOverride(modelId: string): ModelApiConfig | null; + + getModelCapabilityOverride(slug: string, modelId: string): ModelCapabilityOverride | undefined; + setModelCapabilityOverride(slug: string, modelId: string, overrides: ModelCapabilityOverride | undefined): Promise; + getEffectiveModelCapabilities(slug: string, modelId: string): Promise>; + + getRequestConfigForModel(modelId: string, preferredProviderSlug?: string): { + endpoint: string; + apiStyle: 'openai-compatible' | 'anthropic-style' | 'gemini-style' | 'disabled'; + supportsSystemMessage: supportsSystemMessage, + specialToolFormat: specialToolFormat; + headers: Record; + }; + +} + +export const IDynamicProviderRegistryService = createDecorator('dynamicProviderRegistryService'); + +type ProvidersCache = { ts: number; data: OpenRouterProvider[] }; +const PROVIDERS_CACHE_KEY = 'void.openrouter.providers.cache.v1'; +const PROVIDERS_TTL_MS = 24 * 60 * 60 * 1000; // 24h + +type ORCandidate = { + fullId: string; + provider: string; + short: string; + shortNorm: string; + baseKey: string; + caps: Partial; +}; + +type ORIndex = { + byFull: Map; + byShort: Map; + byBase: Map; +}; + +export class DynamicProviderRegistryService implements IDynamicProviderRegistryService { + declare readonly _serviceBrand: undefined; + + private readonly _onDidChangeProviders = new Emitter(); + readonly onDidChangeProviders = this._onDidChangeProviders.event; + + private readonly _onDidChangeProviderModels = new Emitter<{ slug: string }>(); + readonly onDidChangeProviderModels = this._onDidChangeProviderModels.event; + + private providers: OpenRouterProvider[] = []; + private perModelOverrides = new Map(); + private initialized = false; + + constructor( + @IRemoteModelsService private readonly remoteModelsService: IRemoteModelsService, + @IVoidSettingsService private readonly settingsService: IVoidSettingsService, + @IDynamicModelService private readonly dynamicModelService: IDynamicModelService, + @ILogService private readonly logService: ILogService + ) { + + registerProviderConfigResolver((providerSlug) => { + this.logService.debug(`[DEBUG registerProviderConfigResolver] Called with providerSlug: "${providerSlug}"`); + const cfg = this.settingsService.state.customProviders?.[providerSlug]; + this.logService.debug(`[DEBUG registerProviderConfigResolver] cfg:`, cfg); + if (!cfg) { + this.logService.debug(`[DEBUG registerProviderConfigResolver] No config found, returning null`); + return null; + } + const result = { + endpoint: cfg.endpoint, + apiStyle: cfg.apiStyle, + supportsSystemMessage: cfg.supportsSystemMessage, + auth: cfg.auth ?? { header: 'Authorization', format: 'Bearer' } + }; + this.logService.debug(`[DEBUG registerProviderConfigResolver] Returning:`, result); + return result; + }); + + registerUserModelApiConfigGetter((modelId) => { + this.logService.debug(`[DEBUG registerUserModelApiConfigGetter] Called with modelId: "${modelId}"`); + if (this.perModelOverrides.has(modelId)) { + const result = this.perModelOverrides.get(modelId)!; + this.logService.debug(`[DEBUG registerUserModelApiConfigGetter] Found in perModelOverrides:`, result); + return result; + } + const all = this.settingsService.state.customProviders || {}; + for (const slug of Object.keys(all)) { + const entry = all[slug]; + const perModel = entry?.perModel || {}; + if (perModel && perModel[modelId]) { + const p = perModel[modelId]!; + const result = { + apiStyle: p.apiStyle ?? 'openai-compatible', + supportsSystemMessage: p.supportsSystemMessage ?? (p.apiStyle === 'anthropic-style' || p.apiStyle === 'gemini-style' ? 'separated' : 'system-role'), + specialToolFormat: p.specialToolFormat ?? (p.apiStyle === 'anthropic-style' ? 'anthropic-style' : p.apiStyle === 'gemini-style' ? 'gemini-style' : 'openai-style'), + endpoint: p.endpoint ?? all[slug].endpoint ?? 'https://openrouter.ai/api/v1', + auth: p.auth ?? { header: 'Authorization', format: 'Bearer' } + }; + this.logService.debug(`[DEBUG registerUserModelApiConfigGetter] Found in perModel for slug "${slug}":`, result); + return result; + } + } + this.logService.debug(`[DEBUG registerUserModelApiConfigGetter] No config found, returning null`); + return null; + }); + } + + + async initialize(): Promise { + if (this.initialized) return; + + const cached = this.readCache(); + if (cached) { + this.providers = cached; + this._onDidChangeProviders.fire(); + this.refreshProviders(false).catch(() => { }); + this.initialized = true; + return; + } + + await this.refreshProviders(true); + this.initialized = true; + } + + async refreshProviders(force = false): Promise { + const now = Date.now(); + const meta = this.readCacheMeta(); + + if (!force && meta && (now - meta.ts) < PROVIDERS_TTL_MS) { + return; + } + + try { + const json = await this.remoteModelsService.fetchModels('https://openrouter.ai/api/v1/providers', { + 'HTTP-Referer': 'https://voideditor.com', + 'X-Title': 'Void', + 'Accept': 'application/json' + }); + + if (json && typeof json === 'object' && Array.isArray((json as any).data)) { + this.providers = (json as any).data as OpenRouterProvider[]; + this.writeCache({ ts: now, data: this.providers }); + this._onDidChangeProviders.fire(); + } + } catch { + // ignore + } + } + + getProviders(): OpenRouterProvider[] { + return this.providers.slice(); + } + + private toShortName(id: string): string { + const i = id.indexOf('/'); + return i === -1 ? id : id.slice(i + 1); + } + + private isFreeVariant(name: string): boolean { + return name.endsWith(':free') || name.includes(':free'); + } + + isLocalEndpoint(urlStr?: string): boolean { + if (!urlStr) return false; + try { + const u = new URL(urlStr); + const h = u.hostname.toLowerCase(); + if (h === 'localhost' || h === '127.0.0.1' || h === '::1') return true; + // private IPv4: 10/8, 172.16-31/12, 192.168/16 + if (/^10\.\d+\.\d+\.\d+$/.test(h)) return true; + if (/^192\.168\.\d+\.\d+$/.test(h)) return true; + const m = /^172\.(\d+)\.\d+\.\d+$/.exec(h); + if (m) { + const sec = Number(m[1]); + if (sec >= 16 && sec <= 31) return true; + } + } catch { /* ignore */ } + return false; + } + + private sanitizeModelsAndCaps( + models: string[], + caps?: Record>, + opts?: { keepFullIds?: boolean; dropFree?: boolean } + ): { models: string[]; caps?: Record> } { + const keepFull = !!opts?.keepFullIds; + const dropFree = opts?.dropFree === true; + + const out: string[] = []; + for (const m of models) { + const name = keepFull ? m : this.toShortName(m); + if (dropFree && this.isFreeVariant(name)) continue; + if (!out.includes(name)) out.push(name); + } + + let newCaps: Record> | undefined; + if (caps) { + newCaps = {}; + for (const [k, v] of Object.entries(caps)) { + const key = keepFull ? k : this.toShortName(k); + if (dropFree && this.isFreeVariant(key)) continue; + if (!(key in newCaps)) newCaps[key] = v; + } + } + + return { models: out, caps: newCaps }; + } + + private static readonly NAME_QUALIFIERS = new Set([ + 'pro', 'mini', 'search', 'lite', 'high', 'low', 'medium', 'large', 'xl', 'xlarge', 'turbo', 'fast', 'slow', + 'instruct', 'chat', 'reasoning', 'flash', 'dev', 'beta', 'latest', 'preview', 'free' + ]); + + private normalizeName(s: string): string { + return s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim().replace(/\s+/g, ' '); + } + + private toBaseKey(modelShortName: string): string { + const tokens = this.normalizeName(modelShortName).split(' '); + const filtered = tokens.filter(t => !DynamicProviderRegistryService.NAME_QUALIFIERS.has(t) && t.length > 0); + + const finalTokens: string[] = []; + for (const t of filtered) { + const m = /^([a-z]+)(\d+)$/.exec(t); + if (m) { + finalTokens.push(m[1], m[2]); + } else { + finalTokens.push(t); + } + } + return finalTokens.join(' '); + } + + private makeShortName(id: string): string { + return this.toShortName(id); + } + + private splitProvider(idOrShort: string): { provider?: string; short: string } { + const i = idOrShort.indexOf('/'); + if (i === -1) return { short: idOrShort }; + return { provider: idOrShort.slice(0, i), short: idOrShort.slice(i + 1) }; + } + + private buildOpenRouterIndex(): ORIndex { + const all = this.dynamicModelService.getAllDynamicCapabilities(); // fullId -> caps + const byFull = new Map(); + const byShort = new Map(); + const byBase = new Map(); + + for (const [fullId, caps] of Object.entries(all)) { + const { provider, short } = this.splitProvider(fullId); + if (!provider) continue; + const shortNorm = this.normalizeName(short); + const baseKey = this.toBaseKey(short); + const item: ORCandidate = { fullId, provider, short, shortNorm, baseKey, caps }; + + byFull.set(fullId, item); + + const arrS = byShort.get(shortNorm) ?? []; + arrS.push(item); + byShort.set(shortNorm, arrS); + + const arrB = byBase.get(baseKey) ?? []; + arrB.push(item); + byBase.set(baseKey, arrB); + } + return { byFull, byShort, byBase }; + } + + private scoreCandidate(remoteShort: string, remoteProvider: string | undefined, cand: ORCandidate): number { + + const shortNorm = this.normalizeName(remoteShort); + const baseKey = this.toBaseKey(remoteShort); + + let score = 0; + + if (cand.shortNorm === shortNorm) score += 500; + if (cand.baseKey === baseKey) score += 300; + if (remoteProvider && cand.provider === remoteProvider) score += 80; + + + const a = shortNorm, b = cand.shortNorm; + let pref = 0; + for (let i = 0; i < Math.min(a.length, b.length); i++) { + if (a[i] !== b[i]) break; + pref++; + } + score += Math.min(pref, 10) * 5; + + + const lenDiff = Math.abs(a.length - b.length); + score -= Math.min(lenDiff, 10) * 2; + + return score; + } + + private findBestOpenRouterMatch(remoteId: string, index: ORIndex): ORCandidate | null { + const { provider: rProv, short: rShort } = this.splitProvider(remoteId); + const shortNorm = this.normalizeName(rShort); + const baseKey = this.toBaseKey(rShort); + + + if (rProv) { + const fullId = `${rProv}/${rShort}`; + const exact = index.byFull.get(fullId); + if (exact) return exact; + + + const sameShort = index.byShort.get(shortNorm)?.filter(c => c.provider === rProv); + if (sameShort && sameShort.length) { + + let best = sameShort[0], bestScore = this.scoreCandidate(rShort, rProv, best); + for (let i = 1; i < sameShort.length; i++) { + const s = this.scoreCandidate(rShort, rProv, sameShort[i]); + if (s > bestScore) { best = sameShort[i]; bestScore = s; } + } + return best; + } + } + + + const sameShortAll = index.byShort.get(shortNorm); + if (sameShortAll && sameShortAll.length) { + let best = sameShortAll[0], bestScore = this.scoreCandidate(rShort, rProv, best); + for (let i = 1; i < sameShortAll.length; i++) { + const s = this.scoreCandidate(rShort, rProv, sameShortAll[i]); + if (s > bestScore) { best = sameShortAll[i]; bestScore = s; } + } + return best; + } + + + const baseCandidates = index.byBase.get(baseKey); + if (baseCandidates && baseCandidates.length) { + let best = baseCandidates[0], bestScore = this.scoreCandidate(rShort, rProv, best); + for (let i = 1; i < baseCandidates.length; i++) { + const s = this.scoreCandidate(rShort, rProv, baseCandidates[i]); + if (s > bestScore) { best = baseCandidates[i]; bestScore = s; } + } + return best; + } + + + let globalBest: ORCandidate | null = null; + let globalScore = -Infinity; + for (const cand of index.byFull.values()) { + const s = this.scoreCandidate(rShort, rProv, cand); + if (s > globalScore) { globalBest = cand; globalScore = s; } + } + + return globalBest && globalScore >= 200 ? globalBest : null; + } + + private async inferCapabilitiesForRemoteModels(remoteIds: string[]): Promise>> { + await this.dynamicModelService.initialize(); + const index = this.buildOpenRouterIndex(); + + const caps: Record> = {}; + for (const rid of remoteIds) { + const match = this.findBestOpenRouterMatch(rid, index); + + if (match) { + caps[rid] = { + contextWindow: match.caps.contextWindow, + reservedOutputTokenSpace: match.caps.reservedOutputTokenSpace, + cost: match.caps.cost, + supportsSystemMessage: match.caps.supportsSystemMessage, + specialToolFormat: match.caps.specialToolFormat, + supportsFIM: match.caps.supportsFIM ?? false, + reasoningCapabilities: match.caps.reasoningCapabilities, + fimTransport: match.caps.fimTransport, + inputModalities: match.caps.inputModalities, + }; + } + } + return caps; + } + + + private async refreshModelsViaProviderEndpoint(slug: string, endpoint: string): Promise { + const cfg = this.getUserProviderSettings(slug); + const url = endpoint.replace(/\/$/, '') + '/models'; + const headers: Record = { Accept: 'application/json', ...(cfg?.additionalHeaders || {}) }; + + if (cfg?.apiKey) { + const authHeader = cfg.auth?.header || 'Authorization'; + const format = cfg.auth?.format || 'Bearer'; + headers[authHeader] = format === 'Bearer' ? `Bearer ${cfg.apiKey}` : cfg.apiKey; + } + + this.logService.debug(`[DynamicProviderRegistryService] (${slug}) GET ${url}`); + + const json: any = await this.remoteModelsService.fetchModels(url, headers); + const arr: any[] = Array.isArray(json?.data) ? json.data : (Array.isArray(json) ? json : []); + const ids = arr.map((m: any) => m.id ?? m.model ?? m.name).filter(Boolean) as string[]; + + this.logService.debug(`[DynamicProviderRegistryService] (${slug}) /models returned ${ids.length} models`); + if (ids.length) { + const limit = 150; + const head = ids.slice(0, limit).join(', '); + this.logService.debug( + `[DynamicProviderRegistryService] (${slug}) models: ${ids.length > limit ? `${head} …(+${ids.length - limit})` : head}` + ); + } + + return ids; + } + + + private async publishAllConfiguredToChat(): Promise { + const cps = this.settingsService.state.customProviders || {}; + const aggregated = new Set(); + + for (const slug of Object.keys(cps)) { + const isOR = String(slug).toLowerCase() === 'openrouter'; + const models = cps[slug]?.models || []; + + for (const id of models) { + if (isOR) { + aggregated.add(id); + } else { + aggregated.add(`${slug}/${id}`); + } + } + } + + + await this.settingsService.setAutodetectedModels('openRouter', Array.from(aggregated), {}); + } + + + + getUserProviderSettings(slug: string): IUserProviderSettings | undefined { + return this.settingsService.state.customProviders?.[slug]; + } + + getConfiguredProviderSlugs(): string[] { + return Object.keys(this.settingsService.state.customProviders || {}); + } + + async setUserProviderSettings(slug: string, settings: IUserProviderSettings): Promise { + await this.settingsService.setCustomProviderSettings(slug, settings); + this._onDidChangeProviders.fire(); + } + + async deleteUserProviderSettings(slug: string): Promise { + await this.settingsService.setCustomProviderSettings(slug, undefined); + this._onDidChangeProviders.fire(); + } + + async refreshModelsForProvider(slug: string): Promise { + const cfg = this.getUserProviderSettings(slug); + const isOpenRouterSlug = String(slug).toLowerCase() === 'openrouter'; + + + if (this.isLocalEndpoint(cfg?.endpoint)) { + const remoteIds = await this.refreshModelsViaProviderEndpoint(slug, cfg!.endpoint!); + + const inferredCaps = await this.inferCapabilitiesForRemoteModels(remoteIds); + + + const { models, caps } = this.sanitizeModelsAndCaps(remoteIds, inferredCaps, { + keepFullIds: true, + dropFree: false + }); + + await this.setProviderModels(slug, models, caps); + await this.publishAllConfiguredToChat(); + return; + } + + + + if (isOpenRouterSlug) { + await this.dynamicModelService.initialize(); + const allCaps = this.dynamicModelService.getAllDynamicCapabilities(); + const entries = Object.entries(allCaps); + const fullIds = entries.map(([id]) => id); + const capsFull: Record> = {}; + for (const [id, info] of entries) { + capsFull[id] = { + contextWindow: info.contextWindow, + reservedOutputTokenSpace: info.reservedOutputTokenSpace, + cost: info.cost, + supportsSystemMessage: info.supportsSystemMessage, + specialToolFormat: info.specialToolFormat, + supportsFIM: info.supportsFIM ?? false, + reasoningCapabilities: info.reasoningCapabilities, + fimTransport: info.fimTransport, + inputModalities: info.inputModalities, + }; + } + const { models, caps } = this.sanitizeModelsAndCaps(fullIds, capsFull, { keepFullIds: true, dropFree: false }); + await this.setProviderModels(slug, models, caps); + await this.publishAllConfiguredToChat(); + return; + } + + + await this.dynamicModelService.initialize(); + const all = this.dynamicModelService.getAllDynamicCapabilities(); + const bySlug = Object.entries(all).filter(([id]) => id.startsWith(slug + '/')); + + + if (bySlug.length === 0 && cfg?.endpoint) { + try { + const remoteIds = await this.refreshModelsViaProviderEndpoint(slug, cfg.endpoint); + const inferredCaps = await this.inferCapabilitiesForRemoteModels(remoteIds); + + const norm = this.sanitizeModelsAndCaps(remoteIds, inferredCaps, { keepFullIds: true, dropFree: false }); + await this.setProviderModels(slug, norm.models, norm.caps); + await this.publishAllConfiguredToChat(); + return; + } catch { + + } + } + + const ids = bySlug.map(([id]) => id); + const caps: Record> = {}; + for (const [id, info] of bySlug) { + caps[id] = { + contextWindow: info.contextWindow, + reservedOutputTokenSpace: info.reservedOutputTokenSpace, + cost: info.cost, + supportsSystemMessage: info.supportsSystemMessage, + specialToolFormat: info.specialToolFormat, + supportsFIM: info.supportsFIM ?? false, + reasoningCapabilities: info.reasoningCapabilities, + fimTransport: (info as any).fimTransport, + inputModalities: (info as any).inputModalities, + }; + } + const norm = this.sanitizeModelsAndCaps(ids, caps, { keepFullIds: true, dropFree: false }); + await this.setProviderModels(slug, norm.models, norm.caps); + await this.publishAllConfiguredToChat(); + } + + getProviderModels(slug: string): string[] { + return this.settingsService.state.customProviders?.[slug]?.models ?? []; + } + + getModelCapabilityOverride(slug: string, modelId: string): ModelCapabilityOverride | undefined { + const cp = this.settingsService.state.customProviders?.[slug]; + return cp?.modelCapabilityOverrides?.[modelId]; + } + + async setModelCapabilityOverride(slug: string, modelId: string, overrides: ModelCapabilityOverride | undefined): Promise { + const cp = this.settingsService.state.customProviders?.[slug] || {}; + const cur = { ...(cp.modelCapabilityOverrides || {}) }; + if (overrides === undefined) { + delete cur[modelId]; + } else { + cur[modelId] = overrides; + } + await this.settingsService.setCustomProviderSettings(slug, { + ...cp, + modelCapabilityOverrides: cur + }); + + this._onDidChangeProviderModels.fire({ slug }); + } + + async getEffectiveModelCapabilities(slug: string, modelId: string): Promise> { + + const fullId = modelId.includes('/') ? modelId : `${slug}/${modelId}`; + + await this.dynamicModelService.initialize(); + const base = this.dynamicModelService.getDynamicCapabilities(fullId); + + const cp = this.settingsService.state.customProviders?.[slug]; + + const saved = cp?.modelsCapabilities?.[modelId] ?? cp?.modelsCapabilities?.[fullId]; + + const minimal: Partial = base ?? saved ?? { + contextWindow: 4096, + reservedOutputTokenSpace: 4096, + cost: { input: 0, output: 0 }, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: false + }; + + const ov = this.getModelCapabilityOverride(slug, modelId); + return { ...minimal, ...(ov || {}) }; + } + + async setProviderModels( + slug: string, + models: string[], + modelsCapabilities?: Record> + ): Promise { + + + + const keepFullIds = true; + + const norm = this.sanitizeModelsAndCaps(models, modelsCapabilities, { + keepFullIds, + dropFree: false + }); + + const cur = this.settingsService.state.customProviders?.[slug] || {}; + const prevCaps = cur.modelsCapabilities || {}; + + const nextCaps: Record> = {}; + + + if (norm.caps) { + for (const [k, v] of Object.entries(norm.caps)) { + nextCaps[k] = v; + } + } + + + for (const m of norm.models) { + if (!(m in nextCaps) && (m in prevCaps)) { + nextCaps[m] = prevCaps[m]; + } + } + + + + + try { + await this.dynamicModelService.initialize(); + + const missing = norm.models.filter(m => !nextCaps[m]); + if (missing.length) { + const inferred = await this.inferCapabilitiesForRemoteModels(missing); + + let got = 0; + for (const m of missing) { + const cap = inferred[m]; + if (cap) { + nextCaps[m] = cap; + got++; + } + } + + this.logService.debug( + `[DynamicProviderRegistryService] setProviderModels("${slug}"): inferred caps for ${got}/${missing.length}` + ); + } + } catch (e) { + this.logService.warn(`[DynamicProviderRegistryService] setProviderModels("${slug}"): failed to infer caps`, e); + } + + const capsToStore = norm.models.length ? nextCaps : {}; + + await this.settingsService.setCustomProviderSettings(slug, { + ...cur, + models: norm.models, + modelsCapabilities: capsToStore, + modelsLastRefreshedAt: Date.now() + }); + + this._onDidChangeProviderModels.fire({ slug }); + + await this.publishAllConfiguredToChat(); + } + + async setPerModelOverride(modelId: string, cfg: ModelApiConfig | null): Promise { + if (cfg) this.perModelOverrides.set(modelId, cfg); + else this.perModelOverrides.delete(modelId); + } + + getPerModelOverride(modelId: string): ModelApiConfig | null { + return this.perModelOverrides.get(modelId) ?? null; + } + + getRequestConfigForModel(modelId: string, preferredProviderSlug?: string): { + endpoint: string; + apiStyle: 'openai-compatible' | 'anthropic-style' | 'gemini-style' | 'disabled'; + supportsSystemMessage: supportsSystemMessage, + specialToolFormat: specialToolFormat; + headers: Record; + } { + const preferred = preferredProviderSlug?.trim().toLowerCase(); + this.logService.debug(`[DEBUG getRequestConfigForModel] Called with modelId: "${modelId}", preferredProviderSlug: "${preferred || ''}"`); + const base = getModelApiConfiguration(modelId); + const slugFromModel = getProviderSlug(modelId).toLowerCase(); + + const cpPreferred = preferred ? this.getUserProviderSettings(preferred) : undefined; + const cpByModel = this.getUserProviderSettings(slugFromModel); + + + const cpOpenRouter = preferred !== 'openrouter' && slugFromModel !== 'openrouter' + ? this.getUserProviderSettings('openrouter') + : undefined; + const cp = cpPreferred ?? cpByModel ?? cpOpenRouter; + + const usedSlug = cpPreferred ? preferred : (cpByModel ? slugFromModel : (cpOpenRouter ? 'openrouter' : slugFromModel)); + this.logService.debug(`[DEBUG getRequestConfigForModel] slugFromModel="${slugFromModel}", usedSlug="${usedSlug}", hasCp=${!!cp}`); + + this.logService.debug(`[DEBUG getRequestConfigForModel] base:`, JSON.stringify(base, null, 2)); + this.logService.debug(`[DEBUG getRequestConfigForModel] cp(usedSlug="${usedSlug}"):`, cp); + + const endpoint = (cp?.endpoint && cp.endpoint.trim()) || base.endpoint; + this.logService.debug(`[DEBUG getRequestConfigForModel] final endpoint: "${endpoint}"`); + + const headers: Record = { + Accept: 'application/json' + }; + + if (cp?.apiKey) { + const headerName = cp.auth?.header || 'Authorization'; + const format = cp.auth?.format || 'Bearer'; + headers[headerName] = format === 'Bearer' ? `Bearer ${cp.apiKey}` : cp.apiKey; + } + if (cp?.additionalHeaders) { + for (const [k, v] of Object.entries(cp.additionalHeaders)) { + headers[k] = String(v); + } + } + + // allow-any-unicode-next-line + // Pull per­-model capability overrides for this provider/model so that + // semantic flags like supportsSystemMessage and specialToolFormat are + // taken from the same source as ConvertToLLMMessageService / ACP, and + // are NOT silently overwritten by WELL_KNOWN_PROVIDER_DEFAULTS. + let effSupportsSystemMessage = base.supportsSystemMessage; + let effSpecialToolFormat: specialToolFormat = base.specialToolFormat; + try { + if (cp) { + const caps = cp.modelsCapabilities; + // modelsCapabilities are stored under the exact id used in chat + const savedCaps: Partial | undefined = caps?.[modelId]; + const ov = usedSlug ? this.getModelCapabilityOverride(usedSlug, modelId) as (ModelCapabilityOverride | undefined) : undefined; + const merged: Partial = { ...(savedCaps ?? {}), ...(ov ?? {}) }; + if (merged.supportsSystemMessage !== undefined) { + effSupportsSystemMessage = merged.supportsSystemMessage as supportsSystemMessage; + } + if (merged.specialToolFormat !== undefined) { + effSpecialToolFormat = merged.specialToolFormat as specialToolFormat; + } + } + } catch (e) { + this.logService.warn('[DEBUG getRequestConfigForModel] Failed to apply capability overrides, falling back to base:', e); + } + + const result = { + endpoint, + apiStyle: base.apiStyle, + supportsSystemMessage: effSupportsSystemMessage, + specialToolFormat: effSpecialToolFormat, + headers + }; + + // Redact sensitive headers before logging + const redactedResult = { + endpoint: result.endpoint, + apiStyle: result.apiStyle, + supportsSystemMessage: result.supportsSystemMessage, + specialToolFormat: result.specialToolFormat, + headers: { ...result.headers } + }; + + const sensitiveHeaders = new Set([ + 'authorization', + 'x-api-key', + 'api-key', + 'x-goog-api-key', + 'proxy-authorization', + ]); + + for (const [k, v] of Object.entries(redactedResult.headers)) { + if (sensitiveHeaders.has(k.toLowerCase())) { + if (typeof v === 'string' && v.startsWith('Bearer ')) { + redactedResult.headers[k] = 'Bearer ***'; + } else { + redactedResult.headers[k] = '***'; + } + } + } + + this.logService.debug(`[DEBUG getRequestConfigForModel] result:`, JSON.stringify(redactedResult, null, 2)); + return result; + } + + private readCache(): OpenRouterProvider[] | null { + try { + if (typeof localStorage === 'undefined') return null; + const raw = localStorage.getItem(PROVIDERS_CACHE_KEY); + if (!raw) return null; + const obj = JSON.parse(raw) as ProvidersCache; + if (!obj || !obj.ts || !Array.isArray(obj.data)) return null; + if ((Date.now() - obj.ts) > PROVIDERS_TTL_MS) return null; + return obj.data; + } catch { return null; } + } + + private readCacheMeta(): ProvidersCache | null { + try { + if (typeof localStorage === 'undefined') return null; + const raw = localStorage.getItem(PROVIDERS_CACHE_KEY); + if (!raw) return null; + return JSON.parse(raw) as ProvidersCache; + } catch { return null; } + } + + private writeCache(v: ProvidersCache) { + try { + if (typeof localStorage === 'undefined') return; + localStorage.setItem(PROVIDERS_CACHE_KEY, JSON.stringify(v)); + } catch { /* ignore */ } + } +} + +registerSingleton(IDynamicProviderRegistryService, DynamicProviderRegistryService, InstantiationType.Delayed); diff --git a/src/vs/platform/void/common/remoteModelsService.ts b/src/vs/platform/void/common/remoteModelsService.ts new file mode 100644 index 00000000000..169fa102375 --- /dev/null +++ b/src/vs/platform/void/common/remoteModelsService.ts @@ -0,0 +1,14 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { createDecorator } from '../../instantiation/common/instantiation.js'; + +export interface IRemoteModelsService { + readonly _serviceBrand: undefined; + + fetchModels(url: string, headers?: Record): Promise; +} + +export const IRemoteModelsService = createDecorator('remoteModelsService'); diff --git a/src/vs/platform/void/common/requestParams.ts b/src/vs/platform/void/common/requestParams.ts new file mode 100644 index 00000000000..845b8eb3ab8 --- /dev/null +++ b/src/vs/platform/void/common/requestParams.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// Request parameters that should be excluded from UI override template +export const EXCLUDED_REQUEST_PARAMS = new Set([ + 'tools', + 'tool_choice', + 'response_format', + 'structured_outputs', + 'reasoning', + 'include_reasoning', + // Provider routing is configured separately as a top-level `provider` object + 'provider' +]); + +export function isExcluded(key: string): boolean { + return EXCLUDED_REQUEST_PARAMS.has(String(key)); +} + +export function coerceFallbackForKey(key: string): any { + const k = String(key); + if (k === 'temperature') return 0.2; + if (k === 'max_tokens' || k === 'max_completion_tokens') return 6000; + if (k === 'top_p') return 1; + if (k === 'top_k') return 40; + if (k === 'presence_penalty' || k === 'frequency_penalty' || k === 'repetition_penalty') return 0; + if (k === 'logprobs' || k === 'top_logprobs') return 0; + if (k === 'logit_bias') return {}; + if (k === 'seed') return 0; + if (k === 'stop') return []; + if (k === 'min_p') return 0; + return ''; +} + +export function filterSupportedParams(supported: readonly string[] | null | undefined): string[] { + const list = Array.isArray(supported) ? supported : []; + return list.filter(k => !isExcluded(String(k))); +} + +export function computeRequestParamsTemplate( + supportedParams: readonly string[] | null | undefined, + defaultParams?: Record | null +): Record { + const defs = defaultParams && typeof defaultParams === 'object' ? defaultParams : {}; + const filtered = filterSupportedParams(supportedParams); + const entries = filtered.map(rawKey => { + const k = String(rawKey); + const hasDefault = Object.prototype.hasOwnProperty.call(defs, k) && defs[k] !== null; + return [k, hasDefault ? defs[k] : coerceFallbackForKey(k)]; + }); + return Object.fromEntries(entries); +} + diff --git a/src/vs/platform/void/common/sendLLMMessageTypes.ts b/src/vs/platform/void/common/sendLLMMessageTypes.ts new file mode 100644 index 00000000000..a120ee097e4 --- /dev/null +++ b/src/vs/platform/void/common/sendLLMMessageTypes.ts @@ -0,0 +1,341 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { ToolName, ToolParamName } from './toolsServiceTypes.js' +import { ChatMode, supportsSystemMessage, specialToolFormat, ModelSelection, ModelSelectionOptions, OverridesOfModel, ProviderName, RefreshableProviderName, SettingsOfProvider } from './voidSettingsTypes.js' + +// Parameter injection controls (renderer → main) +export type ParameterInjectionMode = 'default' | 'off' | 'override'; +export type RequestParamsConfig = { mode: ParameterInjectionMode; params?: Record }; + +// OpenRouter provider routing object; forwarded as `provider` in request body +// when using the OpenRouter endpoint. The fields correspond to +// https://openrouter.ai/docs/guides/routing/provider-selection +export type ProviderRouting = { + order?: string[]; + allow_fallbacks?: boolean; + require_parameters?: boolean; + data_collection?: 'allow' | 'deny'; + zdr?: boolean; + enforce_distillable_text?: boolean; + only?: string[]; + ignore?: string[]; + quantizations?: string[]; + sort?: string; + max_price?: Record; + // Allow forward‑compatible custom fields without breaking typing + [k: string]: any; +}; + +export type DynamicRequestConfig = { + endpoint: string; + apiStyle: 'openai-compatible' | 'anthropic-style' | 'gemini-style' | 'disabled'; + supportsSystemMessage: supportsSystemMessage, + specialToolFormat: specialToolFormat; + fimTransport?: 'openai-compatible' | 'mistral-native' | 'ollama-native' | 'emulated'; + // Optional effective capabilities passed from renderer (dynamic registry) + reasoningCapabilities?: any; + /** Whether this model should use provider-specific prompt caching (cache_control). */ + supportCacheControl?: boolean; + headers: Record; +}; + +export const errorDetails = (fullError: Error | null): string | null => { + if (fullError === null) { + return null + } + else if (typeof fullError === 'object') { + if (Object.keys(fullError).length === 0) return null + return JSON.stringify(fullError, null, 2) + } + else if (typeof fullError === 'string') { + return null + } + return null +} + +export const getErrorMessage: (error: unknown) => string = (error) => { + if (error instanceof Error) return `${error.name}: ${error.message}` + return error + '' +} + +// Aggregated token usage for a single LLM request. +// All fields are non-negative counts of tokens as reported by the provider. +export type LLMTokenUsage = { + input: number; + cacheCreation: number; + cacheRead: number; + output: number; +} + +export type AnthropicAssistantBlock = + AnthropicReasoning | + { type: 'text'; text: string } | + { type: 'tool_use'; name: string; input: Record; id: string } | + { type: 'image'; source: { type: 'base64'; media_type: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp'; data: string } }; + +export type AnthropicUserBlock = + { type: 'text'; text: string } | + { type: 'tool_result'; tool_use_id: string; content: string } | + { type: 'image'; source: { type: 'base64'; media_type: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp'; data: string } }; + +export type AnthropicLLMChatMessage = + | { + role: 'assistant'; + content: string | AnthropicAssistantBlock[]; + } + | { + role: 'user'; + content: string | AnthropicUserBlock[]; + }; +export type OpenAITextPart = { type: 'text'; text: string }; +export type OpenAIImageURLPart = { type: 'image_url'; image_url: { url: string; detail?: 'auto' | 'low' | 'high' } }; + +export type OpenAILLMChatMessage = { + role: 'system' | 'developer'; + content: string; +} | { + role: 'user'; + content: string | (OpenAITextPart | OpenAIImageURLPart)[]; +} | { + role: 'assistant', + content: string | (AnthropicReasoning | OpenAITextPart)[]; + tool_calls?: { type: 'function'; id: string; function: { name: string; arguments: string; } }[]; +} | { + role: 'tool', + content: string; + tool_call_id: string; +} + +export type GeminiLLMChatMessage = { + role: 'model' + parts: ( + | { text: string; } + | { functionCall: { id: string; name: ToolName, args: Record } } + )[]; +} | { + role: 'user'; + parts: ( + | { text: string; } + | { functionResponse: { id: string; name: ToolName, response: { output: string } } } + | { inlineData: { mimeType: string; data: string } } + )[]; +} + +export type LLMChatMessage = AnthropicLLMChatMessage | OpenAILLMChatMessage | GeminiLLMChatMessage + + + +export type LLMFIMMessage = { + prefix: string; + suffix: string; + stopTokens: string[]; +} + + +export type RawToolParamsObj = { + [paramName in ToolParamName]?: string; +} + +export type RawToolCallObjKnown = { + name: ToolName; + rawParams: RawToolParamsObj; + doneParams: ToolParamName[]; + id: string; + isDone: boolean; +} + +export type RawToolCallObjDynamic = { + name: string; // dynamic/MCP tool name + rawParams: Record; + doneParams: string[]; + id: string; + isDone: boolean; +} + +export type RawToolCallObj = RawToolCallObjKnown | RawToolCallObjDynamic; + +export type AnthropicReasoning = ({ type: 'thinking'; thinking: any; signature: string; } | { type: 'redacted_thinking', data: any }) + +export type LLMPlan = { + title?: string; + items: Array<{ id?: string; text: string; state?: 'pending' | 'running' | 'done' | 'error' }>; +}; + +export type OnText = (p: { + fullText: string; + fullReasoning: string; + toolCall?: RawToolCallObj; + plan?: LLMPlan; + /** Optional per-request token usage snapshot when the provider reports it. */ + tokenUsage?: LLMTokenUsage; +}) => void + +export type OnFinalMessage = (p: { + fullText: string; + fullReasoning: string; + toolCall?: RawToolCallObj; + anthropicReasoning: AnthropicReasoning[] | null; + plan?: LLMPlan; + /** Final per-request token usage when the provider reports it. */ + tokenUsage?: LLMTokenUsage; +}) => void // id is tool_use_id +export type OnError = (p: { message: string; fullError: Error | null }) => void +export type OnAbort = () => void +export type AbortRef = { current: (() => void) | null } + + +// service types +type SendLLMType = { + messagesType: 'chatMessages'; + messages: LLMChatMessage[]; // the type of raw chat messages that we send to Anthropic, OAI, etc + separateSystemMessage: string | undefined; + chatMode: ChatMode | null; + tool_choice?: { type: 'function', function: { name: string } } | 'none' | 'auto' | 'required'; +} | { + messagesType: 'FIMMessage'; + messages: LLMFIMMessage; + separateSystemMessage?: undefined; + chatMode?: undefined; + tool_choice?: undefined; +} +export type ServiceSendLLMMessageParams = { + onText: OnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + logging: { loggingName: string, loggingExtras?: { [k: string]: any } }; + modelSelection: ModelSelection | null; + modelSelectionOptions: ModelSelectionOptions | undefined; + overridesOfModel: OverridesOfModel | undefined; + onAbort: OnAbort; + // Optional OpenRouter provider routing object (sent as top-level `provider` field) + providerRouting?: ProviderRouting; +} & SendLLMType; + +// params to the true sendLLMMessage function +export type SendLLMMessageParams = { + onText: OnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + logging: { loggingName: string, loggingExtras?: { [k: string]: any } }; + abortRef: AbortRef; + + modelSelection: ModelSelection; + modelSelectionOptions: ModelSelectionOptions | undefined; + overridesOfModel: OverridesOfModel | undefined; + + settingsOfProvider: SettingsOfProvider; + additionalTools?: AdditionalToolInfo[]; + /** Disabled static Void tool names. */ + disabledStaticTools?: string[]; + /** Disabled dynamic/MCP tool names (prefixed names). */ + disabledDynamicTools?: string[]; + dynamicRequestConfig?: DynamicRequestConfig; + // Optional per-model request parameter injection (e.g., OpenRouter supported parameters) + requestParams?: RequestParamsConfig; + // Optional OpenRouter provider routing object (sent as top-level `provider` field) + providerRouting?: ProviderRouting; + // Optional UI/global switch: emit warning notification when response is truncated. + notifyOnTruncation?: boolean; +} & SendLLMType + + +export type JsonSchemaLike = { + description?: string; + type?: string; + enum?: any[]; + items?: JsonSchemaLike; + properties?: { [propName: string]: JsonSchemaLike }; + required?: string[]; + default?: any; + minimum?: number; + maximum?: number; + minLength?: number; + maxLength?: number; +}; + + +// can't send functions across a proxy, use listeners instead +export type BlockedMainLLMMessageParams = 'onText' | 'onFinalMessage' | 'onError' | 'abortRef' +// Additional dynamic tools from MCP and other sources +export type AdditionalToolInfo = { + name: string; + description: string; + params?: { + [paramName: string]: JsonSchemaLike; + }; +}; + +export type MainSendLLMMessageParams = + Omit + & { + requestId: string; + } + & SendLLMType +export type MainLLMMessageAbortParams = { requestId: string } + +export type EventLLMMessageOnTextParams = Parameters[0] & { + requestId: string; + /** + * Internal transport optimization flags. + * When true, corresponding field carries only delta relative to previous chunk for this request. + */ + isFullTextDelta?: boolean; + isFullReasoningDelta?: boolean; +} +export type EventLLMMessageOnFinalMessageParams = Parameters[0] & { requestId: string } +export type EventLLMMessageOnErrorParams = Parameters[0] & { requestId: string } + +// service -> main -> internal -> event (back to main) +// (browser) + + +// These are from 'ollama' SDK +interface OllamaModelDetails { + parent_model: string; + format: string; + family: string; + families: string[]; + parameter_size: string; + quantization_level: string; +} + +export type OllamaModelResponse = { + name: string; + modified_at: Date; + size: number; + digest: string; + details: OllamaModelDetails; + expires_at: Date; + size_vram: number; +} + +export type OpenaiCompatibleModelResponse = { + id: string; + created: number; + object: 'model'; + owned_by: string; +} + +// params to the true list fn +export type ModelListParams = { + providerName: ProviderName; + settingsOfProvider: SettingsOfProvider; + onSuccess: (param: { models: ModelResponse[] }) => void; + onError: (param: { error: string }) => void; +} + +// params to the service +export type ServiceModelListParams = { + providerName: RefreshableProviderName; + onSuccess: (param: { models: modelResponse[] }) => void; + onError: (param: { error: any }) => void; +} + +type BlockedMainModelListParams = 'onSuccess' | 'onError' +export type MainModelListParams = Omit, BlockedMainModelListParams> & { providerName: RefreshableProviderName, requestId: string } + +export type EventModelListOnSuccessParams = Parameters['onSuccess']>[0] & { requestId: string } +export type EventModelListOnErrorParams = Parameters['onError']>[0] & { requestId: string } diff --git a/src/vs/platform/void/common/storageKeys.ts b/src/vs/platform/void/common/storageKeys.ts new file mode 100644 index 00000000000..476c05b1307 --- /dev/null +++ b/src/vs/platform/void/common/storageKeys.ts @@ -0,0 +1,19 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// past values: +// 'void.settingsServiceStorage' +// 'void.settingsServiceStorageI' // 1.0.2 + +// 1.0.3 +export const VOID_SETTINGS_STORAGE_KEY = 'void.settingsServiceStorageII' + + +// past values: +// 'void.chatThreadStorage' +// 'void.chatThreadStorageI' // 1.0.2 + +// 1.0.3 +export const THREAD_STORAGE_KEY = 'void.chatThreadStorageII' diff --git a/src/vs/platform/void/common/test/dynamicModelService.test.ts b/src/vs/platform/void/common/test/dynamicModelService.test.ts new file mode 100644 index 00000000000..80a2896c3ac --- /dev/null +++ b/src/vs/platform/void/common/test/dynamicModelService.test.ts @@ -0,0 +1,553 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { DynamicModelService } from '../dynamicModelService.js'; +import { newWriteableStream } from '../../../../base/common/stream.js'; + +const NullLogService = { + trace: () => { }, + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { } +}; + +class MockRequestService { + responseData: unknown; + shouldThrow: boolean; + callCount: number; + + constructor(responseData: unknown = null, shouldThrow = false) { + this.responseData = responseData; + this.shouldThrow = shouldThrow; + this.callCount = 0; + } + + async request(_options: unknown, _token: unknown): Promise<{ res: { statusCode: number }; stream: ReturnType }> { + this.callCount++; + if (this.shouldThrow) throw new Error('Network error'); + const stream = newWriteableStream((strings: string[]) => strings.join()); + if (this.responseData !== undefined && this.responseData !== null) { + stream.write(JSON.stringify(this.responseData)); + } + stream.end(); + return { res: { statusCode: 200 }, stream }; + } +} + + +function stubFetch(service: DynamicModelService, impl: (...args: any[]) => Promise) { + const key = 'fetchOpenRouterModels' as const; + const original = (service as any)[key] as (...args: any[]) => Promise; + let calls = 0; + (service as any)[key] = async (...args: any[]): Promise => { + calls++; + return impl(...args); + }; + return { restore() { (service as any)[key] = original; }, get callCount() { return calls; } }; +} + +suite('DynamicModelService', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + let originalConsole: { log: (...args: unknown[]) => void; error: (...args: unknown[]) => void; warn: (...args: unknown[]) => void }; + let originalStorageOverride: any; + + function makeMemoryStorage() { + const store = new Map(); + return { + getItem(k: string) { return store.has(k) ? store.get(k)! : null; }, + setItem(k: string, v: string) { store.set(k, String(v)); }, + removeItem(k: string) { store.delete(k); }, + clear() { store.clear(); } + }; + } + + setup(() => { + originalConsole = { log: console.log, error: console.error, warn: console.warn }; + console.log = () => { }; + console.error = () => { }; + console.warn = () => { }; + + + const g = globalThis as any; + originalStorageOverride = g.__voidDynamicModelStorage__; + g.__voidDynamicModelStorage__ = makeMemoryStorage(); + }); + + teardown(() => { + console.log = originalConsole.log; + console.error = originalConsole.error; + console.warn = originalConsole.warn; + + const g = globalThis as any; + if (typeof originalStorageOverride === 'undefined') { + delete g.__voidDynamicModelStorage__; + } else { + g.__voidDynamicModelStorage__ = originalStorageOverride; + } + }); + + suite('Constructor and DI', () => { + test('should create service with IRequestService dependency', () => { + const mockRequestService = new MockRequestService(); + const service = new DynamicModelService(mockRequestService as any, NullLogService as any); + assert.ok(service); + assert.strictEqual(service._serviceBrand, undefined); + }); + }); + + suite('initialize', () => { + test('should not initialize twice on success', async () => { + const mockData = { + data: [{ + id: 'openai/o4-mini-deep-research', + canonical_slug: 'openai/o4-mini-deep-research-2025-06-26', + name: 'OpenAI: o4 Mini Deep Research', + context_length: 200000, + pricing: { prompt: '0.000002', completion: '0.000008' }, + top_provider: { max_completion_tokens: 100000, is_moderated: true }, + supported_parameters: ['frequency_penalty', 'include_reasoning', 'reasoning', 'tool_choice', 'tools'], + architecture: { modality: 'text+image->text', input_modalities: ['file', 'image', 'text'], output_modalities: ['text'], tokenizer: 'GPT', instruct_type: null } + }] + }; + + const service = new DynamicModelService( + { request: async () => { throw new Error('should not be used'); } } as any, + NullLogService as any + ); + + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const firstCalls = fetchStub.callCount; + + await service.initialize(); + const secondCalls = fetchStub.callCount; + + assert.strictEqual(secondCalls, firstCalls); + fetchStub.restore(); + }); + + test('should allow retry after a failed initialization', async () => { + const service = new DynamicModelService( + { request: async () => { throw new Error('should not be used'); } } as any, + NullLogService as any + ); + let step = 0; + const mockData = { + data: [{ + id: 'provider/modelA', + canonical_slug: 'provider/modelA-2025', + name: 'Model A', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 1024, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const fetchStub = stubFetch(service, async () => { + if (step === 0) { step++; throw new Error('Network error'); } + return mockData; + }); + + await service.initialize(); + await service.initialize(); + + assert.ok(service.getDynamicCapabilities('provider/modelA')); + fetchStub.restore(); + }); + + test('should handle network errors gracefully', async () => { + const mockRequestService = new MockRequestService(null, true); + const service = new DynamicModelService(mockRequestService as any, NullLogService as any); + + await service.initialize(); + assert.strictEqual(mockRequestService.callCount, 1); + }); + + test('should handle invalid response format', async () => { + const mockRequestService = new MockRequestService({ invalid: 'format' }); + const service = new DynamicModelService(mockRequestService as any, NullLogService as any); + + await service.initialize(); + assert.strictEqual(mockRequestService.callCount, 1); + }); + + test('should handle null response', async () => { + const mockRequestService = new MockRequestService(null); + const service = new DynamicModelService(mockRequestService as any, NullLogService as any); + + await service.initialize(); + assert.strictEqual(mockRequestService.callCount, 1); + }); + }); + + suite('getDynamicCapabilities', () => { + test('should return null for unknown model', async () => { + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const capabilities = service.getDynamicCapabilities('unknown/model'); + assert.strictEqual(capabilities, null); + }); + + test('should return capabilities for known model and keep canonical_slug', async () => { + const mockData = { + data: [{ + id: 'test/model', + canonical_slug: 'canonical/test-model', + name: 'Test Model', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const capabilities = service.getDynamicCapabilities('test/model') as any; + + assert.ok(capabilities); + assert.strictEqual(capabilities.modelName, 'test/model'); + assert.strictEqual(capabilities.recognizedModelName, 'canonical/test-model'); + assert.strictEqual(capabilities.contextWindow, 4096); + assert.strictEqual(capabilities.reservedOutputTokenSpace, 2048); + assert.strictEqual(capabilities.cost.input, 0.01); + assert.strictEqual(capabilities.cost.output, 0.02); + assert.strictEqual(capabilities.specialToolFormat, 'openai-style'); + + assert.strictEqual(capabilities.supportsSystemMessage, 'system-role'); + assert.strictEqual(capabilities.supportsFIM ?? false, false); + assert.strictEqual(capabilities.reasoningCapabilities, false); + + fetchStub.restore(); + }); + }); + + suite('getAllDynamicCapabilities', () => { + test('should return empty object when no capabilities', async () => { + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + await service.initialize(); + const allCapabilities = service.getAllDynamicCapabilities(); + assert.deepStrictEqual(allCapabilities, {}); + }); + + test('should return all capabilities', async () => { + const mockData = { + data: [ + { + id: 'model1', + canonical_slug: 'model1', + name: 'Model 1', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }, + { + id: 'model2', + canonical_slug: 'model2', + name: 'Model 2', + context_length: 8192, + pricing: { prompt: '0.02', completion: '0.04' }, + top_provider: { max_completion_tokens: 4096, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + } + ] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const allCapabilities = service.getAllDynamicCapabilities(); + + assert.strictEqual(Object.keys(allCapabilities).length, 2); + assert.ok(allCapabilities['model1']); + assert.ok(allCapabilities['model2']); + assert.strictEqual(allCapabilities['model1'].contextWindow, 4096); + assert.strictEqual(allCapabilities['model2'].contextWindow, 8192); + + fetchStub.restore(); + }); + }); + + suite('getModelCapabilitiesWithFallback', () => { + test('should return dynamic capabilities when available', async () => { + const mockData = { + data: [{ + id: 'dynamic/model', + canonical_slug: 'dynamic/model-2025', + name: 'Dynamic Model', + context_length: 8192, + pricing: { prompt: '0.05', completion: '0.10' }, + top_provider: { max_completion_tokens: 4096, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const result = await service.getModelCapabilitiesWithFallback('openAI', 'dynamic/model', undefined); + + assert.strictEqual(result.modelName, 'dynamic/model'); + assert.strictEqual(result.recognizedModelName, 'dynamic/model'); + assert.strictEqual(result.isUnrecognizedModel, false); + assert.strictEqual(result.contextWindow, 8192); + + fetchStub.restore(); + }); + + test('should apply overrides when provided and not mutate stored dynamic caps', async () => { + const mockData = { + data: [{ + id: 'test/model', + canonical_slug: 'test/model', + name: 'Test Model', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const overrides = { + openAI: { + 'test/model': { contextWindow: 16384, supportsFIM: true } + } + }; + + const storedBefore = service.getDynamicCapabilities('test/model'); + assert.ok(storedBefore); + assert.strictEqual(storedBefore.supportsFIM ?? false, false); + + const result = await service.getModelCapabilitiesWithFallback('openAI', 'test/model', overrides as any); + + assert.strictEqual(result.contextWindow, 16384); + assert.strictEqual(result.supportsFIM, true); + assert.strictEqual(result.reservedOutputTokenSpace, 2048); + + const storedAfter = service.getDynamicCapabilities('test/model'); + assert.strictEqual(storedAfter, storedBefore); + assert.strictEqual(storedAfter.supportsFIM ?? false, false); + + fetchStub.restore(); + }); + + test('should return fallback capabilities for unknown model', async () => { + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + await service.initialize(); + + const result: any = await service.getModelCapabilitiesWithFallback('openAI', 'unknown/model', undefined as any); + + assert.strictEqual(result.modelName, 'unknown/model'); + assert.strictEqual(result.isUnrecognizedModel, true); + assert.strictEqual(result.contextWindow, 4096); + assert.strictEqual(result.reservedOutputTokenSpace, 4096); + assert.strictEqual(result.cost.input, 0); + assert.strictEqual(result.cost.output, 0); + assert.strictEqual(result.supportsSystemMessage, 'system-role'); + assert.strictEqual(result.specialToolFormat, 'openai-style'); + assert.strictEqual(result.supportsFIM ?? false, false); + assert.strictEqual(result.reasoningCapabilities, false); + assert.strictEqual(result._apiConfig, undefined); + }); + + test('should handle empty overrides gracefully', async () => { + const mockData = { + data: [{ + id: 'test/model', + canonical_slug: 'test/model', + name: 'Test Model', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const result = await service.getModelCapabilitiesWithFallback('openAI', 'test/model', {} as any); + + assert.strictEqual(result.contextWindow, 4096); + assert.strictEqual(result.supportsFIM ?? false, false); + + fetchStub.restore(); + }); + + test('should apply overrides regardless of providerName case', async () => { + const mockData = { + data: [{ + id: 'test/model', + canonical_slug: 'test/model', + name: 'Test Model', + context_length: 4096, + pricing: { prompt: '0.01', completion: '0.02' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + + + const overrides = { + openai: { + 'test/model': { contextWindow: 10000, supportsFIM: true } + } + }; + + + const result = await service.getModelCapabilitiesWithFallback('OpenAI', 'test/model', overrides as any); + + assert.strictEqual(result.contextWindow, 10000); + assert.strictEqual(result.supportsFIM, true); + + fetchStub.restore(); + }); + }); + + suite('API Configuration', () => { + test('should return correct API config for OpenRouter models', async () => { + const mockData = { + data: [{ + id: 'openai/gpt-4', + canonical_slug: 'openai/gpt-4', + name: 'OpenAI: GPT-4', + context_length: 8192, + pricing: { prompt: '0.03', completion: '0.06' }, + top_provider: { max_completion_tokens: 4096, is_moderated: true }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'GPT' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => mockData); + + await service.initialize(); + const capabilities = service.getDynamicCapabilities('openai/gpt-4') as any; + + assert.ok(capabilities); + assert.strictEqual(capabilities._apiConfig.apiStyle, 'openai-compatible'); + assert.strictEqual(capabilities._apiConfig.supportsSystemMessage, 'developer-role'); + assert.strictEqual(capabilities._apiConfig.specialToolFormat, 'openai-style'); + assert.strictEqual(capabilities._apiConfig.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(capabilities._apiConfig.auth.header, 'Authorization'); + assert.strictEqual(capabilities._apiConfig.auth.format, 'Bearer'); + + fetchStub.restore(); + }); + }); + + suite('Caching', () => { + const CACHE_KEY = 'void.openrouter.models.cache.v1'; + + test('should load from fresh cache and not hit network', async () => { + const now = Date.now(); + const cached = { + ts: now, + data: [{ + id: 'cached/model', + canonical_slug: 'cached/model', + name: 'Cached Model', + context_length: 1024, + pricing: { prompt: '0.001', completion: '0.002' }, + top_provider: { max_completion_tokens: 512, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + const storage = (globalThis as any).__voidDynamicModelStorage__; + storage.setItem(CACHE_KEY, JSON.stringify(cached)); + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => { throw new Error('should not be called'); }); + + await service.initialize(); + const caps = service.getDynamicCapabilities('cached/model'); + assert.ok(caps); + assert.strictEqual(fetchStub.callCount, 0); + + fetchStub.restore(); + }); + + test('should refresh from network when cache expired and write cache', async () => { + const past = Date.now() - (25 * 60 * 60 * 1000); + const cached = { + ts: past, + data: [{ + id: 'old/model', + canonical_slug: 'old/model', + name: 'Old Model', + context_length: 2048, + pricing: { prompt: '0', completion: '0' }, + top_provider: { max_completion_tokens: 1024, is_moderated: false }, + supported_parameters: [], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + const storage = (globalThis as any).__voidDynamicModelStorage__; + storage.setItem(CACHE_KEY, JSON.stringify(cached)); + + const fresh = { + data: [{ + id: 'new/model', + canonical_slug: 'new/model', + name: 'New Model', + context_length: 4096, + pricing: { prompt: '0.005', completion: '0.01' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('should not be used'); } } as any, NullLogService as any); + const fetchStub = stubFetch(service, async () => fresh); + + await service.initialize(); + + + const capsNew = service.getDynamicCapabilities('new/model'); + assert.ok(capsNew); + + + const raw = storage.getItem(CACHE_KEY); + assert.ok(raw); + const parsed = JSON.parse(raw); + assert.ok(Array.isArray(parsed.data)); + assert.strictEqual(parsed.data[0].id, 'new/model'); + + fetchStub.restore(); + }); + }); +}); diff --git a/src/vs/platform/void/common/test/loopGuard.test.ts b/src/vs/platform/void/common/test/loopGuard.test.ts new file mode 100644 index 00000000000..6dd3f21166d --- /dev/null +++ b/src/vs/platform/void/common/test/loopGuard.test.ts @@ -0,0 +1,64 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { LLMLoopDetector } from '../loopGuard.js'; + +suite('LLMLoopDetector', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('detects max_turns when assistant turns exceed threshold', () => { + const detector = new LLMLoopDetector({ maxTurnsPerPrompt: 2, maxSameAssistantPrefix: 10, maxSameToolCall: 10 }); + + let res = detector.registerAssistantTurn('first answer'); + assert.strictEqual(res.isLoop, false); + + res = detector.registerAssistantTurn('second answer'); + assert.strictEqual(res.isLoop, false); + + res = detector.registerAssistantTurn('third answer'); + assert.strictEqual(res.isLoop, true); + if (res.isLoop) { + assert.strictEqual(res.reason, 'max_turns'); + } + }); + + test('detects assistant_repeat by first-line prefix', () => { + const detector = new LLMLoopDetector({ maxTurnsPerPrompt: 10, maxSameAssistantPrefix: 2, maxSameToolCall: 10 }); + + let res = detector.registerAssistantTurn('Repeat me\nwith some extra details'); + assert.strictEqual(res.isLoop, false); + + res = detector.registerAssistantTurn('Repeat me \nslightly different body'); + assert.strictEqual(res.isLoop, false); + + res = detector.registerAssistantTurn('Repeat me again'); + assert.strictEqual(res.isLoop, true); + if (res.isLoop) { + assert.strictEqual(res.reason, 'assistant_repeat'); + } + }); + + test('detects tool_repeat for identical tool name and args', () => { + const detector = new LLMLoopDetector({ maxTurnsPerPrompt: 10, maxSameAssistantPrefix: 10, maxSameToolCall: 2 }); + + const args = { uri: '/workspace/file.ts', range: { start: 1, end: 5 } }; + + let res = detector.registerToolCall('edit_file', args); + assert.strictEqual(res.isLoop, false); + + res = detector.registerToolCall('edit_file', { range: { end: 5, start: 1 }, uri: '/workspace/file.ts' }); + assert.strictEqual(res.isLoop, false); + + res = detector.registerToolCall('edit_file', args); + assert.strictEqual(res.isLoop, true); + if (res.isLoop) { + assert.strictEqual(res.reason, 'tool_repeat'); + } + }); +}); diff --git a/src/vs/platform/void/common/test/modelInference.test.ts b/src/vs/platform/void/common/test/modelInference.test.ts new file mode 100644 index 00000000000..06d3ea2695b --- /dev/null +++ b/src/vs/platform/void/common/test/modelInference.test.ts @@ -0,0 +1,399 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { + inferCapabilitiesFromOpenRouterModel, + inferReasoningCapabilities, + getModelApiConfiguration, + registerProviderConfigResolver, + registerUserModelApiConfigGetter, + getProviderSlug, + getSystemMessageType, + inferApiStyle, + __dangerouslyResetApiResolversForTests, + WELL_KNOWN_PROVIDER_DEFAULTS +} from '../modelInference.js'; + +suite('ModelInference', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + suite('getProviderSlug', () => { + test('should extract provider from model ID with slash', () => { + assert.strictEqual(getProviderSlug('openai/gpt-4'), 'openai'); + assert.strictEqual(getProviderSlug('anthropic/claude-3-5-sonnet'), 'anthropic'); + assert.strictEqual(getProviderSlug('minimax/minimax-m2'), 'minimax'); + }); + + test('should return _unknown for model ID without slash', () => { + assert.strictEqual(getProviderSlug('gpt-4'), '_unknown'); + assert.strictEqual(getProviderSlug('claude-3-5'), '_unknown'); + }); + }); + + suite('getModelApiConfiguration', () => { + test('should return OpenRouter config for unknown provider', () => { + const config = getModelApiConfiguration('unknown/model'); + assert.strictEqual(config.apiStyle, 'openai-compatible'); + assert.strictEqual(config.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(config.auth.header, 'Authorization'); + assert.strictEqual(config.auth.format, 'Bearer'); + }); + + test('should return OpenAI config for OpenAI models', () => { + const config = getModelApiConfiguration('openai/gpt-4'); + assert.strictEqual(config.apiStyle, 'openai-compatible'); + assert.strictEqual(config.supportsSystemMessage, 'developer-role'); + assert.strictEqual(config.specialToolFormat, 'openai-style'); + assert.strictEqual(config.endpoint, 'https://api.openai.com/v1'); + }); + + test('should return Anthropic config for Anthropic models', () => { + const config = getModelApiConfiguration('anthropic/claude-3-5-sonnet'); + assert.strictEqual(config.apiStyle, 'anthropic-style'); + assert.strictEqual(config.supportsSystemMessage, 'separated'); + assert.strictEqual(config.specialToolFormat, 'anthropic-style'); + assert.strictEqual(config.endpoint, 'https://api.anthropic.com/v1'); + }); + + test('should return Gemini config for Google models', () => { + const config = getModelApiConfiguration('google/gemini-2.5-pro'); + assert.strictEqual(config.apiStyle, 'gemini-style'); + assert.strictEqual(config.supportsSystemMessage, 'separated'); + assert.strictEqual(config.specialToolFormat, 'gemini-style'); + assert.strictEqual(config.endpoint, 'https://generativelanguage.googleapis.com/v1'); + }); + + test('should return correct config for google-vertex alias', () => { + const config = getModelApiConfiguration('google-vertex/gemini-2.0-pro'); + assert.strictEqual(config.apiStyle, 'gemini-style'); + assert.strictEqual(config.supportsSystemMessage, 'separated'); + assert.strictEqual(config.specialToolFormat, 'gemini-style'); + assert.strictEqual(config.endpoint, 'https://generativelanguage.googleapis.com/v1'); + }); + }); + + suite('inferCapabilitiesFromOpenRouterModel', () => { + test('should infer basic capabilities from OpenRouter model', () => { + const mockModel = { + id: 'openai/gpt-4', + canonical_slug: 'openai/gpt-4', + name: 'OpenAI: GPT-4', + created: Date.now(), + context_length: 8192, + pricing: { + prompt: '0.03', + completion: '0.06' + }, + top_provider: { + max_completion_tokens: 4096, + is_moderated: true + }, + supported_parameters: ['tools', 'tool_choice', 'temperature'], + architecture: { + modality: 'text->text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: 'GPT' + } + }; + + const capabilities = inferCapabilitiesFromOpenRouterModel(mockModel as any); + + assert.strictEqual(capabilities.contextWindow, 8192); + assert.strictEqual(capabilities.reservedOutputTokenSpace, 4096); + // cost is always populated by inferCapabilitiesFromOpenRouterModel for this case + const cost = capabilities.cost!; + assert.strictEqual(cost.input, 0.03); + assert.strictEqual(cost.output, 0.06); + assert.strictEqual(capabilities.specialToolFormat, 'openai-style'); + assert.strictEqual(capabilities.supportsSystemMessage, 'developer-role'); + assert.strictEqual(capabilities.supportsFIM ?? false, false); + }); + + test('should infer FIM support from description', () => { + const mockModel = { + id: 'codestral/codestral', + canonical_slug: 'codestral/codestral', + name: 'Codestral', + created: Date.now(), + context_length: 32768, + pricing: { prompt: '0', completion: '0' }, + top_provider: { max_completion_tokens: 4096, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { + modality: 'text->text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: 'Mistral' + }, + description: 'This model supports fill-in-the-middle for autocomplete tasks' + }; + + const capabilities = inferCapabilitiesFromOpenRouterModel(mockModel as any); + assert.strictEqual(capabilities.supportsFIM, true); + }); + + test('should infer FIM support from architecture instruct_type', () => { + const mockModel = { + id: 'provider/fim-model', + canonical_slug: 'provider/fim-model', + name: 'FIM Model', + created: Date.now(), + context_length: 16384, + pricing: { prompt: '0', completion: '0' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['tools', 'tool_choice'], + architecture: { + modality: 'text->text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: 'Other', + instruct_type: 'fim' + } + }; + + const capabilities = inferCapabilitiesFromOpenRouterModel(mockModel as any); + assert.strictEqual(capabilities.supportsFIM, true); + }); + + test('should set specialToolFormat to disabled when no tools support', () => { + const mockModel = { + id: 'basic/model', + canonical_slug: 'basic/model', + name: 'Basic Model', + created: Date.now(), + context_length: 4096, + pricing: { prompt: '0', completion: '0' }, + top_provider: { max_completion_tokens: 2048, is_moderated: false }, + supported_parameters: ['temperature', 'max_tokens'], // no tools + architecture: { + modality: 'text->text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: 'Other' + } + }; + + const capabilities = inferCapabilitiesFromOpenRouterModel(mockModel as any); + assert.strictEqual(capabilities.specialToolFormat, 'disabled'); + assert.strictEqual(capabilities.supportsSystemMessage, false); + }); + + test('should handle missing optional fields gracefully', () => { + const mockModel = { + id: 'minimal/model', + canonical_slug: 'minimal/model', + name: 'Minimal Model', + created: Date.now(), + supported_parameters: [], + architecture: { + modality: 'text->text', + input_modalities: ['text'], + output_modalities: ['text'], + tokenizer: 'Other' + } + }; + + const capabilities = inferCapabilitiesFromOpenRouterModel(mockModel as any); + + assert.strictEqual(capabilities.contextWindow, 4096); // default + assert.strictEqual(capabilities.reservedOutputTokenSpace, 4096); // default + const cost = capabilities.cost!; + assert.strictEqual(cost.input, 0); // default + assert.strictEqual(cost.output, 0); // default + assert.strictEqual(capabilities.specialToolFormat, 'disabled'); + assert.strictEqual(capabilities.supportsSystemMessage, false); + assert.strictEqual(capabilities.supportsFIM ?? false, false); + }); + }); + + suite('inferReasoningCapabilities', () => { + test('should return false when no reasoning parameters', () => { + const params = ['temperature', 'max_tokens']; + const model: any = { + name: 'Basic Model', + top_provider: { max_completion_tokens: 4096 } + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + assert.strictEqual(reasoning, false); + }); + + test('should return Anthropic reasoning for Claude models', () => { + const params = ['reasoning', 'include_reasoning']; + const model: any = { + name: 'Anthropic Claude 3.5 Sonnet', + top_provider: { max_completion_tokens: 8192 } + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + + assert.strictEqual(reasoning.supportsReasoning, true); + assert.strictEqual(reasoning.canTurnOffReasoning, false); + assert.strictEqual(reasoning.canIOReasoning, true); + assert.strictEqual(reasoning.reasoningReservedOutputTokenSpace, 8192); + assert.strictEqual(reasoning.reasoningSlider.type, 'budget_slider'); + assert.strictEqual(reasoning.reasoningSlider.min, 1024); + assert.strictEqual(reasoning.reasoningSlider.max, 8192); + assert.strictEqual(reasoning.reasoningSlider.default, 1024); + }); + + test('should return OpenAI reasoning for GPT models', () => { + const params = ['reasoning', 'include_reasoning']; + const model: any = { + name: 'OpenAI GPT-4 with reasoning', + top_provider: { max_completion_tokens: 4096 } + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + + assert.strictEqual(reasoning.supportsReasoning, true); + assert.strictEqual(reasoning.canTurnOffReasoning, true); + assert.strictEqual(reasoning.canIOReasoning, true); + assert.strictEqual(reasoning.reasoningSlider.type, 'effort_slider'); + assert.deepStrictEqual(reasoning.reasoningSlider.values, ['low', 'medium', 'high']); + assert.strictEqual(reasoning.reasoningSlider.default, 'low'); + }); + + test('should detect thinking-only models', () => { + const params = ['reasoning', 'include_reasoning']; + const model: any = { + name: 'MiniMax M2 Thinking Model', + canonical_slug: 'minimax/minimax-m2', + description: 'This is a thinking only model for reasoning tasks' + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + + assert.strictEqual(reasoning.supportsReasoning, true); + assert.strictEqual(reasoning.canTurnOffReasoning, false); + assert.strictEqual(reasoning.canIOReasoning, true); + assert.deepStrictEqual(reasoning.openSourceThinkTags, ['', '']); + }); + + test('non-thinking indicator should disable thinking-only path', () => { + const params = ['reasoning', 'include_reasoning']; + const model: any = { + name: 'Some Model', + canonical_slug: 'provider/some-model', + description: 'This is a non-thinking mode even if thinking tag appears ' + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + + assert.strictEqual(reasoning.supportsReasoning, true); + assert.strictEqual(reasoning.canTurnOffReasoning, true); + assert.strictEqual(reasoning.canIOReasoning, true); + assert.strictEqual(reasoning.reasoningSlider.type, 'effort_slider'); + }); + + test('should return default reasoning for unknown models', () => { + const params = ['reasoning', 'include_reasoning']; + const model: any = { + name: 'Unknown Reasoning Model', + top_provider: { max_completion_tokens: 4096 } + }; + + const reasoning = inferReasoningCapabilities(params, model as any); + + assert.strictEqual(reasoning.supportsReasoning, true); + assert.strictEqual(reasoning.canTurnOffReasoning, true); + assert.strictEqual(reasoning.canIOReasoning, true); + assert.strictEqual(reasoning.reasoningSlider.type, 'effort_slider'); + assert.deepStrictEqual(reasoning.reasoningSlider.values, ['low', 'medium', 'high']); + assert.strictEqual(reasoning.reasoningSlider.default, 'low'); + }); + }); + + suite('helpers and configuration', () => { + test('getSystemMessageType should map provider to correct type', () => { + assert.strictEqual(getSystemMessageType('openai'), 'developer-role'); + assert.strictEqual(getSystemMessageType('anthropic'), 'separated'); + assert.strictEqual(getSystemMessageType('google'), 'separated'); + assert.strictEqual(getSystemMessageType('unknown-provider'), 'system-role'); // default + }); + + test('inferApiStyle should map provider to api style', () => { + assert.strictEqual(inferApiStyle('openai'), 'openai-compatible'); + assert.strictEqual(inferApiStyle('anthropic'), 'anthropic-style'); + assert.strictEqual(inferApiStyle('google'), 'gemini-style'); + assert.strictEqual(inferApiStyle('unknown-provider'), 'openai-compatible'); + }); + + test('WELL_KNOWN_PROVIDER_DEFAULTS sanity', () => { + assert.ok(WELL_KNOWN_PROVIDER_DEFAULTS.openai); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.openai.apiStyle, 'openai-compatible'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.openai.supportsSystemMessage, 'developer-role'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.openai.baseEndpoint, 'https://api.openai.com/v1'); + + assert.ok(WELL_KNOWN_PROVIDER_DEFAULTS.anthropic); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.anthropic.apiStyle, 'anthropic-style'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.anthropic.supportsSystemMessage, 'separated'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.anthropic.baseEndpoint, 'https://api.anthropic.com/v1'); + + assert.ok(WELL_KNOWN_PROVIDER_DEFAULTS.google); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.google.apiStyle, 'gemini-style'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.google.supportsSystemMessage, 'separated'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS.google.baseEndpoint, 'https://generativelanguage.googleapis.com/v1'); + + assert.ok(WELL_KNOWN_PROVIDER_DEFAULTS._default); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS._default.apiStyle, 'openai-compatible'); + assert.strictEqual(WELL_KNOWN_PROVIDER_DEFAULTS._default.supportsSystemMessage, 'system-role'); + }); + }); +}); + +suite('API resolver hooks', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + teardown(() => { + __dangerouslyResetApiResolversForTests(); + }); + + test('per-model override takes precedence over provider resolver and defaults', () => { + registerProviderConfigResolver((providerSlug) => { + if (providerSlug === 'fake-provider') { + return { endpoint: 'https://example.com/openai', apiStyle: 'openai-compatible' }; + } + return null; + }); + + registerUserModelApiConfigGetter((modelId) => { + if (modelId === 'fake-provider/special-model') { + return { + apiStyle: 'anthropic-style', + supportsSystemMessage: 'separated', + specialToolFormat: 'anthropic-style', + endpoint: 'https://api.anthropic.com/v1', + auth: { header: 'Authorization', format: 'Bearer' } + }; + } + return null; + }); + + const cfg = getModelApiConfiguration('fake-provider/special-model'); + assert.strictEqual(cfg.apiStyle, 'anthropic-style'); + assert.strictEqual(cfg.supportsSystemMessage, 'separated'); + assert.strictEqual(cfg.specialToolFormat, 'anthropic-style'); + assert.strictEqual(cfg.endpoint, 'https://api.anthropic.com/v1'); + }); + + test('provider resolver is used when per-model override is absent', () => { + registerProviderConfigResolver((providerSlug) => { + if (providerSlug === 'fake-provider') { + return { endpoint: 'https://example.com/anthropic', apiStyle: 'anthropic-style', supportsSystemMessage: 'separated' }; + } + return null; + }); + + const cfg = getModelApiConfiguration('fake-provider/any-model'); + assert.strictEqual(cfg.apiStyle, 'anthropic-style'); + assert.strictEqual(cfg.supportsSystemMessage, 'separated'); + assert.strictEqual(cfg.specialToolFormat, 'anthropic-style'); + assert.strictEqual(cfg.endpoint, 'https://example.com/anthropic'); + }); +}); diff --git a/src/vs/platform/void/common/test/providerReg.test.ts b/src/vs/platform/void/common/test/providerReg.test.ts new file mode 100644 index 00000000000..d32699c9a05 --- /dev/null +++ b/src/vs/platform/void/common/test/providerReg.test.ts @@ -0,0 +1,342 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// src/vs/platform/void/common/test/providerReg.test.ts + +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { DynamicProviderRegistryService } from '../providerReg.js'; +import { NullLogService } from '../../../log/common/log.js'; + +function makeMockRequestService() { + return { + request: async (_opts: unknown, _ct: unknown): Promise => { + return {}; + } + }; +} + +function makeMockSettingsService() { + const state: { customProviders: Record } = { customProviders: {} }; + const calls: { setCustomProviderSettings: any[]; setAutodetectedModels: any[] } = { + setCustomProviderSettings: [], + setAutodetectedModels: [] + }; + return { + state, + calls, + async setCustomProviderSettings(slug: string, settings: any | undefined) { + calls.setCustomProviderSettings.push({ slug, settings }); + if (settings === undefined) { + delete state.customProviders?.[slug]; + } else { + state.customProviders[slug] = settings; + } + }, + async setAutodetectedModels(provider: string, models: string[], caps: Record) { + calls.setAutodetectedModels.push({ provider, models, caps }); + } + }; +} + +function makeMockDynamicModelService(initialCaps?: Record) { + let caps: Record = initialCaps || {}; + return { + async initialize(): Promise { + // no-op + }, + getAllDynamicCapabilities(): Record { + return caps; + }, + getDynamicCapabilities(id: string): any { + return caps[id]; + }, + __setCaps(next: Record) { + caps = next || {}; + } + }; +} + +suite('DynamicProviderRegistryService.refreshModelsForProvider', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('local endpoint: matches capabilities via OpenRouter index and сохраняет ids как есть (включая "/")', async () => { + const requestService = makeMockRequestService(); + + const orCaps = { + 'openai/gpt-5': { + contextWindow: 128000, + reservedOutputTokenSpace: 4096, + cost: { input: 0.01, output: 0.02 }, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true }, + fimTransport: undefined + }, + 'openai/gpt-4o': { + contextWindow: 200000, + reservedOutputTokenSpace: 8192, + cost: { input: 0.005, output: 0.015 }, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true }, + fimTransport: undefined + }, + 'mistral/mistral-large-latest': { + contextWindow: 64000, + reservedOutputTokenSpace: 4096, + cost: { input: 0.002, output: 0.006 }, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: false, + fimTransport: undefined + } + }; + + const dynamicModelService = makeMockDynamicModelService(orCaps); + const settingsService = makeMockSettingsService(); + + settingsService.state.customProviders['local'] = { + endpoint: 'http://127.0.0.1:11434', + models: [] + }; + + const svc = new DynamicProviderRegistryService( + // @ts-ignore + requestService, + // @ts-ignore + settingsService, + // @ts-ignore + dynamicModelService, + new NullLogService() + ); + + // @ts-ignore + svc.refreshModelsViaProviderEndpoint = async (_slug, _endpoint) => ([ + 'gpt-5-high', + 'openai/gpt-4o', + 'mistral-large-latest' + ]); + + await svc.refreshModelsForProvider('local'); + + const saved = settingsService.state.customProviders['local']; + assert.ok(saved); + + + assert.deepStrictEqual(saved.models, ['gpt-5-high', 'openai/gpt-4o', 'mistral-large-latest']); + assert.ok(typeof saved.modelsLastRefreshedAt === 'number' && saved.modelsLastRefreshedAt > 0); + + assert.ok(saved.modelsCapabilities); + const caps = saved.modelsCapabilities; + + // gpt-5-high -> openai/gpt-5 (matcher) + assert.strictEqual(caps['gpt-5-high'].contextWindow, orCaps['openai/gpt-5'].contextWindow); + assert.strictEqual(caps['gpt-5-high'].specialToolFormat, 'openai-style'); + assert.strictEqual(caps['gpt-5-high'].supportsSystemMessage, 'developer-role'); + + // openai/gpt-4o -> exact + assert.strictEqual(caps['openai/gpt-4o'].contextWindow, orCaps['openai/gpt-4o'].contextWindow); + assert.strictEqual(caps['openai/gpt-4o'].cost.input, 0.005); + + // mistral-large-latest -> mistral/mistral-large-latest + assert.strictEqual(caps['mistral-large-latest'].contextWindow, orCaps['mistral/mistral-large-latest'].contextWindow); + assert.strictEqual(caps['mistral-large-latest'].supportsSystemMessage, 'system-role'); + + assert.ok(settingsService.calls.setAutodetectedModels.length >= 1); + const publishArgs = settingsService.calls.setAutodetectedModels.at(-1); + assert.strictEqual(publishArgs.provider, 'openRouter'); + + + assert.ok(publishArgs.models.includes('local/gpt-5-high')); + assert.ok(publishArgs.models.includes('local/openai/gpt-4o')); + assert.ok(publishArgs.models.includes('local/mistral-large-latest')); + }); + + test('non-local unknown provider: falls back to {endpoint}/models and infers capabilities', async () => { + const requestService = makeMockRequestService(); + + const orCaps = { + 'anthropic/claude-3-5-sonnet': { + contextWindow: 200000, + reservedOutputTokenSpace: 8192, + cost: { input: 0.003, output: 0.009 }, + supportsSystemMessage: 'separated', + specialToolFormat: 'anthropic-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true }, + fimTransport: undefined + } + }; + const dynamicModelService = makeMockDynamicModelService(orCaps); + const settingsService = makeMockSettingsService(); + + settingsService.state.customProviders['acme'] = { + endpoint: 'https://api.acme.ai/v1', + models: [] + }; + + const svc = new DynamicProviderRegistryService( + // @ts-ignore + requestService, + // @ts-ignore + settingsService, + // @ts-ignore + dynamicModelService, + new NullLogService() + ); + + // @ts-ignore + svc.refreshModelsViaProviderEndpoint = async (_slug, _endpoint) => ([ + 'claude-3-5-sonnet' + ]); + + await svc.refreshModelsForProvider('acme'); + + const saved = settingsService.state.customProviders['acme']; + assert.ok(saved); + assert.deepStrictEqual(saved.models, ['claude-3-5-sonnet']); + assert.ok(saved.modelsCapabilities); + const caps = saved.modelsCapabilities; + + assert.strictEqual(caps['claude-3-5-sonnet'].supportsSystemMessage, 'separated'); + assert.strictEqual(caps['claude-3-5-sonnet'].specialToolFormat, 'anthropic-style'); + + assert.ok(settingsService.calls.setAutodetectedModels.length >= 1); + const publishArgs = settingsService.calls.setAutodetectedModels.at(-1); + assert.ok(publishArgs.models.includes('acme/claude-3-5-sonnet')); + }); +}); + +suite('DynamicProviderRegistryService.setProviderModels (manual Add)', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('manual Add without caps: infers capabilities via same matcher as refresh', async () => { + const requestService = makeMockRequestService(); + + const orCaps = { + 'openai/gpt-5': { + contextWindow: 128000, + reservedOutputTokenSpace: 4096, + cost: { input: 0.01, output: 0.02 }, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true } + }, + 'openai/gpt-4o': { + contextWindow: 200000, + reservedOutputTokenSpace: 8192, + cost: { input: 0.005, output: 0.015 }, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true } + } + }; + + const dynamicModelService = makeMockDynamicModelService(orCaps); + const settingsService = makeMockSettingsService(); + + settingsService.state.customProviders['local'] = { + endpoint: 'http://127.0.0.1:11434', + models: [] + }; + + const svc = new DynamicProviderRegistryService( + // @ts-ignore + requestService, + // @ts-ignore + settingsService, + // @ts-ignore + dynamicModelService, + new NullLogService() + ); + + + await svc.setProviderModels('local', ['gpt-5-high', 'openai/gpt-4o']); + + const saved = settingsService.state.customProviders['local']; + assert.ok(saved); + + assert.deepStrictEqual(saved.models, ['gpt-5-high', 'openai/gpt-4o']); + assert.ok(saved.modelsCapabilities); + + // gpt-5-high -> matcher -> openai/gpt-5 + assert.strictEqual(saved.modelsCapabilities['gpt-5-high'].contextWindow, 128000); + assert.strictEqual(saved.modelsCapabilities['gpt-5-high'].supportsSystemMessage, 'developer-role'); + + // openai/gpt-4o -> exact + assert.strictEqual(saved.modelsCapabilities['openai/gpt-4o'].contextWindow, 200000); + assert.strictEqual(saved.modelsCapabilities['openai/gpt-4o'].reservedOutputTokenSpace, 8192); + + // publish adds slug prefix only + const publishArgs = settingsService.calls.setAutodetectedModels.at(-1); + assert.ok(publishArgs.models.includes('local/gpt-5-high')); + assert.ok(publishArgs.models.includes('local/openai/gpt-4o')); + }); + + test('manual Add: keeps existing saved caps and infers only missing', async () => { + const requestService = makeMockRequestService(); + + const orCaps = { + 'openai/gpt-5': { + contextWindow: 128000, + reservedOutputTokenSpace: 4096, + cost: { input: 0.01, output: 0.02 }, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { supportsReasoning: true } + } + }; + + const dynamicModelService = makeMockDynamicModelService(orCaps); + const settingsService = makeMockSettingsService(); + + // existing caps must NOT be overwritten + settingsService.state.customProviders['local'] = { + endpoint: 'http://127.0.0.1:11434', + models: ['openai/gpt-4o'], + modelsCapabilities: { + 'openai/gpt-4o': { + contextWindow: 1111, + reservedOutputTokenSpace: 2222, + cost: { input: 9, output: 9 }, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style' + } + } + }; + + const svc = new DynamicProviderRegistryService( + // @ts-ignore + requestService, + // @ts-ignore + settingsService, + // @ts-ignore + dynamicModelService, + new NullLogService() + ); + + await svc.setProviderModels('local', ['openai/gpt-4o', 'gpt-5-high']); + + const saved = settingsService.state.customProviders['local']; + assert.ok(saved.modelsCapabilities); + + // preserved + assert.strictEqual(saved.modelsCapabilities['openai/gpt-4o'].contextWindow, 1111); + assert.strictEqual(saved.modelsCapabilities['openai/gpt-4o'].reservedOutputTokenSpace, 2222); + + // inferred for missing + assert.strictEqual(saved.modelsCapabilities['gpt-5-high'].contextWindow, 128000); + assert.strictEqual(saved.modelsCapabilities['gpt-5-high'].supportsSystemMessage, 'developer-role'); + }); +}); diff --git a/src/vs/platform/void/common/test/requestConfigForModel.test.ts b/src/vs/platform/void/common/test/requestConfigForModel.test.ts new file mode 100644 index 00000000000..81bb135026e --- /dev/null +++ b/src/vs/platform/void/common/test/requestConfigForModel.test.ts @@ -0,0 +1,476 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { DynamicProviderRegistryService } from '../providerReg.js'; +import { NullLogService } from '../../../../platform/log/common/log.js'; + +function makeMockRemoteModelsService() { + return { + async fetchModels(_url: string, _headers: Record) { + // getRequestConfigForModel does not touch remote models, so this should never be called. + throw new Error('fetchModels should not be called in requestConfigForModel tests'); + }, + }; +} + +function makeMockSettingsService(initialCustomProviders?: Record) { + const state: { customProviders: Record } = { + customProviders: { ...(initialCustomProviders || {}) }, + }; + + return { + state, + async setCustomProviderSettings(slug: string, settings: any | undefined) { + if (settings === undefined) { + delete state.customProviders[slug]; + } else { + state.customProviders[slug] = settings; + } + }, + async setAutodetectedModels(_provider: string, _models: string[], _caps: Record) { + // No-op for these tests: we only care about config resolution, not UI publishing. + }, + }; +} + +function makeMockDynamicModelService() { + return { + async initialize(): Promise { + // no-op + }, + getAllDynamicCapabilities(): Record { + return {}; + }, + getDynamicCapabilities(_id: string): any { + return undefined; + }, + }; +} + +// openrouter slug with full ids as model names +const OPENROUTER_MODELS_CAPS = { + 'kwaipilot/kat-coder-pro:free': { + contextWindow: 256000, + reservedOutputTokenSpace: 32000, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: false, + }, + 'tngtech/deepseek-r1t-chimera:free': { + contextWindow: 163840, + reservedOutputTokenSpace: 4096, + supportsSystemMessage: false, + specialToolFormat: 'disabled', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'high', + }, + }, + }, + 'openai/gpt-5.1': { + contextWindow: 400000, + reservedOutputTokenSpace: 128000, + supportsSystemMessage: 'developer-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + 'anthropic/claude-opus-4.5': { + contextWindow: 200000, + reservedOutputTokenSpace: 32000, + supportsSystemMessage: 'separated', + specialToolFormat: 'anthropic-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: false, + canIOReasoning: true, + reasoningReservedOutputTokenSpace: 32000, + reasoningSlider: { + type: 'budget_slider', + min: 1024, + max: 8192, + default: 1024, + }, + }, + }, + 'google/gemini-3-pro-image-preview': { + contextWindow: 65536, + reservedOutputTokenSpace: 32768, + supportsSystemMessage: false, + specialToolFormat: 'disabled', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + 'deepseek/deepseek-chat-v3.1': { + contextWindow: 163840, + reservedOutputTokenSpace: 163840, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + 'x-ai/grok-4.1-fast': { + contextWindow: 2000000, + reservedOutputTokenSpace: 30000, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + 'moonshotai/kimi-k2-thinking': { + contextWindow: 262144, + reservedOutputTokenSpace: 16384, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: false, + canIOReasoning: true, + openSourceThinkTags: ['', ''], + }, + }, +}; + +// Mirror providers (openai/google/anthropic/deepseek/x-ai/cloud-ru) that should +// get exactly the same capabilities for their one model, without any mutation. +const MIRROR_PROVIDER_CAPS = { + openai: { + 'gpt-5.1': OPENROUTER_MODELS_CAPS['openai/gpt-5.1'], + }, + google: { + 'gemini-3-pro-image-preview': OPENROUTER_MODELS_CAPS['google/gemini-3-pro-image-preview'], + }, + anthropic: { + 'claude-opus-4.5': OPENROUTER_MODELS_CAPS['anthropic/claude-opus-4.5'], + }, + deepseek: { + 'deepseek-chat-v3.1': OPENROUTER_MODELS_CAPS['deepseek/deepseek-chat-v3.1'], + }, + 'x-ai': { + 'grok-4.1-fast': OPENROUTER_MODELS_CAPS['x-ai/grok-4.1-fast'], + }, + 'cloud-ru': { + 'MINIMAX/MINIMAX-M2': { + contextWindow: 204800, + reservedOutputTokenSpace: 131072, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + }, +}; + +function buildScenarioCustomProviders() { + const customProviders: Record = { + openrouter: { + endpoint: 'https://openrouter.ai/api/v1', + apiKey: 'test-key', + apiStyle: 'openai-compatible', + models: Object.keys(OPENROUTER_MODELS_CAPS), + modelsCapabilities: OPENROUTER_MODELS_CAPS, + }, + }; + + // Mirror providers with their own endpoints/api-keys but identical caps per model. + for (const [slug, byModel] of Object.entries(MIRROR_PROVIDER_CAPS)) { + customProviders[slug] = { + endpoint: `https://api.${slug}.example/v1`, + apiKey: `${slug}-key`, + apiStyle: slug === 'anthropic' ? 'anthropic-style' : slug === 'google' ? 'gemini-style' : 'openai-compatible', + models: Object.keys(byModel), + modelsCapabilities: byModel, + }; + } + + return customProviders; +} + +suite('DynamicProviderRegistryService.getRequestConfigForModel', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('openrouter + mirror providers: request config uses exact endpoint/api-key and per-model capabilities', () => { + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService(buildScenarioCustomProviders()); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + // 1) openrouter: model ids are full ids as in the dropdown. + for (const [modelId, caps] of Object.entries(OPENROUTER_MODELS_CAPS)) { + const cfg = svc.getRequestConfigForModel(modelId, 'openrouter'); + assert.strictEqual(cfg.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(cfg.headers['Accept'], 'application/json'); + assert.strictEqual(cfg.headers['Authorization'], 'Bearer test-key'); + assert.strictEqual(cfg.supportsSystemMessage, caps.supportsSystemMessage, `openrouter ${modelId} supportsSystemMessage`); + assert.strictEqual(cfg.specialToolFormat, caps.specialToolFormat, `openrouter ${modelId} specialToolFormat`); + } + + // 2) Mirror providers: short ids or custom ids, but capabilities must be taken + // from modelsCapabilities for that slug, not from any provider defaults. + const mirrorCases = [ + { slug: 'openai', modelId: 'gpt-5.1', caps: MIRROR_PROVIDER_CAPS.openai['gpt-5.1'] }, + { slug: 'google', modelId: 'gemini-3-pro-image-preview', caps: MIRROR_PROVIDER_CAPS.google['gemini-3-pro-image-preview'] }, + { slug: 'anthropic', modelId: 'claude-opus-4.5', caps: MIRROR_PROVIDER_CAPS.anthropic['claude-opus-4.5'] }, + { slug: 'deepseek', modelId: 'deepseek-chat-v3.1', caps: MIRROR_PROVIDER_CAPS.deepseek['deepseek-chat-v3.1'] }, + { slug: 'x-ai', modelId: 'grok-4.1-fast', caps: MIRROR_PROVIDER_CAPS['x-ai']['grok-4.1-fast'] }, + { slug: 'cloud-ru', modelId: 'MINIMAX/MINIMAX-M2', caps: MIRROR_PROVIDER_CAPS['cloud-ru']['MINIMAX/MINIMAX-M2'] }, + ]; + + for (const { slug, modelId, caps } of mirrorCases) { + const cfg = svc.getRequestConfigForModel(modelId, slug); + assert.strictEqual(cfg.endpoint, `https://api.${slug}.example/v1`); + assert.strictEqual(cfg.headers['Accept'], 'application/json'); + assert.strictEqual(cfg.headers['Authorization'], `Bearer ${slug}-key`); + assert.strictEqual(cfg.supportsSystemMessage, caps.supportsSystemMessage, `${slug} ${modelId} supportsSystemMessage`); + assert.strictEqual(cfg.specialToolFormat, caps.specialToolFormat, `${slug} ${modelId} specialToolFormat`); + } + }); + + test('openrouter deepseek :free model keeps disabled tools and no system message support from capabilities', () => { + const modelId = 'deepseek/deepseek-r1-0528:free'; + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService({ + openrouter: { + endpoint: 'https://openrouter.ai/api/v1', + apiKey: 'sk-openrouter', + apiStyle: 'openai-compatible', + models: [modelId], + modelsCapabilities: { + [modelId]: { + contextWindow: 163840, + reservedOutputTokenSpace: 4096, + + supportsSystemMessage: false, + specialToolFormat: 'disabled', + supportsFIM: false, + reasoningCapabilities: { + supportsReasoning: true, + canTurnOffReasoning: true, + canIOReasoning: true, + reasoningSlider: { + type: 'effort_slider', + values: ['low', 'medium', 'high'], + default: 'low', + }, + }, + }, + }, + } + }); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + const cfg = svc.getRequestConfigForModel(modelId, 'openrouter'); + + + assert.strictEqual(cfg.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(cfg.headers['Accept'], 'application/json'); + assert.strictEqual(cfg.headers['Authorization'], 'Bearer sk-openrouter'); + + + assert.strictEqual(cfg.supportsSystemMessage, false, 'supportsSystemMessage must stay false from capabilities'); + assert.strictEqual(cfg.specialToolFormat, 'disabled', 'specialToolFormat must stay disabled from capabilities'); + }); + + test('getEffectiveModelCapabilities returns reasoningCapabilities exactly as saved in modelsCapabilities', async () => { + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService(buildScenarioCustomProviders()); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + // openrouter deepseek reasoning caps should be propagated as-is + const deepseekCaps = await svc.getEffectiveModelCapabilities('openrouter', 'tngtech/deepseek-r1t-chimera:free'); + assert.deepStrictEqual(deepseekCaps.reasoningCapabilities, OPENROUTER_MODELS_CAPS['tngtech/deepseek-r1t-chimera:free'].reasoningCapabilities); + + // google gemini reasoning caps + const geminiCaps = await svc.getEffectiveModelCapabilities('google', 'gemini-3-pro-image-preview'); + assert.deepStrictEqual(geminiCaps.reasoningCapabilities, MIRROR_PROVIDER_CAPS.google['gemini-3-pro-image-preview'].reasoningCapabilities); + + // anthropic claude reasoning caps + const claudeCaps = await svc.getEffectiveModelCapabilities('anthropic', 'claude-opus-4.5'); + assert.deepStrictEqual(claudeCaps.reasoningCapabilities, MIRROR_PROVIDER_CAPS.anthropic['claude-opus-4.5'].reasoningCapabilities); + + // cloud-ru custom MINIMAX model + const cloudCaps = await svc.getEffectiveModelCapabilities('cloud-ru', 'MINIMAX/MINIMAX-M2'); + assert.deepStrictEqual(cloudCaps.reasoningCapabilities, MIRROR_PROVIDER_CAPS['cloud-ru']['MINIMAX/MINIMAX-M2'].reasoningCapabilities); + }); + + test('uses modelsCapabilities values for supportsSystemMessage and specialToolFormat without overwriting them', () => { + const modelId = 'deepseek/deepseek-r1-0528:free'; + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService({ + deepseek: { + endpoint: 'https://api.deepseek.com/v1', + apiKey: 'sk-deep', + apiStyle: 'openai-compatible', + models: [modelId], + modelsCapabilities: { + [modelId]: { + supportsSystemMessage: false, + specialToolFormat: 'disabled', + }, + }, + }, + }); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + const cfg = svc.getRequestConfigForModel(modelId, 'deepseek'); + + // Endpoint and headers must come from custom provider settings + assert.strictEqual(cfg.endpoint, 'https://api.deepseek.com/v1'); + assert.strictEqual(cfg.headers['Accept'], 'application/json'); + assert.strictEqual(cfg.headers['Authorization'], 'Bearer sk-deep'); + + // Most important: capabilities from modelsCapabilities must win over + // any defaults from getModelApiConfiguration / WELL_KNOWN_PROVIDER_DEFAULTS. + assert.strictEqual(cfg.supportsSystemMessage, false, 'supportsSystemMessage should come from modelsCapabilities'); + assert.strictEqual(cfg.specialToolFormat, 'disabled', 'specialToolFormat should come from modelsCapabilities'); + }); + + test('applies modelCapabilityOverrides on top of modelsCapabilities', () => { + const modelId = 'acme/model-x'; + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService({ + acme: { + endpoint: 'https://api.acme.ai/v1', + apiKey: 'sk-acme', + apiStyle: 'openai-compatible', + models: [modelId], + modelsCapabilities: { + [modelId]: { + supportsSystemMessage: false, + specialToolFormat: 'disabled', + }, + }, + modelCapabilityOverrides: { + [modelId]: { + supportsSystemMessage: 'developer-role', + }, + }, + }, + }); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + const cfg = svc.getRequestConfigForModel(modelId, 'acme'); + + // Override must win for supportsSystemMessage, but not erase tool format from capabilities. + assert.strictEqual(cfg.supportsSystemMessage, 'developer-role'); + assert.strictEqual(cfg.specialToolFormat, 'disabled'); + }); + + test('builds headers from apiKey, auth and additionalHeaders while keeping capabilities intact', () => { + const modelId = 'acme/claude-3-5'; + const remoteModelsService = makeMockRemoteModelsService(); + const settingsService = makeMockSettingsService({ + acme: { + endpoint: 'https://api.acme.ai/v1', + apiKey: 'super-secret', + apiStyle: 'openai-compatible', + auth: { header: 'X-API-Key', format: 'direct' }, + additionalHeaders: { 'X-Test': '1' }, + models: [modelId], + modelsCapabilities: { + [modelId]: { + supportsSystemMessage: 'separated', + specialToolFormat: 'anthropic-style', + }, + }, + }, + }); + const dynamicModelService = makeMockDynamicModelService(); + const logService = new NullLogService(); + + // @ts-ignore – partial mocks are sufficient for this test + const svc = new DynamicProviderRegistryService(remoteModelsService, settingsService, dynamicModelService, logService); + + const cfg = svc.getRequestConfigForModel(modelId, 'acme'); + + // Endpoint from custom provider + assert.strictEqual(cfg.endpoint, 'https://api.acme.ai/v1'); + + // Custom auth + extra headers should be preserved + assert.strictEqual(cfg.headers['X-API-Key'], 'super-secret'); + assert.strictEqual(cfg.headers['X-Test'], '1'); + assert.strictEqual(cfg.headers['Accept'], 'application/json'); + + // Capabilities must still reflect modelsCapabilities + assert.strictEqual(cfg.supportsSystemMessage, 'separated'); + assert.strictEqual(cfg.specialToolFormat, 'anthropic-style'); + }); +}); diff --git a/src/vs/platform/void/common/test/requestParamsUi.test.ts b/src/vs/platform/void/common/test/requestParamsUi.test.ts new file mode 100644 index 00000000000..47f73ea2bfe --- /dev/null +++ b/src/vs/platform/void/common/test/requestParamsUi.test.ts @@ -0,0 +1,81 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// eslint-disable-next-line local/code-import-patterns +import * as assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { DynamicModelService } from '../dynamicModelService.js'; +import { computeRequestParamsTemplate } from '../requestParams.js'; + +const NullLogService = { + trace: () => { }, + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { } +}; + +// Stub private method fetchOpenRouterModels like other tests do +function stubFetch(service: DynamicModelService, impl: (...args: any[]) => Promise) { + const key = 'fetchOpenRouterModels' as const; + const original = (service as any)[key] as (...args: any[]) => Promise; + (service as any)[key] = async (...args: any[]): Promise => impl(...args); + return { restore() { (service as any)[key] = original; } }; +} + +suite('Request parameters UI template', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('should build full override params and not just max_tokens', async () => { + const mockData = { + data: [{ + id: 'prov/modelX', + canonical_slug: 'prov/modelX', + name: 'Model X', + created: Date.now(), + context_length: 8192, + pricing: { prompt: '0', completion: '0' }, + top_provider: { max_completion_tokens: 4096, is_moderated: false }, + supported_parameters: [ + 'frequency_penalty', 'logit_bias', 'logprobs', 'max_tokens', 'presence_penalty', 'seed', 'stop', 'top_logprobs', + 'temperature', 'top_p', 'top_k', + // excluded ones should be ignored + 'tools', 'tool_choice', 'response_format', 'structured_outputs', 'reasoning', 'include_reasoning' + ], + default_parameters: { + max_tokens: 777, + temperature: 0.33 + }, + architecture: { modality: 'text->text', input_modalities: ['text'], output_modalities: ['text'], tokenizer: 'Other' } + }] + }; + + const service = new DynamicModelService({ request: async () => { throw new Error('not used'); } } as any, NullLogService as any); + const stub = stubFetch(service, async () => mockData); + await service.initialize(); + + const supported = service.getSupportedParameters('prov/modelX'); + const defaults = service.getDefaultParameters('prov/modelX'); + const tpl = computeRequestParamsTemplate(supported, defaults); + + // Ensure many keys, not a single one + const keys = Object.keys(tpl); + assert.ok(keys.length >= 6, 'Expected multiple request params in template'); + // Includes representative keys + for (const k of ['max_tokens', 'frequency_penalty', 'logit_bias', 'logprobs', 'presence_penalty', 'seed', 'stop', 'top_logprobs', 'temperature', 'top_p', 'top_k']) { + assert.ok(k in tpl, `Expected key ${k} in template`); + } + // Defaults respected + assert.strictEqual(tpl.max_tokens, 777); + assert.strictEqual(tpl.temperature, 0.33); + + // Exclusions + for (const k of ['tools', 'tool_choice', 'response_format', 'structured_outputs', 'reasoning', 'include_reasoning']) { + assert.ok(!(k in tpl), `Should not include excluded param ${k}`); + } + + stub.restore(); + }); +}); diff --git a/src/vs/platform/void/common/toolOutputFileNames.ts b/src/vs/platform/void/common/toolOutputFileNames.ts new file mode 100644 index 00000000000..4bb37e7719a --- /dev/null +++ b/src/vs/platform/void/common/toolOutputFileNames.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function normalizeForHash(s: unknown): string { + return String(s ?? '').replace(/\r\n/g, '\n'); +} + +// 32-bit FNV-1a => 8 hex chars +export function fnv1a32Hex(s: unknown): string { + const str = normalizeForHash(s); + let hash = 0x811c9dc5; + for (let i = 0; i < str.length; i++) { + hash ^= str.charCodeAt(i); + hash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0; + } + return hash.toString(16).padStart(8, '0'); +} + +export function sanitizeForFileNamePart(s: unknown): string { + return String(s ?? '') + .trim() + .replace(/[^a-zA-Z0-9._-]+/g, '_') + .slice(0, 80); +} + +export function toolPrefixForToolName(toolName: unknown): string { + const t = String(toolName ?? '').trim(); + + if (t === 'run_command' || t === 'run_persistent_command' || t === 'open_persistent_terminal') return 'terminal'; + + if (t === 'read_file' || t === 'readTextFile' || t === 'fs/read_text_file') return 'read'; + if (t === 'rewrite_file' || t === 'writeTextFile' || t === 'fs/write_text_file') return 'write'; + + if (t === 'edit_file') return 'edit'; + + return sanitizeForFileNamePart(t) || 'output'; +} + +export function toolOutputFileName(prefix: unknown, key: unknown): string { + const p = sanitizeForFileNamePart(prefix) || 'output'; + const h = fnv1a32Hex(key); + return `${p}_${h}.log`; +} + +/** + * Accepts: + * - absolute path + * - relative ".void/tool_outputs/x.log" + * - just "x.log" + * and returns workspace-relative ".void/tool_outputs/" + */ +export function normalizeMetaLogFilePath(p: unknown): string | null { + const s = String(p ?? '').trim(); + if (!s) return null; + + const parts = s.split(/[/\\]/).filter(Boolean); + const base0 = parts[parts.length - 1]; + const base = sanitizeForFileNamePart(base0); + + if (!base) return null; + return `.void/tool_outputs/${base}`; +} + +export function looksLikeStableToolOutputsRelPath(p: unknown): boolean { + const s = String(p ?? ''); + return /^\.void\/tool_outputs\/[a-zA-Z0-9._-]+_[0-9a-f]{8}\.log$/.test(s); +} + +export function stableToolOutputsRelPath(opts: { + toolName?: unknown; + terminalId?: unknown; + toolCallId?: unknown; + keyText?: unknown; + fullText?: unknown; + prefixOverride?: unknown; +}): string { + const prefix = + (sanitizeForFileNamePart(opts.prefixOverride) || '') || + toolPrefixForToolName(opts.toolName); + + const key = + (String(opts.terminalId ?? '').trim() ? String(opts.terminalId) : + String(opts.toolCallId ?? '').trim() ? String(opts.toolCallId) : + (opts.fullText ?? opts.keyText ?? '')); + + const fileName = toolOutputFileName(prefix, key); + return `.void/tool_outputs/${fileName}`; +} diff --git a/src/vs/platform/void/common/toolOutputTruncation.ts b/src/vs/platform/void/common/toolOutputTruncation.ts new file mode 100644 index 00000000000..a57f497d3ef --- /dev/null +++ b/src/vs/platform/void/common/toolOutputTruncation.ts @@ -0,0 +1,60 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +/** + * Shared helper for truncating large tool outputs before sending them to an LLM + * or displaying them in the UI. + * + * This helper is intentionally pure and platform-agnostic so it can be used + * from both renderer (browser) and main/electron processes. + */ +export interface TruncatedToolOutput { + originalLength: number; + truncatedBody: string; + /** true if originalLength > maxLength */ + needsTruncation: boolean; + /** + * 1-based line number after the truncated prefix of the output. + * + * When {@link needsTruncation} is true, this is the line number such that + * consumers can read the remainder of the log starting from the line + * strictly after the part that was shown to the user. When not truncated, + * this is 0. + */ + lineAfterTruncation: number; +} + +/** + * Computes the truncated body of a tool output, without adding any headers + * or file-path hints. Callers are responsible for appending explanatory + * lines (e.g. [VOID] Tool output was truncated...) and any log-file paths. + */ +export function computeTruncatedToolOutput(originalText: string, maxLength: number): TruncatedToolOutput { + const originalLength = originalText.length; + const computeLineAfterTruncation = (body: string): number => { + if (!body) { + return 0; + } + // Treat all common newline sequences as line breaks. + return body.split(/\r\n|\r|\n/).length; + }; + + if (originalLength <= maxLength || maxLength <= 0) { + return { + originalLength, + truncatedBody: originalText, + needsTruncation: false, + lineAfterTruncation: 0, + }; + } + + const truncatedBody = originalText.substring(0, maxLength); + return { + originalLength, + truncatedBody, + needsTruncation: true, + lineAfterTruncation: computeLineAfterTruncation(truncatedBody), + }; +} diff --git a/src/vs/platform/void/common/toolsRegistry.ts b/src/vs/platform/void/common/toolsRegistry.ts new file mode 100644 index 00000000000..de5be46a374 --- /dev/null +++ b/src/vs/platform/void/common/toolsRegistry.ts @@ -0,0 +1,226 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { + MAX_TERMINAL_BG_COMMAND_TIME, + MAX_TERMINAL_INACTIVE_TIME, +} from './prompt/constants.js'; + +import { + ToolCallParams, ToolResultType, + approvalTypeOfToolName, + type SnakeCaseKeys, type ToolName +} from './toolsServiceTypes.js'; +import type { ChatMode } from './voidSettingsTypes.js'; + +export type InternalToolInfo = { + name: string; + description: string; + params: { + [paramName: string]: { description: string; type?: string } + }; +}; + +const uriParam = (object: string) => ({ + uri: { + description: + `The path to the ${object}. Prefer workspace-relative paths starting with ./ (e.g. ./src/...).` + + ` Absolute OS paths are also allowed when needed.`, + }, +}); + +const paginationParam = { + page_number: { description: 'Optional. The page number of the result. Default is 1.' } +} as const; + +const terminalDescHelper = `You can use this tool to run any command: sed, grep, etc. Do not edit any files with this tool; use edit_file instead. When working with git and other tools that open an editor (e.g. git diff), you should pipe to cat to get all results and not get stuck in vim.`; + +const cwdHelper = 'Optional. The directory in which to run the command. Defaults to the first workspace folder.'; + +export const voidTools + : { + [T in keyof ToolCallParams]: { + name: string; + description: string; + params: Partial<{ [paramName in keyof SnakeCaseKeys]: { description: string } }> + } + } + = { + read_file: { + name: 'read_file', + description: 'Reads file contents. Can read entire file, specific line range, or chunks of N lines.', + params: { + ...uriParam('file'), + start_line: { + description: 'Optional. 1-based line number to start reading from. Default = 1.' + }, + end_line: { + description: 'Optional. 1-based line number to stop reading at. If omitted with lines_count, reads to end of file.' + }, + lines_count: { + description: 'Optional. Number of lines to read starting from start_line. Alternative to end_line.' + }, + page_number: { + description: 'Optional. For character-based pagination of large files. Default = 1.' + }, + }, + }, + ls_dir: { + name: 'ls_dir', + description: 'Lists all files and folders in the given URI.', + params: { + uri: { + description: + `Optional. The path to the folder. Leave this as empty or "" to search all folders.` + + ` Prefer workspace-relative paths starting with ./; absolute paths are also allowed.`, + }, + ...paginationParam, + }, + }, + get_dir_tree: { + name: 'get_dir_tree', + description: 'Returns a tree diagram of files and folders in the given folder.', + params: { + ...uriParam('folder') + } + }, + edit_file: { + name: 'edit_file', + description: 'Apply a single, atomic replacement by specifying ORIGINAL and UPDATED snippets.', + params: { + ...uriParam('file'), + original_snippet: { description: 'The exact ORIGINAL snippet to locate in the file.' }, + updated_snippet: { description: 'The UPDATED snippet that should replace the ORIGINAL.' }, + occurrence: { description: 'Optional. 1-based occurrence index to replace. If null, uses replace_all flag behavior.' }, + replace_all: { description: 'Optional. If true, replace all occurrences of ORIGINAL with UPDATED.' }, + location_hint: { description: 'Optional. Opaque hint object to help locate ORIGINAL if necessary.' }, + encoding: { description: 'Optional. File encoding (e.g., utf-8).' }, + newline: { description: 'Optional. Preferred newline style (LF or CRLF).' }, + }, + }, + search_pathnames_only: { + name: 'search_pathnames_only', + description: 'Returns all pathnames that match the given query (searches ONLY file names).', + params: { + query: { description: 'Your query for the search.' }, + include_pattern: { description: 'Optional. Limit your search if there were too many results.' }, + ...paginationParam, + }, + }, + search_for_files: { + name: 'search_for_files', + description: 'Returns files whose content matches the given query (substring or regex).', + params: { + query: { description: 'Your query for the search.' }, + search_in_folder: { description: 'Optional. Fill only if the previous search was truncated. Searches descendants only.' }, + is_regex: { description: 'Optional. Default false. Whether the query is a regex.' }, + ...paginationParam, + }, + }, + search_in_file: { + name: 'search_in_file', + description: 'Returns all start line numbers where the content appears in the file.', + params: { + ...uriParam('file'), + query: { description: 'The string or regex to search for in the file.' }, + is_regex: { description: 'Optional. Default false. Whether the query is a regex.' }, + } + }, + read_lint_errors: { + name: 'read_lint_errors', + description: 'View all lint errors on a file.', + params: { + ...uriParam('file'), + }, + }, + rewrite_file: { + name: 'rewrite_file', + description: 'Replaces entire file contents with provided new contents.', + params: { + ...uriParam('file'), + new_content: { description: 'The new contents of the file. Must be a string.' } + }, + }, + create_file_or_folder: { + name: 'create_file_or_folder', + description: 'Create a file or folder at the given path. To create a folder, the path MUST end with a trailing slash.', + params: { + ...uriParam('file or folder'), + }, + }, + delete_file_or_folder: { + name: 'delete_file_or_folder', + description: 'Delete a file or folder at the given path.', + params: { + ...uriParam('file or folder'), + is_recursive: { description: 'Optional. Return true to delete recursively.' } + }, + }, + run_command: { + name: 'run_command', + description: `Runs a terminal command and waits for the result (times out after ${MAX_TERMINAL_INACTIVE_TIME}s of inactivity). ${terminalDescHelper}`, + params: { + command: { description: 'The terminal command to run.' }, + cwd: { description: cwdHelper }, + }, + }, + run_persistent_command: { + name: 'run_persistent_command', + description: `Runs a terminal command in the persistent terminal created with open_persistent_terminal (results after ${MAX_TERMINAL_BG_COMMAND_TIME}s are returned, command continues in background). ${terminalDescHelper}`, + params: { + command: { description: 'The terminal command to run.' }, + persistent_terminal_id: { description: 'The ID of the terminal created using open_persistent_terminal.' }, + }, + }, + open_persistent_terminal: { + name: 'open_persistent_terminal', + description: 'Open a new persistent terminal (e.g. for npm run dev).', + params: { + cwd: { description: cwdHelper }, + } + }, + kill_persistent_terminal: { + name: 'kill_persistent_terminal', + description: 'Interrupt and close a persistent terminal opened with open_persistent_terminal.', + params: { persistent_terminal_id: { description: 'The ID of the persistent terminal.' } } + } + } satisfies { [T in keyof ToolResultType]: InternalToolInfo }; + +export const toolNames = Object.keys(voidTools) as ToolName[]; +const toolNamesSet = new Set(toolNames); + +export const isAToolName = (toolName: string): toolName is ToolName => toolNamesSet.has(toolName); + +export const dynamicVoidTools = new Map(); + +export const availableTools = (chatMode: ChatMode) => { + if (chatMode === 'normal') { + return undefined; + } + + const toolNamesForMode: ToolName[] | undefined = + chatMode === 'gather' + ? (Object.keys(voidTools) as ToolName[]).filter( + toolName => !(toolName in approvalTypeOfToolName), + ) + : chatMode === 'agent' + ? (Object.keys(voidTools) as ToolName[]) + : undefined; + + if (!toolNamesForMode || toolNamesForMode.length === 0) { + return undefined; + } + + const dynamicByName = new Map(); + for (const dynamicTool of dynamicVoidTools.values()) { + dynamicByName.set(dynamicTool.name, dynamicTool); + } + + const allTools = toolNamesForMode.map(toolName => { + return dynamicByName.get(toolName) ?? voidTools[toolName]; + }); + + return allTools.length > 0 ? allTools : undefined; +}; diff --git a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts b/src/vs/platform/void/common/toolsServiceTypes.ts similarity index 56% rename from src/vs/workbench/contrib/void/common/toolsServiceTypes.ts rename to src/vs/platform/void/common/toolsServiceTypes.ts index d5da3e17bec..8a06295a68c 100644 --- a/src/vs/workbench/contrib/void/common/toolsServiceTypes.ts +++ b/src/vs/platform/void/common/toolsServiceTypes.ts @@ -1,9 +1,9 @@ -import { URI } from '../../../../base/common/uri.js' -import { RawMCPToolCall } from './mcpServiceTypes.js'; -import { builtinTools } from './prompt/prompts.js'; -import { RawToolParamsObj } from './sendLLMMessageTypes.js'; - +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ +import { URI } from '../../../base/common/uri.js' export type TerminalResolveReason = { type: 'timeout' } | { type: 'done', exitCode: number } @@ -17,33 +17,9 @@ export type ShallowDirectoryItem = { isSymbolicLink: boolean; } - -export const approvalTypeOfBuiltinToolName: Partial<{ [T in BuiltinToolName]?: 'edits' | 'terminal' | 'MCP tools' }> = { - 'create_file_or_folder': 'edits', - 'delete_file_or_folder': 'edits', - 'rewrite_file': 'edits', - 'edit_file': 'edits', - 'run_command': 'terminal', - 'run_persistent_command': 'terminal', - 'open_persistent_terminal': 'terminal', - 'kill_persistent_terminal': 'terminal', -} - - -export type ToolApprovalType = NonNullable<(typeof approvalTypeOfBuiltinToolName)[keyof typeof approvalTypeOfBuiltinToolName]>; - - -export const toolApprovalTypes = new Set([ - ...Object.values(approvalTypeOfBuiltinToolName), - 'MCP tools', -]) - - - - // PARAMS OF TOOL CALL -export type BuiltinToolCallParams = { - 'read_file': { uri: URI, startLine: number | null, endLine: number | null, pageNumber: number }, +export type ToolCallParams = { + 'read_file': { uri: URI, startLine: number | null, endLine: number | null, linesCount: number | null; pageNumber: number }, 'ls_dir': { uri: URI, pageNumber: number }, 'get_dir_tree': { uri: URI }, 'search_pathnames_only': { query: string, includePattern: string | null, pageNumber: number }, @@ -52,7 +28,7 @@ export type BuiltinToolCallParams = { 'read_lint_errors': { uri: URI }, // --- 'rewrite_file': { uri: URI, newContent: string }, - 'edit_file': { uri: URI, searchReplaceBlocks: string }, + 'edit_file': { uri: URI, originalSnippet: string, updatedSnippet: string, occurrence: number | null, replaceAll: boolean, locationHint: any | null, encoding: string | null, newline: string | null }, 'create_file_or_folder': { uri: URI, isFolder: boolean }, 'delete_file_or_folder': { uri: URI, isRecursive: boolean, isFolder: boolean }, // --- @@ -63,8 +39,15 @@ export type BuiltinToolCallParams = { } // RESULT OF TOOL CALL -export type BuiltinToolResultType = { - 'read_file': { fileContents: string, totalFileLen: number, totalNumLines: number, hasNextPage: boolean }, +export type ToolResultType = { + 'read_file': { + fileContents: string, + totalFileLen: number, + totalNumLines: number, + hasNextPage: boolean, + readingLines?: string, + readLinesCount?: number, + }, 'ls_dir': { children: ShallowDirectoryItem[] | null, hasNextPage: boolean, hasPrevPage: boolean, itemsRemaining: number }, 'get_dir_tree': { str: string, }, 'search_pathnames_only': { uris: URI[], hasNextPage: boolean }, @@ -73,7 +56,7 @@ export type BuiltinToolResultType = { 'read_lint_errors': { lintErrors: LintErrorItem[] | null }, // --- 'rewrite_file': Promise<{ lintErrors: LintErrorItem[] | null }>, - 'edit_file': Promise<{ lintErrors: LintErrorItem[] | null }>, + 'edit_file': Promise<{ applied: boolean; occurrences_found?: number; occurrence_applied?: number; updated_text?: string; preview?: { before: string; after: string } }>, 'create_file_or_folder': {}, 'delete_file_or_folder': {}, // --- @@ -83,15 +66,37 @@ export type BuiltinToolResultType = { 'kill_persistent_terminal': {}, } +export type ToolName = keyof ToolResultType + +export const approvalTypeOfToolName: Partial<{ [T in ToolName]?: 'edits' | 'terminal' }> = { + 'create_file_or_folder': 'edits', + 'delete_file_or_folder': 'edits', + 'rewrite_file': 'edits', + 'edit_file': 'edits', + 'run_command': 'terminal', + 'run_persistent_command': 'terminal', + 'open_persistent_terminal': 'terminal', + 'kill_persistent_terminal': 'terminal', +} -export type ToolCallParams = T extends BuiltinToolName ? BuiltinToolCallParams[T] : RawToolParamsObj -export type ToolResult = T extends BuiltinToolName ? BuiltinToolResultType[T] : RawMCPToolCall +// {{add: define new type for approval types}} +export type ToolApprovalType = NonNullable<(typeof approvalTypeOfToolName)[keyof typeof approvalTypeOfToolName]>; -export type BuiltinToolName = keyof BuiltinToolResultType +export const toolApprovalTypes = new Set( + Object.values(approvalTypeOfToolName).filter((v): v is ToolApprovalType => v !== undefined) +) -type BuiltinToolParamNameOfTool = keyof (typeof builtinTools)[T]['params'] -export type BuiltinToolParamName = { [T in BuiltinToolName]: BuiltinToolParamNameOfTool }[BuiltinToolName] +export type SnakeCase = + S extends 'URI' ? 'uri' + : S extends `${infer Prefix}URI` ? `${SnakeCase}_uri` + : S extends `${infer C}${infer Rest}` + ? `${C extends Lowercase ? C : `_${Lowercase}`}${SnakeCase}` + : S; +export type SnakeCaseKeys> = { + [K in keyof T as SnakeCase>]: T[K] +}; -export type ToolName = BuiltinToolName | (string & {}) -export type ToolParamName = T extends BuiltinToolName ? BuiltinToolParamNameOfTool : string +export type ToolParamName = { + [K in ToolName]: keyof SnakeCaseKeys +}[ToolName] & string; diff --git a/src/vs/workbench/contrib/void/common/voidSCMTypes.ts b/src/vs/platform/void/common/voidSCMTypes.ts similarity index 92% rename from src/vs/workbench/contrib/void/common/voidSCMTypes.ts rename to src/vs/platform/void/common/voidSCMTypes.ts index e9e6bbb2280..e51898a21fb 100644 --- a/src/vs/workbench/contrib/void/common/voidSCMTypes.ts +++ b/src/vs/platform/void/common/voidSCMTypes.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; export interface IVoidSCMService { readonly _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/void/common/voidSettingsService.ts b/src/vs/platform/void/common/voidSettingsService.ts similarity index 64% rename from src/vs/workbench/contrib/void/common/voidSettingsService.ts rename to src/vs/platform/void/common/voidSettingsService.ts index 3e0c229520c..17ddeacf8bf 100644 --- a/src/vs/workbench/contrib/void/common/voidSettingsService.ts +++ b/src/vs/platform/void/common/voidSettingsService.ts @@ -3,29 +3,72 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { deepClone } from '../../../../base/common/objects.js'; -import { IEncryptionService } from '../../../../platform/encryption/common/encryptionService.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { deepClone } from '../../../base/common/objects.js'; +import { IEncryptionService } from '../../../platform/encryption/common/encryptionService.js'; +import { registerSingleton, InstantiationType } from '../../../platform/instantiation/common/extensions.js'; +import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../platform/storage/common/storage.js'; import { IMetricsService } from './metricsService.js'; -import { defaultProviderSettings, getModelCapabilities, ModelOverrides } from './modelCapabilities.js'; +import { getModelCapabilities, VoidStaticModelInfo, ModelOverrides } from './modelInference.js'; import { VOID_SETTINGS_STORAGE_KEY } from './storageKeys.js'; -import { defaultSettingsOfProvider, FeatureName, ProviderName, ModelSelectionOfFeature, SettingsOfProvider, SettingName, providerNames, ModelSelection, modelSelectionsEqual, featureNames, VoidStatefulModelInfo, GlobalSettings, GlobalSettingName, defaultGlobalSettings, ModelSelectionOptions, OptionsOfModelSelection, ChatMode, OverridesOfModel, defaultOverridesOfModel, MCPUserStateOfName as MCPUserStateOfName, MCPUserState } from './voidSettingsTypes.js'; - +import { + FeatureName, ProviderName, + ModelSelectionOfFeature, + SettingsOfProvider, + SettingName, + ModelSelection, + modelSelectionsEqual, + featureNames, + specialToolFormat, + supportsSystemMessage, + VoidStatefulModelInfo, + GlobalSettings, + GlobalSettingName, + defaultGlobalSettings, + ModelSelectionOptions, + OptionsOfModelSelection, + MCPUserStateOfName, + MCPUserState, + ChatMode, + OverridesOfModel, + defaultOverridesOfModel +} from './voidSettingsTypes.js'; // name is the name in the dropdown export type ModelOption = { name: string, selection: ModelSelection } - - -type SetSettingOfProviderFn = ( - providerName: ProviderName, - settingName: S, - newVal: SettingsOfProvider[ProviderName][S extends keyof SettingsOfProvider[ProviderName] ? S : never], -) => Promise; +export type ModelCapabilityOverride = { + contextWindow?: number; + reservedOutputTokenSpace?: number; + supportsSystemMessage?: supportsSystemMessage; + specialToolFormat?: specialToolFormat; + supportsFIM?: boolean; + reasoningCapabilities?: false | any; + fimTransport?: 'openai-compatible' | 'mistral-native' | 'ollama-native' | 'emulated'; + supportCacheControl?: boolean; +}; + +export type CustomProviderSettings = { + endpoint?: string; + apiKey?: string; + apiStyle?: 'openai-compatible' | 'anthropic-style' | 'gemini-style'; + supportsSystemMessage?: supportsSystemMessage; + auth?: { header: string; format: 'Bearer' | 'direct' }; + additionalHeaders?: Record; + perModel?: Record; + models?: string[]; + modelsCapabilities?: Record>; + modelCapabilityOverrides?: Record; + modelsLastRefreshedAt?: number; +}; + +// Narrowed overloads to avoid ambiguous intersection types for `models` +type SetSettingOfProviderFn = { + (providerName: ProviderName, settingName: 'models', newVal: VoidStatefulModelInfo[]): Promise; + (providerName: ProviderName, settingName: Exclude, newVal: any): Promise; +}; type SetModelSelectionOfFeatureFn = ( featureName: K, @@ -34,7 +77,7 @@ type SetModelSelectionOfFeatureFn = ( type SetGlobalSettingFn = (settingName: T, newVal: GlobalSettings[T]) => void; -type SetOptionsOfModelSelection = (featureName: FeatureName, providerName: ProviderName, modelName: string, newVal: Partial) => void +type SetOptionsOfModelSelection = (featureName: FeatureName, providerName: string, modelName: string, newVal: Partial) => void export type VoidSettingsState = { @@ -43,15 +86,11 @@ export type VoidSettingsState = { readonly optionsOfModelSelection: OptionsOfModelSelection; readonly overridesOfModel: OverridesOfModel; readonly globalSettings: GlobalSettings; + readonly customProviders: Record; readonly mcpUserStateOfName: MCPUserStateOfName; // user-controlled state of MCP servers - readonly _modelOptions: ModelOption[] // computed based on the two above items } -// type RealVoidSettings = Exclude -// type EventProp = T extends 'globalSettings' ? [T, keyof VoidSettingsState[T]] : T | 'all' - - export interface IVoidSettingsService { readonly _serviceBrand: undefined; readonly state: VoidSettingsState; // in order to play nicely with react, you should immutably change state @@ -63,7 +102,6 @@ export interface IVoidSettingsService { setModelSelectionOfFeature: SetModelSelectionOfFeatureFn; setOptionsOfModelSelection: SetOptionsOfModelSelection; setGlobalSetting: SetGlobalSettingFn; - // setMCPServerStates: (newStates: MCPServerStates) => Promise; // setting to undefined CLEARS it, unlike others: setOverridesOfModel(providerName: ProviderName, modelName: string, overrides: Partial | undefined): Promise; @@ -75,15 +113,14 @@ export interface IVoidSettingsService { toggleModelHidden(providerName: ProviderName, modelName: string): void; addModel(providerName: ProviderName, modelName: string): void; deleteModel(providerName: ProviderName, modelName: string): boolean; + setCustomProviderSettings(slug: string, settings: CustomProviderSettings | undefined): Promise; addMCPUserStateOfNames(userStateOfName: MCPUserStateOfName): Promise; removeMCPUserStateOfNames(serverNames: string[]): Promise; setMCPServerState(serverName: string, state: MCPUserState): Promise; + setToolDisabled(toolName: string, disabled: boolean): Promise; } - - - const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulModelInfo[], models: string[], type: 'autodetected' | 'default' }) => { const { existingModels, models, type } = options @@ -92,7 +129,7 @@ const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulMo existingModelsMap[existingModel.modelName] = existingModel } - const newDefaultModels = models.map((modelName, i) => ({ modelName, type, isHidden: !!existingModelsMap[modelName]?.isHidden, })) + const newDefaultModels = models.map((modelName) => ({ modelName, type, isHidden: !!existingModelsMap[modelName]?.isHidden, })) return [ ...newDefaultModels, // swap out all the models of this type for the new models of this type @@ -103,7 +140,6 @@ const _modelsWithSwappedInNewModels = (options: { existingModels: VoidStatefulMo ] } - export const modelFilterOfFeatureName: { [featureName in FeatureName]: { filter: ( @@ -112,49 +148,30 @@ export const modelFilterOfFeatureName: { ) => boolean; emptyMessage: null | { message: string, priority: 'always' | 'fallback' } } } = { - 'Autocomplete': { filter: (o, opts) => getModelCapabilities(o.providerName, o.modelName, opts.overridesOfModel).supportsFIM, emptyMessage: { message: 'No models support FIM', priority: 'always' } }, - 'Chat': { filter: o => true, emptyMessage: null, }, - 'Ctrl+K': { filter: o => true, emptyMessage: null, }, - 'Apply': { filter: o => true, emptyMessage: null, }, - 'SCM': { filter: o => true, emptyMessage: null, }, -} - - -const _stateWithMergedDefaultModels = (state: VoidSettingsState): VoidSettingsState => { - let newSettingsOfProvider = state.settingsOfProvider - - // recompute default models - for (const providerName of providerNames) { - const defaultModels = defaultSettingsOfProvider[providerName]?.models ?? [] - const currentModels = newSettingsOfProvider[providerName]?.models ?? [] - const defaultModelNames = defaultModels.map(m => m.modelName) - const newModels = _modelsWithSwappedInNewModels({ existingModels: currentModels, models: defaultModelNames, type: 'default' }) - newSettingsOfProvider = { - ...newSettingsOfProvider, - [providerName]: { - ...newSettingsOfProvider[providerName], - models: newModels, - }, + 'Autocomplete': { + filter: (o, opts) => getModelCapabilities( + o.providerName as ProviderName, + o.modelName, opts.overridesOfModel + ).supportsFIM, emptyMessage: { + message: 'No models support FIM', priority: 'always' } - } - return { - ...state, - settingsOfProvider: newSettingsOfProvider, - } + }, + 'Chat': { filter: () => true, emptyMessage: null, }, + 'Ctrl+K': { filter: () => true, emptyMessage: null, }, + 'Apply': { filter: () => true, emptyMessage: null, }, + 'SCM': { filter: () => true, emptyMessage: null, }, } const _validatedModelState = (state: Omit): VoidSettingsState => { let newSettingsOfProvider = state.settingsOfProvider - // recompute _didFillInProviderSettings - for (const providerName of providerNames) { + // recompute _didFillInProviderSettings for any existing entries + for (const providerName of Object.keys(newSettingsOfProvider)) { const settingsAtProvider = newSettingsOfProvider[providerName] - - const didFillInProviderSettings = Object.keys(defaultProviderSettings[providerName]).every(key => !!settingsAtProvider[key as keyof typeof settingsAtProvider]) - + if (!settingsAtProvider) continue; + const didFillInProviderSettings = !!(settingsAtProvider as any).endpoint || !!(settingsAtProvider as any).apiKey; if (didFillInProviderSettings === settingsAtProvider._didFillInProviderSettings) continue - newSettingsOfProvider = { ...newSettingsOfProvider, [providerName]: { @@ -164,14 +181,20 @@ const _validatedModelState = (state: Omit): } } - // update model options + // update model options from dynamic custom providers only let newModelOptions: ModelOption[] = [] - for (const providerName of providerNames) { - const providerTitle = providerName // displayInfoOfProviderName(providerName).title.toLowerCase() // looks better lowercase, best practice to not use raw providerName - if (!newSettingsOfProvider[providerName]._didFillInProviderSettings) continue // if disabled, don't display model options - for (const { modelName, isHidden } of newSettingsOfProvider[providerName].models) { - if (isHidden) continue - newModelOptions.push({ name: `${modelName} (${providerTitle})`, selection: { providerName, modelName } }) + { + const seen = new Set(); + const customProviders = state.customProviders || {}; + for (const [slug, cp] of Object.entries(customProviders)) { + const models = Array.isArray(cp?.models) ? cp!.models! : []; + if (models.length === 0) continue; + for (const m of models) { + const key = `${slug}::${m}`; + if (seen.has(key)) continue; + seen.add(key); + newModelOptions.push({ name: m, selection: { providerName: slug, modelName: m } }); + } } } @@ -207,17 +230,15 @@ const _validatedModelState = (state: Omit): return newState } - - - - const defaultState = () => { const d: VoidSettingsState = { - settingsOfProvider: deepClone(defaultSettingsOfProvider), + // start empty; dynamic providers populate via customProviders + settingsOfProvider: {}, modelSelectionOfFeature: { 'Chat': null, 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null, 'SCM': null }, globalSettings: deepClone(defaultGlobalSettings), optionsOfModelSelection: { 'Chat': {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {}, 'SCM': {} }, overridesOfModel: deepClone(defaultOverridesOfModel), + customProviders: {}, _modelOptions: [], // computed later mcpUserStateOfName: {}, } @@ -230,12 +251,12 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { _serviceBrand: undefined; private readonly _onDidChangeState = new Emitter(); - readonly onDidChangeState: Event = this._onDidChangeState.event; // this is primarily for use in react, so react can listen + update on state changes + readonly onDidChangeState: Event = this._onDidChangeState.event; state: VoidSettingsState; private readonly _resolver: () => void - waitForInitState: Promise // await this if you need a valid state initially + waitForInitState: Promise constructor( @IStorageService private readonly _storageService: IStorageService, @@ -249,13 +270,31 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // at the start, we haven't read the partial config yet, but we need to set state to something this.state = defaultState() let resolver: () => void = () => { } - this.waitForInitState = new Promise((res, rej) => resolver = res) + this.waitForInitState = new Promise((res) => { resolver = res }) this._resolver = resolver this.readAndInitializeState() } + setCustomProviderSettings = async (slug: string, settings: CustomProviderSettings | undefined) => { + const newMap = { ...this.state.customProviders }; + if (settings === undefined) { + delete newMap[slug]; + } else { + newMap[slug] = settings; + } + + const newState: VoidSettingsState = { + ...this.state, + customProviders: newMap + }; + this.state = _validatedModelState(newState); + await this._storeState(); + this._onDidChangeState.fire(); + + this._metricsService.capture('Update Custom Provider', { slug, hasSettings: settings !== undefined }); + }; dangerousSetState = async (newState: VoidSettingsState) => { @@ -263,83 +302,77 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { await this._storeState() this._onDidChangeState.fire() this._onUpdate_syncApplyToChat() - this._onUpdate_syncSCMToChat() } async resetState() { await this.dangerousSetState(defaultState()) } - - - async readAndInitializeState() { let readS: VoidSettingsState try { readS = await this._readState(); + const gs = readS.globalSettings as any; // 1.0.3 addition, remove when enough users have had this code run - if (readS.globalSettings.includeToolLintErrors === undefined) readS.globalSettings.includeToolLintErrors = true + if (gs.includeToolLintErrors === undefined) gs.includeToolLintErrors = true; + if (gs.applyAstInference === undefined) gs.applyAstInference = defaultGlobalSettings.applyAstInference; // autoapprove is now an obj not a boolean (1.2.5) - if (typeof readS.globalSettings.autoApprove === 'boolean') readS.globalSettings.autoApprove = {} + if (typeof gs.autoApprove === 'boolean') gs.autoApprove = {}; // 1.3.5 add source control feature if (readS.modelSelectionOfFeature && !readS.modelSelectionOfFeature['SCM']) { readS.modelSelectionOfFeature['SCM'] = deepClone(readS.modelSelectionOfFeature['Chat']) readS.optionsOfModelSelection['SCM'] = deepClone(readS.optionsOfModelSelection['Chat']) } - // add disableSystemMessage feature - if (readS.globalSettings.disableSystemMessage === undefined) readS.globalSettings.disableSystemMessage = false; - - // add autoAcceptLLMChanges feature - if (readS.globalSettings.autoAcceptLLMChanges === undefined) readS.globalSettings.autoAcceptLLMChanges = false; + + // Loop guard thresholds (added later): backfill from defaults if missing. + if (gs.loopGuardMaxTurnsPerPrompt === undefined) { + gs.loopGuardMaxTurnsPerPrompt = defaultGlobalSettings.loopGuardMaxTurnsPerPrompt; + } + if (gs.loopGuardMaxSameAssistantPrefix === undefined) { + gs.loopGuardMaxSameAssistantPrefix = defaultGlobalSettings.loopGuardMaxSameAssistantPrefix; + } + if (gs.loopGuardMaxSameToolCall === undefined) { + gs.loopGuardMaxSameToolCall = defaultGlobalSettings.loopGuardMaxSameToolCall; + } + + // Chat retries and tool output limits (added later) + if (gs.chatRetries === undefined) { + gs.chatRetries = defaultGlobalSettings.chatRetries; + } + if (gs.retryDelay === undefined) { + gs.retryDelay = defaultGlobalSettings.retryDelay; + } + if (gs.maxToolOutputLength === undefined) { + gs.maxToolOutputLength = defaultGlobalSettings.maxToolOutputLength; + } + if (gs.notifyOnTruncation === undefined) { + gs.notifyOnTruncation = defaultGlobalSettings.notifyOnTruncation; + } + if (!Array.isArray(gs.disabledToolNames)) { + gs.disabledToolNames = []; + } } catch (e) { readS = defaultState() } + if (!readS.customProviders) { + (readS as any).customProviders = {}; + } + // the stored data structure might be outdated, so we need to update it here try { - readS = { - ...defaultState(), - ...readS, - // no idea why this was here, seems like a bug - // ...defaultSettingsOfProvider, - // ...readS.settingsOfProvider, - } - - for (const providerName of providerNames) { - readS.settingsOfProvider[providerName] = { - ...defaultSettingsOfProvider[providerName], - ...readS.settingsOfProvider[providerName], - } as any - - // conversion from 1.0.3 to 1.2.5 (can remove this when enough people update) - for (const m of readS.settingsOfProvider[providerName].models) { - if (!m.type) { - const old = (m as { isAutodetected?: boolean; isDefault?: boolean }) - if (old.isAutodetected) - m.type = 'autodetected' - else if (old.isDefault) - m.type = 'default' - else m.type = 'custom' - } - } - - // remove when enough people have had it run (default is now {}) - if (providerName === 'openAICompatible' && !readS.settingsOfProvider[providerName].headersJSON) { - readS.settingsOfProvider[providerName].headersJSON = '{}' - } - } + readS = { ...defaultState(), ...readS }; } catch (e) { readS = defaultState() } - this.state = readS - this.state = _stateWithMergedDefaultModels(this.state) - this.state = _validatedModelState(this.state); + this.state = _validatedModelState(readS); + //await initializeOpenRouterWithDynamicModels(this.state.settingsOfProvider.openRouter); this._resolver(); this._onDidChangeState.fire(); @@ -365,22 +398,25 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._storageService.store(VOID_SETTINGS_STORAGE_KEY, encryptedState, StorageScope.APPLICATION, StorageTarget.USER); } - setSettingOfProvider: SetSettingOfProviderFn = async (providerName, settingName, newVal) => { + // Implementation compatible with the overloads above + setSettingOfProvider: SetSettingOfProviderFn = async (providerName: ProviderName, settingName: any, newVal: any) => { const newModelSelectionOfFeature = this.state.modelSelectionOfFeature const newOptionsOfModelSelection = this.state.optionsOfModelSelection + const existing = this.state.settingsOfProvider[providerName] || { _didFillInProviderSettings: undefined, models: [] as VoidStatefulModelInfo[] }; const newSettingsOfProvider: SettingsOfProvider = { ...this.state.settingsOfProvider, [providerName]: { - ...this.state.settingsOfProvider[providerName], + ...existing, [settingName]: newVal, } } const newGlobalSettings = this.state.globalSettings const newOverridesOfModel = this.state.overridesOfModel + const newcustomProviders = this.state.customProviders const newMCPUserStateOfName = this.state.mcpUserStateOfName const newState = { @@ -389,26 +425,24 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { settingsOfProvider: newSettingsOfProvider, globalSettings: newGlobalSettings, overridesOfModel: newOverridesOfModel, + customProviders: newcustomProviders, mcpUserStateOfName: newMCPUserStateOfName, } this.state = _validatedModelState(newState) + //await initializeOpenRouterWithDynamicModels(this.state.settingsOfProvider.openRouter); + await this._storeState() this._onDidChangeState.fire() } - private _onUpdate_syncApplyToChat() { // if sync is turned on, sync (call this whenever Chat model or !!sync changes) this.setModelSelectionOfFeature('Apply', deepClone(this.state.modelSelectionOfFeature['Chat'])) } - private _onUpdate_syncSCMToChat() { - this.setModelSelectionOfFeature('SCM', deepClone(this.state.modelSelectionOfFeature['Chat'])) - } - setGlobalSetting: SetGlobalSettingFn = async (settingName, newVal) => { const newState: VoidSettingsState = { ...this.state, @@ -423,11 +457,8 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // hooks if (this.state.globalSettings.syncApplyToChat) this._onUpdate_syncApplyToChat() - if (this.state.globalSettings.syncSCMToChat) this._onUpdate_syncSCMToChat() - } - setModelSelectionOfFeature: SetModelSelectionOfFeatureFn = async (featureName, newVal) => { const newState: VoidSettingsState = { ...this.state, @@ -444,14 +475,11 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { // hooks if (featureName === 'Chat') { - // When Chat model changes, update synced features - this._onUpdate_syncApplyToChat() - this._onUpdate_syncSCMToChat() + if (this.state.globalSettings.syncApplyToChat) this._onUpdate_syncApplyToChat() } } - - setOptionsOfModelSelection = async (featureName: FeatureName, providerName: ProviderName, modelName: string, newVal: Partial) => { + setOptionsOfModelSelection = async (featureName: FeatureName, providerName: string, modelName: string, newVal: Partial) => { const newState: VoidSettingsState = { ...this.state, optionsOfModelSelection: { @@ -461,7 +489,7 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { [providerName]: { ...this.state.optionsOfModelSelection[featureName][providerName], [modelName]: { - ...this.state.optionsOfModelSelection[featureName][providerName]?.[modelName], + ...(this.state.optionsOfModelSelection[featureName][providerName]?.[modelName] ?? {}), ...newVal } } @@ -496,12 +524,10 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._metricsService.capture('Update Model Overrides', { providerName, modelName, overrides }); } - - - setAutodetectedModels(providerName: ProviderName, autodetectedModelNames: string[], logging: object) { - const { models } = this.state.settingsOfProvider[providerName] + const current = this.state.settingsOfProvider[providerName] || { models: [] as VoidStatefulModelInfo[] } as any; + const models = (current.models ?? []) as VoidStatefulModelInfo[] const oldModelNames = models.map(m => m.modelName) const newModels = _modelsWithSwappedInNewModels({ existingModels: models, models: autodetectedModelNames, type: 'autodetected' }) @@ -518,7 +544,8 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { toggleModelHidden(providerName: ProviderName, modelName: string) { - const { models } = this.state.settingsOfProvider[providerName] + const current = this.state.settingsOfProvider[providerName] || { models: [] as VoidStatefulModelInfo[] } as any; + const models = (current.models ?? []) as VoidStatefulModelInfo[] const modelIdx = models.findIndex(m => m.modelName === modelName) if (modelIdx === -1) return const newIsHidden = !models[modelIdx].isHidden @@ -533,9 +560,10 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } addModel(providerName: ProviderName, modelName: string) { - const { models } = this.state.settingsOfProvider[providerName] + const current = this.state.settingsOfProvider[providerName] || { models: [] as VoidStatefulModelInfo[] } as any; + const models = (current.models ?? []) as VoidStatefulModelInfo[] const existingIdx = models.findIndex(m => m.modelName === modelName) - if (existingIdx !== -1) return // if exists, do nothing + if (existingIdx !== -1) return const newModels = [ ...models, { modelName, type: 'custom', isHidden: false } as const @@ -546,11 +574,11 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { } deleteModel(providerName: ProviderName, modelName: string): boolean { - const { models } = this.state.settingsOfProvider[providerName] + const models = (this.state.settingsOfProvider[providerName]?.models ?? []) as VoidStatefulModelInfo[] const delIdx = models.findIndex(m => m.modelName === modelName) if (delIdx === -1) return false const newModels = [ - ...models.slice(0, delIdx), // delete the idx + ...models.slice(0, delIdx), ...models.slice(delIdx + 1, Infinity) ] this.setSettingOfProvider(providerName, 'models', newModels) @@ -560,7 +588,6 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { return true } - // MCP Server State private _setMCPUserStateOfName = async (newStates: MCPUserStateOfName) => { const newState: VoidSettingsState = { ...this.state, @@ -609,7 +636,32 @@ class VoidSettingsService extends Disposable implements IVoidSettingsService { this._metricsService.capture('Update MCP Server State', { serverName, state }); } -} + setToolDisabled = async (toolName: string, disabled: boolean) => { + const normalized = String(toolName ?? '').trim(); + if (!normalized) return; + + const current = Array.isArray(this.state.globalSettings.disabledToolNames) + ? this.state.globalSettings.disabledToolNames + : []; + const set = new Set(current.map(v => String(v).trim()).filter(Boolean)); + if (disabled) set.add(normalized); + else set.delete(normalized); + + const next = Array.from(set.values()).sort((a, b) => a.localeCompare(b)); + const newState: VoidSettingsState = { + ...this.state, + globalSettings: { + ...this.state.globalSettings, + disabledToolNames: next + } + }; + this.state = _validatedModelState(newState); + await this._storeState(); + this._onDidChangeState.fire(); + this._metricsService.capture('Set Tool Disabled', { toolName: normalized, disabled }); + } + +} registerSingleton(IVoidSettingsService, VoidSettingsService, InstantiationType.Eager); diff --git a/src/vs/platform/void/common/voidSettingsTypes.ts b/src/vs/platform/void/common/voidSettingsTypes.ts new file mode 100644 index 00000000000..92d81258ff3 --- /dev/null +++ b/src/vs/platform/void/common/voidSettingsTypes.ts @@ -0,0 +1,281 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { ModelOverrides } from './modelInference.js'; +import { ToolApprovalType } from './toolsServiceTypes.js'; +import { VoidSettingsState, CustomProviderSettings } from './voidSettingsService.js' +import { IDynamicProviderRegistryService, ProviderMeta } from './providerReg.js'; + + +let __dynamicProviderRegistry: IDynamicProviderRegistryService | undefined; +export const setDynamicProviderRegistryService = (svc: IDynamicProviderRegistryService) => { + __dynamicProviderRegistry = svc; +}; + +export type specialToolFormat = 'openai-style' | 'anthropic-style' | 'gemini-style' | 'disabled'; +export type supportsSystemMessage = false | 'system-role' | 'developer-role' | 'separated'; +export type ProviderName = string; + + +// Minimal dynamic provider metadata lookup. Falls back to simple title when not available. +export const getProviderMeta = (providerName: ProviderName): ProviderMeta | null => { + const reg = __dynamicProviderRegistry; + const slug = String(providerName).toLowerCase(); + try { + const providers = reg?.getProviders() ?? []; + const found = providers.find(p => (p.slug?.toLowerCase() === slug) || (p.name?.toLowerCase() === slug)); + if (found) return { title: found.name || found.slug }; + } catch { /* ignore */ } + return { title: String(providerName) } as ProviderMeta; +}; + +export const getLocalProviderNames = (): string[] => { + const reg = __dynamicProviderRegistry as any; + const providers = __dynamicProviderRegistry?.getProviders() ?? []; + const out: string[] = []; + for (const p of providers) { + const endpoint = (p as any).base_url || (p as any).api_base || ''; + if (p.slug && reg?.isLocalEndpoint(endpoint)) out.push(p.slug); + } + return out; +}; + +export type VoidStatefulModelInfo = { // <-- STATEFUL + modelName: string, + type: 'default' | 'autodetected' | 'custom'; + isHidden: boolean, // whether or not the user is hiding it (switched off) +} // TODO!!! eventually we'd want to let the user change supportsFIM, etc on the model themselves + +type CommonProviderSettings = { + _didFillInProviderSettings: boolean | undefined, // undefined initially, computed when user types in all fields + models: VoidStatefulModelInfo[], +} + +// Important: exclude 'models' from CustomProviderSettings to avoid conflict with stateful models list +export type SettingsAtProvider = Omit & CommonProviderSettings + +// part of state +export type SettingsOfProvider = Record + +// Legacy fields removed; dynamic config uses generic fields only +export type CustomSettingName = 'apiKey' | 'endpoint' | 'headersJSON' +export type SettingName = CustomSettingName | '_didFillInProviderSettings' | 'models' + +type DisplayInfoForProviderName = { + title: string, + desc?: string, +} + +export const displayInfoOfProviderName = (providerName: ProviderName): DisplayInfoForProviderName => { + const meta = getProviderMeta(providerName) + if (meta) return { title: meta.title } + return { title: providerName } +} + +export const subTextMdOfProviderName = (providerName: ProviderName): string => { + const meta = getProviderMeta(providerName) + return meta?.subTextMd ?? '' +} + +export const customSettingNamesOfProvider = (_providerName: ProviderName): CustomSettingName[] => { + return ['apiKey', 'endpoint', 'headersJSON']; +} + +// Dynamic approach: no static defaults. Start with an empty map and let user/dynamic registry populate. +export const defaultSettingsOfProvider: SettingsOfProvider = {} as SettingsOfProvider; + + +export type ModelSelection = { providerName: string, modelName: string } + +export const modelSelectionsEqual = (m1: ModelSelection, m2: ModelSelection) => { + return m1.modelName === m2.modelName && m1.providerName === m2.providerName +} + +// this is a state +export const featureNames = ['Chat', 'Ctrl+K', 'Autocomplete', 'Apply', 'SCM'] as const +export type ModelSelectionOfFeature = Record<(typeof featureNames)[number], ModelSelection | null> +export type FeatureName = keyof ModelSelectionOfFeature + +export const displayInfoOfFeatureName = (featureName: FeatureName) => { + // editor: + if (featureName === 'Autocomplete') + return 'Autocomplete' + else if (featureName === 'Ctrl+K') + return 'Quick Edit' + // sidebar: + else if (featureName === 'Chat') + return 'Chat' + else if (featureName === 'Apply') + return 'Apply' + else if (featureName === 'SCM') + return 'Commit Message Generator' + else + throw new Error(`Feature Name ${featureName} not allowed`) +} + +// the models of these can be refreshed (in theory all can, but not all should) +export const localProviderNames: ProviderName[] = []; +export const nonlocalProviderNames: ProviderName[] = []; +export const refreshableProviderNames: ProviderName[] = localProviderNames; +export type RefreshableProviderName = ProviderName; + +// models that come with download buttons +export const hasDownloadButtonsOnModelsProviderNames: ProviderName[] = [] + +// use this in isFeatuerNameDissbled +export const isProviderNameDisabled = (providerName: ProviderName, settingsState: VoidSettingsState) => { + const settingsAtProvider = (settingsState.settingsOfProvider[providerName] as any) || { models: [], _didFillInProviderSettings: false }; + const isAutodetected = (refreshableProviderNames as string[]).includes(providerName) + const isDisabled = (settingsAtProvider.models ?? []).length === 0 + if (isDisabled) { + return isAutodetected ? 'providerNotAutoDetected' : (!settingsAtProvider._didFillInProviderSettings ? 'notFilledIn' : 'addModel') + } + return false +} + +export const isFeatureNameDisabled = (featureName: FeatureName, settingsState: VoidSettingsState) => { + // if has a selected provider, check if it's enabled + const selectedProvider = settingsState.modelSelectionOfFeature[featureName] + + if (selectedProvider) { + const { providerName } = selectedProvider + + + const customProvider = settingsState.customProviders?.[providerName] + if (!customProvider?.endpoint) { + return 'addProvider' + } + return false + } + + // Dynamic providers: if any configured provider exists, suggest adding a model; else suggest adding provider + const anyConfigured = Object.values(settingsState.customProviders || {}).some(v => !!v?.endpoint) + if (anyConfigured) return 'addModel' + + return 'addProvider' +} + +export type ChatMode = 'agent' | 'gather' | 'normal' + +export const DISABLE_TELEMETRY_KEY = 'void.settings.disableTelemetry'; + +export type GlobalSettings = { + autoRefreshModels: boolean; + aiInstructions: string; + enableAutocomplete: boolean; + syncApplyToChat: boolean; + syncSCMToChat: boolean; + enableFastApply: boolean; + applyAstInference: boolean; + chatMode: ChatMode; + autoApprove: { [approvalType in ToolApprovalType]?: boolean }; + mcpAutoApprove: boolean; + showInlineSuggestions: boolean; + includeToolLintErrors: boolean; + // Loop guard thresholds shared between non-ACP chat and ACP agent. + // These map directly onto LLMLoopDetector options (except prefix length, which uses a fixed default). + loopGuardMaxTurnsPerPrompt: number; + loopGuardMaxSameAssistantPrefix: number; + loopGuardMaxSameToolCall: number; + isOnboardingComplete: boolean; + disableTelemetry: boolean; + + useAcp: boolean; + // Connection mode: 'builtin' | 'websocket' | 'process' + acpMode: 'builtin' | 'websocket' | 'process'; + acpAgentUrl: string; // for websocket + // for process: + acpProcessCommand: string; + acpProcessArgs: string[]; + acpProcessEnv: Record; + + acpModel: string | null; + acpSystemPrompt: string | null; + showAcpPlanInChat: boolean; + + chatRetries: number; + retryDelay: number; + maxToolOutputLength: number; + readFileChunkLines: number; + notifyOnTruncation: boolean; + /** Tool names (static and dynamic) disabled by user in settings UI. */ + disabledToolNames: string[]; +} + +export const defaultGlobalSettings: GlobalSettings = { + autoRefreshModels: true, + aiInstructions: '', + enableAutocomplete: false, + syncApplyToChat: true, + enableFastApply: true, + applyAstInference: true, + syncSCMToChat: true, + chatMode: 'agent', + autoApprove: {}, + mcpAutoApprove: false, + showInlineSuggestions: true, + includeToolLintErrors: true, + loopGuardMaxTurnsPerPrompt: 38, + loopGuardMaxSameAssistantPrefix: 16, + loopGuardMaxSameToolCall: 16, + isOnboardingComplete: false, + disableTelemetry: true, + useAcp: false, + acpMode: 'builtin', + acpAgentUrl: 'ws://127.0.0.1:8719', + acpProcessCommand: '', + acpProcessArgs: [], + acpProcessEnv: {}, + acpModel: null, + acpSystemPrompt: null, + showAcpPlanInChat: true, + + chatRetries: 0, + retryDelay: 2500, + maxToolOutputLength: 40000, + readFileChunkLines: 200, + notifyOnTruncation: true, + disabledToolNames: [], +} + +export type GlobalSettingName = keyof GlobalSettings +export const globalSettingNames = Object.keys(defaultGlobalSettings) as GlobalSettingName[] + +export type ModelSelectionOptions = { + reasoningEnabled?: boolean; + reasoningBudget?: number; + reasoningEffort?: string; + /** Custom temperature for OpenAI-compatible providers */ + temperature?: number; + /** Custom max_tokens for OpenAI-compatible providers */ + maxTokens?: number; +} + +export type OptionsOfModelSelection = { + [featureName in FeatureName]: { + [providerName: string]: { + [modelName: string]: ModelSelectionOptions | undefined + } + } +} + +export type OverridesOfModel = { + [providerName: string]: { + [modelName: string]: Partial | undefined + } +} + +export const defaultOverridesOfModel: OverridesOfModel = {} + +// Back-compat shim for older imports; dynamic list should be retrieved from registry instead. +export const providerNames: ProviderName[] = [] + +export interface MCPUserState { + isOn: boolean; +} + +export interface MCPUserStateOfName { + [serverName: string]: MCPUserState | undefined; +} diff --git a/src/vs/workbench/contrib/void/common/voidUpdateService.ts b/src/vs/platform/void/common/voidUpdateService.ts similarity index 79% rename from src/vs/workbench/contrib/void/common/voidUpdateService.ts rename to src/vs/platform/void/common/voidUpdateService.ts index fbf72b547ba..8a378895e0c 100644 --- a/src/vs/workbench/contrib/void/common/voidUpdateService.ts +++ b/src/vs/platform/void/common/voidUpdateService.ts @@ -3,23 +3,19 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { ProxyChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { registerSingleton, InstantiationType } from '../../instantiation/common/extensions.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; +import { IMainProcessService } from '../../ipc/common/mainProcessService.js'; import { VoidCheckUpdateRespose } from './voidUpdateServiceTypes.js'; - - export interface IVoidUpdateService { readonly _serviceBrand: undefined; check: (explicit: boolean) => Promise; } - export const IVoidUpdateService = createDecorator('VoidUpdateService'); - // implemented by calling channel export class VoidUpdateService implements IVoidUpdateService { diff --git a/src/vs/workbench/contrib/void/common/voidUpdateServiceTypes.ts b/src/vs/platform/void/common/voidUpdateServiceTypes.ts similarity index 100% rename from src/vs/workbench/contrib/void/common/voidUpdateServiceTypes.ts rename to src/vs/platform/void/common/voidUpdateServiceTypes.ts diff --git a/src/vs/platform/void/electron-main/llmMessage/extractGrammar.ts b/src/vs/platform/void/electron-main/llmMessage/extractGrammar.ts new file mode 100644 index 00000000000..f416604b90a --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/extractGrammar.ts @@ -0,0 +1,1045 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { generateUuid } from '../../../../base/common/uuid.js' +import { endsWithAnyPrefixOf } from '../../common/helpers/extractCodeFromResult.js' +import { availableTools, InternalToolInfo } from '../../common/toolsRegistry.js' +import { ToolName, ToolParamName } from '../../common/toolsServiceTypes.js' +import { OnFinalMessage, OnText, RawToolCallObj, RawToolParamsObj } from '../../common/sendLLMMessageTypes.js' +import { ChatMode } from '../../common/voidSettingsTypes.js' + + +const escapeRegExp = (s: string) => + s.replace(/[\\\^\$\.\*\+\?\(\)\[\]\{\}\|\/\-]/g, '\\$&'); + +type ToolOfToolName = { [toolName: string]: InternalToolInfo | undefined } + +//this need if llm tool (edit_file) call with CDATA block +const extractParamValue = ( + str: string, + paramName: string, + toolName: string +): { value: string | undefined; endIndex: number } => { + const needsCDATA = toolName === 'edit_file' && + (paramName === 'original_snippet' || paramName === 'updated_snippet'); + + const esc = escapeRegExp(paramName); + const openRe = new RegExp(`<${esc}\\b[^>]*>`, 'i'); // + const closeRe = new RegExp(``, 'i'); // + + const openMatch = openRe.exec(str); + if (!openMatch) return { value: undefined, endIndex: -1 }; + + const contentStart = (openMatch.index ?? 0) + openMatch[0].length; + + if (needsCDATA) { + const cStart = str.indexOf('', cStart + 9); + if (cEnd !== -1) { + const tailAfterCdata = str.slice(cEnd + 3); + const closeMatchAfter = closeRe.exec(tailAfterCdata); + if (closeMatchAfter) { + const value = str.slice(cStart + 9, cEnd); + const endIndex = (cEnd + 3) + ((closeMatchAfter.index ?? 0) + closeMatchAfter[0].length); + return { value, endIndex }; + } + } + } + } + + const tail = str.slice(contentStart); + const closeMatch = closeRe.exec(tail); + if (!closeMatch) return { value: undefined, endIndex: -1 }; + + const valueEnd = contentStart + (closeMatch.index ?? 0); + const value = str.slice(contentStart, valueEnd); + return { value, endIndex: valueEnd + closeMatch[0].length }; +}; + + +const getRequiredParamNames = ( + toolName: ToolName, + toolOfToolName: ToolOfToolName +): ToolParamName[] => { + const def: any = toolOfToolName[toolName]?.params; + if (!def) return []; + const required: ToolParamName[] = []; + for (const key of Object.keys(def)) { + const meta = def[key]; + if (meta && typeof meta === 'object' && (meta.required === true || meta?.schema?.required === true)) { + required.push(key as ToolParamName); + } + } + return required; +}; + +const hasAllRequiredParams = ( + toolName: ToolName, + paramsObj: RawToolParamsObj, + toolOfToolName: ToolOfToolName +): boolean => { + const required = getRequiredParamNames(toolName, toolOfToolName); + if (required.length === 0) return false; + return required.every(p => paramsObj[p] !== undefined); +}; + + +const findLastOpenBefore = (s: string, toolName: string, beforeIdx: number): { index: number; len: number } | null => { + const esc = escapeRegExp(toolName); + const openReG = new RegExp(`<${esc}\\b[^>]*>`, 'ig'); + let lastIdx = -1; + let lastLen = 0; + let m: RegExpExecArray | null; + while ((m = openReG.exec(s))) { + if (m.index < beforeIdx) { + lastIdx = m.index; + lastLen = m[0].length; + } else { + break; + } + } + return lastIdx >= 0 ? { index: lastIdx, len: lastLen } : null; +}; + + +const findParamAnchorBefore = ( + s: string, + toolName: ToolName, + beforeIdx: number, + toolOfToolName: ToolOfToolName +): number => { + const params = Object.keys(toolOfToolName[toolName]?.params ?? {}); + if (params.length === 0) return -1; + const alt = params.map(escapeRegExp).join('|'); + const re = new RegExp(`<(?:${alt})\\b[^>]*>`, 'ig'); + + let firstIdx = -1; + let lastIdx = -1; + let m: RegExpExecArray | null; + while ((m = re.exec(s))) { + if (m.index < beforeIdx) { + if (firstIdx === -1) firstIdx = m.index; + lastIdx = m.index; + } else { + break; + } + } + return firstIdx >= 0 ? firstIdx : lastIdx; +}; + +const maskCodeBlocks = (s: string): string => { + if (!s) return s; + const len = s.length; + const mask = new Uint8Array(len); + + + const mark = (from: number, to: number) => { + if (from < 0 || to <= from) return; + const a = Math.max(0, Math.min(len, from)); + const b = Math.max(0, Math.min(len, to)); + for (let i = a; i < b; i++) mask[i] = 1; + }; + + + { + const re = /```[\s\S]*?```/g; + let m: RegExpExecArray | null; + while ((m = re.exec(s))) { + mark(m.index, m.index + m[0].length); + } + } + + + { + const re = /~~~[\s\S]*?~~~/g; + let m: RegExpExecArray | null; + while ((m = re.exec(s))) { + mark(m.index, m.index + m[0].length); + } + } + + + { + const re = /`[^`\n]+`/g; + let m: RegExpExecArray | null; + while ((m = re.exec(s))) { + mark(m.index, m.index + m[0].length); + } + } + + + if (!mask.includes(1)) return s; + const out: string[] = new Array(len); + for (let i = 0; i < len; i++) { + out[i] = mask[i] ? ' ' : s[i]; + } + return out.join(''); +}; + + +type ToolRegion = + | { kind: 'self'; toolName: ToolName; start: number; end: number } + | { kind: 'openOnly'; toolName: ToolName; start: number; end: number } + | { kind: 'openClose'; toolName: ToolName; start: number; end: number } + | { kind: 'closeOnly'; toolName: ToolName; start: number; end: number }; + +const findFirstToolRegionEnhanced = ( + text: string, + tools: InternalToolInfo[], + toolOfToolName: ToolOfToolName +): ToolRegion | null => { + if (!text || !tools.length) return null; + + const namesAlt = tools.map(t => escapeRegExp(t.name)).join('|'); + if (!namesAlt) return null; + + + const anyTagRe = new RegExp(`<\\/?(${namesAlt})\\b[^>]*?>`, 'ig'); + const first = anyTagRe.exec(text); + if (!first) return null; + + const idx = first.index; + const raw = first[0]; + const name = (first[1] || '').toLowerCase() as ToolName; + + const isClose = raw.startsWith('$/i.test(raw); + + if (isSelf) { + return { kind: 'self', toolName: name, start: idx, end: idx + raw.length }; + } + + if (!isClose) { + + const esc = escapeRegExp(name); + const openLen = raw.length; + const tail = text.slice(idx + openLen); + const closeRe = new RegExp(``, 'i'); + const mClose = closeRe.exec(tail); + if (mClose && mClose.index !== undefined) { + const end = (idx + openLen) + mClose.index + mClose[0].length; + return { kind: 'openClose', toolName: name, start: idx, end }; + } + return { kind: 'openOnly', toolName: name, start: idx, end: text.length }; + } + + + const closeLen = raw.length; + const mOpenPrev = findLastOpenBefore(text, name, idx); + if (mOpenPrev) { + return { kind: 'openClose', toolName: name, start: mOpenPrev.index, end: idx + closeLen }; + } + + + const anchor = findParamAnchorBefore(text, name, idx, toolOfToolName); + const start = (anchor >= 0 ? anchor : idx); + return { kind: 'closeOnly', toolName: name, start, end: idx + closeLen }; +}; + + +const normalizeParamName = (s: string) => { + let out = (s || '').trim(); + out = out.replace(/-/g, '_'); + // camelCase -> snake_case + out = out.replace(/([a-z0-9])([A-Z])/g, '$1_$2').toLowerCase(); + return out; +}; + +// If we ever wrap CDATA, avoid breaking on "]]>" +const escapeForCdata = (s: string) => { + if (!s) return s; + return s.includes(']]>') ? s.split(']]>').join(']]]]>') : s; +}; + +const findToolCallRegion = (maskedText: string): { start: number; end: number } | null => { + const openRe = /]*>/i; + const mOpen = openRe.exec(maskedText); + if (!mOpen || mOpen.index === undefined) return null; + + const start = mOpen.index; + + // try find close + const tail = maskedText.slice(start + mOpen[0].length); + const mClose = /<\/tool_call\s*>/i.exec(tail); + if (mClose && mClose.index !== undefined) { + const end = (start + mOpen[0].length) + mClose.index + mClose[0].length; + return { start, end }; + } + + // no close yet (streaming) -> treat as open until end + return { start, end: maskedText.length }; +}; + +const buildVoidXmlFromToolCallWrapper = ( + wrapperXml: string, + toolOfToolName: { [toolName: string]: InternalToolInfo | undefined } +): { toolName: ToolName; xml: string } | null => { + // function name variants: + // 1) ... + // 2) ... + const fnMatch = + //i.exec(wrapperXml) || + /]*\bname\s*=\s*["']?([a-zA-Z0-9_\-]+)["']?[^>]*>/i.exec(wrapperXml); + + if (!fnMatch) return null; + + const toolNameRaw = (fnMatch[1] || '').toLowerCase(); + const toolName = toolNameRaw as ToolName; + + // Only transform if tool is actually known/allowed in this context + if (!toolOfToolName[toolName]) return null; + + const allowedParams = new Set(Object.keys(toolOfToolName[toolName]?.params ?? {})); + + // parameter variants: + // 1) ... + // 2) ... + const params: Array<{ name: string; value: string }> = []; + const paramRe = + /([\s\S]*?)<\/parameter\s*>|]*\bname\s*=\s*["']?([a-zA-Z0-9_\-]+)["']?[^>]*>([\s\S]*?)<\/parameter\s*>/ig; + + let m: RegExpExecArray | null; + while ((m = paramRe.exec(wrapperXml))) { + const rawName = (m[1] ?? m[3] ?? '').trim(); + const rawVal = (m[2] ?? m[4] ?? ''); + + if (!rawName) continue; + + const name = normalizeParamName(rawName); + + // If tool has a known param set, keep only matching ones (optional, but safer) + if (allowedParams.size > 0 && !allowedParams.has(name)) { + continue; + } + const needsCdata = toolName === 'edit_file' && (name === 'original_snippet' || name === 'updated_snippet'); + const value = needsCdata ? rawVal : rawVal.trim(); + + params.push({ name, value }); + } + + // Build void XML + const inner = params.map(p => { + const needsCdata = toolName === 'edit_file' && (p.name === 'original_snippet' || p.name === 'updated_snippet'); + if (needsCdata) { + return `<${p.name}>`; + } + return `<${p.name}>${p.value}`; + }).join('\n'); + + // If wrapper has we can close; if not, leave open (streaming-friendly) + const hasClose = /<\/tool_call\s*>/i.test(wrapperXml); + const xml = hasClose + ? `<${toolName}>\n${inner}\n` + : `<${toolName}>\n${inner}\n`; + + return { toolName, xml }; +}; + +const parseXMLPrefixToToolCall = ( + toolName: ToolName, + toolId: string, + str: string, + toolOfToolName: ToolOfToolName +): RawToolCallObj => { + + const paramsObj: RawToolParamsObj = {}; + const doneParams: ToolParamName[] = []; + let isDone = false; + + const getAnswer = (): RawToolCallObj => { + for (const p in paramsObj) { + const paramName = p as ToolParamName; + const orig = paramsObj[paramName]; + if (orig === undefined) continue; + + const isCDATAParam = toolName === 'edit_file' && + (paramName === 'original_snippet' || paramName === 'updated_snippet'); + if (!isCDATAParam) { + paramsObj[paramName] = trimBeforeAndAfterNewLines(orig as string); + } + } + return { + name: toolName, + rawParams: paramsObj, + doneParams, + isDone, + id: toolId, + }; + }; + + const esc = escapeRegExp(toolName); + const openRe = new RegExp(`<${esc}\\b[^>]*>`, 'i'); // + const closeRe = new RegExp(``, 'i'); // + const selfRe = new RegExp(`<${esc}\\b[^>]*/>`, 'i'); // + + const openMatch = openRe.exec(str); + const selfMatch = selfRe.exec(str); + + + if (!openMatch && !selfMatch) return getAnswer(); + + + if (selfMatch && (!openMatch || (selfMatch.index ?? 0) < (openMatch.index ?? 0))) { + isDone = true; + return getAnswer(); + } + + + const start = (openMatch!.index ?? 0) + openMatch![0].length; + const tail = str.slice(start); + const closeMatch = closeRe.exec(tail); + + let inner = ''; + if (!closeMatch) { + + inner = tail; + } else { + inner = tail.slice(0, closeMatch.index ?? 0); + isDone = true; + } + + const allowedParams = Object.keys(toolOfToolName[toolName]?.params ?? {}) as ToolParamName[]; + for (const paramName of allowedParams) { + const { value } = extractParamValue(inner, paramName, toolName); + if (value !== undefined) { + paramsObj[paramName] = value; + doneParams.push(paramName); + } + } + + if (!isDone) { + const allRequiredPresent = hasAllRequiredParams(toolName, paramsObj, toolOfToolName); + const allAllowedPresent = allowedParams.length > 0 && doneParams.length === allowedParams.length; + if (allRequiredPresent || allAllowedPresent) { + isDone = true; + } + } + + return getAnswer(); +}; + +const splitProviderReasoning = (s: string, tags: [string, string] | null): { reasoning: string; after: string } => { + if (!s) return { reasoning: '', after: '' }; + if (!tags) return { reasoning: s, after: '' }; + + const [openTag, closeTag] = tags; + + + const closeIdx = s.lastIndexOf(closeTag); + if (closeIdx >= 0) { + const beforeClose = s.slice(0, closeIdx); + const after = s.slice(closeIdx + closeTag.length); + const reasoning = beforeClose.split(openTag).join('').split(closeTag).join(''); + return { reasoning, after }; + } + + + const reasoning = s.split(openTag).join('').split(closeTag).join(''); + return { reasoning, after: '' }; +}; + + +type ExtractReasoningWrapperOpts = { + toolsListOverride?: InternalToolInfo[]; + enableXmlToolParsing?: boolean; +}; + +export const extractReasoningAndXMLToolsWrapper = ( + onText: OnText, + onFinalMessage: OnFinalMessage, + thinkTagsInit: [string, string] | null, + chatMode: ChatMode | null, + opts?: ExtractReasoningWrapperOpts +): { newOnText: OnText; newOnFinalMessage: OnFinalMessage } => { + + // Tools + const enableXmlToolParsing = opts?.enableXmlToolParsing !== false; + const allowed = enableXmlToolParsing && chatMode ? availableTools(chatMode) : null; + const toolsList: InternalToolInfo[] = + !enableXmlToolParsing + ? [] + : (opts?.toolsListOverride && opts.toolsListOverride.length) + ? opts.toolsListOverride + : (Array.isArray(allowed) + ? allowed + : (allowed ? Object.values(allowed) : [])); + + type ToolOfToolName = { [toolName: string]: InternalToolInfo | undefined }; + const toolOfToolName: ToolOfToolName = {}; + for (const t of toolsList) toolOfToolName[t.name] = t; + + const toolId = generateUuid(); + + const toolTagHints = toolsList.map(t => String(t.name || '').toLowerCase()).filter(Boolean); + const incrementLookbackChars = 96; + const reasoningPrefixProbeLen = 96; + let lastReasoningParseObservedLen = 0; + let lastThinkDetectObservedLen = 0; + + + let r_foundTag1 = false; + let r_foundTag2 = false; + let r_latestAddIdx = 0; + let r_fullTextSoFar = ''; + let r_fullReasoningSoFar = ''; + let r_providerReasoningAcc = ''; + + + let lastReasoning = ''; + let latestToolCall: RawToolCallObj | undefined = undefined; + + + let activeThinkTags: [string, string] | null = thinkTagsInit; + + const likelyContainsToolMarkup = (s: string): boolean => { + if (!s || s.indexOf('<') === -1) return false; + const lower = s.toLowerCase(); + + if ( + lower.includes(' `<${h}`), + ...toolTagHints.map(h => ` { + if (!s || !enableXmlToolParsing || toolOpenMarkers.length === 0) return s; + if (s.indexOf('<') === -1) return s; + + const lower = s.toLowerCase(); + let trimLen = 0; + + for (const marker of toolOpenMarkers) { + const m = endsWithAnyPrefixOf(lower, marker); + if (!m) continue; + + // marker itself still represents an unfinished open/close sequence in streaming context, + // so trim both partial prefixes and exact marker matches. + const curr = m.length; + if (curr > trimLen) trimLen = curr; + } + + if (trimLen <= 0) return s; + return s.slice(0, Math.max(0, s.length - trimLen)).trimEnd(); + }; + + const shouldParseByIncrement = (full: string, prevLen: number): boolean => { + if (!enableXmlToolParsing || toolsList.length === 0 || !full) return false; + const safePrev = Math.max(0, Math.min(prevLen, full.length)); + const start = Math.max(0, safePrev - incrementLookbackChars); + return likelyContainsToolMarkup(full.slice(start)); + }; + + const stripThinkTagsFromText = (s: string): string => { + if (!s || !activeThinkTags) return s; + const [openTag, closeTag] = activeThinkTags; + return s.split(openTag).join('').split(closeTag).join(''); + }; + + const stripToolCallWrapperOnce = (s: string): string => { + if (!s) return s; + + + const full = s.replace(//i, ''); + if (full !== s) return full.trim(); + + + return s.replace(/ { + + const lastNl = beforeText.lastIndexOf('\n'); + const tail = lastNl >= 0 ? beforeText.slice(lastNl + 1) : beforeText; + return tail.trim() === ''; + }; + + // ============ Utils ============ + const stripToolXmlOnce = (s: string, toolName: string): string => { + if (!s) return s; + const esc = escapeRegExp(toolName); + const re = new RegExp(`<${esc}\\b[\\s\\S]*?<\\/${esc}>`, 'i'); + return s.replace(re, '').trim(); + }; + + const stripToolXmlOrOpenTailOnce = (s: string, toolName: string): string => { + if (!s) return s; + const esc = escapeRegExp(toolName); + + // Prefer removing a closed block first. + const closedRe = new RegExp(`<${esc}\\b[\\s\\S]*?<\\/${esc}>`, 'i'); + const removedClosed = s.replace(closedRe, '').trim(); + if (removedClosed !== s) return removedClosed; + + // Streaming/open-only fallback: drop everything from opening tag to end. + const openTailRe = new RegExp(`<${esc}\\b[^>]*>[\\s\\S]*$`, 'i'); + const removedOpenTail = s.replace(openTailRe, '').trimEnd(); + if (removedOpenTail !== s) return removedOpenTail; + + return s; + }; + + const stripFakeResultTail = (s: string): string => { + if (!s) return s; + const m = s.match(/<\w+_result\b[\s\S]*$/i); + if (!m || m.index === undefined || m.index < 0) return s; + return s.slice(0, m.index).trimEnd(); + }; + + const parseToolFromText = ( + text: string, + opts?: { skipLikelihoodCheck?: boolean } + ): { beforeText: string; call?: RawToolCallObj; source?: 'tool_call' | 'tool_tag' } => { + if (!text) return { beforeText: '' }; + if (!enableXmlToolParsing || !toolsList.length) return { beforeText: text }; + if (!opts?.skipLikelihoodCheck && !likelyContainsToolMarkup(text)) return { beforeText: text }; + + const masked = (text.includes('`') || text.includes('~')) ? maskCodeBlocks(text) : text; + + + const toolCallRegion = findToolCallRegion(masked); + + + const region = findFirstToolRegionEnhanced(masked, toolsList, toolOfToolName); + + const toolCallStartsFirst = + toolCallRegion && + (!region || toolCallRegion.start < region.start); + + if (toolCallStartsFirst) { + const beforeText = text.slice(0, toolCallRegion.start); + + const wrapperSlice = text.slice(toolCallRegion.start, toolCallRegion.end); + const built = buildVoidXmlFromToolCallWrapper(wrapperSlice, toolOfToolName); + if (built) { + const call = parseXMLPrefixToToolCall( + built.toolName, + toolId, + built.xml, + toolOfToolName + ); + return { beforeText, call, source: 'tool_call' }; + } + return { beforeText }; + } + + if (!region) return { beforeText: text }; + + const before = text.slice(0, region.start); + let xmlForParse = ''; + + if (region.kind === 'closeOnly') { + + const esc = escapeRegExp(region.toolName); + const slice = text.slice(region.start, region.end); + const inner = slice.replace(new RegExp(`\\s*$`, 'i'), ''); + xmlForParse = `<${region.toolName}>` + inner + ``; + } else { + + xmlForParse = text.slice(region.start); + } + + const call = parseXMLPrefixToToolCall( + region.toolName as ToolName, + toolId, + xmlForParse, + toolOfToolName + ); + + return { beforeText: before, call, source: 'tool_tag' }; + }; + + const mergeToolCall = (curr: RawToolCallObj | undefined, cand: RawToolCallObj | undefined) => { + if (!cand) return curr; + if (!curr) return cand; + + + if (curr.name === cand.name) { + const rawParams = { ...(curr.rawParams ?? {}), ...(cand.rawParams ?? {}) } as RawToolParamsObj; + const doneParams = Array.from(new Set([...(curr.doneParams ?? []), ...(cand.doneParams ?? [])])) as ToolParamName[]; + + let isDone = !!(curr.isDone || cand.isDone); + if (!isDone) { + try { + isDone = hasAllRequiredParams(curr.name as ToolName, rawParams, toolOfToolName); + } catch { } + } + + return { + ...curr, + rawParams, + doneParams, + isDone, + }; + } + + + if (curr.isDone) return curr; + if (cand.isDone) return cand; + return curr; + }; + + + const THINK_PAIRS: [string, string][] = [ + ['', ''], + ['', ''], + ['◁think▷', '◁/think▷'], + ['◁thinking▷', '◁/thinking▷'], + ['‹think›', '‹/think›'], + ['〈think〉', '〈/think〉'], + ['【think】', '【/think】'], + ['【thinking】', '【/thinking】'], + ]; + + const detectThinkTags = (s: string): [string, string] | null => { + if (!s) return null; + for (const [open, close] of THINK_PAIRS) { + if (s.includes(open)) return [open, close]; + const partial = endsWithAnyPrefixOf(s, open); + if (partial && partial !== '') return [open, close]; + } + return null; + }; + + const maxThinkOpenTagLen = THINK_PAIRS.reduce((m, [open]) => Math.max(m, open.length), 0); + const detectThinkTagsByIncrement = (full: string): [string, string] | null => { + if (!full) return null; + const safePrev = full.length < lastThinkDetectObservedLen + ? 0 + : Math.max(0, Math.min(lastThinkDetectObservedLen, full.length)); + const start = Math.max(0, safePrev - maxThinkOpenTagLen); + const maybe = detectThinkTags(full.slice(start)); + lastThinkDetectObservedLen = full.length; + return maybe; + }; + + const extractReasoningViaTags = ( + fullText_: string, + tags: [string, string] + ): { textOut: string; reasoningOut: string; earlyReturn?: boolean } => { + const [openTag, closeTag] = tags; + + + if (!r_foundTag1) { + const endsWithOpen = endsWithAnyPrefixOf(fullText_, openTag); + if (endsWithOpen && endsWithOpen !== openTag) { + return { textOut: r_fullTextSoFar, reasoningOut: r_fullReasoningSoFar, earlyReturn: true }; + } + const tag1Index = fullText_.indexOf(openTag); + if (tag1Index !== -1) { + r_foundTag1 = true; + r_fullTextSoFar += fullText_.substring(0, tag1Index); + r_latestAddIdx = tag1Index + openTag.length; + } else { + r_fullTextSoFar = fullText_; + r_latestAddIdx = fullText_.length; + return { textOut: r_fullTextSoFar, reasoningOut: r_fullReasoningSoFar }; + } + } + + + if (!r_foundTag2) { + const endsWithClose = endsWithAnyPrefixOf(fullText_, closeTag); + if (endsWithClose && endsWithClose !== closeTag) { + if (fullText_.length > r_latestAddIdx) { + r_fullReasoningSoFar += fullText_.substring(r_latestAddIdx); + r_latestAddIdx = fullText_.length; + } + return { textOut: r_fullTextSoFar, reasoningOut: r_fullReasoningSoFar, earlyReturn: true }; + } + const tag2Index = fullText_.indexOf(closeTag, r_latestAddIdx); + if (tag2Index !== -1) { + r_fullReasoningSoFar += fullText_.substring(r_latestAddIdx, tag2Index); + r_foundTag2 = true; + r_latestAddIdx = tag2Index + closeTag.length; + } else { + if (fullText_.length > r_latestAddIdx) { + r_fullReasoningSoFar += fullText_.substring(r_latestAddIdx); + r_latestAddIdx = fullText_.length; + } + return { textOut: r_fullTextSoFar, reasoningOut: r_fullReasoningSoFar }; + } + } + + + if (fullText_.length > r_latestAddIdx) { + r_fullTextSoFar += fullText_.substring(r_latestAddIdx); + r_latestAddIdx = fullText_.length; + } + return { textOut: r_fullTextSoFar, reasoningOut: r_fullReasoningSoFar }; + }; + + // ============ Handlers ============ + + const isValidToolCall = (t?: RawToolCallObj): t is RawToolCallObj => + !!(t && t.name && String(t.name).trim().length > 0); + + const newOnText: OnText = (params) => { + const rawFullText = params.fullText || ''; + const providerReasoning = params.fullReasoning ?? undefined; + const incomingPlan = params.plan; + + let textForXml = rawFullText; + let reasoningForSearch = lastReasoning; + + + if (providerReasoning !== undefined) { + if (!r_providerReasoningAcc) { + r_providerReasoningAcc = providerReasoning; + } else { + const prevLen = r_providerReasoningAcc.length; + const probeLen = Math.min(reasoningPrefixProbeLen, prevLen, providerReasoning.length); + const hasSamePrefix = + probeLen > 0 && + providerReasoning.slice(0, probeLen) === r_providerReasoningAcc.slice(0, probeLen); + + if (providerReasoning.length > prevLen && hasSamePrefix) { + + r_providerReasoningAcc = providerReasoning; + } else if (providerReasoning.length < prevLen && hasSamePrefix) { + + r_providerReasoningAcc = providerReasoning; + } else if (!(providerReasoning.length === prevLen && hasSamePrefix)) { + + r_providerReasoningAcc += providerReasoning; + } + } + + + if (!activeThinkTags) { + const maybe = detectThinkTagsByIncrement(r_providerReasoningAcc); + if (maybe) { activeThinkTags = maybe; } + } + + + if (activeThinkTags) { + const [openTag, closeTag] = activeThinkTags; + const pOpen = endsWithAnyPrefixOf(providerReasoning, openTag); + const pClose = endsWithAnyPrefixOf(providerReasoning, closeTag); + if ((pOpen && pOpen !== openTag) || (pClose && pClose !== closeTag)) { + return; + } + } + + + const { reasoning, after } = splitProviderReasoning(r_providerReasoningAcc, activeThinkTags); + reasoningForSearch = reasoning; + textForXml = (textForXml || '') + (after || ''); + } else { + + if (!activeThinkTags) { + const maybe = detectThinkTagsByIncrement(rawFullText); + if (maybe) { activeThinkTags = maybe; } + } + + + if (activeThinkTags) { + const r = extractReasoningViaTags(rawFullText, activeThinkTags); + if (r.earlyReturn) { + return; + } + textForXml = r.textOut; + reasoningForSearch = r.reasoningOut; + } + } + + lastReasoning = reasoningForSearch; + textForXml = stripThinkTagsFromText(textForXml); + + const shouldParseMainText = + enableXmlToolParsing && + toolsList.length > 0 && + likelyContainsToolMarkup(textForXml); + const { beforeText, call: callFromText } = shouldParseMainText + ? parseToolFromText(textForXml, { skipLikelihoodCheck: true }) + : { beforeText: textForXml }; + let uiText = beforeText; + if (chatMode && chatMode !== 'normal') { + const shouldParseReasoning = shouldParseByIncrement(reasoningForSearch, lastReasoningParseObservedLen); + lastReasoningParseObservedLen = reasoningForSearch.length; + const parsedR = shouldParseReasoning + ? parseToolFromText(reasoningForSearch, { skipLikelihoodCheck: true }) + : { beforeText: reasoningForSearch }; + if (parsedR.call && (parsedR.source === 'tool_call' || isBlockToolCall(parsedR.beforeText))) { + latestToolCall = mergeToolCall(latestToolCall, parsedR.call); + } + } + + + const inboundTool = params.toolCall; + latestToolCall = mergeToolCall(latestToolCall, callFromText); + if (isValidToolCall(inboundTool)) { + latestToolCall = mergeToolCall(latestToolCall, inboundTool); + } + + let uiReasoning = lastReasoning; + if (latestToolCall?.isDone) { + uiText = stripToolCallWrapperOnce(uiText); + uiText = stripToolXmlOrOpenTailOnce(uiText, latestToolCall.name); + uiReasoning = stripToolCallWrapperOnce(uiReasoning); + uiReasoning = stripToolXmlOnce(uiReasoning, latestToolCall.name); + uiText = stripFakeResultTail(uiText); + } + uiText = stripThinkTagsFromText(uiText); + uiText = stripTrailingPartialToolMarker(uiText); + uiReasoning = stripTrailingPartialToolMarker(uiReasoning); + + onText({ + fullText: uiText.trim(), + fullReasoning: uiReasoning, + toolCall: isValidToolCall(latestToolCall) + ? latestToolCall + : (isValidToolCall(inboundTool) ? inboundTool : undefined), + plan: incomingPlan, + // propagate provider token usage unchanged + tokenUsage: (params as any).tokenUsage, + }); + }; + + const newOnFinalMessage: OnFinalMessage = (params) => { + + newOnText(params); + + const providerReasoning = params.fullReasoning ?? ''; + if (!activeThinkTags) { + const maybe = detectThinkTags(providerReasoning || params.fullText || ''); + if (maybe) activeThinkTags = maybe; + } + + let extraFromReasoning = ''; + let finalReasoning = lastReasoning; + if (providerReasoning) { + const { reasoning, after } = splitProviderReasoning(providerReasoning, activeThinkTags); + finalReasoning = reasoning; + extraFromReasoning = after; + } + + const baseTextForUiRaw = (params.fullText || '') + (extraFromReasoning || ''); + let baseTextForUi = stripThinkTagsFromText(baseTextForUiRaw); + const plan = params.plan; + + if (latestToolCall && !latestToolCall.isDone && typeof latestToolCall.name === 'string') { + try { + if (hasAllRequiredParams(latestToolCall.name as ToolName, latestToolCall.rawParams as RawToolParamsObj, toolOfToolName)) { + latestToolCall = { ...latestToolCall, isDone: true }; + } + } catch { } + } + + const inboundTool = params.toolCall; + + if ((latestToolCall && !latestToolCall.isDone) && !(isValidToolCall(inboundTool) && inboundTool.isDone)) { + onFinalMessage({ + fullText: baseTextForUi.trim(), + fullReasoning: finalReasoning, + anthropicReasoning: params.anthropicReasoning, + toolCall: undefined, + plan, + // preserve original token usage info + tokenUsage: (params as any).tokenUsage, + }); + return; + } + + const { beforeText, call } = parseToolFromText(baseTextForUi); + let uiText = beforeText.trim(); + let uiReasoning = finalReasoning; + if (chatMode && chatMode !== 'normal') { + const parsedR = parseToolFromText(finalReasoning); + if (parsedR.call && (parsedR.source === 'tool_call' || isBlockToolCall(parsedR.beforeText))) { + latestToolCall = mergeToolCall(latestToolCall, parsedR.call); + } + } + + if (call?.isDone) { + uiText = stripToolXmlOrOpenTailOnce(uiText, call.name); + uiReasoning = stripToolXmlOnce(uiReasoning, call.name); + uiText = stripFakeResultTail(uiText); + } + + uiText = stripThinkTagsFromText(uiText); + + // Parse Plan again if needed? + // We already parsed from baseTextForUi which is the source for uiText (via parseToolFromText). + // parseToolFromText returns a slice. If we stripped plan from baseTextForUi, it's gone from uiText too. + // So we don't need to parse again. + + const finalTool = + (isValidToolCall(inboundTool) && inboundTool.isDone ? inboundTool : undefined) || + (isValidToolCall(latestToolCall) && latestToolCall.isDone ? latestToolCall : undefined) || + (isValidToolCall(call) && call.isDone ? call : undefined); + + if (finalTool?.isDone) { + uiText = stripToolCallWrapperOnce(uiText); + uiText = stripToolXmlOrOpenTailOnce(uiText, finalTool.name); + uiReasoning = stripToolCallWrapperOnce(uiReasoning); + uiReasoning = stripToolXmlOnce(uiReasoning, finalTool.name); + } + + onFinalMessage({ + fullText: uiText, + fullReasoning: uiReasoning, + anthropicReasoning: params.anthropicReasoning, + toolCall: finalTool, + plan, + // preserve original token usage info + tokenUsage: (params as any).tokenUsage, + }); + }; + + return { newOnText, newOnFinalMessage }; +}; + +export const extractReasoningWrapper = ( + onText: OnText, + onFinalMessage: OnFinalMessage, + thinkTagsInit: [string, string] | null, + chatMode: ChatMode | null +): { newOnText: OnText; newOnFinalMessage: OnFinalMessage } => { + return extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + thinkTagsInit, + chatMode, + { enableXmlToolParsing: false } + ); +}; + + +// trim all whitespace up until the first newline, and all whitespace up until the last newline +const trimBeforeAndAfterNewLines = (s: string) => { + if (!s) return s; + const firstNewLineIndex = s.indexOf('\n'); + if (firstNewLineIndex !== -1 && s.substring(0, firstNewLineIndex).trim() === '') { + s = s.substring(firstNewLineIndex + 1, Infinity) + } + const lastNewLineIndex = s.lastIndexOf('\n'); + if (lastNewLineIndex !== -1 && s.substring(lastNewLineIndex + 1, Infinity).trim() === '') { + s = s.substring(0, lastNewLineIndex) + } + return s +} diff --git a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.impl.ts b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.impl.ts new file mode 100644 index 00000000000..31f4c426cbd --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.impl.ts @@ -0,0 +1,2862 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { AdditionalToolInfo, AnthropicLLMChatMessage, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, ModelListParams, OllamaModelResponse, OnError, OnFinalMessage, OnText, RawToolCallObj, RawToolCallObjKnown, RawToolCallObjDynamic, RawToolParamsObj, DynamicRequestConfig, RequestParamsConfig, ProviderRouting, LLMTokenUsage } from '../../common/sendLLMMessageTypes.js'; +import { ChatMode, specialToolFormat, displayInfoOfProviderName, ModelSelectionOptions, OverridesOfModel, ProviderName, SettingsOfProvider } from '../../common/voidSettingsTypes.js'; +import { getSendableReasoningInfo, getModelCapabilities, getProviderCapabilities, getReservedOutputTokenSpace } from '../../common/modelInference.js'; + +import { extractReasoningAndXMLToolsWrapper, extractReasoningWrapper } from './extractGrammar.js'; +import { availableTools, InternalToolInfo, isAToolName, voidTools } from '../../common/toolsRegistry.js'; +import { ToolName, ToolParamName } from '../../common/toolsServiceTypes.js' +import { generateUuid } from '../../../../base/common/uuid.js'; +import { toOpenAICompatibleTool, toAnthropicTool, toGeminiTool } from './toolSchemaConversion.js'; +import { ILogService } from '../../../log/common/log.js'; +import { INotificationService, Severity, NotificationPriority, NeverShowAgainScope } from '../../../notification/common/notification.js'; + +let getSendableReasoningInfoImpl = getSendableReasoningInfo; + +const XML_TOOL_FORMAT_CORRECTION_PROMPT = [ + 'Your previous response contained an invalid XML tool call that could not be parsed.', + 'Reply in English only.', + 'Respond again now and strictly follow the XML tool-call format defined in the system instructions.', + 'Use direct tool tags only: value.', + 'Do not use attributes for tool parameters.', + 'Do not wrap tool calls in JSON or wrappers unless the system instructions explicitly require it.', + 'If no tool call is needed, reply with plain text and do not include XML-like tool tags.', +].join(' '); + +type ChatCompletionCreateParamsStreaming = import('openai/resources/chat/completions').ChatCompletionCreateParamsStreaming; +//type ChatCompletionChunk = import('openai/resources/chat/completions').ChatCompletionChunk; +//type OpenAIStream = import('openai/streaming').Stream; +type OpenAIChatCompletionTool = import('openai/resources/chat/completions/completions.js').ChatCompletionTool; +type OpenAIClient = import('openai').OpenAI; +type OpenAIClientOptions = import('openai').ClientOptions; +type GoogleGeminiTool = import('@google/genai').Tool; +type GoogleThinkingConfig = import('@google/genai').ThinkingConfig; +type AnthropicToolUseBlock = import('@anthropic-ai/sdk').Anthropic.ToolUseBlock; + +let openAIModule: (typeof import('openai')) | undefined; +const getOpenAIModule = async () => openAIModule ??= await import('openai'); +const getOpenAIModuleSync = () => openAIModule; + +let anthropicModule: (typeof import('@anthropic-ai/sdk')) | undefined; +const getAnthropicModule = async () => anthropicModule ??= await import('@anthropic-ai/sdk'); + +let mistralCoreModule: (typeof import('@mistralai/mistralai/core.js')) | undefined; +const getMistralCoreModule = async () => mistralCoreModule ??= await import('@mistralai/mistralai/core.js'); + +let mistralFimModule: (typeof import('@mistralai/mistralai/funcs/fimComplete.js')) | undefined; +const getMistralFimModule = async () => mistralFimModule ??= await import('@mistralai/mistralai/funcs/fimComplete.js'); + +let googleGenAIModule: (typeof import('@google/genai')) | undefined; +const getGoogleGenAIModule = async () => googleGenAIModule ??= await import('@google/genai'); + +let ollamaModule: (typeof import('ollama')) | undefined; +const getOllamaModule = async () => ollamaModule ??= await import('ollama'); + +const normalizeHeaders = (h: any): Record => { + try { + if (!h) return {}; + // WHATWG Headers + if (typeof h.entries === 'function') return Object.fromEntries(Array.from(h.entries())); + // [ [k,v], ... ] + if (Array.isArray(h)) return Object.fromEntries(h); + // plain object + if (typeof h === 'object') return { ...h }; + return { value: String(h) }; + } catch { + return {}; + } +}; + +let _fetchDebugInstalled = false; + +const _safeJson = (v: unknown): string => { + try { return JSON.stringify(v, null, 2); } catch { return String(v); } +}; + +const _logDebug = (logService: ILogService | undefined, msg: string, data?: unknown) => { + logService?.debug?.(`[LLM][debug] ${msg}${data === undefined ? '' : `\n${_safeJson(data)}`}`); +}; + +const _logWarn = (logService: ILogService | undefined, msg: string, data?: unknown) => { + logService?.warn?.(`[LLM][warn] ${msg}${data === undefined ? '' : `\n${_safeJson(data)}`}`); +}; + +const _isPlainObject = (v: unknown): v is Record => { + return !!v && typeof v === 'object' && !Array.isArray(v); +}; + +const _redactHeaderValue = (key: string, value: string): string => { + const k = key.toLowerCase(); + if (k === 'authorization') { + // "Bearer xxx" -> "Bearer ***" + if (value.startsWith('Bearer ')) return 'Bearer ***'; + return '***'; + } + return '***'; +}; + +const _redactHeaders = (headers: Record): Record => { + const out: Record = { ...headers }; + const sensitive = new Set([ + 'authorization', + 'x-api-key', + 'api-key', + 'x-goog-api-key', + 'proxy-authorization', + ]); + + for (const [k, v] of Object.entries(out)) { + if (sensitive.has(k.toLowerCase())) { + out[k] = _redactHeaderValue(k, String(v ?? '')); + } + } + return out; +}; + +const _shouldRedactKey = (key: string): boolean => { + return /(api[-_]?key|authorization|token$|secret|password|session)/i.test(key); +}; + +const _deepRedact = (v: unknown): unknown => { + if (Array.isArray(v)) return v.map(_deepRedact); + if (!_isPlainObject(v)) return v; + + const out: Record = {}; + for (const [k, val] of Object.entries(v)) { + if (_shouldRedactKey(k)) out[k] = '***'; + else out[k] = _deepRedact(val); + } + return out; +}; + +/** + * Optional debugging helper. Call from electron-main startup when log level is Debug/Trace. + * Redacts API keys/tokens from headers and JSON bodies. + */ +export function installDebugFetchLogging(logService: ILogService): void { + if (_fetchDebugInstalled) return; + _fetchDebugInstalled = true; + + try { + const desc = Object.getOwnPropertyDescriptor(globalThis, 'fetch'); + if (!desc || desc.writable) { + const orig = globalThis.fetch; + globalThis.fetch = async (input: any, init?: any) => { + let url = ''; + let method = 'GET'; + + try { + url = typeof input === 'string' ? input : (input?.url ?? String(input)); + method = init?.method ?? 'GET'; + + const hdrsRaw = normalizeHeaders(init?.headers ?? input?.headers); + const hdrs = _redactHeaders(hdrsRaw); + + _logDebug(logService, `HTTP Request ${method} ${url}`); + _logDebug(logService, `HTTP Headers`, hdrs); + + if (init?.body) { + const bodyStr = (typeof init.body === 'string') ? init.body : ''; + if (bodyStr) { + try { + const parsed = JSON.parse(bodyStr); + _logDebug(logService, `HTTP Body (json, redacted)`, _deepRedact(parsed)); + } catch { + _logDebug(logService, `HTTP Body (non-json)`, '[omitted]'); + } + } else { + _logDebug(logService, `HTTP Body`, '[non-string body omitted]'); + } + } + } catch { + // ignore + } + + const resp = await (orig as any)(input, init); + + try { + const respHdrsRaw = normalizeHeaders(resp?.headers); + const respHdrs: Record = { ...respHdrsRaw }; + + // redact response sensitive headers too + const sensitive = new Set([ + 'authorization', + 'x-api-key', + 'api-key', + 'x-goog-api-key', + 'proxy-authorization', + 'set-cookie', + ]); + for (const [k, v] of Object.entries(respHdrs)) { + if (sensitive.has(k.toLowerCase())) respHdrs[k] = '***'; + else respHdrs[k] = String(v); + } + + _logDebug(logService, `HTTP Response ${resp?.status} ${resp?.statusText ?? ''} for ${method} ${url}`); + _logDebug(logService, `HTTP Response Headers`, respHdrs); + } catch { + // ignore + } + + return resp; + }; + } + } catch { + // ignore + } +} + +const extractBearer = (headers: Record): string => { + const auth = headers.Authorization ?? headers.authorization ?? ''; + return auth.startsWith('Bearer ') ? auth.slice('Bearer '.length) : auth; +}; + + +type InternalCommonMessageParams = { + onText: OnText; + onFinalMessage: OnFinalMessage; + onError: OnError; + providerName: ProviderName; + settingsOfProvider: SettingsOfProvider; + modelSelectionOptions: ModelSelectionOptions | undefined; + overridesOfModel: OverridesOfModel | undefined; + modelName: string; + _setAborter: (aborter: () => void) => void; + dynamicRequestConfig?: DynamicRequestConfig; + requestParams?: RequestParamsConfig; + providerRouting?: ProviderRouting; + logService?: ILogService; + + // NEW (optional): pass from caller if you have it (workbench/renderer side usually) + notificationService?: INotificationService; + notifyOnTruncation?: boolean; +} + +type SendChatParams_Internal = InternalCommonMessageParams & { + messages: LLMChatMessage[]; + separateSystemMessage: string | undefined; + tool_choice?: { type: 'function', function: { name: string } } | 'none' | 'auto' | 'required'; + chatMode: ChatMode | null; + additionalTools?: AdditionalToolInfo[]; + disabledStaticTools?: string[]; + disabledDynamicTools?: string[]; +} +type SendFIMParams_Internal = InternalCommonMessageParams & { messages: LLMFIMMessage; separateSystemMessage: string | undefined; } +export type ListParams_Internal = ModelListParams + + +const invalidApiKeyMessage = (providerName: ProviderName) => `Invalid ${displayInfoOfProviderName(providerName).title} API key.` + +const isAbortError = (e: any) => { + const msg = String(e?.message || '').toLowerCase(); + return e?.name === 'AbortError' || msg.includes('abort') || msg.includes('canceled'); +}; + +// Try to detect completeness of a streamed JSON (counts braces and handles strings/escapes) +const tryParseJsonWhenComplete = (s: string): { ok: boolean; value?: any } => { + let inStr = false, escaped = false, depth = 0, started = false; + for (let i = 0; i < s.length; i++) { + const ch = s[i]; + if (inStr) { + if (escaped) escaped = false; + else if (ch === '\\') escaped = true; + else if (ch === '"') inStr = false; + } else { + if (ch === '"') inStr = true; + else if (ch === '{') { depth++; started = true; } + else if (ch === '}') { if (depth > 0) depth--; } + } + } + if (started && depth === 0 && !inStr) { + try { + const json = JSON.parse(s.trim()); + return { ok: true, value: json }; + } catch { /* not ready */ } + } + return { ok: false }; +}; + +const getCurrentMaxTokens = (opts: any) => { + const v1 = typeof opts?.max_completion_tokens === 'number' ? opts.max_completion_tokens : undefined; + const v2 = typeof opts?.max_tokens === 'number' ? opts.max_tokens : undefined; + const result = (v1 ?? v2 ?? 1024); + return result; +}; + +const mapOpenAIUsageToLLMTokenUsage = (usage: any): LLMTokenUsage | undefined => { + if (!usage || typeof usage !== 'object') return undefined; + const promptDetails = (usage as any).prompt_tokens_details ?? (usage as any).promptTokensDetails ?? {}; + return { + input: Number((usage as any).prompt_tokens ?? (usage as any).input_tokens ?? 0) || 0, + cacheCreation: Number(promptDetails.cached_creation_tokens ?? promptDetails.cached_tokens ?? 0) || 0, + cacheRead: Number(promptDetails.cached_read_tokens ?? 0) || 0, + output: Number((usage as any).completion_tokens ?? (usage as any).output_tokens ?? 0) || 0, + }; +}; + +const mapAnthropicUsageToLLMTokenUsage = (usage: any): LLMTokenUsage | undefined => { + if (!usage || typeof usage !== 'object') return undefined; + return { + input: Number((usage as any).input_tokens ?? 0) || 0, + cacheCreation: Number((usage as any).cache_creation_input_tokens ?? 0) || 0, + cacheRead: Number((usage as any).cache_read_input_tokens ?? 0) || 0, + output: Number((usage as any).output_tokens ?? 0) || 0, + }; +}; + +const mapGeminiUsageToLLMTokenUsage = (usage: any): LLMTokenUsage | undefined => { + if (!usage || typeof usage !== 'object') return undefined; + const input = Number((usage as any).promptTokenCount ?? 0) || 0; + // Gemini reports candidatesTokenCount (output) and totalTokenCount; prefer explicit candidatesTokenCount. + let output = Number((usage as any).candidatesTokenCount ?? 0) || 0; + if (!output && (usage as any).totalTokenCount !== null) { + const total = Number((usage as any).totalTokenCount) || 0; + if (total && total >= input) output = total - input; + } + return { + input, + cacheCreation: 0, + cacheRead: 0, + output, + }; +}; + +/** + * Validates LLMTokenUsage to ensure all fields are valid numbers. + * Returns undefined if usage is invalid or all zeros. + */ +function validateLLMTokenUsage(usage: LLMTokenUsage | undefined, logService?: ILogService): LLMTokenUsage | undefined { + if (!usage || typeof usage !== 'object') { + return undefined; + } + + const validated: LLMTokenUsage = { + input: safeNumber(usage.input, 'input', logService), + output: safeNumber(usage.output, 'output', logService), + cacheCreation: safeNumber(usage.cacheCreation, 'cacheCreation', logService), + cacheRead: safeNumber(usage.cacheRead, 'cacheRead', logService) + }; + + if ( + validated.input === 0 && validated.output === 0 && + validated.cacheCreation === 0 && validated.cacheRead === 0 + ) { + _logWarn(logService, 'tokenUsage normalized to all zeros, treating as invalid', usage); + return undefined; + } + + return validated; +} + +function safeNumber(value: unknown, fieldName: string, logService?: ILogService): number { + if (typeof value === 'number' && !isNaN(value) && isFinite(value) && value >= 0) { + return value; + } + if (value !== undefined && value !== null && value !== 0) { + _logWarn(logService, `Invalid ${fieldName} value (${typeof value}); defaulting to 0`, value); + } + return 0; +} + +const _escapeRegex = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + +const stripMarkdownCodeForXmlToolDetection = (s: string): string => { + if (!s) return s; + return s + .replace(/```[\s\S]*?```/g, ' ') + .replace(/~~~[\s\S]*?~~~/g, ' ') + .replace(/`[^`\n]*`/g, ' '); +}; + +const hasLikelyUnparsedXmlToolCall = ({ + fullText, + fullReasoning, + toolNames, +}: { + fullText: string; + fullReasoning: string; + toolNames?: readonly string[]; +}): boolean => { + const merged = `${fullText || ''}\n${fullReasoning || ''}`; + if (!merged || merged.indexOf('<') === -1) return false; + + const cleaned = stripMarkdownCodeForXmlToolDetection(merged); + if (!cleaned || cleaned.indexOf('<') === -1) return false; + + const namesAlt = (toolNames || []) + .map(n => String(n || '').trim()) + .filter(Boolean) + .map(_escapeRegex) + .join('|'); + const extra = namesAlt ? `|${namesAlt}` : ''; + const re = new RegExp(`<\\s*(?:\\/\\s*)?(?:tool_call|function|parameter${extra})\\b`, 'i'); + return re.test(cleaned); +}; + +const hasLikelyToolParamAttributes = ({ + fullText, + fullReasoning, + toolNames, +}: { + fullText: string; + fullReasoning: string; + toolNames?: readonly string[]; +}): boolean => { + const names = (toolNames || []) + .map(n => String(n || '').trim()) + .filter(Boolean) + .map(_escapeRegex); + if (names.length === 0) return false; + + const merged = `${fullText || ''}\n${fullReasoning || ''}`; + if (!merged || merged.indexOf('<') === -1) return false; + const cleaned = stripMarkdownCodeForXmlToolDetection(merged); + if (!cleaned || cleaned.indexOf('<') === -1) return false; + + const re = new RegExp(`<\\s*(?:${names.join('|')})\\b[^>]*\\s[a-zA-Z_][a-zA-Z0-9_\\-]*\\s*=`, 'i'); + return re.test(cleaned); +}; + +type LengthRetryPolicy = { + enabled?: boolean; + maxAttempts?: number; + maxTokensCap?: number; + increaseStrategy?: 'add' | 'multiply'; + step?: number; + factor?: number; +}; + +const bumpMaxTokens = (mutableOptions: any, policy: LengthRetryPolicy) => { + const prev = getCurrentMaxTokens(mutableOptions); + const cap = policy.maxTokensCap ?? 8192; + const next = (policy.increaseStrategy ?? 'add') === 'add' + ? prev + (policy.step ?? 500) + : Math.ceil(prev * (policy.factor ?? 1.5)); + const newVal = Math.min(next, cap); + if (typeof mutableOptions.max_completion_tokens === 'number') mutableOptions.max_completion_tokens = newVal; + else mutableOptions.max_tokens = newVal; +}; + +const isAsyncIterable = (x: any): x is AsyncIterable => + x && typeof x[Symbol.asyncIterator] === 'function'; + +const newOpenAICompatibleSDK = async ({ settingsOfProvider, providerName, includeInPayload }: { settingsOfProvider: SettingsOfProvider, providerName: ProviderName, includeInPayload?: { [s: string]: any } }) => { + const { default: OpenAI } = await getOpenAIModule(); + const commonPayloadOpts: OpenAIClientOptions = { + dangerouslyAllowBrowser: true, + ...includeInPayload, + }; + + // Generic dynamic provider path: use endpoint/apiKey/auth/additionalHeaders from custom provider settings + const thisConfig: any = (settingsOfProvider as any)[providerName] || {}; + const endpoint = (thisConfig.endpoint ?? '').toString(); + if (!endpoint) throw new Error(`OpenAI-compatible endpoint is not configured for provider "${providerName}".`); + + const apiKey = (thisConfig.apiKey ?? '').toString(); + const additionalHeaders: Record = { ...(thisConfig.additionalHeaders || {}) }; + const authHeaderName: string = (thisConfig.auth?.header || 'Authorization'); + const authFormat: 'Bearer' | 'direct' = (thisConfig.auth?.format || 'Bearer'); + + // Decide whether to rely on OpenAI client apiKey (adds Authorization: Bearer) or custom header + let apiKeyForOpenAI = 'noop'; + if (apiKey) { + if (authHeaderName.toLowerCase() === 'authorization' && authFormat === 'Bearer') { + apiKeyForOpenAI = apiKey; + } else { + additionalHeaders[authHeaderName] = authFormat === 'Bearer' ? `Bearer ${apiKey}` : apiKey; + } + } + + return new OpenAI({ baseURL: endpoint, apiKey: apiKeyForOpenAI, defaultHeaders: additionalHeaders, ...commonPayloadOpts }); +} + +const attachCacheControlToTextMessage = (content: any): any => { + if (!content) return content; + // String -> single text part with cache_control + if (typeof content === 'string') { + return [{ type: 'text', text: content, cache_control: { type: 'ephemeral' } }]; + } + // Array of parts -> mark first text part without cache_control + if (Array.isArray(content)) { + const parts = content.slice(); + for (let i = 0; i < parts.length; i++) { + const p = parts[i]; + if (p && typeof p === 'object' && p.type === 'text' && !p.cache_control) { + parts[i] = { ...p, cache_control: { type: 'ephemeral' } }; + return parts; + } + } + return parts; + } + return content; +}; + +const applyCacheControlOpenAIStyle = (messages: any[], enabled: boolean): any[] => { + if (!enabled || !Array.isArray(messages) || messages.length === 0) return messages; + // Heuristic: mark at most the first 2 textual messages (e.g. system + first user) + let remaining = 2; + const out = messages.map((m) => m); + for (let i = 0; i < out.length && remaining > 0; i++) { + const msg = out[i]; + if (!msg || typeof msg !== 'object') continue; + if (msg.role !== 'system' && msg.role !== 'developer' && msg.role !== 'user') continue; + if (!msg.content) continue; + const newContent = attachCacheControlToTextMessage(msg.content); + if (newContent === msg.content) continue; + out[i] = { ...msg, content: newContent }; + remaining--; + } + return out; +}; + +const _sendOpenAICompatibleFIM = async (params: SendFIMParams_Internal) => { + const { + messages: { prefix, suffix, stopTokens }, + onFinalMessage, + onError, + settingsOfProvider, + modelName: modelName_, + providerName, + overridesOfModel, + dynamicRequestConfig, + } = params; + + const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_, overridesOfModel); + if (!supportsFIM) { + if (modelName === modelName_) + onError({ message: `Model ${modelName} does not support FIM.`, fullError: null }); + else + onError({ message: `Model ${modelName_} (${modelName}) does not support FIM.`, fullError: null }); + return; + } + + let openai: OpenAIClient; + let modelForRequest = modelName; + + if (dynamicRequestConfig?.apiStyle === 'openai-compatible') { + const { default: OpenAI } = await getOpenAIModule(); + const token = extractBearer(dynamicRequestConfig.headers) || 'noop'; + + const headersNoAuth: Record = { ...dynamicRequestConfig.headers }; + delete (headersNoAuth as any).Authorization; + delete (headersNoAuth as any).authorization; + + openai = new OpenAI({ + baseURL: dynamicRequestConfig.endpoint, + apiKey: token, + defaultHeaders: headersNoAuth, + dangerouslyAllowBrowser: true, + maxRetries: 0, + }); + + + modelForRequest = modelName; + } else { + openai = await newOpenAICompatibleSDK({ providerName, settingsOfProvider }); + } + + + const basePayload: any = { + model: modelForRequest, + prompt: prefix, + suffix: suffix, + stop: stopTokens, + max_tokens: 300, + }; + + // Inject OpenRouter provider routing when talking to OpenRouter endpoint + if (params.providerRouting && dynamicRequestConfig?.endpoint?.includes('openrouter.ai')) { + basePayload.provider = params.providerRouting; + } + + return openai.completions + .create(basePayload) + .then(async response => { + const fullText = response.choices[0]?.text; + const usage = validateLLMTokenUsage(mapOpenAIUsageToLLMTokenUsage((response as any)?.usage), params.logService); + onFinalMessage({ + fullText, + fullReasoning: '', + anthropicReasoning: null, + ...(usage ? { tokenUsage: usage } : {}), + }); + }) + .catch(async (error: any) => { + const { APIError } = await getOpenAIModule(); + if (error instanceof APIError && error.status === 401) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + } else { + onError({ message: error + '', fullError: error }); + } + }); +}; + +/** + * Get static tools for a given chat mode + */ +const normalizeToolNameSet = (names?: readonly string[]): Set => { + if (!Array.isArray(names)) return new Set(); + return new Set(names.map(v => String(v ?? '').trim()).filter(Boolean)); +}; + +const filterDynamicTools = ( + dynamicTools?: AdditionalToolInfo[], + disabledDynamicTools?: readonly string[] +): AdditionalToolInfo[] | undefined => { + if (!dynamicTools?.length) return dynamicTools; + const disabledSet = normalizeToolNameSet(disabledDynamicTools); + if (disabledSet.size === 0) return dynamicTools; + const filtered = dynamicTools.filter(tool => !disabledSet.has(String(tool?.name ?? '').trim())); + return filtered.length ? filtered : undefined; +}; + +const getStaticTools = (chatMode: ChatMode, disabledStaticTools?: readonly string[]): InternalToolInfo[] => { + const allowedUnknown = availableTools(chatMode); + if (!Array.isArray(allowedUnknown) || allowedUnknown.length === 0) return []; + + const staticTools: InternalToolInfo[] = []; + for (const tool of allowedUnknown) { + if (!tool || typeof tool !== 'object') continue; + const maybeTool = tool as Partial; + if (typeof maybeTool.name !== 'string' || typeof maybeTool.description !== 'string') continue; + staticTools.push(maybeTool as InternalToolInfo); + } + if (staticTools.length === 0) return []; + + const disabledSet = normalizeToolNameSet(disabledStaticTools); + if (disabledSet.size === 0) return staticTools; + return staticTools.filter(tool => !disabledSet.has(String(tool.name ?? '').trim())); +}; + +/** + * Merge static and dynamic tools, with dynamic tools taking precedence + */ +const mergeTools = (staticTools: InternalToolInfo[], dynamicTools?: AdditionalToolInfo[]): (InternalToolInfo | AdditionalToolInfo)[] => { + const allTools: (InternalToolInfo | AdditionalToolInfo)[] = [...staticTools]; + + if (dynamicTools) { + // Add dynamic tools, overriding static ones with same name + for (const dynamicTool of dynamicTools) { + const staticIndex = allTools.findIndex(t => t.name === dynamicTool.name); + if (staticIndex >= 0) { + allTools[staticIndex] = dynamicTool; + } else { + allTools.push(dynamicTool); + } + } + } + + return allTools; +} + +const openAITools = ( + chatMode: ChatMode, + additionalTools?: AdditionalToolInfo[], + logService?: ILogService, + disabledStaticTools?: readonly string[], + disabledDynamicTools?: readonly string[], +): OpenAIChatCompletionTool[] | null => { + + const staticTools = getStaticTools(chatMode, disabledStaticTools); + const dynamicTools = filterDynamicTools(additionalTools, disabledDynamicTools); + const allTools = mergeTools(staticTools, dynamicTools); + if (allTools.length === 0) { + return null; + } + + const convertedTools = allTools.map(toolInfo => toOpenAICompatibleTool(toolInfo, logService)); + return convertedTools.length ? convertedTools : null; +}; + +function snakeToCamel(s: string): string { + return s + .split('_') + .filter(Boolean) + .map((chunk, i) => + i === 0 + ? chunk + : chunk[0].toUpperCase() + chunk.slice(1) + ) + .join(''); +} + +type ToolInfoUnion = InternalToolInfo | AdditionalToolInfo; + +function camelToSnake(s: string): string { + return s.replace(/[A-Z]/g, (m) => '_' + m.toLowerCase()); +} + +const buildToolDefsMap = ( + staticTools: InternalToolInfo[], + dynamicTools?: AdditionalToolInfo[] +): Map => { + const all = mergeTools(staticTools, dynamicTools); + const map = new Map(); + for (const t of all) map.set(t.name, t as ToolInfoUnion); + return map; +}; + +const rawToolCallObjOf = ( + name: string, + toolParamsStr: string, + id: string, + toolDefsMap?: ReadonlyMap +): RawToolCallObj | null => { + if (!name || !String(name).trim()) return null; + + let input: unknown; + try { + input = toolParamsStr ? JSON.parse(toolParamsStr) : {}; + } catch { + return null; + } + if (!input || typeof input !== 'object') return null; + + const toolInfo = toolDefsMap?.get(name); + if (toolDefsMap && !toolInfo) { + return null; + } + + + if (!toolInfo && isAToolName(name)) { + const rawParams: RawToolParamsObj = {} as RawToolParamsObj; + for (const snakeParam in voidTools[name].params) { + const camelParam = snakeToCamel(snakeParam); + const snakeAlt = camelToSnake(snakeParam); + const val = + (input as Record)[snakeParam] ?? + (input as Record)[camelParam] ?? + (input as Record)[snakeAlt]; + (rawParams as unknown as Record)[snakeParam] = val; + } + return { + id, + name: name as ToolName, + rawParams, + doneParams: Object.keys(rawParams) as ToolParamName[], + isDone: true, + } as RawToolCallObj; + } + + + if (toolInfo && (toolInfo as ToolInfoUnion & { params?: Record }).params) { + const params = (toolInfo as ToolInfoUnion & { params?: Record }).params || {}; + const rawParams: Record = {}; + for (const paramName of Object.keys(params)) { + const camel = snakeToCamel(paramName); + const snake = camelToSnake(paramName); + const val = + (input as Record)[paramName] ?? + (input as Record)[camel] ?? + (input as Record)[snake]; + if (val !== undefined) rawParams[paramName] = val; + } + return { + id, + name: name as ToolName, + rawParams: rawParams as RawToolParamsObj, + doneParams: Object.keys(rawParams) as string[], + isDone: true, + } as RawToolCallObjKnown; + } + + + const rawParams = input as Record; + return { + id, + name: name as string, + rawParams, + doneParams: Object.keys(rawParams), + isDone: true, + } as RawToolCallObjDynamic; +}; + +// ------------ OPENAI-COMPATIBLE ------------ +export interface RunStreamParams { + openai: OpenAIClient + options: ChatCompletionCreateParamsStreaming + onText: OnText + onFinalMessage: OnFinalMessage + onError: OnError + _setAborter: (aborter: () => void) => void + nameOfReasoningFieldInDelta?: string + providerName: any + + // tool definitions map (static + dynamic) + toolDefsMap?: ReadonlyMap + logService?: ILogService + + stopOnFirstToolCall?: boolean // default: true + allowedToolNames?: string[] // default: undefined (no filter) + emitToolCallProgress?: boolean // default: true + timeoutMs?: number // default: undefined (no timeout) + lengthRetryPolicy?: LengthRetryPolicy // default: { enabled: true, maxAttempts: 2, ... } + + // NEW (optional) + notificationService?: INotificationService + notifyOnTruncation?: boolean +} + + +export async function runStream({ + openai, + options, + onText, + onFinalMessage, + onError, + _setAborter, + nameOfReasoningFieldInDelta, + providerName, + toolDefsMap, + logService, + stopOnFirstToolCall = true, + allowedToolNames, + emitToolCallProgress = true, + timeoutMs, + lengthRetryPolicy = { enabled: true, maxAttempts: 2, maxTokensCap: 16384, increaseStrategy: 'add', step: 2000, factor: 1.5 }, + notificationService, + notifyOnTruncation = true, +}: RunStreamParams): Promise { + + const policy: Required = { + enabled: lengthRetryPolicy?.enabled !== false, + maxAttempts: Math.max(1, lengthRetryPolicy?.maxAttempts ?? 2), + maxTokensCap: lengthRetryPolicy?.maxTokensCap ?? 8192, + increaseStrategy: lengthRetryPolicy?.increaseStrategy ?? 'add', + step: lengthRetryPolicy?.step ?? 500, + factor: lengthRetryPolicy?.factor ?? 1.5, + }; + + const sleep = (ms: number) => new Promise(r => setTimeout(r, ms)); + + const openAIExports = getOpenAIModuleSync(); + const OpenAIApiError = openAIExports?.APIError; + + let everHadAnyData = false; + let currentOptions: any = { ...options, stream: true }; + let lastTokenUsage: LLMTokenUsage | undefined; + + // ---------------- [LLM][debug][runStream] helpers ---------------- + const __hasDebug = !!logService && typeof logService.debug === 'function'; + + const __safeJson = (v: unknown, maxLen = 6000): string => { + try { + const seen = new WeakSet(); + const s = JSON.stringify( + v, + (_k, val) => { + if (typeof val === 'bigint') return val.toString(); + if (val && typeof val === 'object') { + if (seen.has(val as any)) return '[Circular]'; + seen.add(val as any); + } + return val; + }, + 2 + ); + if (typeof s === 'string' && s.length > maxLen) { + return s.slice(0, maxLen) + `…(+${s.length - maxLen})`; + } + return s; + } catch (e) { + try { return String(v); } catch { return `[unstringifiable: ${String(e)}]`; } + } + }; + + const __dbg = (msg: string, data?: unknown) => { + if (!__hasDebug) return; + try { + logService?.debug?.(`[LLM][debug][runStream] ${msg}${data === undefined ? '' : `\n${__safeJson(data)}`}`); + } catch { + // ignore + } + }; + + const __isHeavyDebugEnabled = __hasDebug; + + const __toolNames = (() => { + try { return toolDefsMap ? Array.from(toolDefsMap.keys()) : []; } catch { return []; } + })(); + + const __escapeRe = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + + const __TOOL_TAG_RE: RegExp = (() => { + const names = __toolNames.map(__escapeRe).filter(Boolean); + + // - / + + const alts = names.length ? `|${names.join('|')}` : ''; + return new RegExp(`<\\s*(?:\\/\\s*)?(?:tool_call\\b${alts})`, 'i'); + })(); + + const __previewAroundFirstMatch = (s: string, max = 260): string => { + try { + if (!s) return ''; + const m = s.match(__TOOL_TAG_RE); + if (!m || typeof m.index !== 'number') { + return s.length <= max ? s : (s.slice(0, max) + `…(+${s.length - max})`); + } + const i = m.index; + const from = Math.max(0, i - Math.floor(max / 2)); + const to = Math.min(s.length, from + max); + const chunk = s.slice(from, to); + return (from > 0 ? '…' : '') + chunk + (to < s.length ? '…' : ''); + } catch { + return ''; + } + }; + // ---------------------------------------------------------------- + + let didNotifyTruncation = false; + + const notifyTruncationOnce = (info: { + kind: 'length' | 'timeout'; + model: string; + provider: any; + maxTokens: number; + attempt: number; + maxAttempts: number; + textLen: number; + reasoningLen: number; + }) => { + if (!notifyOnTruncation) return; + if (didNotifyTruncation) return; + if (!notificationService) return; + + didNotifyTruncation = true; + + const isReasoningOnly = info.textLen === 0 && info.reasoningLen > 0; + const why = info.kind === 'timeout' ? 'timeout' : 'token limit (finish_reason=length)'; + + const msg = + `LLM response was truncated: ${why}.\n` + + `Provider: ${String(info.provider)}. LLM: ${String(info.model)}.\n` + + `max_tokens/max_completion_tokens: ${info.maxTokens}. Attempt: ${info.attempt}/${info.maxAttempts}.\n` + + `Received: text=${info.textLen}, reasoning=${info.reasoningLen}.\n` + + (isReasoningOnly + ? `It appears the entire budget was consumed by reasoning, and the model didn't have time to produce a final answer.\n` + : ``) + + `How to fix: increase max_tokens/max_completion_tokens (or maxTokensCap for retries), or reduce reasoning effort / disable reasoning.`; + + try { + notificationService.notify({ + id: 'void.llm.outputTruncated', + severity: Severity.Warning, + message: msg, + priority: NotificationPriority.DEFAULT, + neverShowAgain: { + id: 'void.llm.outputTruncated', + isSecondary: true, + scope: NeverShowAgainScope.PROFILE + } + }); + } catch { + // best-effort + } + }; + + const normalizeToolIndex = (idx: unknown): number => { + if (typeof idx === 'number' && isFinite(idx)) return idx; + if (typeof idx === 'string' && idx.trim() !== '' && isFinite(Number(idx))) return Number(idx); + return 0; + }; + + const coerceArgsToString = (args: unknown): string => { + if (typeof args === 'string') return args; + if (args && typeof args === 'object') { + try { return JSON.stringify(args); } catch { return ''; } + } + return ''; + }; + + const getDeltaContentValue = (delta: any): unknown => { + if (delta?.content !== undefined) return delta.content; + if (delta?.contentParts !== undefined) return delta.contentParts; + if (delta?.content_parts !== undefined) return delta.content_parts; + if (delta?.message?.content !== undefined) return delta.message.content; + return undefined; + }; + + const getDeltaToolCallsValue = (delta: any): unknown => { + return delta?.tool_calls ?? delta?.toolCalls; + }; + + const coerceContentToString = (content: unknown): string => { + if (typeof content === 'string') return content; + if (!content) return ''; + + if (Array.isArray(content)) { + let out = ''; + for (const part of content) { + if (typeof part === 'string') { out += part; continue; } + if (!part || typeof part !== 'object') continue; + const p: any = part; + + if (typeof p.text === 'string') { out += p.text; continue; } + if (p.text && typeof p.text === 'object' && typeof p.text.value === 'string') { out += p.text.value; continue; } + + if (typeof p.content === 'string') { out += p.content; continue; } + if (typeof p.value === 'string') { out += p.value; continue; } + } + return out; + } + + if (typeof content === 'object') { + const c: any = content; + if (typeof c.text === 'string') return c.text; + if (c.text && typeof c.text === 'object' && typeof c.text.value === 'string') return c.text.value; + if (typeof c.value === 'string') return c.value; + } + + return ''; + }; + + const coerceToolCallsArray = (toolCalls: unknown): any[] => { + if (Array.isArray(toolCalls)) return toolCalls; + if (toolCalls && typeof toolCalls === 'object') return [toolCalls]; + return []; + }; + + // ---- hard "prove it runs" log (even if heavy debug disabled) ---- + __dbg('entered', { + providerName, + model: (currentOptions as any)?.model, + hasToolDefsMap: !!toolDefsMap, + toolNamesCount: __toolNames.length, + nameOfReasoningFieldInDelta, + stopOnFirstToolCall, + emitToolCallProgress, + timeoutMs: timeoutMs ?? null, + policy, + heavyDebug: __isHeavyDebugEnabled, + }); + // ----------------------------------------------------------------- + + type ToolAcc = { name: string; id: string; args: string; }; + + const pickPreferredToolAcc = (m: Map): ToolAcc | undefined => { + if (m.has(0)) return m.get(0); + let bestIdx: number | null = null; + for (const k of m.keys()) { + if (bestIdx === null || k < bestIdx) bestIdx = k; + } + return bestIdx === null ? undefined : m.get(bestIdx); + }; + + for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) { + let fullReasoningSoFar = ''; + let fullTextSoFar = ''; + let lastFinishReason: string | undefined; + + let abortedByUsForCompletedTool = false; + let abortedByTimeout = false; + let timeoutHandle: any | null = null; + + let chunkCount = 0; + let hasReceivedToolCall = false; + + let reasoningSource: 'details' | 'deltaField' | null = null; + let sawAnyReasoningDelta = false; + let sawAnyTextDelta = false; + + // “reasoning stopped” heuristic + let lastReasoningAppendChunk = -1; + let loggedReasoningStop = false; + + // “XML tool tags seen” + let sawXmlToolTagInText = false; + let sawXmlToolTagInReasoning = false; + let sawToolCallsStructured = false; + + const toolAccByIdx = new Map(); + const getAcc = (idx: number): ToolAcc => { + let acc = toolAccByIdx.get(idx); + if (!acc) { + acc = { name: '', id: '', args: '' }; + toolAccByIdx.set(idx, acc); + } + return acc; + }; + + const buildToolCall = (): RawToolCallObj | null => { + const pref = pickPreferredToolAcc(toolAccByIdx); + if (!pref) return null; + return rawToolCallObjOf(pref.name, pref.args, pref.id, toolDefsMap); + }; + + __dbg('attempt start', { + attempt, + maxAttempts: policy.maxAttempts, + model: (currentOptions as any)?.model, + max_tokens: (currentOptions as any)?.max_tokens ?? (currentOptions as any)?.max_completion_tokens ?? null, + }); + + try { + const resp = await openai.chat.completions.create(currentOptions) as any; + const controller: AbortController | undefined = resp?.controller; + _setAborter(() => controller?.abort()); + + const isIter = isAsyncIterable(resp); + + if (!isIter) { + // ----- non-stream path ----- + const nonStreamResp = resp; + const choice = nonStreamResp?.choices?.[0]; + const msg = choice?.message ?? {}; + + const text = coerceContentToString(msg?.content ?? msg?.contentParts ?? msg?.content_parts); + + const rawUsage = nonStreamResp?.usage; + const tokenUsage = validateLLMTokenUsage(mapOpenAIUsageToLLMTokenUsage(rawUsage), logService); + + let collectedReasoning = ''; + const details = msg?.reasoning_details ?? msg?.reasoningDetails; + if (Array.isArray(details) && details.length) { + const textParts: string[] = []; + let sawEncrypted = false; + for (const d of details) { + if (typeof (d as any)?.text === 'string') textParts.push((d as any).text); + if (d && typeof d === 'object' && (d as any).type === 'reasoning.encrypted') sawEncrypted = true; + } + collectedReasoning = textParts.join(''); + if (!collectedReasoning && sawEncrypted) { + collectedReasoning = 'Reasoning content is encrypted by the provider and cannot be displayed'; + } + } else if (nameOfReasoningFieldInDelta) { + const maybe = msg?.[nameOfReasoningFieldInDelta]; + if (typeof maybe === 'string' && maybe) collectedReasoning = maybe; + } + + if (__isHeavyDebugEnabled) { + if (__TOOL_TAG_RE.test(text)) { + sawXmlToolTagInText = true; + __dbg('XML tool tag detected (NON-STREAM main)', { + attempt, + preview: __previewAroundFirstMatch(text), + }); + } + if (__TOOL_TAG_RE.test(collectedReasoning)) { + sawXmlToolTagInReasoning = true; + __dbg('XML tool tag detected (NON-STREAM reasoning)', { + attempt, + preview: __previewAroundFirstMatch(collectedReasoning), + }); + } + } + + const toolCalls = msg?.tool_calls ?? msg?.toolCalls ?? []; + if (Array.isArray(toolCalls) && toolCalls.length > 0) { + sawToolCallsStructured = true; + const t0 = toolCalls[0]; + const acc = getAcc(0); + acc.name = t0?.function?.name ?? ''; + acc.id = t0?.id ?? ''; + acc.args = coerceArgsToString(t0?.function?.arguments ?? ''); + } + + const legacyFC = msg?.function_call; + if (legacyFC && !toolAccByIdx.size) { + sawToolCallsStructured = true; + const acc = getAcc(0); + acc.name = legacyFC?.name ?? ''; + acc.id = legacyFC?.id ?? ''; + acc.args = coerceArgsToString(legacyFC?.arguments ?? ''); + } + + const toolCall = buildToolCall(); + + __dbg('non-stream end', { + attempt, + textLen: text?.length ?? 0, + reasoningLen: collectedReasoning?.length ?? 0, + hasToolCall: !!toolCall, + toolName: (toolCall as any)?.name ?? null, + sawToolCallsStructured, + sawXmlToolTagInText, + sawXmlToolTagInReasoning, + }); + + // Check for truncation in non-stream path + const finishReason = choice?.finish_reason; + const hitLimit = (finishReason === 'length'); + + if (!toolCall && hitLimit && policy.enabled && attempt < policy.maxAttempts) { + const prev = getCurrentMaxTokens(currentOptions); + bumpMaxTokens(currentOptions, policy); + const next = getCurrentMaxTokens(currentOptions); + __dbg('retrying non-stream due to truncation', { attempt, prevMaxTokens: prev, nextMaxTokens: next }); + await sleep(150 * attempt); + continue; + } + + if (!toolCall && hitLimit) { + notifyTruncationOnce({ + kind: 'length', + model: String((currentOptions as any)?.model ?? ''), + provider: providerName, + maxTokens: getCurrentMaxTokens(currentOptions), + attempt, + maxAttempts: policy.maxAttempts, + textLen: (text ?? '').length, + reasoningLen: (collectedReasoning ?? '').length, + }); + } + + if (text || collectedReasoning || toolCall) { + onFinalMessage({ + fullText: text ?? '', + fullReasoning: collectedReasoning ?? '', + anthropicReasoning: null, + ...(toolCall ? { toolCall } : {}), + ...(tokenUsage ? { tokenUsage } : {}), + }); + return; + } + + _logWarn(logService, 'Void: Response from model was empty (non-stream path)', { + providerName, + attempt, + isIter, + nonStreamKeys: Object.keys(nonStreamResp || {}), + messageKeys: Object.keys(msg || {}), + messageContentType: Array.isArray(msg?.content) ? 'array' : typeof msg?.content, + }); + onError({ message: 'Void: Response from model was empty.', fullError: null }); + return; + } + + // ---- streaming path ---- + if (timeoutMs && controller) { + timeoutHandle = setTimeout(() => { + abortedByTimeout = true; + try { controller.abort(); } catch { } + }, timeoutMs); + } + + for await (const chunk of resp as any) { + chunkCount++; + + const choice = chunk?.choices?.[0]; + if (!choice) continue; + + const rawUsage = chunk?.usage; + const usage = validateLLMTokenUsage(mapOpenAIUsageToLLMTokenUsage(rawUsage), logService); + if (usage) lastTokenUsage = usage; + + if (choice.finish_reason) { + lastFinishReason = choice.finish_reason; + if (__isHeavyDebugEnabled) { + __dbg('finish_reason seen (stream)', { attempt, chunkCount, finish_reason: choice.finish_reason }); + } + } + + const delta = choice.delta; + + // MAIN TEXT stream + const contentVal = getDeltaContentValue(delta); + const newText = coerceContentToString(contentVal); + + if (newText) { + sawAnyTextDelta = true; + fullTextSoFar += newText; + everHadAnyData = true; + + if (!sawXmlToolTagInText && __isHeavyDebugEnabled && (__TOOL_TAG_RE.test(newText) || __TOOL_TAG_RE.test(fullTextSoFar.slice(-2000)))) { + sawXmlToolTagInText = true; + __dbg('XML tool tag detected (MAIN text stream)', { + attempt, + chunkCount, + newTextPreview: __previewAroundFirstMatch(newText), + }); + } + } + + // REASONING (details priority) + let appendedReasoningThisChunk = false; + + const details = (delta as any)?.reasoning_details ?? (delta as any)?.reasoningDetails; + if (Array.isArray(details) && details.length) { + const textParts: string[] = []; + let sawEncrypted = false; + for (const d of details) { + if (typeof (d as any)?.text === 'string') textParts.push((d as any).text); + if (d && typeof d === 'object' && (d as any).type === 'reasoning.encrypted') sawEncrypted = true; + } + const add = textParts.join(''); + if (add) { + sawAnyReasoningDelta = true; + if (reasoningSource !== 'details') { + fullReasoningSoFar = ''; + reasoningSource = 'details'; + } + fullReasoningSoFar += add; + everHadAnyData = true; + appendedReasoningThisChunk = true; + lastReasoningAppendChunk = chunkCount; + + if (!sawXmlToolTagInReasoning && __isHeavyDebugEnabled && (__TOOL_TAG_RE.test(add) || __TOOL_TAG_RE.test(fullReasoningSoFar.slice(-2000)))) { + sawXmlToolTagInReasoning = true; + __dbg('XML tool tag detected (REASONING_DETAILS stream)', { + attempt, + chunkCount, + addPreview: __previewAroundFirstMatch(add), + }); + } + } else if (sawEncrypted && !fullReasoningSoFar) { + sawAnyReasoningDelta = true; + fullReasoningSoFar = 'Reasoning content is encrypted by the provider and cannot be displayed'; + reasoningSource = 'details'; + everHadAnyData = true; + appendedReasoningThisChunk = true; + lastReasoningAppendChunk = chunkCount; + } + } + + // REASONING (field fallback) + if (!appendedReasoningThisChunk && nameOfReasoningFieldInDelta) { + const maybeField = (delta as any)?.[nameOfReasoningFieldInDelta]; + if (typeof maybeField === 'string' && maybeField) { + sawAnyReasoningDelta = true; + if (!reasoningSource) reasoningSource = 'deltaField'; + fullReasoningSoFar += maybeField; + everHadAnyData = true; + lastReasoningAppendChunk = chunkCount; + + if (!sawXmlToolTagInReasoning && __isHeavyDebugEnabled && (__TOOL_TAG_RE.test(maybeField) || __TOOL_TAG_RE.test(fullReasoningSoFar.slice(-2000)))) { + sawXmlToolTagInReasoning = true; + __dbg('XML tool tag detected (REASONING_FIELD stream)', { + attempt, + chunkCount, + field: nameOfReasoningFieldInDelta, + fieldPreview: __previewAroundFirstMatch(maybeField), + }); + } + } + } + + // Reasoning “stopped coming” heuristic (log once) + if (__isHeavyDebugEnabled && !loggedReasoningStop && sawAnyReasoningDelta && lastReasoningAppendChunk >= 0) { + if ((chunkCount - lastReasoningAppendChunk) >= 30) { + loggedReasoningStop = true; + __dbg('Reasoning appears to have stopped (no reasoning deltas for N chunks)', { + attempt, + chunkCount, + lastReasoningAppendChunk, + reasoningSource, + textLen: fullTextSoFar.length, + reasoningLen: fullReasoningSoFar.length, + }); + } + } + + // tool_calls (structured) + const toolCalls = coerceToolCallsArray(getDeltaToolCallsValue(delta)); + if (toolCalls.length) { + hasReceivedToolCall = true; + sawToolCallsStructured = true; + + if (__isHeavyDebugEnabled) { + __dbg('Structured tool_calls delta seen', { + attempt, + chunkCount, + len: toolCalls.length, + names: toolCalls.map(t => t?.function?.name ?? null).filter(Boolean).slice(0, 5), + }); + } + } + + for (const tool of toolCalls) { + const idx = normalizeToolIndex((tool as any)?.index); + const acc = getAcc(idx); + + const functionName = tool.function?.name ?? ''; + const id = tool.id ?? ''; + const functionArgs = coerceArgsToString(tool.function?.arguments); + + if (id && !acc.id) acc.id = id; + if (functionName && !acc.name) acc.name = functionName; + + if (allowedToolNames && acc.name && !allowedToolNames.includes(acc.name)) { + continue; + } + + if (functionArgs) { + acc.args += functionArgs; + everHadAnyData = true; + + if (stopOnFirstToolCall && controller && acc.name) { + const parsed = tryParseJsonWhenComplete(acc.args); + if (parsed.ok) { + abortedByUsForCompletedTool = true; + + if (__isHeavyDebugEnabled) { + __dbg('Aborting stream: tool args JSON complete (stopOnFirstToolCall)', { + attempt, + chunkCount, + toolName: acc.name, + toolId: acc.id, + argsLen: acc.args.length, + }); + } + + try { controller.abort(); } catch { } + } + } + } + } + + // legacy function_call + const legacyFC = (delta as any)?.function_call; + if (legacyFC) { + hasReceivedToolCall = true; + sawToolCallsStructured = true; + + const acc = getAcc(0); + const fcName = legacyFC?.name ?? ''; + const fcArgs = coerceArgsToString(legacyFC?.arguments); + const fcId = legacyFC?.id ?? ''; + + if (fcId && !acc.id) acc.id = fcId; + if (fcName && !acc.name) acc.name = fcName; + + if (__isHeavyDebugEnabled) { + __dbg('Legacy function_call delta seen', { attempt, chunkCount, name: fcName || null }); + } + + if (allowedToolNames && acc.name && !allowedToolNames.includes(acc.name)) { + continue; + } + + if (fcArgs) { + acc.args += fcArgs; + everHadAnyData = true; + + if (stopOnFirstToolCall && controller && acc.name) { + const parsed = tryParseJsonWhenComplete(acc.args); + if (parsed.ok) { + abortedByUsForCompletedTool = true; + + if (__isHeavyDebugEnabled) { + __dbg('Aborting stream: legacy function_call args JSON complete (stopOnFirstToolCall)', { + attempt, + chunkCount, + toolName: acc.name, + toolId: acc.id, + argsLen: acc.args.length, + }); + } + + try { controller.abort(); } catch { } + } + } + } + } + + // progress + if (emitToolCallProgress || fullTextSoFar || fullReasoningSoFar) { + const pref = pickPreferredToolAcc(toolAccByIdx); + const prefName = pref?.name ?? ''; + const prefId = pref?.id ?? ''; + + const knownTool = !!prefName && ((toolDefsMap?.has(prefName)) || isAToolName(prefName)); + const toolCallInfo = knownTool + ? { name: prefName as string, rawParams: {}, isDone: false, doneParams: [], id: prefId } as RawToolCallObj + : undefined; + + const usagePayload = lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}; + onText({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + toolCall: toolCallInfo, + ...usagePayload, + }); + } + + if (hasReceivedToolCall && (choice.finish_reason === 'tool_calls' || choice.finish_reason === 'function_call')) { + break; + } + } + + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = null; + } + + const toolCall = buildToolCall(); + const usagePayload = lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}; + + __dbg('attempt end', { + attempt, + chunkCount, + lastFinishReason: lastFinishReason ?? null, + abortedByUsForCompletedTool, + abortedByTimeout, + textLen: fullTextSoFar.length, + reasoningLen: fullReasoningSoFar.length, + reasoningSource, + sawAnyTextDelta, + sawAnyReasoningDelta, + sawToolCallsStructured, + sawXmlToolTagInText, + sawXmlToolTagInReasoning, + hasFinalToolCall: !!toolCall, + finalToolName: (toolCall as any)?.name ?? null, + }); + + // ✅ IMPORTANT: retry on length/timeout EVEN IF we already got partial output, + // but only when there is NO tool call (tool calls are handled differently). + const hitLimit = (lastFinishReason === 'length'); + const hitTimeout = abortedByTimeout; + + if (!toolCall && (hitLimit || hitTimeout) && policy.enabled && attempt < policy.maxAttempts) { + const prev = getCurrentMaxTokens(currentOptions); + bumpMaxTokens(currentOptions, policy); + const next = getCurrentMaxTokens(currentOptions); + + __dbg('retrying due to truncation', { + attempt, + reason: hitTimeout ? 'timeout' : 'length', + prevMaxTokens: prev, + nextMaxTokens: next, + textLen: fullTextSoFar.length, + reasoningLen: fullReasoningSoFar.length, + }); + + // give provider a tiny breather + await sleep(150 * attempt); + continue; + } + + // ✅ No more retries → notify (best-effort) if truncated + if (!toolCall && (hitLimit || hitTimeout)) { + notifyTruncationOnce({ + kind: hitTimeout ? 'timeout' : 'length', + model: String((currentOptions as any)?.model ?? ''), + provider: providerName, + maxTokens: getCurrentMaxTokens(currentOptions), + attempt, + maxAttempts: policy.maxAttempts, + textLen: fullTextSoFar.length, + reasoningLen: fullReasoningSoFar.length, + }); + } + + if (fullTextSoFar || fullReasoningSoFar || toolCall) { + onFinalMessage({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + anthropicReasoning: null, + ...(toolCall ? { toolCall } : {}), + ...usagePayload, + }); + return; + } + + // retry on empty (unchanged) + if (policy.enabled && attempt < policy.maxAttempts) { + _logWarn(logService, 'Empty/unenriched stream from provider; retrying', { + providerName, + attempt, + chunkCount, + lastFinishReason, + abortedByTimeout, + }); + await sleep(200 * attempt); + continue; + } + + _logWarn(logService, 'Void: Response from model was empty (stream finished)', { + providerName, + attempt, + chunkCount, + lastFinishReason, + abortedByTimeout, + }); + + onError({ message: 'Void: Response from model was empty.', fullError: null }); + return; + + } catch (error: any) { + if (timeoutHandle) { + clearTimeout(timeoutHandle); + timeoutHandle = null; + } + + if (isAbortError(error) && abortedByUsForCompletedTool) { + const toolCall = buildToolCall(); + if (toolCall) { + const usagePayload = lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}; + __dbg('caught AbortError after tool completion; finalizing with toolCall', { + attempt, + toolName: (toolCall as any)?.name ?? null, + }); + onFinalMessage({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + anthropicReasoning: null, + toolCall, + ...usagePayload, + }); + return; + } + } + + if (isAbortError(error) && (fullTextSoFar || fullReasoningSoFar)) { + __dbg('caught AbortError with partial output; finalizing partial', { + attempt, + textLen: fullTextSoFar.length, + reasoningLen: fullReasoningSoFar.length, + }); + onFinalMessage({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + anthropicReasoning: null, + ...(lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}), + }); + return; + } + + _logWarn(logService, 'runStream threw error', { + providerName, + attempt, + errorName: error?.name, + errorMessage: error?.message ?? String(error), + errorCode: error?.code, + cause: { + name: error?.cause?.name, + message: error?.cause?.message, + code: error?.cause?.code, + }, + stack: error?.stack, + }); + + if (OpenAIApiError && error instanceof OpenAIApiError) { + if (error.status === 401) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + } else { + onError({ message: `API Error: ${error.message}`, fullError: error }); + } + } else { + onError({ message: error?.message || String(error), fullError: error }); + } + return; + } + } + + if (!everHadAnyData) { + _logWarn(logService, 'Failed to get response after retries (everHadAnyData=false)', { + providerName, + maxAttempts: policy.maxAttempts, + }); + onError({ message: 'Failed to get response after retries', fullError: null }); + } +} + +function createNativeTools( + potentialTools: any[], + tool_choice: { + type: 'function'; + function: { + name: string; + }; + } | 'none' | 'auto' | 'required' | undefined, + specialToolFormat: specialToolFormat, +) { + // Get default tool choice setting based on format + const getDefaultToolChoice = (format: typeof specialToolFormat) => { + switch (format) { + case 'anthropic-style': return { type: 'auto' } as const; + case 'openai-style': return 'auto' as const; + case 'gemini-style': return undefined; + default: return undefined; + } + }; + + // Use provided tool_choice if available and tools are enabled, otherwise use default + const effectiveToolChoice = + (tool_choice !== undefined) + ? tool_choice + : getDefaultToolChoice(specialToolFormat); + + return { + tools: potentialTools, + ...(effectiveToolChoice !== undefined ? { tool_choice: effectiveToolChoice } : {}) + }; +} + + +const _sendOpenAICompatibleChat = async (params: SendChatParams_Internal) => { + const { + messages, + separateSystemMessage, + tool_choice, + onText: onTextInput, + onFinalMessage: onFinalMessageInput, + onError, + settingsOfProvider, + modelSelectionOptions, + modelName: modelName_, + _setAborter, + providerName, + chatMode, + overridesOfModel, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + dynamicRequestConfig, + requestParams, + notifyOnTruncation, + } = params; + + + const dyn = dynamicRequestConfig; + + if (!dyn && providerName === 'anthropic') { + return sendAnthropicChat(params); + } + + if (!dyn && providerName === 'gemini') { + return sendGeminiChat(params); + } + + + const baseCaps = getModelCapabilities(providerName, modelName_, overridesOfModel); + const { modelName } = baseCaps; + let supportsCacheControl = !!(baseCaps as any).supportCacheControl; + + // If renderer provided an explicit supportCacheControl via dynamic config (dynamic provider overrides), prefer it. + if (typeof (dyn as any)?.supportCacheControl === 'boolean') { + supportsCacheControl = !!(dyn as any).supportCacheControl; + } + + const baseReasoningCaps = baseCaps.reasoningCapabilities; + + const fmtForTools: specialToolFormat | undefined = + (dyn && dyn.specialToolFormat !== undefined) + ? dyn.specialToolFormat + : baseCaps.specialToolFormat; + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName_, overridesOfModel); + + // reasoning + // Prefer dynamic reasoning capabilities from renderer if provided + const effectiveReasoningCaps = (dynamicRequestConfig as any)?.reasoningCapabilities ?? baseReasoningCaps; + const { canIOReasoning, openSourceThinkTags } = effectiveReasoningCaps || {}; + + // Compute sendable reasoning info locally to avoid relying on static caps in main + const computeReasoningInfo = (): ReturnType => { + const caps = effectiveReasoningCaps as any; + if (!caps || !caps.supportsReasoning) return null; + const canTurnOff = !!caps.canTurnOffReasoning; + const defaultEnabled = true || !canTurnOff; // Chat default: enabled + const enabled = (modelSelectionOptions?.reasoningEnabled ?? defaultEnabled) === true; + if (!enabled) return null; + const slider = caps?.reasoningSlider; + if (slider && slider.type === 'budget_slider') { + const v = modelSelectionOptions?.reasoningBudget ?? slider.default; + return { type: 'budget_slider_value', isReasoningEnabled: true, reasoningBudget: v } as const; + } + if (slider && slider.type === 'effort_slider') { + const v = modelSelectionOptions?.reasoningEffort ?? slider.default; + return { type: 'effort_slider_value', isReasoningEnabled: true, reasoningEffort: v } as const; + } + return { type: 'enabled_only', isReasoningEnabled: true } as const; + }; + + const reasoningInfo = computeReasoningInfo(); + let includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}; + + if (Object.keys(includeInPayload).length === 0) { + const isReasoningEnabledState = !!reasoningInfo; + if (isReasoningEnabledState && canIOReasoning && Array.isArray(openSourceThinkTags)) { + includeInPayload = { reasoning: { enabled: true } }; + } + } + + // tools + const staticToolsForCall = chatMode !== null ? getStaticTools(chatMode, disabledStaticTools) : []; + const dynamicToolsForCall = filterDynamicTools(additionalTools, disabledDynamicTools); + const allToolsForCall = mergeTools(staticToolsForCall, dynamicToolsForCall); + const allowedToolNames = chatMode !== null ? allToolsForCall.map(tool => tool.name) : undefined; + + const potentialTools = chatMode !== null + ? openAITools(chatMode, additionalTools, params.logService, disabledStaticTools, disabledDynamicTools) + : null; + const nativeToolsObj = + potentialTools && fmtForTools && fmtForTools !== 'disabled' + ? createNativeTools(potentialTools, tool_choice, fmtForTools) + : {}; + + + let openai: OpenAIClient; + let modelForRequest = modelName; + + if (dyn?.apiStyle === 'openai-compatible') { + const { default: OpenAI } = await getOpenAIModule(); + const token = extractBearer(dyn.headers) || 'noop'; + + + const headersNoAuth: Record = { ...dyn.headers }; + delete (headersNoAuth as any).Authorization; + delete (headersNoAuth as any).authorization; + + openai = new OpenAI({ + baseURL: dyn.endpoint, + apiKey: token, + defaultHeaders: headersNoAuth, + dangerouslyAllowBrowser: true, + maxRetries: 0, + }); + + modelForRequest = modelName; + + } else { + openai = await newOpenAICompatibleSDK({ providerName, settingsOfProvider, includeInPayload }); + } + + const { needsManualParse: needsManualReasoningParse, nameOfFieldInDelta: nameOfReasoningFieldInDelta } = providerReasoningIOSettings?.output ?? {}; + const manuallyParseReasoning = !!(needsManualReasoningParse && canIOReasoning && openSourceThinkTags); + const hasReasoningFieldInDelta = !!nameOfReasoningFieldInDelta; + + const needsReasoningProcessing = hasReasoningFieldInDelta || manuallyParseReasoning; + const needsXMLToolsProcessing = !fmtForTools || fmtForTools === 'disabled'; + const needsProcessing = needsReasoningProcessing || needsXMLToolsProcessing; + + try { + params.logService?.debug?.( + `[LLM][debug] [sendLLMMessage] processing flags\n` + + JSON.stringify({ + providerName, + modelName_input: modelName_, + modelName_resolved: modelName, + fmtForTools: fmtForTools ?? null, + fmtForTools_sources: { + dyn_specialToolFormat: dyn?.specialToolFormat ?? null, + base_specialToolFormat: (baseCaps as any)?.specialToolFormat ?? null, + }, + providerReasoningIOSettings_output: { + needsManualParse: needsManualReasoningParse ?? null, + nameOfFieldInDelta: nameOfReasoningFieldInDelta ?? null, + }, + reasoningCaps: { + canIOReasoning: !!canIOReasoning, + openSourceThinkTags: Array.isArray(openSourceThinkTags) ? openSourceThinkTags : null, + }, + derived: { + manuallyParseReasoning, + hasReasoningFieldInDelta, + needsReasoningProcessing, + needsXMLToolsProcessing, + needsProcessing, + }, + }, null, 2) + ); + } catch { + + } + + let processedMessages = messages as any; + if (separateSystemMessage) { + processedMessages = [ + { role: 'system', content: separateSystemMessage }, + ...processedMessages, + ]; + } + + // Optionally inject cache_control breakpoints for providers that support it + if (supportsCacheControl) { + processedMessages = applyCacheControlOpenAIStyle(processedMessages, true); + } + + type ChatCreateParamsWithExtras = + ChatCompletionCreateParamsStreaming & + Record & + Partial<{ max_tokens: number; max_completion_tokens: number }>; + + const options: ChatCreateParamsWithExtras = { + model: modelForRequest, + messages: processedMessages, + stream: true, + ...nativeToolsObj, + }; + + // Inject user-defined request params for OpenAI-compatible payloads + if (requestParams && requestParams.mode !== 'off') { + if (requestParams.mode === 'override' && requestParams.params && typeof requestParams.params === 'object') { + for (const [k, v] of Object.entries(requestParams.params)) { + if (k === 'tools' || k === 'tool_choice' || k === 'response_format') continue; + (options as any)[k] = v as any; + } + } + // 'default' mode: nothing extra here; defaults already applied elsewhere + } + + // Add reasoning payload to options + if (Object.keys(includeInPayload).length > 0) { + Object.assign(options, includeInPayload); + } + + // Inject OpenRouter provider routing when talking to OpenRouter endpoint + if (params.providerRouting && dyn?.endpoint?.includes('openrouter.ai')) { + (options as any).provider = params.providerRouting; + } + + // Do not set max_tokens/max_completion_tokens by default. + + let onText = onTextInput; + let onFinalMessage = onFinalMessageInput; + let sawParsedDoneToolCallInCurrentRun = false; + + if (needsProcessing) { + const think = (canIOReasoning && openSourceThinkTags) ? openSourceThinkTags : null; + + const debugEnabled = !!params.logService?.debug; + + const safeJson = (v: unknown, maxLen = 2500) => { +void safeJson; + try { + const s = JSON.stringify(v, null, 2); + return s.length > maxLen ? s.slice(0, maxLen) + `…(+${s.length - maxLen})` : s; + } catch { + return String(v); + } + }; + + const preview = (s: unknown, n = 260) => { + const str = typeof s === 'string' ? s : ''; + return str.length <= n ? str : (str.slice(0, n) + `…(+${str.length - n})`); + }; + + const baseOnText = onText; + const baseOnFinal = onFinalMessage; + + let lastToolSig = ''; + + const onTextAfterParse: OnText = (p) => { + const tc = p.toolCall; + if (tc?.isDone) { + sawParsedDoneToolCallInCurrentRun = true; + } + if (debugEnabled && tc?.name) { + const sig = `${tc.id ?? ''}|${tc.name ?? ''}|${tc.isDone ? 'done' : 'progress'}|${(tc.doneParams ?? []).join(',')}`; + if (sig !== lastToolSig) { + lastToolSig = sig; + params.logService?.debug?.( + `[LLM][debug][toolParse] onText toolCall\n` + + safeJson({ + sig, + toolCall: tc, + textPreview: preview(p.fullText), + reasoningPreview: preview(p.fullReasoning), + tokenUsage: p.tokenUsage ?? null, + }) + ); + } + } + baseOnText(p); + }; + + const onFinalAfterParse: OnFinalMessage = (p) => { + const tc = p.toolCall; + if (tc?.isDone) { + sawParsedDoneToolCallInCurrentRun = true; + } + if (debugEnabled && tc?.name) { + params.logService?.debug?.( + `[LLM][debug][toolParse] onFinalMessage toolCall\n` + + safeJson({ + toolCall: tc, + finalTextLen: p.fullText.length, + finalReasoningLen: p.fullReasoning.length, + finalTextPreview: preview(p.fullText), + finalReasoningPreview: preview(p.fullReasoning), + tokenUsage: p.tokenUsage ?? null, + }) + ); + } + baseOnFinal(p); + }; + + const { newOnText, newOnFinalMessage } = needsXMLToolsProcessing + ? extractReasoningAndXMLToolsWrapper( + onTextAfterParse, + onFinalAfterParse, + think, + chatMode + ) + : extractReasoningWrapper( + onTextAfterParse, + onFinalAfterParse, + think, + chatMode + ); + + onText = newOnText; + onFinalMessage = newOnFinalMessage; + } + + { + const pickLongerString = (a: string, b: string): string => (a.length >= b.length ? a : b); + let maxSeenUiText = ''; + let maxSeenUiReasoning = ''; + const updateMaxSeenUi = (p: { fullText: string; fullReasoning: string }) => { + maxSeenUiText = pickLongerString(maxSeenUiText, p.fullText); + maxSeenUiReasoning = pickLongerString(maxSeenUiReasoning, p.fullReasoning); + }; + const toolDefsMap = chatMode !== null + ? buildToolDefsMap(staticToolsForCall, dynamicToolsForCall) + : undefined; + const xmlRepairMaxRetries = + fmtForTools === 'disabled' && Array.isArray(allowedToolNames) && allowedToolNames.length > 0 + ? 1 + : 0; + let xmlRepairRetriesUsed = 0; + let messagesForRun: any[] = Array.isArray(processedMessages) ? [...processedMessages] : []; + let xmlRepairCarryText = ''; + let xmlRepairCarryReasoning = ''; + let currentRunAborter: (() => void) | null = null; + const withXmlRepairCarry = (p: T): T => { + if (!xmlRepairCarryText && !xmlRepairCarryReasoning) return p; + const mergedText = pickLongerString(p.fullText, xmlRepairCarryText); + const mergedReasoning = pickLongerString(p.fullReasoning, xmlRepairCarryReasoning); + if (mergedText === p.fullText && mergedReasoning === p.fullReasoning) return p; + return { ...p, fullText: mergedText, fullReasoning: mergedReasoning } as T; + }; + + while (true) { + let shouldRetryForXmlRepair = false; + let hadRunError = false; + let gatedFinalText = ''; + let gatedFinalReasoning = ''; + sawParsedDoneToolCallInCurrentRun = false; + currentRunAborter = null; + let earlyAbortedForXmlRepair = false; + let earlyAbortedForParsedDoneToolCall = false; + let lastXmlRepairCheckedCombinedLen = -1; + + const maybeAbortEarlyForXmlRepair = (p: { fullText: string; fullReasoning: string }) => { + if (earlyAbortedForXmlRepair) return; + if (earlyAbortedForParsedDoneToolCall) return; + if (xmlRepairRetriesUsed >= xmlRepairMaxRetries) return; + if (sawParsedDoneToolCallInCurrentRun) return; + + const combinedLen = (p.fullText?.length ?? 0) + (p.fullReasoning?.length ?? 0); + // Avoid O(n^2)-style repeated scans on every tiny chunk. + if (combinedLen <= (lastXmlRepairCheckedCombinedLen + 256)) return; + lastXmlRepairCheckedCombinedLen = combinedLen; + + const xmlHeuristicText = (p.fullText || '').slice(-24_000); + const xmlHeuristicReasoning = (p.fullReasoning || '').slice(-24_000); + + const hasLikelyXmlMarkup = hasLikelyUnparsedXmlToolCall({ + fullText: xmlHeuristicText, + fullReasoning: xmlHeuristicReasoning, + toolNames: allowedToolNames, + }); + if (!hasLikelyXmlMarkup) return; + + // Attributes on XML tool tags are always invalid in our required format. + const hasLikelyParamAttributes = hasLikelyToolParamAttributes({ + fullText: xmlHeuristicText, + fullReasoning: xmlHeuristicReasoning, + toolNames: allowedToolNames, + }); + if (!hasLikelyParamAttributes) return; + + earlyAbortedForXmlRepair = true; + shouldRetryForXmlRepair = true; + gatedFinalText = pickLongerString(p.fullText, maxSeenUiText); + gatedFinalReasoning = pickLongerString(p.fullReasoning, maxSeenUiReasoning); + xmlRepairCarryText = pickLongerString(xmlRepairCarryText, gatedFinalText); + xmlRepairCarryReasoning = pickLongerString(xmlRepairCarryReasoning, gatedFinalReasoning); + params.logService?.warn?.( + '[LLM][warn][toolParse] Invalid XML tool parameter attributes detected; aborting stream early and requesting corrected XML tool format retry' + ); + try { + currentRunAborter?.(); + } catch { } + }; + + const onTextWithXmlRepairCarry: OnText = (p) => { + updateMaxSeenUi(p); + const sawDoneBefore = sawParsedDoneToolCallInCurrentRun; + onText(withXmlRepairCarry(p)); + const sawDoneAfter = sawParsedDoneToolCallInCurrentRun; + if ( + !earlyAbortedForParsedDoneToolCall && + !sawDoneBefore && + sawDoneAfter && + typeof currentRunAborter === 'function' + ) { + earlyAbortedForParsedDoneToolCall = true; + params.logService?.debug?.( + '[LLM][debug][toolParse] Parsed done XML toolCall in stream; aborting stream early to avoid extra reasoning tail' + ); + try { + currentRunAborter(); + } catch { } + return; + } + maybeAbortEarlyForXmlRepair(p); + }; + + const onFinalWithXmlRepairGate: OnFinalMessage = (p) => { + updateMaxSeenUi(p); + if (earlyAbortedForXmlRepair && xmlRepairRetriesUsed < xmlRepairMaxRetries) { + return; + } + const hasDoneToolCall = !!p.toolCall?.isDone; + const hasLikelyXmlMarkup = hasLikelyUnparsedXmlToolCall({ + fullText: p.fullText, + fullReasoning: p.fullReasoning, + toolNames: allowedToolNames, + }); + const hasLikelyParamAttributes = hasLikelyToolParamAttributes({ + fullText: p.fullText, + fullReasoning: p.fullReasoning, + toolNames: allowedToolNames, + }); + + const shouldRepair = + hasLikelyXmlMarkup && + ((!hasDoneToolCall && !sawParsedDoneToolCallInCurrentRun) || hasLikelyParamAttributes); + + if (shouldRepair && xmlRepairRetriesUsed < xmlRepairMaxRetries) { + shouldRetryForXmlRepair = true; + gatedFinalText = pickLongerString(p.fullText, maxSeenUiText); + gatedFinalReasoning = pickLongerString(p.fullReasoning, maxSeenUiReasoning); + xmlRepairCarryText = pickLongerString(xmlRepairCarryText, gatedFinalText); + xmlRepairCarryReasoning = pickLongerString(xmlRepairCarryReasoning, gatedFinalReasoning); + params.logService?.warn?.( + '[LLM][warn][toolParse] XML-like tool markup detected but no parsed toolCall; requesting corrected XML tool format retry' + ); + return; + } + onFinalMessage(withXmlRepairCarry(p)); + }; + + const onErrorForRun: OnError = (err) => { + hadRunError = true; + onError(err); + }; + + await runStream({ + openai, + options: { ...options, messages: messagesForRun }, + onText: onTextWithXmlRepairCarry, + onFinalMessage: onFinalWithXmlRepairGate, + onError: onErrorForRun, + _setAborter: (aborter) => { + currentRunAborter = aborter; + _setAborter(aborter); + }, + nameOfReasoningFieldInDelta, + providerName, + toolDefsMap, + allowedToolNames, + logService: params.logService, + + notificationService: params.notificationService, + notifyOnTruncation: notifyOnTruncation ?? true, + }); + + if (hadRunError || !shouldRetryForXmlRepair) { + return; + } + + xmlRepairRetriesUsed += 1; + let assistantEcho = [ + gatedFinalText, + gatedFinalReasoning, + ].filter(Boolean).join('\n\n').trim(); + const assistantEchoCap = 8_000; + if (assistantEcho.length > assistantEchoCap) { + assistantEcho = assistantEcho.slice(-assistantEchoCap); + } + + if (assistantEcho) { + messagesForRun = [ + ...messagesForRun, + { role: 'assistant', content: assistantEcho }, + ]; + } + messagesForRun = [ + ...messagesForRun, + { role: 'user', content: XML_TOOL_FORMAT_CORRECTION_PROMPT }, + ]; + params.logService?.warn?.( + `[LLM][warn][toolParse] Retrying request with XML format correction prompt (${xmlRepairRetriesUsed}/${xmlRepairMaxRetries})` + ); + } + } +}; + +const anthropicTools = ( + chatMode: ChatMode, + additionalTools?: AdditionalToolInfo[], + logService?: ILogService, + disabledStaticTools?: readonly string[], + disabledDynamicTools?: readonly string[], +) => { + const staticTools = getStaticTools(chatMode, disabledStaticTools); + const dynamicTools = filterDynamicTools(additionalTools, disabledDynamicTools); + const allTools = mergeTools(staticTools, dynamicTools); + + if (allTools.length === 0) return null; + + const convertedTools = allTools.map(toolInfo => toAnthropicTool(toolInfo, logService)); + return convertedTools.length ? convertedTools : null; +}; + +const anthropicToolToRawToolCallObj = ( + toolBlock: AnthropicToolUseBlock, + toolDefsMap?: ReadonlyMap +): RawToolCallObj | null => { + const { id, name, input } = toolBlock; + if (!name) return null; + const toolParamsStr = JSON.stringify(input ?? {}); + return rawToolCallObjOf(name, toolParamsStr, id, toolDefsMap); +} + +// ------------ ANTHROPIC ------------ +const sendAnthropicChat = async ({ + messages, + providerName, + onText, + onFinalMessage, + onError, + settingsOfProvider, + modelSelectionOptions, + overridesOfModel, + modelName: modelName_, + _setAborter, + separateSystemMessage, + chatMode, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + requestParams, + dynamicRequestConfig, + logService, +}: SendChatParams_Internal) => { + const { default: Anthropic, APIError: AnthropicAPIError } = await getAnthropicModule(); + + const { + modelName, + specialToolFormat, + reasoningCapabilities, + supportCacheControl, + } = getModelCapabilities(providerName, modelName_, overridesOfModel); + + const thisConfig = settingsOfProvider.anthropic; + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName_, overridesOfModel); + + // reasoning + const { canIOReasoning, openSourceThinkTags } = reasoningCapabilities || {}; + const reasoningInfo = getSendableReasoningInfoImpl('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel); + const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {}; + + // anthropic-specific - max tokens + let maxTokens = getReservedOutputTokenSpace(providerName, modelName_, { + isReasoningEnabled: !!reasoningInfo?.isReasoningEnabled, + overridesOfModel, + }); + + // tools + const staticToolsForCall = chatMode !== null ? getStaticTools(chatMode, disabledStaticTools) : []; + const dynamicToolsForCall = filterDynamicTools(additionalTools, disabledDynamicTools); + const potentialTools = chatMode !== null + ? anthropicTools(chatMode, additionalTools, logService, disabledStaticTools, disabledDynamicTools) + : null; + const toolDefsMap = chatMode !== null ? buildToolDefsMap(staticToolsForCall, dynamicToolsForCall) : undefined; + const nativeToolsObj = + potentialTools && specialToolFormat === 'anthropic-style' + ? ({ tools: potentialTools, tool_choice: { type: 'auto' } } as const) + : {}; + + // ---- dynamic headers/baseURL support ---- + const dyn = dynamicRequestConfig; + + // apiKey: prefer dyn Authorization Bearer token if present + const tokenFromDyn = dyn?.headers ? extractBearer(dyn.headers) : ''; + const apiKey = tokenFromDyn || thisConfig.apiKey; + + // Merge headers: provider saved headers + dyn headers (dyn wins) + const mergedHeaders: Record = { + ...((thisConfig as any)?.additionalHeaders || {}), + ...(dyn?.headers || {}), + }; + + // Don’t forward Authorization to Anthropic SDK; it uses x-api-key internally + delete (mergedHeaders as any).Authorization; + delete (mergedHeaders as any).authorization; + delete (mergedHeaders as any)['x-api-key']; + delete (mergedHeaders as any)['X-API-Key']; + + // baseURL: if endpoint provided as ".../v1", strip it to avoid "/v1/v1/messages" + const baseURL = + typeof dyn?.endpoint === 'string' && dyn.endpoint.trim() + ? dyn.endpoint.trim().replace(/\/v1\/?$/i, '') + : undefined; + + const anthropic = new Anthropic({ + apiKey, + dangerouslyAllowBrowser: true, + ...(baseURL ? { baseURL } : {}), + // NOTE: SDK supports defaultHeaders in modern versions; keep as any to be safe with typing drift + ...(Object.keys(mergedHeaders).length ? ({ defaultHeaders: mergedHeaders } as any) : {}), + } as any); + + // Map requestParams (override mode) to Anthropic fields + let overrideAnthropic: Record = {}; + if (requestParams && requestParams.mode === 'override' && requestParams.params && typeof requestParams.params === 'object') { + const p: any = requestParams.params; + if (typeof p.temperature === 'number') overrideAnthropic.temperature = p.temperature; + if (typeof p.top_p === 'number') overrideAnthropic.top_p = p.top_p; + if (p.stop !== undefined) overrideAnthropic.stop_sequences = Array.isArray(p.stop) ? p.stop : [p.stop]; + if (typeof p.seed === 'number') overrideAnthropic.seed = p.seed; + if (typeof p.max_tokens === 'number') maxTokens = p.max_tokens; + else if (typeof p.max_completion_tokens === 'number') maxTokens = p.max_completion_tokens; + if (p.reasoning && typeof p.reasoning === 'object') { + const bt = p.reasoning.max_tokens ?? p.reasoning.budget_tokens; + if (typeof bt === 'number') overrideAnthropic.thinking = { type: 'enabled', budget_tokens: bt }; + } + } + + let anthropicMessages = messages as AnthropicLLMChatMessage[]; + if (supportCacheControl) { + anthropicMessages = applyCacheControlOpenAIStyle(anthropicMessages as any, true) as AnthropicLLMChatMessage[]; + } + + const systemPayload: any = + separateSystemMessage && supportCacheControl + ? [{ type: 'text', text: separateSystemMessage, cache_control: { type: 'ephemeral' } }] + : separateSystemMessage ?? undefined; + + const stream = anthropic.messages.stream({ + system: systemPayload, + messages: anthropicMessages, + model: modelName, + max_tokens: maxTokens ?? 4_096, + ...overrideAnthropic, + ...includeInPayload, + ...nativeToolsObj, + }); + + const { needsManualParse: needsManualReasoningParse } = providerReasoningIOSettings?.output ?? {}; + const manuallyParseReasoning = needsManualReasoningParse && canIOReasoning && openSourceThinkTags; + const needsXMLTools = !specialToolFormat || specialToolFormat === 'disabled'; + + if (manuallyParseReasoning || needsXMLTools) { + const thinkTags = manuallyParseReasoning ? openSourceThinkTags : null; + const { newOnText, newOnFinalMessage } = needsXMLTools + ? extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + thinkTags, + chatMode + ) + : extractReasoningWrapper( + onText, + onFinalMessage, + thinkTags, + chatMode + ); + onText = newOnText; + onFinalMessage = newOnFinalMessage; + } + + // when receive text + let fullText = ''; + let fullReasoning = ''; + let fullToolName = ''; + let fullToolParams = ''; + let lastTokenUsage: LLMTokenUsage | undefined; + + const runOnText = () => { + const knownTool = !!fullToolName && ((toolDefsMap?.has(fullToolName)) || isAToolName(fullToolName)); + const usagePayload = lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}; + onText({ + fullText, + fullReasoning, + toolCall: knownTool ? { name: fullToolName as any, rawParams: {}, isDone: false, doneParams: [], id: 'dummy' } : undefined, + ...usagePayload, + }); + }; + + stream.on('streamEvent', e => { + if (e.type === 'message_start' && (e as any)?.message?.usage) { + const usage = validateLLMTokenUsage(mapAnthropicUsageToLLMTokenUsage((e as any).message.usage), logService); + if (usage) lastTokenUsage = usage; + } + + if (e.type === 'content_block_start') { + if (e.content_block.type === 'text') { + if (fullText) fullText += '\n\n'; + fullText += e.content_block.text; + runOnText(); + } + else if (e.content_block.type === 'thinking') { + if (fullReasoning) fullReasoning += '\n\n'; + fullReasoning += e.content_block.thinking; + runOnText(); + } + else if (e.content_block.type === 'redacted_thinking') { + if (fullReasoning) fullReasoning += '\n\n'; + fullReasoning += '[redacted_thinking]'; + runOnText(); + } + else if (e.content_block.type === 'tool_use') { + fullToolName += e.content_block.name ?? ''; + runOnText(); + } + } + else if (e.type === 'content_block_delta') { + if (e.delta.type === 'text_delta') { + fullText += e.delta.text; + runOnText(); + } + else if (e.delta.type === 'thinking_delta') { + fullReasoning += e.delta.thinking; + runOnText(); + } + else if (e.delta.type === 'input_json_delta') { + fullToolParams += e.delta.partial_json ?? ''; + runOnText(); + } + } + }); + + stream.on('finalMessage', (response) => { + const anthropicReasoning = response.content.filter(c => c.type === 'thinking' || c.type === 'redacted_thinking'); + const tools = response.content.filter(c => c.type === 'tool_use'); + const toolCall = tools[0] && anthropicToolToRawToolCallObj(tools[0] as any, toolDefsMap); + const toolCallObj = toolCall ? { toolCall } : {}; + const tokenUsageFromResp = validateLLMTokenUsage(mapAnthropicUsageToLLMTokenUsage((response as any)?.usage), logService); + if (tokenUsageFromResp) lastTokenUsage = tokenUsageFromResp; + + onFinalMessage({ + fullText, + fullReasoning, + anthropicReasoning, + ...toolCallObj, + ...(lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}), + }); + }); + + stream.on('error', (error) => { + if (error instanceof AnthropicAPIError && (error as any).status === 401) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + } else { + onError({ message: error + '', fullError: error }); + } + }); + + _setAborter(() => { + try { (stream as any).controller.abort(); } catch { } + }); +}; + +// ------------ OLLAMA ------------ +const newOllamaSDK = async ({ endpoint }: { endpoint: string | undefined }) => { + // if endpoint is empty, normally ollama will send to 11434, but we want it to fail - the user should type it in + if (!endpoint) throw new Error(`Ollama endpoint is empty. Please enter your Ollama endpoint (e.g. http://127.0.0.1:11434) in Void Settings.`) + const { Ollama } = await getOllamaModule(); + return new Ollama({ host: endpoint }) +} + +const sendOllamaFIM = async (params: SendFIMParams_Internal) => { + const { messages, onFinalMessage, onError, settingsOfProvider, modelName, _setAborter, dynamicRequestConfig } = params; + + const fallback = settingsOfProvider.ollama; + const endpoint = dynamicRequestConfig?.endpoint || fallback.endpoint; + + const ollama = await newOllamaSDK({ endpoint }); + + let fullText = ''; + try { + const stream = await ollama.generate({ + model: modelName, + prompt: messages.prefix, + suffix: messages.suffix, + options: { + stop: messages.stopTokens, + num_predict: 300, + }, + raw: true, + stream: true, + }); + _setAborter(() => stream.abort()); + for await (const chunk of stream) { + const newText = chunk.response || ''; + fullText += newText; + } + onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); + } catch (error) { + onError({ message: String(error), fullError: error }); + } +}; + +// ---------------- GEMINI NATIVE IMPLEMENTATION ---------------- + +const geminiTools = ( + chatMode: ChatMode, + additionalTools?: AdditionalToolInfo[], + logService?: ILogService, + disabledStaticTools?: readonly string[], + disabledDynamicTools?: readonly string[], +): GoogleGeminiTool[] | null => { + const staticTools = getStaticTools(chatMode, disabledStaticTools); + const dynamicTools = filterDynamicTools(additionalTools, disabledDynamicTools); + const allTools = mergeTools(staticTools, dynamicTools); + + if (allTools.length === 0) return null; + + const functionDecls = allTools.map(toolInfo => toGeminiTool(toolInfo, logService)); + if (functionDecls.length === 0) return null; + + const tools: GoogleGeminiTool = { functionDeclarations: functionDecls }; + return [tools]; +}; + +const sendGeminiChat = async ({ + messages, + separateSystemMessage, + onText, + onFinalMessage, + onError, + settingsOfProvider, + overridesOfModel, + modelName: modelName_, + _setAborter, + providerName, + modelSelectionOptions, + chatMode, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + requestParams, + logService, +}: SendChatParams_Internal) => { + const { GoogleGenAI } = await getGoogleGenAIModule(); + + if (providerName !== 'gemini') throw new Error(`Sending Gemini chat, but provider was ${providerName}`) + + const thisConfig = settingsOfProvider[providerName] + + const { + modelName, + specialToolFormat, + reasoningCapabilities, + } = getModelCapabilities(providerName, modelName_, overridesOfModel) + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName_, overridesOfModel) + + // reasoning + const { canIOReasoning, openSourceThinkTags } = reasoningCapabilities || {} + const reasoningInfo = getSendableReasoningInfoImpl('Chat', providerName, modelName_, modelSelectionOptions, overridesOfModel) + // const includeInPayload = providerReasoningIOSettings?.input?.includeInPayload?.(reasoningInfo) || {} + + const thinkingConfig: GoogleThinkingConfig | undefined = !reasoningInfo?.isReasoningEnabled ? undefined + : reasoningInfo.type === 'budget_slider_value' ? + { thinkingBudget: reasoningInfo.reasoningBudget } + : undefined + + // tools + const staticToolsForCall = chatMode !== null ? getStaticTools(chatMode, disabledStaticTools) : []; + const dynamicToolsForCall = filterDynamicTools(additionalTools, disabledDynamicTools); + const potentialTools = chatMode !== null + ? geminiTools(chatMode, additionalTools, logService, disabledStaticTools, disabledDynamicTools) + : undefined; + const toolDefsMap = chatMode !== null ? buildToolDefsMap(staticToolsForCall, dynamicToolsForCall) : undefined; + const toolConfig = potentialTools && specialToolFormat === 'gemini-style' ? + potentialTools + : undefined + + // instance + const genAI = new GoogleGenAI({ apiKey: thisConfig.apiKey }); + + const { needsManualParse: needsManualReasoningParse } = providerReasoningIOSettings?.output ?? {}; + const manuallyParseReasoning = needsManualReasoningParse && canIOReasoning && openSourceThinkTags; + const needsXMLTools = !specialToolFormat || specialToolFormat === 'disabled'; + + if (manuallyParseReasoning || needsXMLTools) { + const thinkTags = manuallyParseReasoning ? openSourceThinkTags : null; + const { newOnText, newOnFinalMessage } = needsXMLTools + ? extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + thinkTags, + chatMode + ) + : extractReasoningWrapper( + onText, + onFinalMessage, + thinkTags, + chatMode + ); + onText = newOnText; + onFinalMessage = newOnFinalMessage; + } + + // when receive text + let fullReasoningSoFar = '' + let fullTextSoFar = '' + + let toolName = '' + let toolParamsStr = '' + let toolId = '' + let lastTokenUsage: LLMTokenUsage | undefined; + + + // Map requestParams (override mode) to Gemini generation config + let generationConfig: any = undefined; + if (requestParams && requestParams.mode === 'override' && requestParams.params && typeof requestParams.params === 'object') { + const p: any = requestParams.params; + generationConfig = { + ...(typeof p.temperature === 'number' ? { temperature: p.temperature } : {}), + ...(typeof p.top_p === 'number' ? { topP: p.top_p } : {}), + ...(typeof p.top_k === 'number' ? { topK: p.top_k } : {}), + ...(typeof p.max_tokens === 'number' ? { maxOutputTokens: p.max_tokens } : (typeof p.max_completion_tokens === 'number' ? { maxOutputTokens: p.max_completion_tokens } : {})), + ...(p.stop ? { stopSequences: (Array.isArray(p.stop) ? p.stop : [p.stop]) } : {}), + ...(typeof p.seed === 'number' ? { seed: p.seed } : {}), + }; + } + + genAI.models.generateContentStream({ + model: modelName, + config: { + systemInstruction: separateSystemMessage, + thinkingConfig: thinkingConfig, + tools: toolConfig, + ...(generationConfig ? { generationConfig } : {}), + }, + contents: messages as GeminiLLMChatMessage[], + }) + .then(async (stream) => { + _setAborter(() => { + try { + stream.return(fullTextSoFar); + } catch (e) { + // Ignore errors during abort + } + }); + + // Process the stream + for await (const chunk of stream) { + // message + const newText = chunk.text ?? '' + fullTextSoFar += newText + + // usage (best-effort; some chunks may not include it) + const usage = validateLLMTokenUsage(mapGeminiUsageToLLMTokenUsage((chunk as any)?.usageMetadata), logService); + if (usage) { + lastTokenUsage = usage; + } + + // tool call + const functionCalls = chunk.functionCalls + if (functionCalls && functionCalls.length > 0) { + const functionCall = functionCalls[0] // Get the first function call + toolName = functionCall.name ?? '' + toolParamsStr = JSON.stringify(functionCall.args ?? {}) + toolId = functionCall.id ?? '' + } + + // (do not handle reasoning yet) + + // call onText + const knownTool = !!toolName && ((toolDefsMap?.has(toolName)) || isAToolName(toolName)); + const usagePayload = lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}; + onText({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + toolCall: knownTool ? { name: toolName as ToolName, rawParams: {}, isDone: false, doneParams: [], id: toolId } : undefined, + ...usagePayload, + }) + } + + // on final + if (!fullTextSoFar && !fullReasoningSoFar && !toolName) { + onError({ message: 'Void: Response from model was empty.', fullError: null }) + } else { + if (!toolId) toolId = generateUuid() // ids are empty, but other providers might expect an id + const toolCall = rawToolCallObjOf(toolName, toolParamsStr, toolId, toolDefsMap) + const toolCallObj = toolCall ? { toolCall } : {} + onFinalMessage({ + fullText: fullTextSoFar, + fullReasoning: fullReasoningSoFar, + anthropicReasoning: null, + ...toolCallObj, + ...(lastTokenUsage ? { tokenUsage: lastTokenUsage } : {}), + }); + } + }) + .catch(error => { + const message = error?.message + if (typeof message === 'string') { + + if (error.message?.includes('API key')) { + onError({ message: invalidApiKeyMessage(providerName), fullError: error }); + } + else if (error?.message?.includes('429')) { + onError({ message: 'Rate limit reached. ' + error, fullError: error }); + } + else + onError({ message: error + '', fullError: error }); + } + else { + onError({ message: error + '', fullError: error }); + } + }) +}; + +const sendMistralFIMDynamic = async (params: SendFIMParams_Internal) => { + const { messages, onFinalMessage, onError, overridesOfModel, modelName: modelName_, providerName, dynamicRequestConfig } = params; + + const { modelName, supportsFIM } = getModelCapabilities(providerName, modelName_, overridesOfModel); + if (!supportsFIM) { + onError({ message: `Model ${modelName_} does not support FIM.`, fullError: null }); + return; + } + + try { + const { MistralCore } = await getMistralCoreModule(); + const { fimComplete } = await getMistralFimModule(); + + const apiKey = extractBearer(dynamicRequestConfig?.headers || {}); + const mistral = new MistralCore({ apiKey }); + + const response = await fimComplete(mistral, { + model: modelName, + prompt: messages.prefix, + suffix: messages.suffix, + stream: false, + maxTokens: 300, + stop: messages.stopTokens, + }); + + const content = response?.ok ? response.value.choices?.[0]?.message?.content ?? '' : ''; + const fullText = typeof content === 'string' + ? content + : (content || []).map((chunk: any) => (chunk.type === 'text' ? chunk.text : '')).join(''); + + onFinalMessage({ fullText, fullReasoning: '', anthropicReasoning: null }); + } catch (error) { + onError({ message: String(error), fullError: error }); + } +}; + +export const sendChatRouter = (params: SendChatParams_Internal) => { + return _sendOpenAICompatibleChat(params); +}; + +export const sendFIMRouter = async (params: SendFIMParams_Internal) => { + + if (params.dynamicRequestConfig?.fimTransport) { + switch (params.dynamicRequestConfig.fimTransport) { + case 'ollama-native': + return sendOllamaFIM({ ...params }); + case 'mistral-native': + return sendMistralFIMDynamic(params); + case 'openai-compatible': + return _sendOpenAICompatibleFIM(params); + case 'emulated': + params.onError({ message: `Emulated FIM is not yet implemented.`, fullError: null }); + return; + } + } + params.onError({ message: `FIM transport method not configured for this model.`, fullError: null }); +}; + +type OpenAIModel = { + id: string; + created: number; + object: 'model'; + owned_by: string; +}; + + +export const openaiCompatibleList = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider, providerName }: ListParams_Internal) => { + const onSuccess = ({ models }: { models: OpenAIModel[] }) => onSuccess_({ models }); + const onError = ({ error }: { error: string }) => onError_({ error }); + + try { + const openai = await newOpenAICompatibleSDK({ providerName, settingsOfProvider }); + openai.models.list() + .then(async (response) => { + const models: OpenAIModel[] = []; + models.push(...response.data); + while (response.hasNextPage()) { + models.push(...(await response.getNextPage()).data); + } + onSuccess({ models }); + }) + .catch((error) => onError({ error: String(error) })); + } catch (error) { + onError({ error: String(error) }); + } +}; + + +export const ollamaList = async ({ onSuccess: onSuccess_, onError: onError_, settingsOfProvider }: ListParams_Internal) => { + const onSuccess = ({ models }: { models: OllamaModelResponse[] }) => onSuccess_({ models }); + const onError = ({ error }: { error: string }) => onError_({ error }); + + try { + const thisConfig = settingsOfProvider.ollama; + const ollama = await newOllamaSDK({ endpoint: thisConfig.endpoint }); + try { + const response = await ollama.list(); + const { models } = response; + onSuccess({ models }); + } catch (error) { + onError({ error: String(error) }); + } + } catch (error) { + onError({ error: String(error) }); + } +}; + +export const listModelsRouter = async (params: ListParams_Internal) => { + const { providerName } = params; + if (providerName === 'ollama') { + return ollamaList(params as any); + } + return openaiCompatibleList(params as any); +}; + +export const __test = { + setAnthropicModule(mod: any) { + if (mod && mod.default) { + anthropicModule = mod as any; + } else { + anthropicModule = { + default: mod, + APIError: (mod?.APIError || class extends Error { }) + } as any; + } + }, + setGoogleGenAIModule(mod: any) { + googleGenAIModule = mod as any; + }, + setOpenAIModule(mod: any) { + openAIModule = mod as any; + }, + setGetSendableReasoningInfo(fn: typeof getSendableReasoningInfo) { + getSendableReasoningInfoImpl = fn; + }, + reset() { + openAIModule = undefined as any; + anthropicModule = undefined as any; + mistralCoreModule = undefined as any; + mistralFimModule = undefined as any; + googleGenAIModule = undefined as any; + ollamaModule = undefined as any; + getSendableReasoningInfoImpl = getSendableReasoningInfo; + }, +}; diff --git a/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts new file mode 100644 index 00000000000..a130b846a63 --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/sendLLMMessage.ts @@ -0,0 +1,181 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { SendLLMMessageParams, OnText, OnFinalMessage, OnError } from '../../common/sendLLMMessageTypes.js'; +import { IMetricsService } from '../../common/metricsService.js'; +import { displayInfoOfProviderName } from '../../common/voidSettingsTypes.js'; +import { sendChatRouter, sendFIMRouter } from './sendLLMMessage.impl.js'; +import { ILogService } from '../../../log/common/log.js'; +import type { INotificationService } from '../../../notification/common/notification.js'; + +export const sendLLMMessage = async ( + params: SendLLMMessageParams, + metricsService: IMetricsService, + logService?: ILogService, + notificationService?: INotificationService +) => { + const { + messagesType, + messages: messages_, + onText: onText_, + onFinalMessage: onFinalMessage_, + onError: onError_, + abortRef: abortRef_, + logging: { loggingName, loggingExtras }, + settingsOfProvider, + modelSelection, + modelSelectionOptions, + overridesOfModel, + chatMode, + separateSystemMessage, + tool_choice, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + dynamicRequestConfig, + requestParams, + providerRouting, + notifyOnTruncation, + } = params; + + const { providerName, modelName } = modelSelection; + + const captureLLMEvent = (eventId: string, extras?: object) => { + metricsService.capture(eventId, { + providerName, + modelName, + customEndpointURL: settingsOfProvider[providerName]?.endpoint, + numModelsAtEndpoint: settingsOfProvider[providerName]?.models?.length, + ...(messagesType === 'chatMessages' + ? { numMessages: messages_?.length } + : messagesType === 'FIMMessage' + ? { prefixLength: messages_.prefix.length, suffixLength: messages_.suffix.length } + : {}), + ...loggingExtras, + ...extras, + }); + }; + + const submitAt = Date.now(); + + let fullTextSoFar = ''; + let aborter: (() => void) | null = null; + let didAbort = false; + + const setAborter = (fn: () => void) => { + aborter = fn; + }; + + const onText: OnText = (p) => { + if (didAbort) return; + fullTextSoFar = p.fullText; + onText_(p); + }; + + const onFinalMessage: OnFinalMessage = (p) => { + if (didAbort) return; + const durationMs = Date.now() - submitAt; + captureLLMEvent(`${loggingName} - Received Full Message`, { + messageLength: p.fullText?.length ?? 0, + reasoningLength: p.fullReasoning?.length ?? 0, + durationMs, + toolCallName: p.toolCall?.name, + }); + onFinalMessage_(p); + }; + + const onError: OnError = ({ message, fullError }) => { + if (didAbort) return; + + let errorMessage = message; + if (errorMessage === 'TypeError: fetch failed') { + errorMessage = `Failed to fetch from ${displayInfoOfProviderName(providerName).title}. This likely means you specified the wrong endpoint in Void's Settings, or your local model provider like Ollama is powered off.`; + } + + captureLLMEvent(`${loggingName} - Error`, { error: errorMessage }); + onError_({ message: errorMessage, fullError }); + }; + + const onAbort = () => { + captureLLMEvent(`${loggingName} - Abort`, { messageLengthSoFar: fullTextSoFar.length }); + try { + aborter?.(); + } catch { + // ignore + } + didAbort = true; + }; + abortRef_.current = onAbort; + + if (messagesType === 'chatMessages') { + captureLLMEvent(`${loggingName} - Sending Message`); + } else if (messagesType === 'FIMMessage') { + captureLLMEvent(`${loggingName} - Sending FIM`, { + prefixLen: messages_?.prefix?.length, + suffixLen: messages_?.suffix?.length, + }); + } + + try { + if (messagesType === 'chatMessages') { + await sendChatRouter({ + messages: messages_, + onText, + onFinalMessage, + onError, + settingsOfProvider, + modelSelectionOptions, + overridesOfModel, + modelName, + _setAborter: setAborter, + providerName, + separateSystemMessage, + tool_choice, + chatMode, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + dynamicRequestConfig, + requestParams, + providerRouting, + notifyOnTruncation, + logService, + notificationService, + }); + return; + } + + if (messagesType === 'FIMMessage') { + await sendFIMRouter({ + messages: messages_, + onText, + onFinalMessage, + onError, + settingsOfProvider, + modelSelectionOptions, + overridesOfModel, + modelName, + _setAborter: setAborter, + providerName, + separateSystemMessage, + dynamicRequestConfig, + requestParams, + providerRouting, + notifyOnTruncation, + logService, + notificationService, + }); + return; + } + + onError({ message: `Error: Message type "${messagesType}" not recognized.`, fullError: null }); + } catch (error) { + if (error instanceof Error) { + onError({ message: error + '', fullError: error }); + } else { + onError({ message: `Unexpected Error in sendLLMMessage: ${error}`, fullError: error as any }); + } + } +}; diff --git a/src/vs/platform/void/electron-main/llmMessage/test/extractGrammar.test.ts b/src/vs/platform/void/electron-main/llmMessage/test/extractGrammar.test.ts new file mode 100644 index 00000000000..a803e7f11cf --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/test/extractGrammar.test.ts @@ -0,0 +1,754 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import type { RawToolCallObj, LLMPlan } from '../../../common/sendLLMMessageTypes.js'; +import { extractReasoningAndXMLToolsWrapper, extractReasoningWrapper } from '../extractGrammar.js'; + +type CapturedText = { fullText: string; fullReasoning: string; toolCall?: RawToolCallObj; plan?: LLMPlan }; +type CapturedFinal = { fullText: string; fullReasoning: string; toolCall?: RawToolCallObj; anthropicReasoning: any; plan?: LLMPlan }; + +const toolsListOverride = [ + { + name: 'edit_file', + params: { + uri: { required: true }, + original_snippet: { required: true }, + updated_snippet: { required: true }, + } + }, + { + name: 'search_in_file', + params: { + uri: { required: true }, + query: { required: true }, + } + }, +] as any; + +suite('extractReasoningAndXMLToolsWrapper', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + let capturedText: CapturedText[]; + let capturedFinal: CapturedFinal | null; + + const onText = (p: CapturedText) => { capturedText.push(p); }; + const onFinalMessage = (p: CapturedFinal) => { capturedFinal = p; }; + + + setup(() => { + capturedText = []; + capturedFinal = null; + }); + + test('parses edit_file with open+close and CDATA, marks done and strips XML from UI', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, // think tags + {} as any, // chatMode + { toolsListOverride } + ); + + const xml = [ + '', + ' /path/to/file.ts', + ' ', + ' ', + '' + ].join('\n'); + + newOnText({ + fullText: `Intro\n${xml}\nOutro`, + fullReasoning: '', + }); + + newOnFinalMessage({ + fullText: `Intro\n${xml}\nOutro`, + fullReasoning: '', + anthropicReasoning: null + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'edit_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + assert.deepStrictEqual( + [...lastText.toolCall!.doneParams].sort(), + ['uri', 'original_snippet', 'updated_snippet'].sort() + ); + + + assert.strictEqual(lastText.fullText.trim(), 'Intro'); + + assert.ok(capturedFinal, 'final message should exist'); + assert.strictEqual(capturedFinal!.toolCall?.name, 'edit_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('auto-completes when missing closing tag (openOnly), if all required params are present', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + const xmlOpenOnly = [ + '', + ' /path/to/file.ts', + ' old', + ' new', + + ].join('\n'); + + newOnText({ + fullText: `X\n${xmlOpenOnly}\nY`, + fullReasoning: '' + }); + + newOnFinalMessage({ + fullText: `X\n${xmlOpenOnly}\nY`, + fullReasoning: '', + anthropicReasoning: null + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'edit_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + assert.deepStrictEqual( + [...lastText.toolCall!.doneParams].sort(), + ['uri', 'original_snippet', 'updated_snippet'].sort() + ); + assert.strictEqual(lastText.fullText.trim(), 'X'); + + assert.strictEqual(capturedFinal!.toolCall?.name, 'edit_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('synthesizes open when only closing tag exists (closeOnly) and parameters exist before it', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + const fragment = [ + ' /path/to/file.ts', + ' aaa', + ' bbb', + '' + ].join('\n'); + + newOnText({ + fullText: `Alpha\n${fragment}\nBeta`, + fullReasoning: '' + }); + + newOnFinalMessage({ + fullText: `Alpha\n${fragment}\nBeta`, + fullReasoning: '', + anthropicReasoning: null + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'edit_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + assert.deepStrictEqual( + [...lastText.toolCall!.doneParams].sort(), + ['uri', 'original_snippet', 'updated_snippet'].sort() + ); + assert.strictEqual(lastText.fullText.trim(), 'Alpha'); + + assert.strictEqual(capturedFinal!.toolCall?.name, 'edit_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('does not override XML tool with inbound empty-name toolCall', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + const xml = [ + '', + ' /p', + ' o', + ' u', + '' + ].join('\n'); + + + const inbound = { name: '', rawParams: {}, doneParams: [], id: 'x', isDone: true } as unknown as RawToolCallObj; + + newOnText({ + fullText: xml, + fullReasoning: '', + toolCall: inbound + }); + + newOnFinalMessage({ + fullText: xml, + fullReasoning: '', + anthropicReasoning: null, + toolCall: inbound + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'edit_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + + assert.strictEqual(capturedFinal!.toolCall?.name, 'edit_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('splits provider reasoning by : before -> Reasoning, after -> visible text', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + {} as any, + { toolsListOverride } + ); + + newOnText({ + fullText: '', + fullReasoning: 'AAABBB', + }); + + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.fullReasoning, 'AAA'); + assert.strictEqual(last.fullText, 'BBB'); + assert.strictEqual(last.toolCall, undefined); + }); + + test('ignores XML-like tool tags inside fenced code block (```...```)', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + const txt = [ + 'Intro', + '```xml', + '', + ' /ignored/in/code.ts', + ' should_not_trigger', + ' should_not_trigger', + '', + '```', + 'Outro' + ].join('\n'); + + newOnText({ fullText: txt, fullReasoning: '' }); + newOnFinalMessage({ fullText: txt, fullReasoning: '', anthropicReasoning: null }); + + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.toolCall, undefined, 'toolCall must NOT be detected from code block'); + + assert.ok(last.fullText.includes('```xml'), 'fenced block should remain visible in UI text'); + assert.ok(last.fullText.includes(''), 'XML inside code block should remain as plain text'); + }); + + test('parses real XML tool outside code block while ignoring fenced block content', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + const fenced = [ + '```xml', + '', + ' /ignored.ts', + ' no', + ' no', + '', + '```' + ].join('\n'); + + const realXml = [ + '', + ' /apply.ts', + ' old', + ' new', + '' + ].join('\n'); + + const txt = ['Intro', fenced, 'Real call below:', realXml, 'Tail'].join('\n'); + + newOnText({ fullText: txt, fullReasoning: '' }); + newOnFinalMessage({ fullText: txt, fullReasoning: '', anthropicReasoning: null }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'real XML tool outside code block should be detected'); + assert.strictEqual(last.toolCall!.name, 'edit_file'); + assert.strictEqual(last.toolCall!.isDone, true); + assert.deepStrictEqual( + [...last.toolCall!.doneParams].sort(), + ['uri', 'original_snippet', 'updated_snippet'].sort() + ); + + + assert.ok(last.fullText.includes('```xml'), 'fenced block should remain in UI text'); + assert.ok(last.fullText.includes('Real call below:'), 'prefix before real XML should remain'); + }); + + test('handles partial tag split across chunks', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + {} as any, + { toolsListOverride: [] as any } + ); + + + const beforeLen = capturedText.length; + newOnText({ fullText: 'some ', fullReasoning: 'preinnertail' }); + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.fullReasoning, 'preinner'); + assert.strictEqual(last.fullText, 'tail'); + }); + + test('auto-detects think tags incrementally when tags are split across cumulative provider reasoning', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride: [] as any } + ); + + const beforeLen = capturedText.length; + newOnText({ fullText: '', fullReasoning: 'prefinnertail' }); + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.fullReasoning, 'prefinner'); + assert.strictEqual(last.fullText, 'tail'); + }); + + test('hides trailing partial { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + newOnText({ + fullText: 'Intro\n { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + {} as any, + { toolsListOverride } + ); + + newOnText({ + fullText: 'Intro\n { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any, + { toolsListOverride } + ); + + const xml = [ + '', + ' /path/r.ts', + ' ', + ' ', + '', + ].join('\n'); + + newOnText({ + fullText: '', + fullReasoning: `\nPrefix\n${xml}\nSuffix\n`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'toolCall should exist from reasoning'); + assert.strictEqual(last.toolCall!.name, 'edit_file'); + assert.strictEqual(last.toolCall!.isDone, true); + assert.ok(last.fullReasoning.includes('Prefix'), 'prefix should remain in UI reasoning'); + assert.ok(last.fullReasoning.includes('Suffix'), 'suffix should remain in UI reasoning'); + assert.ok(!last.fullReasoning.includes(''), 'XML should be stripped from UI reasoning'); + }); + + test('parses wrapper from provider reasoning (reasoning-only) and strips wrapper from UI reasoning when done', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any, + { toolsListOverride } + ); + + + const wrapper = [ + '', + ' ', + ' /wrapped.ts', + ' old', + ' new', + ' ', + '' + ].join('\n'); + + newOnText({ + fullText: '', + fullReasoning: `\nBefore\n${wrapper}\nAfter\n`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'toolCall should exist from wrapper in reasoning'); + assert.strictEqual(last.toolCall!.name, 'edit_file'); + assert.strictEqual(last.toolCall!.isDone, true); + assert.ok(!last.fullReasoning.includes(' { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'normal' as any, + { toolsListOverride } + ); + + const xml = [ + '', + ' /nope.ts', + ' o', + ' u', + '', + ].join('\n'); + + newOnText({ + fullText: '', + fullReasoning: `\n${xml}\n`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.toolCall, undefined, 'toolCall should NOT be parsed from reasoning in normal mode'); + }); + + test('reasoning-only wrapper: extracts reasoning but does NOT parse XML tool calls', () => { + const { newOnText } = extractReasoningWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any + ); + + const xml = [ + '', + ' /no-xml-parse.ts', + ' old', + ' new', + '', + ].join('\n'); + + newOnText({ + fullText: '', + fullReasoning: `\nreasoning\n${xml}`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.fullReasoning, '\nreasoning\n'); + assert.ok(last.fullText.includes(''), 'xml should remain in visible text in reasoning-only mode'); + assert.strictEqual(last.toolCall, undefined, 'toolCall must NOT be extracted in reasoning-only mode'); + }); + + test('ignores XML-like tool tags inside fenced code block in provider reasoning', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any, + { toolsListOverride } + ); + + const txt = [ + '', + 'Intro', + '```xml', + '', + ' /ignored.ts', + ' no', + ' no', + '', + '```', + 'Outro', + '', + ].join('\n'); + + newOnText({ fullText: '', fullReasoning: txt }); + + const last = capturedText[capturedText.length - 1]; + assert.strictEqual(last.toolCall, undefined, 'toolCall must NOT be detected from XML inside fenced block in reasoning'); + }); + + test('MERGE BUG: tool params split across reasoning/text should be merged (requires mergeToolCall union)', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any, + { toolsListOverride } + ); + + + newOnText({ + fullText: '', + fullReasoning: `\n\n/split.ts\n`, + }); + + + newOnText({ + fullText: [ + '', + ' o', + ' u', + '', + ].join('\n'), + fullReasoning: '', + }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'toolCall should exist'); + assert.strictEqual(last.toolCall!.name, 'edit_file'); + + + assert.strictEqual(last.toolCall!.rawParams.uri, '/split.ts'); + assert.strictEqual(last.toolCall!.rawParams.original_snippet, 'o'); + assert.strictEqual(last.toolCall!.rawParams.updated_snippet, 'u'); + assert.strictEqual(last.toolCall!.isDone, true); + }); + test('does not strip open wrapper from reasoning while toolCall is not done (streaming)', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + ['', ''], + 'agent' as any, + { toolsListOverride } + ); + + const wrapperOpenOnly = [ + '', + ' ', + ' /wrapped.ts', + ].join('\n'); + + newOnText({ + fullText: '', + fullReasoning: `\nBefore\n${wrapperOpenOnly}\n`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(!last.toolCall || last.toolCall.isDone === false, 'toolCall should be absent or not done'); + assert.ok(last.fullReasoning.includes(' { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + null, + { toolsListOverride } + ); + + const wrapper = [ + '', + ' ', + ' ./src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts ', + ' squigglyStart ', + ' ', + '', + ].join(''); + + newOnText({ + fullText: `Intro ${wrapper}`, + fullReasoning: '', + }); + + newOnFinalMessage({ + fullText: `Intro ${wrapper}`, + fullReasoning: '', + anthropicReasoning: null + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'search_in_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + assert.strictEqual(lastText.toolCall!.rawParams.uri, './src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts'); + assert.strictEqual(lastText.toolCall!.rawParams.query, 'squigglyStart'); + assert.strictEqual(lastText.fullText.trim(), 'Intro'); + + assert.ok(capturedFinal, 'final message should exist'); + assert.strictEqual(capturedFinal!.toolCall?.name, 'search_in_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('parses inline wrapper from provider reasoning in agent mode, strips wrapper from UI reasoning when done', () => { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + 'agent' as any, + { toolsListOverride } + ); + + const wrapper = [ + '', + ' ', + ' ./src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts ', + ' squigglyStart ', + ' ', + '', + ].join(''); + + newOnText({ + fullText: '', + fullReasoning: `Before ${wrapper} After`, + }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'toolCall should exist from reasoning'); + assert.strictEqual(last.toolCall!.name, 'search_in_file'); + assert.strictEqual(last.toolCall!.isDone, true); + assert.strictEqual(last.toolCall!.rawParams.uri, './src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts'); + assert.strictEqual(last.toolCall!.rawParams.query, 'squigglyStart'); + + assert.ok(last.fullReasoning.includes('Before'), 'prefix should remain in UI reasoning'); + assert.ok(last.fullReasoning.includes('After'), 'suffix should remain in UI reasoning'); + assert.ok(!last.fullReasoning.includes(' wrapper from main text stream even when inline (not block), converts to toolCall and strips wrapper from UI text', () => { + const { newOnText, newOnFinalMessage } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + null, + { toolsListOverride } + ); + + // IMPORTANT: wrapper is inline (no newlines), reproduces real UI stream case + const wrapper = [ + '', + ' ', + ' ./src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts ', + ' squigglyStart ', + ' ', + '', + ].join(''); + + newOnText({ + fullText: `Intro ${wrapper} Outro`, + fullReasoning: '', + }); + + newOnFinalMessage({ + fullText: `Intro ${wrapper} Outro`, + fullReasoning: '', + anthropicReasoning: null + }); + + const lastText = capturedText[capturedText.length - 1]; + assert.ok(lastText.toolCall, 'toolCall should exist'); + assert.strictEqual(lastText.toolCall!.name, 'search_in_file'); + assert.strictEqual(lastText.toolCall!.isDone, true); + + // Values should be trimmed (provider often pads with spaces) + assert.strictEqual( + lastText.toolCall!.rawParams.uri, + './src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts' + ); + assert.strictEqual(lastText.toolCall!.rawParams.query, 'squigglyStart'); + + // Wrapper should be stripped from UI text (only visible text remains) + assert.strictEqual(lastText.fullText, 'Intro'); + + assert.ok(capturedFinal, 'final message should exist'); + assert.strictEqual(capturedFinal!.toolCall?.name, 'search_in_file'); + assert.strictEqual(capturedFinal!.toolCall?.isDone, true); + }); + + test('parses tool_call when " { + const { newOnText } = extractReasoningAndXMLToolsWrapper( + onText, + onFinalMessage, + null, + null, + { toolsListOverride } + ); + + const firstChunk = 'Intro ', + ' ', + ' ./split.ts ', + ' splitQuery ', + ' ', + '', + ].join(''); + + newOnText({ fullText: firstChunk, fullReasoning: '' }); + newOnText({ fullText: secondChunk, fullReasoning: '' }); + + const last = capturedText[capturedText.length - 1]; + assert.ok(last.toolCall, 'toolCall should exist after second chunk'); + assert.strictEqual(last.toolCall!.name, 'search_in_file'); + assert.strictEqual(last.toolCall!.isDone, true); + assert.strictEqual(last.toolCall!.rawParams.uri, './split.ts'); + assert.strictEqual(last.toolCall!.rawParams.query, 'splitQuery'); + }); +}); diff --git a/src/vs/platform/void/electron-main/llmMessage/test/reasoningConfigPropagation.test.ts b/src/vs/platform/void/electron-main/llmMessage/test/reasoningConfigPropagation.test.ts new file mode 100644 index 00000000000..338c1c88b33 --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/test/reasoningConfigPropagation.test.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { getProviderCapabilities, SendableReasoningInfo } from '../../../common/modelInference.js'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; + +// Sanity for provider capability wiring for OpenRouter/Groq-like +suite('Reasoning config propagation', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + test('OpenRouter: budget slider yields includeInPayload and debug fields', async () => { + const providerName = 'openrouter' as any; + const modelName = 'moonshotai/kimi-k2-thinking'; + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName); + // Simulate UI state: enabled + budget value + const reasoningState: SendableReasoningInfo = { type: 'budget_slider_value', isReasoningEnabled: true, reasoningBudget: 1234 }; + const payload = providerReasoningIOSettings.input?.includeInPayload?.(reasoningState) || {}; + + assert.deepStrictEqual(payload, { reasoning: { max_tokens: 1234 } }); + + // Verify debug fields exist from providerReasoningIOSettings.output + const output = providerReasoningIOSettings.output; + // For OpenRouter path we expect a delta field + assert.strictEqual(output?.nameOfFieldInDelta, 'reasoning'); + assert.strictEqual((output as any)?.needsManualParse, undefined); + }); + + test('Groq: enabled reasoning sets parsed reasoning and delta field', async () => { + const providerName = 'groq' as any; + const modelName = 'llama-3.3-70b-versatile'; + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName); + const reasoningState: SendableReasoningInfo = { type: 'enabled_only', isReasoningEnabled: true }; + const payload = providerReasoningIOSettings.input?.includeInPayload?.(reasoningState) || {}; + + assert.deepStrictEqual(payload, { reasoning_format: 'parsed' }); + assert.strictEqual(providerReasoningIOSettings.output?.nameOfFieldInDelta, 'reasoning'); + }); + + test('Anthropic: budget slider maps to thinking config and no delta name', async () => { + const providerName = 'anthropic' as any; + const modelName = 'claude-3-5-sonnet-latest'; + + const { providerReasoningIOSettings } = getProviderCapabilities(providerName, modelName); + const reasoningState: SendableReasoningInfo = { type: 'budget_slider_value', isReasoningEnabled: true, reasoningBudget: 256 }; + const payload = providerReasoningIOSettings.input?.includeInPayload?.(reasoningState) || {}; + + assert.deepStrictEqual(payload, { thinking: { type: 'enabled', budget_tokens: 256 } }); + // Anthropic handled via content blocks; we should not rely on a specific delta field name here + assert.strictEqual(providerReasoningIOSettings.output?.nameOfFieldInDelta, undefined); + }); +}); diff --git a/src/vs/platform/void/electron-main/llmMessage/test/sendLLMMessage.impl.test.ts b/src/vs/platform/void/electron-main/llmMessage/test/sendLLMMessage.impl.test.ts new file mode 100644 index 00000000000..fe7bc8c0bdc --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/test/sendLLMMessage.impl.test.ts @@ -0,0 +1,1147 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { sendChatRouter, runStream, __test as implTestExports } from '../sendLLMMessage.impl.js'; +import { setDynamicModelService } from '../../../common/modelInference.js'; +import type { OnFinalMessage, OnText } from '../../../common/sendLLMMessageTypes.js'; +// eslint-disable-next-line local/code-import-patterns +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; + +type Delta = { + content?: string | null | any[]; // <-- allow array-of-parts + tool_calls?: Array<{ + index?: number; + type: 'function'; + id?: string; + function?: { name?: string; arguments?: any }; + }> | null; + + function_call?: { name?: string; arguments?: any } | null; + + [key: string]: any; +}; + +type Choice = { + delta?: Delta; + finish_reason?: 'stop' | 'tool_calls' | 'function_call' | 'length' | null; +}; + +type Chunk = { choices: [Choice] }; + +const makeChunk = ({ + content, + contentParts, + finish, + tool, + legacyFunctionCall, + reasoningFieldName, + reasoningDelta, + reasoningDetails, +}: { + content?: string | null | any[]; + contentParts?: any[] | null; + finish?: Choice['finish_reason']; + tool?: { index?: number; id?: string; name?: string; args?: any } | null; + legacyFunctionCall?: { name?: string; args?: any } | null; + reasoningFieldName?: string; + reasoningDelta?: string | null; + reasoningDetails?: Array<{ type: string; text: string }> | null; +}): Chunk => { + const delta: Delta = {}; + + // IMPORTANT: contentParts is just a test convenience: we map it to delta.content + // to simulate providers that stream content as an array-of-parts. + if (contentParts !== undefined) delta.content = contentParts; + else if (content !== undefined) delta.content = content; + + if (tool) { + const tc: any = { + type: 'function', + id: tool.id ?? 'call_x', + function: { name: tool.name, arguments: tool.args ?? '' }, + }; + if (tool.index !== undefined) tc.index = tool.index; + delta.tool_calls = [tc]; + } + + if (legacyFunctionCall) { + delta.function_call = { + name: legacyFunctionCall.name, + arguments: legacyFunctionCall.args ?? '', + }; + } + + if (reasoningFieldName && reasoningDelta) { + delta[reasoningFieldName] = reasoningDelta; + } + + if (reasoningDetails) { + delta.reasoning_details = reasoningDetails; + } + + return { choices: [{ delta, finish_reason: finish ?? null }] }; +}; + +const makeAsyncStream = (chunks: Chunk[], opts?: { abortError?: boolean; delayMs?: number }) => { + let aborted = false; + const delayMs = opts?.delayMs ?? 0; + + const controller = { + abort: () => { + aborted = true; + }, + }; + + async function* iterator() { + for (const ch of chunks) { + if (aborted && opts?.abortError) { + const err: any = new Error('AbortError'); + err.name = 'AbortError'; + throw err; + } + if (delayMs) await new Promise(r => setTimeout(r, delayMs)); + yield ch; + } + } + + return { + controller, + [Symbol.asyncIterator]: iterator, + }; +}; + +const makeNonStreamResp = (choice: { + content?: string; + tool_calls?: Array<{ id?: string; type: 'function'; function: { name?: string; arguments?: string } }>; + reasoningFieldName?: string; + reasoningContent?: string; + reasoning_details?: Array<{ type: string; text: string }>; +}) => { + const message: any = { content: choice.content ?? '' }; + if (choice.tool_calls) message.tool_calls = choice.tool_calls; + if (choice.reasoning_details) message.reasoning_details = choice.reasoning_details; + if (choice.reasoningFieldName && choice.reasoningContent) { + message[choice.reasoningFieldName] = choice.reasoningContent; + } + return { choices: [{ message }] }; +}; + +const makeFakeOpenAIClient = (response: any) => { + return { + chat: { + completions: { + create: async (_opts: any) => response, + }, + }, + } as any; +}; + +// ---------- capture helpers ---------- + +const newCaptures = () => { + const texts: Array<{ fullText: string; fullReasoning: string; toolCall?: any }> = []; + let final: { fullText: string; fullReasoning: string; toolCall?: any; anthropicReasoning: any } | null = null; + + const onText: OnText = (p) => texts.push(p); + const onFinalMessage: OnFinalMessage = (p) => { final = p as any; }; + + return { + texts, + onText, + onFinalMessage, + getFinal: () => final, + get lastText() { + return texts[texts.length - 1]; + } + }; +}; + +const newNotificationCapture = () => { + const notifications: any[] = []; + return { + notifications, + notificationService: { + notify: (payload: any) => { + notifications.push(payload); + }, + }, + }; +}; + +// minimal toolDefsMap (optional) +const toolDefsMap: Map = new Map([ + ['read_file', { name: 'read_file', params: { uri: {}, start_line: {}, end_line: {} } }], +]); + +suite('runStream (OpenAI-compatible)', () => { + ensureNoDisposablesAreLeakedInTestSuite() + test('A1: stream only text (content)', async () => { + const stream = makeAsyncStream([ + makeChunk({ content: 'Hello', finish: null }), + makeChunk({ content: ' world', finish: 'stop' }), + ]); + + const caps = newCaptures(); + + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + assert.ok(caps.texts.length >= 2); + assert.strictEqual(caps.lastText.fullText, 'Hello world'); + assert.strictEqual(caps.lastText.fullReasoning, ''); + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'Hello world'); + assert.strictEqual(fin.fullReasoning, ''); + assert.strictEqual(fin.toolCall, undefined); + }); + + test('A1b: stream text as array-of-parts (delta.content = [{text}])', async () => { + const stream = makeAsyncStream([ + makeChunk({ contentParts: [{ type: 'text', text: 'Hello' }], finish: null }), + makeChunk({ contentParts: [{ type: 'text', text: ' world' }], finish: 'stop' }), + ]); + + const caps = newCaptures(); + + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'Hello world'); + }); + + test('A1c: truncated stream emits notification by default', async () => { + const stream = makeAsyncStream([ + makeChunk({ content: 'partial answer', finish: 'length' }), + ]); + + const caps = newCaptures(); + const n = newNotificationCapture(); + + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + lengthRetryPolicy: { enabled: false }, + notificationService: n.notificationService as any, + }); + + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'partial answer'); + assert.strictEqual(n.notifications.length, 1, 'must notify once on truncation'); + assert.strictEqual(n.notifications[0]?.id, 'void.llm.outputTruncated'); + }); + + test('A1d: notifyOnTruncation=false suppresses truncation notification', async () => { + const stream = makeAsyncStream([ + makeChunk({ content: 'partial answer', finish: 'length' }), + ]); + + const caps = newCaptures(); + const n = newNotificationCapture(); + + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + lengthRetryPolicy: { enabled: false }, + notificationService: n.notificationService as any, + notifyOnTruncation: false, + }); + + assert.strictEqual(n.notifications.length, 0, 'notification must be suppressed'); + }); + + test('A3: reasoning_details overrides reasoning_field', async () => { + const chunks = [ + makeChunk({ reasoningFieldName: 'reasoning_content', reasoningDelta: 'scratch 1' }), + makeChunk({ reasoningDetails: [{ type: 'reasoning.text', text: 'real ' }] }), + makeChunk({ reasoningDetails: [{ type: 'reasoning.text', text: 'reasoning' }], finish: 'stop' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: 'reasoning_content', + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + assert.ok(caps.lastText.fullReasoning.endsWith('reasoning')); + assert.strictEqual(caps.getFinal()!.fullReasoning, 'real reasoning'); + }); + + test('B4: openai-style tool_calls (stream, multi-part args)', async () => { + const partial1 = '{"uri":"/tmp/a.ts", "start_line": 1'; + const partial2 = ', "end_line": 50}'; + const chunks = [ + makeChunk({ tool: { name: 'read_file', id: 'call_1', args: partial1 } }), + makeChunk({ tool: { name: 'read_file', id: 'call_1', args: partial2 }, finish: 'tool_calls' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + + assert.ok(caps.texts.some(t => t.toolCall && t.toolCall.name === 'read_file' && t.toolCall.isDone === false)); + const fin = caps.getFinal()!; + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + assert.strictEqual(fin.toolCall!.isDone, true); + }); + + test('B4b: tool_calls without index must still be parsed (no empty response)', async () => { + const partial1 = '{"uri":"/tmp/a.ts", "start_line": 1'; + const partial2 = ', "end_line": 50}'; + + const chunks = [ + // index intentionally omitted + makeChunk({ tool: { index: undefined, name: 'read_file', id: 'call_1', args: partial1 } }), + makeChunk({ tool: { index: undefined, name: 'read_file', id: 'call_1', args: partial2 }, finish: 'tool_calls' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + const fin = caps.getFinal()!; + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + assert.strictEqual(fin.toolCall!.isDone, true); + }); + + test('B4c: legacy delta.function_call must be supported (stream)', async () => { + const partial1 = '{"uri":"/tmp/a.ts", "start_line": 1'; + const partial2 = ', "end_line": 2}'; + + const chunks = [ + makeChunk({ legacyFunctionCall: { name: 'read_file', args: partial1 } }), + makeChunk({ legacyFunctionCall: { name: 'read_file', args: partial2 }, finish: 'function_call' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + const fin = caps.getFinal()!; + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + assert.strictEqual(fin.toolCall!.isDone, true); + }); + + test('B4d: tool_calls arguments can be an object (not only string)', async () => { + const chunks = [ + makeChunk({ + tool: { + // also omit index to cover both at once + index: undefined, + id: 'call_2', + name: 'read_file', + args: { uri: '/tmp/a.ts', start_line: 1, end_line: 3 }, // <-- object + }, + finish: 'tool_calls', + }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + const fin = caps.getFinal()!; + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + assert.strictEqual(fin.toolCall!.isDone, true); + }); + + test('B4e: stopOnFirstToolCall abort path must still yield final toolCall (AbortError simulation)', async () => { + // first chunk already contains complete JSON, so runStream may abort immediately + const chunks = [ + makeChunk({ + tool: { index: undefined, id: 'call_abort', name: 'read_file', args: '{"uri":"/x","start_line":1,"end_line":2}' }, + finish: null, + }), + // would be next chunk, but generator will throw AbortError when aborted + makeChunk({ content: 'SHOULD_NOT_REACH', finish: 'stop' }), + ]; + + // abortError=true makes iterator throw AbortError once controller.abort() was called + const stream = makeAsyncStream(chunks, { abortError: true }); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + stopOnFirstToolCall: true, + }); + + const fin = caps.getFinal()!; + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + }); + + test('B4f: tool_calls with index=1 must still be parsed (no empty response)', async () => { + const chunks = [ + makeChunk({ tool: { index: 1, id: 'call_i1', name: 'read_file', args: '{"uri":"/x","start_line":1' } }), + makeChunk({ tool: { index: 1, id: 'call_i1', name: 'read_file', args: ',"end_line":2}' }, finish: 'tool_calls' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + const fin = caps.getFinal()!; + assert.ok(fin.toolCall); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + assert.strictEqual(fin.toolCall!.isDone, true); + }); + + test('C7: non-stream with content + tool_calls + reasoning_details', async () => { + const nonStream = makeNonStreamResp({ + content: 'Answer', + tool_calls: [{ type: 'function', function: { name: 'read_file', arguments: '{"uri":"/x","start_line":1,"end_line":2}' } }], + reasoning_details: [{ type: 'reasoning.text', text: 'think A' }, { type: 'reasoning.text', text: ' + B' }], + }); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(nonStream), + options: { model: 'o4-mini', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: 'reasoning_content', + providerName: 'openAICompatible' as any, + toolDefsMap, + }); + + assert.strictEqual(caps.texts.length, 0, 'non-stream should not emit progress onText'); + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'Answer'); + assert.strictEqual(fin.fullReasoning, 'think A + B'); + assert.ok(fin.toolCall); + assert.strictEqual(fin.toolCall!.name, 'read_file'); + }); + + test('E13: empty tool name -> no toolCall in final', async () => { + const nonStream = makeNonStreamResp({ + content: 'ok', + tool_calls: [{ type: 'function', function: { name: '', arguments: '{}' } }], + }); + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(nonStream), + options: { model: 'x', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: undefined, + providerName: 'openAICompatible' as any, + }); + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'ok'); + assert.strictEqual(fin.toolCall, undefined); + }); +}); + +suite('sendLLMMessageToProviderImplementation integrations', () => { + ensureNoDisposablesAreLeakedInTestSuite() + setup(() => { + implTestExports.reset?.(); + }); + + test('Anthropic: streams text/thinking/tool_use, produces final with toolCall', async () => { + // Fake Anthropic module + class FakeAnthropic { + static APIError = class extends Error { status = 401 }; + constructor(_opts: any) { } + messages = { + stream: (_args: any) => { + const handlers: Record = {}; + const api = { + on: (event: string, cb: Function) => { + (handlers[event] ||= []).push(cb); + }, + controller: { abort() { /* no-op for test */ } }, + }; + // schedule events + queueMicrotask(() => { + // text block start + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'text', text: 'Hi' } })); + // thinking block start + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'thinking', thinking: 'thoughts' } })); + // tool use start (name only) + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'tool_use', name: 'read_file', id: 'tool_1' } })); + // tool args delta + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_delta', delta: { type: 'input_json_delta', partial_json: '{"uri":"/a","start_line":1,"end_line":2}' } })); + // finalMessage + const final = { + content: [ + { type: 'thinking', thinking: 'final-think' }, + { type: 'tool_use', id: 'tool_1', name: 'read_file', input: { uri: '/a', start_line: 1, end_line: 2 } }, + ], + }; + handlers['finalMessage']?.forEach(fn => fn(final)); + }); + return api; + } + } + } + implTestExports.setAnthropicModule?.(FakeAnthropic as any); + + const caps = newCaptures(); + let resolveDone: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { caps.onFinalMessage(p); resolveDone!(); }; + const onText: OnText = caps.onText; + const onError = (e: any) => assert.fail('onError: ' + e.message); + + await sendChatRouter({ + messages: [{ role: 'user', content: 'hi' } as any], + providerName: 'anthropic' as any, + onText, onFinalMessage: onFinal, onError, + settingsOfProvider: { anthropic: { apiKey: 'k' } } as any, + modelSelectionOptions: {} as any, + overridesOfModel: {} as any, + modelName: 'claude-3', + _setAborter: () => { }, + separateSystemMessage: undefined, + chatMode: null as any, + additionalTools: undefined, + }); + + await done; + + assert.ok(caps.texts.length > 0); + const fin = caps.getFinal()!; + assert.ok(fin, 'final expected'); + assert.ok(fin.toolCall, 'final toolCall expected'); + assert.strictEqual(fin.toolCall.name, 'read_file'); + assert.strictEqual(fin.toolCall.isDone, true); + }); + + test('Gemini: generateContentStream yields text and functionCalls, final returns toolCall', async () => { + // Fake GoogleGenAI module + class FakeStream { + private chunks: any[]; + constructor(chunks: any[]) { this.chunks = chunks; } + async *[Symbol.asyncIterator]() { + for (const ch of this.chunks) yield ch; + } + return(_x: any) { /* allow abort */ } + } + class FakeGoogleGenAI { + constructor(_opts: any) { } + models = { + generateContentStream: (_args: any) => { + const chunks = [ + { text: 'Hello ' }, + { text: 'world' }, + { + functionCalls: [{ name: 'read_file', args: { uri: '/b', start_line: 1, end_line: 3 }, id: 'g1' }] + }, + ]; + return Promise.resolve(new FakeStream(chunks)); + } + } + } + implTestExports.setGoogleGenAIModule?.({ GoogleGenAI: FakeGoogleGenAI } as any); + + const caps = newCaptures(); + let resolveDone: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { caps.onFinalMessage(p); resolveDone!(); }; + const onText: OnText = caps.onText; + const onError = (e: any) => assert.fail('onError: ' + e.message); + + await sendChatRouter({ + messages: [{ role: 'user', parts: [{ text: 'hi' }] } as any], + separateSystemMessage: undefined, + onText, onFinalMessage: onFinal, onError, + settingsOfProvider: { gemini: { apiKey: 'k' } } as any, + overridesOfModel: {} as any, + modelName: 'gemini-1.5', + _setAborter: () => { }, + providerName: 'gemini' as any, + modelSelectionOptions: {} as any, + chatMode: null as any, + additionalTools: undefined, + }); + + await done; + + assert.ok(caps.texts.length >= 2, 'should emit text progress'); + const fin = caps.getFinal()!; + assert.ok(fin, 'final expected'); + assert.strictEqual(fin.fullText, 'Hello world'); + assert.ok(fin.toolCall); + assert.strictEqual(fin.toolCall.name, 'read_file'); + assert.strictEqual(fin.toolCall.isDone, true); + }); + + test('stream only reasoning_field (no details)', async () => { + const chunks = [ + makeChunk({ reasoningFieldName: 'reasoning_content', reasoningDelta: 'foo' }), + makeChunk({ reasoningFieldName: 'reasoning_content', reasoningDelta: 'bar', finish: 'stop' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'x', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: 'reasoning_content', + providerName: 'openAICompatible' as any, + }); + + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullReasoning, 'foobar'); + assert.strictEqual(fin.fullText, ''); + }); + + test('stream content + reasoning_field together', async () => { + const chunks = [ + makeChunk({ content: 'X', reasoningFieldName: 'reasoning_content', reasoningDelta: 'r1' }), + makeChunk({ content: 'Y', reasoningFieldName: 'reasoning_content', reasoningDelta: 'r2', finish: 'stop' }), + ]; + const stream = makeAsyncStream(chunks); + + const caps = newCaptures(); + await runStream({ + openai: makeFakeOpenAIClient(stream), + options: { model: 'x', messages: [], stream: true } as any, + onText: caps.onText, + onFinalMessage: caps.onFinalMessage, + onError: (e) => assert.fail('onError ' + e.message), + _setAborter: () => { }, + nameOfReasoningFieldInDelta: 'reasoning_content', + providerName: 'openAICompatible' as any, + }); + + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'XY'); + assert.strictEqual(fin.fullReasoning, 'r1r2'); + }); + + test('Anthropic: includes redacted_thinking along with thinking and tool_use in final', async () => { + class FakeAnthropic { + static APIError = class extends Error { status = 401 }; + constructor(_opts: any) { } + messages = { + stream: (_args: any) => { + const handlers: Record = {}; + const api = { + on: (event: string, cb: Function) => { (handlers[event] ||= []).push(cb); }, + controller: { abort() { } }, + }; + queueMicrotask(() => { + // text + thinking + tool_use start + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'text', text: 'Hi' } })); + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'thinking', thinking: 'scratch' } })); + handlers['streamEvent']?.forEach(fn => fn({ type: 'content_block_start', content_block: { type: 'tool_use', name: 'read_file', id: 'tool_1' } })); + + const final = { + content: [ + { type: 'thinking', thinking: 'visible-think' }, + { type: 'redacted_thinking', data: { hidden: true } }, + { type: 'tool_use', id: 'tool_1', name: 'read_file', input: { uri: '/a', start_line: 1, end_line: 2 } }, + ], + }; + handlers['finalMessage']?.forEach(fn => fn(final)); + }); + return api; + } + } + } + implTestExports.setAnthropicModule?.(FakeAnthropic as any); + + const caps = newCaptures(); + let resolveDone!: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { caps.onFinalMessage(p); resolveDone(); }; + + await sendChatRouter({ + messages: [{ role: 'user', content: 'hi' } as any], + providerName: 'anthropic' as any, + onText: caps.onText, + onFinalMessage: onFinal, + onError: (e) => assert.fail('onError: ' + e.message), + settingsOfProvider: { anthropic: { apiKey: 'k' } } as any, + modelSelectionOptions: {} as any, + overridesOfModel: {} as any, + modelName: 'claude-3', + _setAborter: () => { }, + separateSystemMessage: undefined, + chatMode: null as any, + additionalTools: undefined, + }); + + await done; + + const fin = caps.getFinal()!; + assert.ok(Array.isArray(fin.anthropicReasoning), 'anthropicReasoning should be array'); + assert.ok(fin.anthropicReasoning.some((x: any) => x.type === 'thinking')); + assert.ok(fin.anthropicReasoning.some((x: any) => x.type === 'redacted_thinking')); + assert.ok(fin.toolCall, 'toolCall expected'); + assert.strictEqual(fin.toolCall.name, 'read_file'); + assert.strictEqual(fin.toolCall.isDone, true); + }); + + test('OpenRouter deepseek :free respects dynamicRequestConfig.specialToolFormat="disabled" (no tools sent)', async () => { + + + + + const fakeDynamicService: any = { + getDynamicCapabilities(modelName: string) { + if (modelName === 'deepseek/deepseek-r1-0528:free') { + return { + contextWindow: 4096, + reservedOutputTokenSpace: 4096, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: false, + cost: { input: 0, output: 0 }, + }; + } + return null; + }, + getAllDynamicCapabilities() { + return {}; + } + }; + setDynamicModelService(fakeDynamicService); + + let capturedOptions: any = null; + class FakeOpenAI { + chat = { + completions: { + create: async (opts: any) => { + capturedOptions = opts; + + return { + choices: [{ + message: { content: 'ok', tool_calls: undefined } + }] + }; + }, + }, + }; + } + implTestExports.setOpenAIModule?.({ default: FakeOpenAI, APIError: class extends Error { } } as any); + + const caps = newCaptures(); + let resolveDone!: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { caps.onFinalMessage(p); resolveDone(); }; + + await sendChatRouter({ + messages: [{ role: 'user', content: 'hi' } as any], + separateSystemMessage: undefined, + onText: caps.onText, + onFinalMessage: onFinal, + onError: (e) => assert.fail('onError: ' + e.message), + settingsOfProvider: { + openrouter: { + endpoint: 'https://openrouter.ai/api/v1', + apiKey: 'sk-test', + apiStyle: 'openai-compatible', + } + } as any, + modelSelectionOptions: {} as any, + overridesOfModel: {} as any, + modelName: 'deepseek/deepseek-r1-0528:free', + _setAborter: () => { }, + providerName: 'openrouter' as any, + chatMode: null as any, + additionalTools: [ + { name: 'read_file', description: 'd', params: { uri: {}, start_line: {}, end_line: {} } } + ] as any, + dynamicRequestConfig: { + endpoint: 'https://openrouter.ai/api/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: false as any, + specialToolFormat: 'disabled' as any, + headers: { Authorization: 'Bearer sk-test', Accept: 'application/json' }, + } as any, + requestParams: undefined, + }); + + await done; + + assert.ok(capturedOptions, 'OpenAI.create should have been called'); + + + + assert.strictEqual( + capturedOptions.tools, + undefined, + 'options.tools must be omitted when specialToolFormat is disabled for the selected model' + ); + }); + + test('OpenAI-compatible chat forwards notifyOnTruncation=false into runStream', async () => { + setDynamicModelService({ + getDynamicCapabilities(_modelName: string) { + return { + contextWindow: 4096, + reservedOutputTokenSpace: 4096, + supportsSystemMessage: 'system-role', + specialToolFormat: 'openai-style', + supportsFIM: false, + reasoningCapabilities: false, + cost: { input: 0, output: 0 }, + }; + }, + getAllDynamicCapabilities() { + return {}; + }, + } as any); + + let createCallCount = 0; + class FakeOpenAI { + chat = { + completions: { + create: async (_opts: any) => { + createCallCount += 1; + return { + choices: [{ + finish_reason: 'length', + message: { content: `attempt-${createCallCount}` }, + }], + }; + }, + }, + }; + } + implTestExports.setOpenAIModule?.({ default: FakeOpenAI, APIError: class extends Error { } } as any); + + const n = newNotificationCapture(); + const caps = newCaptures(); + let resolveDone!: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { + caps.onFinalMessage(p); + resolveDone(); + }; + + await sendChatRouter({ + messages: [{ role: 'user', content: 'hi' } as any], + separateSystemMessage: undefined, + onText: caps.onText, + onFinalMessage: onFinal, + onError: (e) => assert.fail('onError: ' + e.message), + settingsOfProvider: { + openrouter: { + endpoint: 'https://openrouter.ai/api/v1', + apiKey: 'sk-test', + apiStyle: 'openai-compatible', + } + } as any, + modelSelectionOptions: {} as any, + overridesOfModel: {} as any, + modelName: 'deepseek/deepseek-r1-0528:free', + _setAborter: () => { }, + providerName: 'openrouter' as any, + chatMode: null as any, + additionalTools: undefined, + dynamicRequestConfig: { + endpoint: 'https://openrouter.ai/api/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: false as any, + specialToolFormat: 'openai-style' as any, + headers: { Authorization: 'Bearer sk-test', Accept: 'application/json' }, + } as any, + requestParams: undefined, + notificationService: n.notificationService as any, + notifyOnTruncation: false, + }); + + await done; + + assert.strictEqual(createCallCount, 2, 'runStream should retry once on finish_reason=length'); + assert.strictEqual(n.notifications.length, 0, 'notification should respect notifyOnTruncation=false'); + }); + + test('OpenAI-compatible XML mode: retries once with correction prompt when XML tool-call is malformed', async () => { + setDynamicModelService({ + getDynamicCapabilities(_modelName: string) { + return { + contextWindow: 4096, + reservedOutputTokenSpace: 4096, + supportsSystemMessage: 'system-role', + specialToolFormat: 'disabled', + supportsFIM: false, + reasoningCapabilities: false, + cost: { input: 0, output: 0 }, + }; + }, + getAllDynamicCapabilities() { + return {}; + }, + } as any); + + const createCalls: any[] = []; + class FakeOpenAI { + chat = { + completions: { + create: async (opts: any) => { + createCalls.push(opts); + const callNo = createCalls.length; + + // 1st response: malformed attribute-style XML on tool tag + if (callNo === 1) { + return { + choices: [{ + message: { + content: '', + tool_calls: undefined, + }, + }], + }; + } + + // 2nd response: valid nested-params XML + return { + choices: [{ + message: { + content: [ + '', + ' ./x.ts', + ' ', + ' ', + '', + ].join('\n'), + tool_calls: undefined, + }, + }], + }; + }, + }, + }; + } + implTestExports.setOpenAIModule?.({ default: FakeOpenAI, APIError: class extends Error { } } as any); + + const caps = newCaptures(); + let resolveDone!: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { + caps.onFinalMessage(p); + resolveDone(); + }; + + await sendChatRouter({ + messages: [{ role: 'user', content: 'fix ./x.ts' } as any], + separateSystemMessage: undefined, + onText: caps.onText, + onFinalMessage: onFinal, + onError: (e) => assert.fail('onError: ' + e.message), + settingsOfProvider: { + openrouter: { + endpoint: 'https://openrouter.ai/api/v1', + apiKey: 'sk-test', + apiStyle: 'openai-compatible', + } + } as any, + modelSelectionOptions: {} as any, + overridesOfModel: {} as any, + modelName: 'deepseek/deepseek-r1-0528:free', + _setAborter: () => { }, + providerName: 'openrouter' as any, + chatMode: 'agent' as any, + additionalTools: undefined, + dynamicRequestConfig: { + endpoint: 'https://openrouter.ai/api/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: false as any, + specialToolFormat: 'disabled' as any, + headers: { Authorization: 'Bearer sk-test', Accept: 'application/json' }, + } as any, + requestParams: undefined, + }); + + await done; + + assert.strictEqual(createCalls.length, 2, 'should retry once after malformed XML tool-call'); + const secondMessages = createCalls[1]?.messages ?? []; + const correctionMsg = secondMessages[secondMessages.length - 1]; + assert.strictEqual(correctionMsg?.role, 'user', 'retry should append a user correction message'); + assert.ok( + typeof correctionMsg?.content === 'string' && correctionMsg.content.includes('invalid XML tool call'), + 'correction prompt should explicitly mention invalid XML tool call' + ); + + const fin = caps.getFinal()!; + assert.ok(fin?.toolCall, 'final toolCall should be parsed after retry'); + assert.strictEqual(fin.toolCall?.name, 'edit_file'); + assert.strictEqual(fin.toolCall?.isDone, true); + }); + + test('Gemini: passes thinkingConfig (budget slider) when reasoning enabled', async () => { + + implTestExports.setGetSendableReasoningInfo((_context: any, _providerName: any, _modelName: any, _modelSelectionOptions: any, _overridesOfModel: any) => { + return { isReasoningEnabled: true, type: 'budget_slider_value', reasoningBudget: 2048 }; + }); + + let capturedConfig: any = null; + + class FakeStream { + private chunks: any[]; + constructor(chunks: any[]) { this.chunks = chunks; } + async *[Symbol.asyncIterator]() { for (const ch of this.chunks) yield ch; } + return(_x: any) { } + } + class FakeGoogleGenAI { + constructor(_opts: any) { } + models = { + generateContentStream: (args: any) => { + capturedConfig = args.config; + const chunks = [ + { text: 'Hello ' }, + { text: 'world' }, + { functionCalls: [{ name: 'read_file', args: { uri: '/b', start_line: 1, end_line: 3 }, id: 'g1' }] }, + ]; + return Promise.resolve(new FakeStream(chunks)); + } + } + } + implTestExports.setGoogleGenAIModule?.({ GoogleGenAI: FakeGoogleGenAI } as any); + + const caps = newCaptures(); + let resolveDone!: () => void; + const done = new Promise(r => { resolveDone = r; }); + const onFinal: OnFinalMessage = (p) => { caps.onFinalMessage(p); resolveDone(); }; + + await sendChatRouter({ + messages: [{ role: 'user', parts: [{ text: 'hi' }] } as any], + separateSystemMessage: undefined, + onText: caps.onText, + onFinalMessage: onFinal, + onError: (e) => assert.fail('onError: ' + e.message), + settingsOfProvider: { gemini: { apiKey: 'k' } } as any, + overridesOfModel: {} as any, + modelName: 'gemini-1.5', + _setAborter: () => { }, + providerName: 'gemini' as any, + modelSelectionOptions: {} as any, + chatMode: null as any, + additionalTools: undefined, + }); + + await done; + + + assert.ok(capturedConfig && capturedConfig.thinkingConfig, 'thinkingConfig should be passed'); + assert.strictEqual(capturedConfig.thinkingConfig.thinkingBudget, 2048); + + const fin = caps.getFinal()!; + assert.strictEqual(fin.fullText, 'Hello world'); + assert.ok(fin.toolCall); + assert.strictEqual(fin.toolCall.name, 'read_file'); + assert.strictEqual(fin.toolCall.isDone, true); + }); +}); diff --git a/src/vs/platform/void/electron-main/llmMessage/toolSchemaConversion.ts b/src/vs/platform/void/electron-main/llmMessage/toolSchemaConversion.ts new file mode 100644 index 00000000000..aa7a23800ff --- /dev/null +++ b/src/vs/platform/void/electron-main/llmMessage/toolSchemaConversion.ts @@ -0,0 +1,459 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { createRequire } from 'node:module'; + +import type { AdditionalToolInfo } from '../../common/sendLLMMessageTypes.js'; +import type { ToolCallParams } from '../../common/toolsServiceTypes.js'; +import { voidTools, type InternalToolInfo } from '../../common/toolsRegistry.js'; +import type { ILogService } from '../../../log/common/log.js'; + +const require = createRequire(import.meta.url); + +type ZodModule = typeof import('zod'); +const { z } = require('zod') as ZodModule; + +type ZodNumber = import('zod').ZodNumber; +type ZodBoolean = import('zod').ZodBoolean; +type ZodTypeAny = import('zod').ZodTypeAny; +type AnyZodObject = import('zod').ZodObject; + +type ZodToJsonSchemaModule = typeof import('zod-to-json-schema'); +const { zodToJsonSchema } = require('zod-to-json-schema') as ZodToJsonSchemaModule; + +type GoogleGenAIModule = typeof import('@google/genai'); +const googleGenAIModule = require('@google/genai') as GoogleGenAIModule; +const { Type } = googleGenAIModule; + +type FunctionDeclaration = import('@google/genai').FunctionDeclaration; +type Schema = import('@google/genai').Schema; +type GeminiType = (typeof Type)[keyof typeof Type]; + +type OpenAIChatCompletionTool = import('openai/resources/chat/completions/completions.js').ChatCompletionTool; +type AnthropicTool = import('@anthropic-ai/sdk').Anthropic.Tool; + +export const ToolSchemas = { + read_file: z.object({ + uri: z.string().describe('URI of the file'), + start_line: z.number().int().optional().describe('1-based start line (optional)'), + end_line: z.number().int().optional().describe('1-based end line (optional)'), + lines_count: z.number().int().optional().describe('Number of lines to read from start_line (optional)'), + page_number: z.number().int().optional().describe('Page number (optional)'), + }), + ls_dir: z.object({ + uri: z.string().optional().describe('Directory URI (optional)'), + page_number: z.number().int().optional().describe('Page number (optional)'), + }), + get_dir_tree: z.object({ + uri: z.string().describe('Directory URI'), + }), + search_pathnames_only: z.object({ + query: z.string().describe('Search query'), + include_pattern: z.string().nullable().optional().describe('File pattern to include (optional)'), + page_number: z.number().int().optional().describe('Page number (optional)'), + }), + search_for_files: z.object({ + query: z.string().describe('Search query'), + is_regex: z.boolean().optional().describe('Whether the query is a regex (optional)'), + search_in_folder: z.string().nullable().optional().describe('Folder to search in (optional)'), + page_number: z.number().int().optional().describe('Page number (optional)'), + }), + search_in_file: z.object({ + uri: z.string().describe('File URI'), + query: z.string().describe('Search query'), + is_regex: z.boolean().optional().describe('Whether the query is a regex (optional)'), + }), + read_lint_errors: z.object({ + uri: z.string().describe('File URI'), + }), + rewrite_file: z.object({ + uri: z.string().describe('File URI'), + new_content: z.string().describe('New content of the file'), + }), + edit_file: z.object({ + uri: z.string().describe('File URI'), + original_snippet: z.string().describe('Exact snippet to find (copy verbatim from file)'), + updated_snippet: z.string().describe('Replacement content'), + occurrence: z.number().int().nullable().optional().describe('1-based occurrence index to replace (optional)'), + replace_all: z.boolean().optional().describe('If true, replace all occurrences'), + location_hint: z.object({ + line: z.number().int().optional().describe('Approx 1-based line number (optional)'), + anchor_before: z.string().optional().describe('Short unique line before snippet (optional)'), + anchor_after: z.string().optional().describe('Short unique line after snippet (optional)'), + }).nullable().optional().describe('Optional disambiguation hints'), + encoding: z.string().nullable().optional().describe('File encoding (default utf8)'), + newline: z.string().nullable().optional().describe('newline handling: preserve|lf|crlf'), + }), + create_file_or_folder: z.object({ + uri: z.string().describe('URI of the file or folder'), + }), + delete_file_or_folder: z.object({ + uri: z.string().describe('URI of the file or folder'), + is_recursive: z.boolean().optional().describe('Whether to delete recursively (optional)'), + }), + run_command: z.object({ + command: z.string().describe('Command to execute'), + cwd: z.string().nullable().optional().describe('Working directory (optional)'), + }), + open_persistent_terminal: z.object({ + cwd: z.string().nullable().optional().describe('Working directory (optional)'), + }), + run_persistent_command: z.object({ + command: z.string().describe('Command to run'), + persistent_terminal_id: z.string().describe('Persistent terminal ID'), + }), + kill_persistent_terminal: z.object({ + persistent_terminal_id: z.string().describe('Persistent terminal ID to kill'), + }), +} satisfies { [K in keyof ToolCallParams]: AnyZodObject }; + +type AnyToolInfo = InternalToolInfo | AdditionalToolInfo; + +const dbg = (logService: ILogService | undefined, msg: string, data?: unknown) => { + if (!logService?.debug) return; + logService.debug(`[toolSchemaConversion] ${msg}`, data); +}; + +const warn = (logService: ILogService | undefined, msg: string, data?: unknown) => { + if (!logService?.warn) return; + logService.warn(`[toolSchemaConversion] ${msg}`, data); +}; + +const safeJson = (obj: any) => { + try { + return JSON.stringify(obj, null, 2); + } catch { + return String(obj); + } +}; + +const isOptionalParam = (paramInfo: any): boolean => { + if (paramInfo?.required === false) return true; + const desc = String(paramInfo?.description || '').toLowerCase(); + return /\boptional\b/.test(desc); +}; + +const normalizeType = (raw?: string): 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object' => { + const t = String(raw || 'string').toLowerCase(); + if (['int', 'integer', 'int32', 'int64'].includes(t)) return 'integer'; + if (['number', 'float', 'double'].includes(t)) return 'number'; + if (['boolean', 'bool'].includes(t)) return 'boolean'; + if (t === 'array') return 'array'; + if (t === 'object') return 'object'; + if (t === 'string') return 'string'; + return 'string'; +}; + +const tryNumber = (val: any) => { + if (typeof val === 'string' && val.trim() !== '') { + const n = Number(val); + if (!isNaN(n)) return n; + } + return val; +}; + +const preprocessedNumber = (schema: ZodNumber) => + z.preprocess(tryNumber, schema); + +const preprocessedBoolean = (schema: ZodBoolean) => + z.preprocess((val) => { + if (typeof val === 'string') { + const v = val.toLowerCase(); + if (v === 'true') return true; + if (v === 'false') return false; + } + return val; + }, schema); + +const preprocessedArray = (itemSchema: ZodTypeAny) => + z.preprocess((val) => { + if (typeof val === 'string') { + try { + const parsed = JSON.parse(val); + return parsed; + } catch { /* noop */ } + } + return val; + }, z.array(itemSchema)); + +const preprocessedObject = (objSchema: AnyZodObject) => + z.preprocess((val) => { + if (typeof val === 'string') { + try { + const parsed = JSON.parse(val); + return parsed; + } catch { /* noop */ } + } + return val; + }, objSchema); + +const isBuiltInTool = (name: string): name is keyof ToolCallParams => { + return name in ToolSchemas; +}; + +export const paramInfoToZod = (paramInfo: any, logService?: ILogService): ZodTypeAny => { + if (!paramInfo || typeof paramInfo !== 'object') return z.string(); + + dbg(logService, 'paramInfoToZod called with', paramInfo); + + const t = normalizeType(paramInfo.type); + + + if (Array.isArray(paramInfo.enum) && paramInfo.enum.length > 0) { + const values = paramInfo.enum; + + const allNumbers = values.every( + (v: any) => typeof v === 'number' || (typeof v === 'string' && v.trim() !== '' && !isNaN(Number(v))) + ); + const allBooleans = values.every( + (v: any) => typeof v === 'boolean' || (typeof v === 'string' && ['true', 'false'].includes(v.toLowerCase())) + ); + + if (allNumbers || t === 'number' || t === 'integer') { + const nums = values.map((v: any) => Number(v)); + const base = + t === 'integer' + ? preprocessedNumber(z.number().int()) + : preprocessedNumber(z.number()); + return base + .refine((v) => nums.includes(v), paramInfo.description || 'Must be one of enum values') + .describe(paramInfo.description || ''); + } + + if (allBooleans || t === 'boolean') { + const bools = values.map((v: any) => (typeof v === 'boolean' ? v : v.toLowerCase() === 'true')); + return preprocessedBoolean(z.boolean()) + .refine((v) => bools.includes(v), paramInfo.description || 'Must be one of enum values') + .describe(paramInfo.description || ''); + } + + return z.enum(values.map(String) as [string, ...string[]]).describe(paramInfo.description || ''); + } + + switch (t) { + case 'number': { + return preprocessedNumber(z.number()).describe(paramInfo.description || ''); + } + case 'integer': { + return preprocessedNumber(z.number().int()).describe(paramInfo.description || ''); + } + case 'boolean': { + return preprocessedBoolean(z.boolean()).describe(paramInfo.description || ''); + } + case 'array': { + const items = paramInfo.items || { type: 'string' }; + return preprocessedArray(paramInfoToZod(items, logService)).describe(paramInfo.description || ''); + } + case 'object': { + const shape: Record = {}; + const props = paramInfo.properties || {}; + for (const [k, v] of Object.entries(props)) { + let child = paramInfoToZod(v, logService); + if (isOptionalParam(v)) child = child.optional(); + shape[k] = child; + } + if (Array.isArray(paramInfo.required) && paramInfo.required.length > 0) { + for (const key of Object.keys(shape)) { + if (!paramInfo.required.includes(key)) { + shape[key] = shape[key].optional(); + } + } + } + return preprocessedObject(z.object(shape)).describe(paramInfo.description || ''); + } + case 'string': + default: { + return z.string().describe(paramInfo.description || ''); + } + } +}; + +export const buildZodSchemaForTool = (toolInfo: AnyToolInfo, logService?: ILogService): AnyZodObject => { + const name = toolInfo.name as keyof ToolCallParams; + + if (name in ToolSchemas) { + return ToolSchemas[name]; + } + + const dynamicParams = (toolInfo as AdditionalToolInfo).params || {}; + dbg(logService, 'MCP tool received', { + name: toolInfo.name, + description: (toolInfo as any).description, + params: dynamicParams, + }); + + const zodProps: Record = {}; + + for (const [paramName, paramInfo] of Object.entries(dynamicParams)) { + const rawType = (paramInfo as any)?.type; + const normType = normalizeType(rawType); + + dbg(logService, 'MCP param mapping', { + tool: toolInfo.name, + param: paramName, + rawType, + normType, + enum: (paramInfo as any)?.enum, + optional: isOptionalParam(paramInfo), + description: (paramInfo as any)?.description, + }); + + let zodType = paramInfoToZod(paramInfo, logService); + if (isOptionalParam(paramInfo)) zodType = zodType.optional(); + zodProps[paramName] = zodType; + } + + const schema = z.object(zodProps); + try { + const json = zodToJsonSchema(schema, { target: 'openApi3', $refStrategy: 'none' }); + dbg(logService, 'Generated JSON schema from MCP (pre-provider)', json); + } catch (e) { + warn(logService, 'Failed to generate JSON schema for debug', e); + } + + return schema; +}; + +const applyVoidToolsDescriptionOverrides = (toolName: string, jsonSchema: any) => { + if (!jsonSchema?.properties) return; + const vt = (voidTools as any)[toolName]; + if (!vt?.params) return; + + for (const key of Object.keys(jsonSchema.properties)) { + const overrideDesc = vt.params?.[key]?.description; + if (overrideDesc) { + jsonSchema.properties[key] = jsonSchema.properties[key] || {}; + jsonSchema.properties[key].description = overrideDesc; + } + } +}; + + +const jsonSchemaToGeminiSchema = (js: any): Schema => { + const toType = (t?: string): GeminiType => { + switch ((t || '').toLowerCase()) { + case 'object': return Type.OBJECT; + case 'array': return Type.ARRAY; + case 'number': return Type.NUMBER; + case 'integer': return Type.NUMBER; + case 'boolean': return Type.BOOLEAN; + case 'string': + default: return Type.STRING; + } + }; + + const recurse = (node: any): Schema => { + if (!node || typeof node !== 'object') { + return { type: Type.STRING }; + } + + if (Array.isArray(node.enum) && node.enum.length > 0) { + return { + type: toType(node.type), + description: node.description, + enum: node.enum, + }; + } + + const t = toType(node.type); + + if (t === Type.OBJECT) { + const out: Schema = { + type: t, + description: node.description, + properties: {}, + required: Array.isArray(node.required) ? node.required : [], + }; + if (node.properties && typeof node.properties === 'object') { + for (const [k, v] of Object.entries(node.properties)) { + (out.properties as any)[k] = recurse(v); + } + } + return out; + } + + if (t === Type.ARRAY) { + return { + type: t, + description: node.description, + items: recurse(node.items || { type: 'string' }), + }; + } + + return { + type: t, + description: node.description, + }; + }; + + return recurse(js); +}; + +const getZodSchemaForTool = (toolInfo: AnyToolInfo, logService?: ILogService): AnyZodObject => { + const name = (toolInfo as any).name as string; + if (isBuiltInTool(name)) { + return ToolSchemas[name as keyof ToolCallParams]; + } + return buildZodSchemaForTool(toolInfo as AdditionalToolInfo, logService); +}; + +export const toOpenAICompatibleTool = ( + toolInfo: AnyToolInfo, + logService?: ILogService +): OpenAIChatCompletionTool => { + const { name, description } = toolInfo as { name: string; description: string }; + + const zodSchema = getZodSchemaForTool(toolInfo, logService); + const parameters = { + ...zodToJsonSchema(zodSchema, { target: 'openApi3', $refStrategy: 'none' }), + additionalProperties: false, + }; + + applyVoidToolsDescriptionOverrides(name, parameters); + + return { + type: 'function', + function: { + name, + description, + parameters, + strict: false, + }, + }; +}; + +export const toAnthropicTool = (toolInfo: AnyToolInfo, logService?: ILogService): AnthropicTool => { + const { name, description } = toolInfo as { name: string; description: string }; + + const zodSchema = getZodSchemaForTool(toolInfo, logService); + const input_schema = { + ...zodToJsonSchema(zodSchema, { $refStrategy: 'none' }), + additionalProperties: false, + }; + + dbg(logService, `Anthropic tool.input_schema for ${name}`, input_schema); + + return { + name, + description, + input_schema, + } as AnthropicTool; +}; + +export const toGeminiTool = (toolInfo: AnyToolInfo, logService?: ILogService): FunctionDeclaration => { + const { name, description } = toolInfo as { name: string; description: string }; + + const zodSchema = getZodSchemaForTool(toolInfo, logService); + const parametersJson = zodToJsonSchema(zodSchema, { $refStrategy: 'none' }); + const parameters = jsonSchemaToGeminiSchema(parametersJson); + const finalParams: Schema = + parameters.type === (Type as any).OBJECT + ? parameters + : { type: (Type as any).OBJECT, properties: {} }; + + dbg(logService, `Gemini tool.parameters for ${name}`, finalParams); + + return { name, description, parameters: finalParams }; +}; diff --git a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts b/src/vs/platform/void/electron-main/mcpChannel.ts similarity index 54% rename from src/vs/workbench/contrib/void/electron-main/mcpChannel.ts rename to src/vs/platform/void/electron-main/mcpChannel.ts index e5c4fb6e72b..7c198df5ddd 100644 --- a/src/vs/workbench/contrib/void/electron-main/mcpChannel.ts +++ b/src/vs/platform/void/electron-main/mcpChannel.ts @@ -7,16 +7,27 @@ // can't make a service responsible for this, because it needs // to be connected to the main process and node dependencies -import { IServerChannel } from '../../../../base/parts/ipc/common/ipc.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; +import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { Emitter, Event } from '../../../base/common/event.js'; import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; -import { MCPConfigFileJSON, MCPConfigFileEntryJSON, MCPServer, RawMCPToolCall, MCPToolErrorResponse, MCPServerEventResponse, MCPToolCallParams, removeMCPToolNamePrefix } from '../common/mcpServiceTypes.js'; +import { + MCPConfigFileJSON, + MCPConfigFileEntryJSON, + MCPServer, + RawMCPToolCall, + MCPToolErrorResponse, + MCPServerEventResponse, + MCPToolCallParams, + addMCPToolNamePrefix, + removeMCPToolNamePrefix +} from '../common/mcpServiceTypes.js'; import { Transport } from '@modelcontextprotocol/sdk/shared/transport.js'; import { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; import { MCPUserStateOfName } from '../common/voidSettingsTypes.js'; +import { ILogService } from '../../../platform/log/common/log.js'; const getClientConfig = (serverName: string) => { return { @@ -26,20 +37,11 @@ const getClientConfig = (serverName: string) => { } } -type MCPServerNonError = MCPServer & { status: Omit } -type MCPServerError = MCPServer & { status: 'error' } - - - type ClientInfo = { - _client: Client, // _client is the client that connects with an mcp client. We're calling mcp clients "server" everywhere except here for naming consistency. - mcpServerEntryJSON: MCPConfigFileEntryJSON, - mcpServer: MCPServerNonError, -} | { - _client?: undefined, - mcpServerEntryJSON: MCPConfigFileEntryJSON, - mcpServer: MCPServerError, -} + _client?: Client; + mcpServerEntryJSON: MCPConfigFileEntryJSON; + mcpServer: MCPServer; +}; type InfoOfClientId = { [clientId: string]: ClientInfo @@ -66,6 +68,7 @@ export class MCPChannel implements IServerChannel { } constructor( + @ILogService private readonly logService: ILogService ) { } // browser uses this to listen for changes @@ -105,97 +108,138 @@ export class MCPChannel implements IServerChannel { } } catch (e) { - console.error('mcp channel: Call Error:', e) + this.logService.error('mcp channel: Call Error:', e) } } - // server functions - + private _prefixToolNames(serverName: string, tools: { name: string;[k: string]: any }[]) { + return tools.map(({ name, ...rest }) => ({ + name: addMCPToolNamePrefix(serverName, name), + ...rest + })); + } - private async _refreshMCPServers(params: { mcpConfigFileJSON: MCPConfigFileJSON, userStateOfName: MCPUserStateOfName, addedServerNames: string[], removedServerNames: string[], updatedServerNames: string[] }) { + private _filterToolsByExclude(server: MCPConfigFileEntryJSON, tools: { name: string; [k: string]: any }[]) { + const exclude = Array.isArray(server.excludeTools) + ? new Set(server.excludeTools.map(v => String(v ?? '').trim()).filter(Boolean)) + : new Set(); + if (exclude.size === 0) return tools; + return tools.filter(t => !exclude.has(String(t?.name ?? '').trim())); + } - const { - mcpConfigFileJSON, - userStateOfName, - addedServerNames, - removedServerNames, - updatedServerNames, - } = params + private _commandString(server: MCPConfigFileEntryJSON): string { + if ((server as any).url) { + const u = (server as any).url; + return typeof u === 'string' ? u : u.toString(); + } + if (server.command) { + return `${server.command} ${server.args?.join(' ') || ''}`.trim(); + } + return ''; + } - const { mcpServers: mcpServersJSON } = mcpConfigFileJSON + private async _refreshMCPServers(params: { + mcpConfigFileJSON: MCPConfigFileJSON, + userStateOfName: MCPUserStateOfName, + addedServerNames: string[], + removedServerNames: string[], + updatedServerNames: string[] + }) { + const { mcpConfigFileJSON, userStateOfName, addedServerNames, removedServerNames, updatedServerNames } = params; + const { mcpServers: mcpServersJSON } = mcpConfigFileJSON; const allChanges: { type: 'added' | 'removed' | 'updated', serverName: string }[] = [ ...addedServerNames.map(n => ({ serverName: n, type: 'added' }) as const), ...removedServerNames.map(n => ({ serverName: n, type: 'removed' }) as const), ...updatedServerNames.map(n => ({ serverName: n, type: 'updated' }) as const), - ] - - await Promise.all( - allChanges.map(async ({ serverName, type }) => { + ]; - // check if already refreshing - if (this._refreshingServerNames.has(serverName)) return - this._refreshingServerNames.add(serverName) + await Promise.all(allChanges.map(async ({ serverName, type }) => { + if (this._refreshingServerNames.has(serverName)) return; + this._refreshingServerNames.add(serverName); + try { const prevServer = this.infoOfClientId[serverName]?.mcpServer; - // close and delete the old client + // close+delete old if (type === 'removed' || type === 'updated') { - await this._closeClient(serverName) - delete this.infoOfClientId[serverName] - this.mcpEmitters.serverEvent.onDelete.fire({ response: { prevServer, name: serverName, } }) + await this._closeClient(serverName); + delete this.infoOfClientId[serverName]; + this.mcpEmitters.serverEvent.onDelete.fire({ response: { prevServer, name: serverName } }); } - // create a new client + // create new if (type === 'added' || type === 'updated') { - const clientInfo = await this._createClient(mcpServersJSON[serverName], serverName, userStateOfName[serverName]?.isOn) - this.infoOfClientId[serverName] = clientInfo - this.mcpEmitters.serverEvent.onAdd.fire({ response: { newServer: clientInfo.mcpServer, name: serverName, } }) + const isOn = !!userStateOfName?.[serverName]?.isOn; + const clientInfo = await this._createClient(mcpServersJSON[serverName], serverName, isOn); + this.infoOfClientId[serverName] = clientInfo; + this.mcpEmitters.serverEvent.onAdd.fire({ response: { newServer: clientInfo.mcpServer, name: serverName } }); } - }) - ) - - allChanges.forEach(({ serverName, type }) => { - this._refreshingServerNames.delete(serverName) - }) - + } catch (e) { + this.logService.error('[MCP] refreshMCPServers failed for ' + serverName, e); + } finally { + this._refreshingServerNames.delete(serverName); + } + })); } - private async _createClientUnsafe(server: MCPConfigFileEntryJSON, serverName: string, isOn: boolean): Promise { - - const clientConfig = getClientConfig(serverName) - const client = new Client(clientConfig) + private async _createClientUnsafe(server: MCPConfigFileEntryJSON, serverName: string): Promise { + const clientConfig = getClientConfig(serverName); + const client = new Client(clientConfig); let transport: Transport; - let info: MCPServerNonError; - if (server.url) { + + const rawUrl: any = (server as any).url; + const url: URL | undefined = rawUrl + ? (typeof rawUrl === 'string' ? new URL(rawUrl) : rawUrl) + : undefined; + + if (url) { // first try HTTP, fall back to SSE try { - transport = new StreamableHTTPClientTransport(server.url); + transport = new StreamableHTTPClientTransport(url); await client.connect(transport); - console.log(`Connected via HTTP to ${serverName}`); - const { tools } = await client.listTools() - const toolsWithUniqueName = tools.map(({ name, ...rest }) => ({ name: this._addUniquePrefix(name), ...rest })) - info = { - status: isOn ? 'success' : 'offline', - tools: toolsWithUniqueName, - command: server.url.toString(), - } + this.logService.debug(`Connected via HTTP to ${serverName}`); + + const { tools } = await client.listTools(); + const filtered = this._filterToolsByExclude(server, tools as any); + const toolsWithStableNames = this._prefixToolNames(serverName, filtered as any); + + return { + _client: client, + mcpServerEntryJSON: server, + mcpServer: { + status: 'success', + tools: toolsWithStableNames, + command: url.toString(), + } + }; } catch (httpErr) { - console.warn(`HTTP failed for ${serverName}, trying SSE…`, httpErr); - transport = new SSEClientTransport(server.url); + this.logService.warn(`HTTP failed for ${serverName}, trying SSE…`, httpErr); + + transport = new SSEClientTransport(url); await client.connect(transport); - const { tools } = await client.listTools() - const toolsWithUniqueName = tools.map(({ name, ...rest }) => ({ name: this._addUniquePrefix(name), ...rest })) - console.log(`Connected via SSE to ${serverName}`); - info = { - status: isOn ? 'success' : 'offline', - tools: toolsWithUniqueName, - command: server.url.toString(), - } + + const { tools } = await client.listTools(); + const filtered = this._filterToolsByExclude(server, tools as any); + const toolsWithStableNames = this._prefixToolNames(serverName, filtered as any); + + this.logService.debug(`Connected via SSE to ${serverName}`); + return { + _client: client, + mcpServerEntryJSON: server, + mcpServer: { + status: 'success', + tools: toolsWithStableNames, + command: url.toString(), + } + }; } - } else if (server.command) { - // console.log('ENV DATA: ', server.env) + } + + if (server.command) { + this.logService.debug('ENV DATA: ', server.env); + transport = new StdioClientTransport({ command: server.command, args: server.args, @@ -205,43 +249,55 @@ export class MCPChannel implements IServerChannel { } as Record, }); - await client.connect(transport) - - // Get the tools from the server - const { tools } = await client.listTools() - const toolsWithUniqueName = tools.map(({ name, ...rest }) => ({ name: this._addUniquePrefix(name), ...rest })) + await client.connect(transport); - // Create a full command string for display - const fullCommand = `${server.command} ${server.args?.join(' ') || ''}` + const { tools } = await client.listTools(); + const filtered = this._filterToolsByExclude(server, tools as any); + const toolsWithStableNames = this._prefixToolNames(serverName, filtered as any); - // Format server object - info = { - status: isOn ? 'success' : 'offline', - tools: toolsWithUniqueName, - command: fullCommand, - } + const fullCommand = `${server.command} ${server.args?.join(' ') || ''}`.trim(); - } else { - throw new Error(`No url or command for server ${serverName}`); + return { + _client: client, + mcpServerEntryJSON: server, + mcpServer: { + status: 'success', + tools: toolsWithStableNames, + command: fullCommand, + } + }; } - - return { _client: client, mcpServerEntryJSON: server, mcpServer: info } - } - - private _addUniquePrefix(base: string) { - return `${Math.random().toString(36).slice(2, 8)}_${base}`; + throw new Error(`No url or command for server ${serverName}`); } private async _createClient(serverConfig: MCPConfigFileEntryJSON, serverName: string, isOn = true): Promise { + + if (!isOn) { + return { + _client: undefined, + mcpServerEntryJSON: serverConfig, + mcpServer: { + status: 'offline', + tools: [], + command: this._commandString(serverConfig), + } + }; + } + try { - const c: ClientInfo = await this._createClientUnsafe(serverConfig, serverName, isOn) - return c + return await this._createClientUnsafe(serverConfig, serverName); } catch (err) { - console.error(`❌ Failed to connect to server "${serverName}":`, err) - const fullCommand = !serverConfig.command ? '' : `${serverConfig.command} ${serverConfig.args?.join(' ') || ''}` - const c: MCPServerError = { status: 'error', error: err + '', command: fullCommand, } - return { mcpServerEntryJSON: serverConfig, mcpServer: c, } + this.logService.error(`Failed to connect to server "${serverName}":`, err); + return { + _client: undefined, + mcpServerEntryJSON: serverConfig, + mcpServer: { + status: 'error', + error: err + '', + command: this._commandString(serverConfig), + } + }; } } @@ -250,7 +306,7 @@ export class MCPChannel implements IServerChannel { await this._closeClient(serverName) delete this.infoOfClientId[serverName] } - console.log('Closed all MCP servers'); + this.logService.debug('Closed all MCP servers'); } private async _closeClient(serverName: string) { @@ -260,48 +316,48 @@ export class MCPChannel implements IServerChannel { if (client) { await client.close() } - console.log(`Closed MCP server ${serverName}`); + this.logService.debug(`Closed MCP server ${serverName}`); } private async _toggleMCPServer(serverName: string, isOn: boolean) { - const prevServer = this.infoOfClientId[serverName]?.mcpServer - // Handle turning on the server + const prevServer = this.infoOfClientId[serverName]?.mcpServer; + const existing = this.infoOfClientId[serverName]; + if (!existing) return; + if (isOn) { - // this.mcpEmitters.serverEvent.onChangeLoading.fire(getLoadingServerObject(serverName, isOn)) - const clientInfo = await this._createClientUnsafe(this.infoOfClientId[serverName].mcpServerEntryJSON, serverName, isOn) + + await this._closeClient(serverName); + + const clientInfo = await this._createClient(existing.mcpServerEntryJSON, serverName, true); + + + this.infoOfClientId[serverName] = clientInfo; + this.mcpEmitters.serverEvent.onUpdate.fire({ - response: { - name: serverName, - newServer: clientInfo.mcpServer, - prevServer: prevServer, + response: { name: serverName, newServer: clientInfo.mcpServer, prevServer } + }); + } else { + + await this._closeClient(serverName); + + + this.infoOfClientId[serverName] = { + _client: undefined, + mcpServerEntryJSON: existing.mcpServerEntryJSON, + mcpServer: { + status: 'offline', + tools: [], + command: this._commandString(existing.mcpServerEntryJSON), } - }) - } - // Handle turning off the server - else { - // this.mcpEmitters.serverEvent.onChangeLoading.fire(getLoadingServerObject(serverName, isOn)) - this._closeClient(serverName) - delete this.infoOfClientId[serverName]._client + }; this.mcpEmitters.serverEvent.onUpdate.fire({ - response: { - name: serverName, - newServer: { - status: 'offline', - tools: [], - command: '', - // Explicitly set error to undefined to reset the error state - error: undefined, - }, - prevServer: prevServer, - } - }) + response: { name: serverName, newServer: this.infoOfClientId[serverName].mcpServer, prevServer } + }); } } - // tool call functions - private async _callTool(serverName: string, toolName: string, params: any): Promise { const server = this.infoOfClientId[serverName] if (!server) throw new Error(`Server ${serverName} not found`) @@ -332,18 +388,6 @@ export class MCPChannel implements IServerChannel { } } - // if (returnValue.type === 'audio') { - // // handle audio response - // } - - // if (returnValue.type === 'image') { - // // handle image response - // } - - // if (returnValue.type === 'resource') { - // // handle resource response - // } - throw new Error(`Tool call error: We don\'t support ${returnValue.type} tool response yet for tool ${toolName} on server ${serverName}`) } @@ -380,7 +424,7 @@ export class MCPChannel implements IServerChannel { errorMessage = JSON.stringify(err, null, 2); } - const fullErrorMessage = `❌ Failed to call tool "${toolName}" on server "${serverName}": ${errorMessage}`; + const fullErrorMessage = `Failed to call tool "${toolName}" on server "${serverName}": ${errorMessage}`; const errorResponse: MCPToolErrorResponse = { event: 'error', text: fullErrorMessage, @@ -392,4 +436,3 @@ export class MCPChannel implements IServerChannel { } } - diff --git a/src/vs/workbench/contrib/void/electron-main/metricsMainService.ts b/src/vs/platform/void/electron-main/metricsMainService.ts similarity index 52% rename from src/vs/workbench/contrib/void/electron-main/metricsMainService.ts rename to src/vs/platform/void/electron-main/metricsMainService.ts index b6553c47dec..e5098d62bc5 100644 --- a/src/vs/workbench/contrib/void/electron-main/metricsMainService.ts +++ b/src/vs/platform/void/electron-main/metricsMainService.ts @@ -3,17 +3,26 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { isLinux, isMacintosh, isWindows } from '../../../../base/common/platform.js'; -import { generateUuid } from '../../../../base/common/uuid.js'; -import { IEnvironmentMainService } from '../../../../platform/environment/electron-main/environmentMainService.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; -import { StorageTarget, StorageScope } from '../../../../platform/storage/common/storage.js'; -import { IApplicationStorageMainService } from '../../../../platform/storage/electron-main/storageMainService.js'; - +import { createRequire } from 'node:module'; + +import { Disposable, DisposableStore } from '../../../base/common/lifecycle.js'; +import { isLinux, isMacintosh, isWindows } from '../../../base/common/platform.js'; +import { generateUuid } from '../../../base/common/uuid.js'; +import { IEnvironmentMainService } from '../../../platform/environment/electron-main/environmentMainService.js'; +import { IProductService } from '../../../platform/product/common/productService.js'; +import { StorageTarget, StorageScope } from '../../../platform/storage/common/storage.js'; +import { IApplicationStorageMainService } from '../../../platform/storage/electron-main/storageMainService.js'; import { IMetricsService } from '../common/metricsService.js'; -import { PostHog } from 'posthog-node' -import { OPT_OUT_KEY } from '../common/storageKeys.js'; +import { defaultGlobalSettings, DISABLE_TELEMETRY_KEY } from '../../void/common/voidSettingsTypes.js'; + +const POSTHOG_API_KEY = 'phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2'; +const POSTHOG_HOST = 'https://us.i.posthog.com'; + +const require = createRequire(import.meta.url); + +type PostHogModule = typeof import('posthog-node'); +const { PostHog } = require('posthog-node') as PostHogModule; +type PostHogClient = import('posthog-node').PostHog; const os = isWindows ? 'windows' : isMacintosh ? 'mac' : isLinux ? 'linux' : null @@ -30,13 +39,10 @@ const osInfo = _getOSInfo() // we'd like to use devDeviceId on telemetryService, but that gets sanitized by the time it gets here as 'someValue.devDeviceId' - - export class MetricsMainService extends Disposable implements IMetricsService { _serviceBrand: undefined; - private readonly client: PostHog - + private client: PostHogClient | undefined private _initProperties: object = {} @@ -69,42 +75,47 @@ export class MetricsMainService extends Disposable implements IMetricsService { // } } - - // the main id - private get distinctId() { - const oldId = this.oldId - const setValIfNotExist = oldId === 'NULL' ? undefined : oldId - return this._memoStorage('void.app.machineId', StorageTarget.MACHINE, setValIfNotExist) + private _getTelemetryDisabled(): boolean { + const val = this._appStorage.get(DISABLE_TELEMETRY_KEY, StorageScope.APPLICATION); + return typeof val === 'boolean' ? val : defaultGlobalSettings.disableTelemetry; } - // just to see if there are ever multiple machineIDs per userID (instead of this, we should just track by the user's email) - private get userId() { - return this._memoStorage('void.app.userMachineId', StorageTarget.USER) + private _ensureDefaultTelemetryFlag(): void { + const existing = this._appStorage.get(DISABLE_TELEMETRY_KEY, StorageScope.APPLICATION); + if (existing === undefined) { + this._appStorage.store( + DISABLE_TELEMETRY_KEY, + defaultGlobalSettings.disableTelemetry, + StorageScope.APPLICATION, + StorageTarget.USER + ); + } } - constructor( - @IProductService private readonly _productService: IProductService, - @IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService, - @IApplicationStorageMainService private readonly _appStorage: IApplicationStorageMainService, - ) { - super() - this.client = new PostHog('phc_UanIdujHiLp55BkUTjB1AuBXcasVkdqRwgnwRlWESH2', { - host: 'https://us.i.posthog.com', - }) - - this.initialize() // async + private _ensureClient(): void { + if (!this.client) { + this.client = new PostHog(POSTHOG_API_KEY, { host: POSTHOG_HOST }); + } } - async initialize() { - // very important to await whenReady! - await this._appStorage.whenReady + private _shutdownClient(): void { + this.client?.shutdown?.(); + this.client = undefined; + } - const { commit, version, voidVersion, release, quality } = this._productService + private _updateClientStateFromFlag(): void { + if (this._getTelemetryDisabled()) { + this._shutdownClient(); + } else { + this._ensureClient(); + } + } - const isDevMode = !this._envMainService.isBuilt // found in abstractUpdateService.ts + private _buildInitProperties(): object { + const { commit, version, voidVersion, release, quality } = this._productService; + const isDevMode = !this._envMainService.isBuilt; - // custom properties we identify - this._initProperties = { + return { commit, vscodeVersion: version, voidVersion: voidVersion, @@ -116,44 +127,96 @@ export class MetricsMainService extends Disposable implements IMetricsService { oldId: this.oldId, isDevMode, ...osInfo, - } + }; + } + private _identifyIfActive(): void { + if (!this.client || this._getTelemetryDisabled()) return; const identifyMessage = { distinctId: this.distinctId, properties: this._initProperties, - } - - const didOptOut = this._appStorage.getBoolean(OPT_OUT_KEY, StorageScope.APPLICATION, false) + }; + this.client.identify(identifyMessage); + console.log('Void posthog metrics info:', JSON.stringify(identifyMessage, null, 2)); + } - console.log('User is opted out of basic Void metrics?', didOptOut) - if (didOptOut) { - this.client.optOut() - } - else { - this.client.optIn() - this.client.identify(identifyMessage) - } + private _subscribeToTelemetryChanges(): void { + const changeStore = this._register(new DisposableStore()); + + const onAppTelemetryChange = this._appStorage.onDidChangeValue( + StorageScope.APPLICATION, + DISABLE_TELEMETRY_KEY, + changeStore + ); + + this._register( + onAppTelemetryChange(() => { + const disabled = this._getTelemetryDisabled(); + if (disabled) { + this._shutdownClient(); + } else { + const hadClient = !!this.client; + this._ensureClient(); + + if (!hadClient) { + this._identifyIfActive(); + } + } + }) + ); + } - console.log('Void posthog metrics info:', JSON.stringify(identifyMessage, null, 2)) + // the main id + private get distinctId() { + const oldId = this.oldId + const setValIfNotExist = oldId === 'NULL' ? undefined : oldId + return this._memoStorage('void.app.machineId', StorageTarget.MACHINE, setValIfNotExist) } + // just to see if there are ever multiple machineIDs per userID (instead of this, we should just track by the user's email) + private get userId() { + return this._memoStorage('void.app.userMachineId', StorageTarget.USER) + } - capture: IMetricsService['capture'] = (event, params) => { - const capture = { distinctId: this.distinctId, event, properties: params } as const - // console.log('full capture:', this.distinctId) - this.client.capture(capture) + constructor( + @IProductService private readonly _productService: IProductService, + @IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService, + @IApplicationStorageMainService private readonly _appStorage: IApplicationStorageMainService, + ) { + super(); + this.initialize() // async } - setOptOut: IMetricsService['setOptOut'] = (newVal: boolean) => { - if (newVal) { - this._appStorage.store(OPT_OUT_KEY, 'true', StorageScope.APPLICATION, StorageTarget.MACHINE) - } - else { - this._appStorage.remove(OPT_OUT_KEY, StorageScope.APPLICATION) - } + async initialize() { + + await this._appStorage.whenReady; + + + this._ensureDefaultTelemetryFlag(); + + + this._initProperties = this._buildInitProperties(); + + + this._updateClientStateFromFlag(); + + + this._identifyIfActive(); + + + this._subscribeToTelemetryChanges(); } + capture: IMetricsService['capture'] = (event, params) => { + + if (!this.client) return; + + const capture = { distinctId: this.distinctId, event, properties: params } as const; + this.client.capture(capture); + }; + + async getDebuggingProperties() { return this._initProperties } diff --git a/src/vs/platform/void/electron-main/remoteModelsService.ts b/src/vs/platform/void/electron-main/remoteModelsService.ts new file mode 100644 index 00000000000..6baf20bd920 --- /dev/null +++ b/src/vs/platform/void/electron-main/remoteModelsService.ts @@ -0,0 +1,36 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { IRemoteModelsService } from '../common/remoteModelsService.js'; +import { IRequestService, asJson } from '../../../platform/request/common/request.js'; +import { CancellationToken } from '../../../base/common/cancellation.js'; +import { registerSingleton, InstantiationType } from '../../../platform/instantiation/common/extensions.js'; + +export class RemoteModelsService implements IRemoteModelsService { + declare readonly _serviceBrand: undefined; + + constructor( + @IRequestService private readonly requestService: IRequestService + ) { } + + async fetchModels(url: string, headers?: Record): Promise { + try { + const ctx = await this.requestService.request({ + type: 'GET', + url, + headers: { Accept: 'application/json', ...(headers || {}) }, + timeout: 30_000, + }, CancellationToken.None); + + const json = await asJson(ctx); + return json; + } catch (error) { + console.error('Error in RemoteModelsService:', error); + throw error; + } + } +} + +registerSingleton(IRemoteModelsService, RemoteModelsService, InstantiationType.Delayed); diff --git a/src/vs/platform/void/electron-main/sendLLMMessageChannel.ts b/src/vs/platform/void/electron-main/sendLLMMessageChannel.ts new file mode 100644 index 00000000000..97bead992c8 --- /dev/null +++ b/src/vs/platform/void/electron-main/sendLLMMessageChannel.ts @@ -0,0 +1,289 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +// registered in app.ts +// code convention is to make a service responsible for this stuff, and not a channel, but having fewer files is simpler... +import { ILogService } from '../../log/common/log.js'; +import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { + RawToolParamsObj, EventLLMMessageOnTextParams, + EventLLMMessageOnErrorParams, EventLLMMessageOnFinalMessageParams, + MainSendLLMMessageParams, AbortRef, SendLLMMessageParams, + MainLLMMessageAbortParams, ModelListParams, EventModelListOnSuccessParams, + EventModelListOnErrorParams, OllamaModelResponse, OpenaiCompatibleModelResponse, + MainModelListParams, +} from '../common/sendLLMMessageTypes.js'; +import { sendLLMMessage } from './llmMessage/sendLLMMessage.js' +import { IMetricsService } from '../common/metricsService.js'; +import type { ToolName } from '../common/toolsServiceTypes.js'; +import { listModelsRouter } from './llmMessage/sendLLMMessage.impl.js'; +import type { INotificationService } from '../../notification/common/notification.js'; + +// NODE IMPLEMENTATION - calls actual sendLLMMessage() and returns listeners to it + +type StreamDeltaState = { + totalLength: number; + prefix: string; +}; + +type RequestStreamDeltaState = { + text: StreamDeltaState; + reasoning: StreamDeltaState; +}; + +const STREAM_PREFIX_PROBE_LEN = 96; + +const emptyStreamDeltaState = (): StreamDeltaState => ({ totalLength: 0, prefix: '' }); +const emptyRequestStreamDeltaState = (): RequestStreamDeltaState => ({ + text: emptyStreamDeltaState(), + reasoning: emptyStreamDeltaState(), +}); + +const makePrefixProbe = (s: string): string => s.slice(0, STREAM_PREFIX_PROBE_LEN); + +const toDeltaPayload = ( + incomingRaw: unknown, + prev: StreamDeltaState +): { payload: string; isDelta: boolean; next: StreamDeltaState } => { + const incoming = typeof incomingRaw === 'string' ? incomingRaw : ''; + if (!incoming) return { payload: '', isDelta: true, next: prev }; + + if (prev.totalLength <= 0) { + return { + payload: incoming, + isDelta: false, + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + const probeLen = Math.min(prev.prefix.length, incoming.length); + const prevProbe = probeLen > 0 ? prev.prefix.slice(0, probeLen) : ''; + const incomingProbe = probeLen > 0 ? incoming.slice(0, probeLen) : ''; + const hasSamePrefix = probeLen > 0 && prevProbe === incomingProbe; + + if (incoming.length > prev.totalLength && hasSamePrefix) { + return { + payload: incoming.slice(prev.totalLength), + isDelta: true, + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + if (incoming.length === prev.totalLength && hasSamePrefix) { + return { + payload: '', + isDelta: true, + next: { totalLength: incoming.length, prefix: makePrefixProbe(incoming) }, + }; + } + + if (incoming.length < prev.totalLength && hasSamePrefix) { + // Ignore regressive snapshots to keep stream monotonic downstream. + return { + payload: '', + isDelta: true, + next: prev, + }; + } + + // Fallback: treat incoming as plain delta chunk. + return { + payload: incoming, + isDelta: true, + next: { + totalLength: prev.totalLength + incoming.length, + prefix: prev.prefix || makePrefixProbe(incoming), + }, + }; +}; + +export class LLMMessageChannel implements IServerChannel { + + // sendLLMMessage + private readonly llmMessageEmitters = { + onText: new Emitter(), + onFinalMessage: new Emitter(), + onError: new Emitter(), + onNotify: new Emitter<{ requestId: string; payload: any }>(), + } + + // aborters for above + private readonly _infoOfRunningRequest: Record | undefined, abortRef: AbortRef }> = {} + private readonly _streamingTextStateByRequest: Record = {}; + + // tool delegation: main -> renderer + private readonly toolRequestEmitter = new Emitter<{ requestId: string; toolCallId: string; name: ToolName; rawParams: RawToolParamsObj }>(); + private readonly toolWaiters = new Map void; reject: (e: any) => void }>(); + + + // list + private readonly listEmitters = { + ollama: { + success: new Emitter>(), + error: new Emitter>(), + }, + openaiCompat: { + success: new Emitter>(), + error: new Emitter>(), + }, + } satisfies { + [providerName in 'ollama' | 'openaiCompat']: { + success: Emitter>, + error: Emitter>, + } + } + + // stupidly, channels can't take in @IService + constructor( + private readonly metricsService: IMetricsService, + private readonly logService: ILogService, + ) { } + + // browser uses this to listen for changes + listen(_: unknown, event: string): Event { + // text + if (event === 'onText_sendLLMMessage') return this.llmMessageEmitters.onText.event; + else if (event === 'onFinalMessage_sendLLMMessage') return this.llmMessageEmitters.onFinalMessage.event; + else if (event === 'onError_sendLLMMessage') return this.llmMessageEmitters.onError.event; + else if (event === 'onNotify_sendLLMMessage') return this.llmMessageEmitters.onNotify.event; + // list + else if (event === 'onSuccess_list_ollama') return this.listEmitters.ollama.success.event; + else if (event === 'onError_list_ollama') return this.listEmitters.ollama.error.event; + else if (event === 'onSuccess_list_openAICompatible') return this.listEmitters.openaiCompat.success.event; + else if (event === 'onError_list_openAICompatible') return this.listEmitters.openaiCompat.error.event; + // tool request (main -> renderer) + else if (event === 'onToolRequest') return this.toolRequestEmitter.event; + + else throw new Error(`Event not found: ${event}`); + } + + // browser uses this to call (see this.channel.call() in llmMessageService.ts for all usages) + async call(_: unknown, command: string, params: any): Promise { + try { + if (command === 'sendLLMMessage') { + this._callSendLLMMessage(params) + } + else if (command === 'abort') { + await this._callAbort(params) + } + else if (command === 'ollamaList') { + this._callOllamaList(params) + } + else if (command === 'openAICompatibleList') { + this._callOpenAICompatibleList(params) + } + else if (command === 'toolExecResult') { + this._receiveToolExecResult(params) + } + else { + throw new Error(`Void sendLLM: command "${command}" not recognized.`) + } + } + catch (e) { + this.logService.error?.('llmMessageChannel: Call Error:', e); + } + } + + private _receiveToolExecResult(params: { requestId: string; toolCallId: string; ok: boolean; value: string }) { + const waiter = this.toolWaiters.get(params.toolCallId); + if (!waiter) return; + if (params.ok) waiter.resolve(params.value); + else waiter.reject(new Error(params.value)); + this.toolWaiters.delete(params.toolCallId); + } + + private async _callSendLLMMessage(params: MainSendLLMMessageParams) { + const { requestId, additionalTools, ...rest } = params; + + this.logService.debug?.('[LLMChannel] sendLLMMessage', { + requestId, + hasAdditionalTools: !!additionalTools, + toolsCount: additionalTools?.length || 0, + tools: additionalTools?.map(t => t.name), + }); + + if (!(requestId in this._infoOfRunningRequest)) { + this._infoOfRunningRequest[requestId] = { waitForSend: undefined, abortRef: { current: null } }; + } + this._streamingTextStateByRequest[requestId] = emptyRequestStreamDeltaState(); + + const mainThreadParams = { + ...(rest as any), + additionalTools, + onText: (p: any) => { + const prev = this._streamingTextStateByRequest[requestId] ?? emptyRequestStreamDeltaState(); + const textDelta = toDeltaPayload(p?.fullText, prev.text); + const reasoningDelta = toDeltaPayload(p?.fullReasoning, prev.reasoning); + + this._streamingTextStateByRequest[requestId] = { + text: textDelta.next, + reasoning: reasoningDelta.next, + }; + + this.llmMessageEmitters.onText.fire({ + requestId, + ...p, + fullText: textDelta.payload, + fullReasoning: reasoningDelta.payload, + isFullTextDelta: textDelta.isDelta, + isFullReasoningDelta: reasoningDelta.isDelta, + }); + }, + onFinalMessage: (p: any) => { + this.llmMessageEmitters.onFinalMessage.fire({ requestId, ...p }); + delete this._streamingTextStateByRequest[requestId]; + }, + onError: (p: any) => { + this.logService.debug?.('[LLMChannel] sendLLMMessage -> onError fired', { requestId }); + this.llmMessageEmitters.onError.fire({ requestId, ...p }); + delete this._streamingTextStateByRequest[requestId]; + }, + abortRef: this._infoOfRunningRequest[requestId].abortRef, + } as SendLLMMessageParams; + + const notificationBridge = { + notify: (payload: any) => { + this.llmMessageEmitters.onNotify.fire({ requestId, payload }); + return undefined as any; + }, + } as unknown as INotificationService; + + const p = sendLLMMessage(mainThreadParams, this.metricsService, this.logService, notificationBridge); + this._infoOfRunningRequest[requestId].waitForSend = p; + } + + private async _callAbort(params: MainLLMMessageAbortParams) { + const { requestId } = params; + delete this._streamingTextStateByRequest[requestId] + if (!(requestId in this._infoOfRunningRequest)) return + const { waitForSend, abortRef } = this._infoOfRunningRequest[requestId] + await waitForSend // wait for the send to finish so we know abortRef was set + abortRef?.current?.() + delete this._infoOfRunningRequest[requestId] + } + + _callOllamaList = (params: MainModelListParams) => { + const { requestId } = params + const emitters = this.listEmitters.ollama + const mainThreadParams: ModelListParams = { + ...params, + onSuccess: (p) => { emitters.success.fire({ requestId, ...p }); }, + onError: (p) => { emitters.error.fire({ requestId, ...p }); }, + } + listModelsRouter(mainThreadParams as any); + } + + _callOpenAICompatibleList = (params: MainModelListParams) => { + const { requestId } = params + const emitters = this.listEmitters.openaiCompat + const mainThreadParams: ModelListParams = { + ...params, + onSuccess: (p) => { emitters.success.fire({ requestId, ...p }); }, + onError: (p) => { emitters.error.fire({ requestId, ...p }); }, + } + listModelsRouter(mainThreadParams as any); + } +} diff --git a/src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts b/src/vs/platform/void/electron-main/voidSCMMainService.ts similarity index 100% rename from src/vs/workbench/contrib/void/electron-main/voidSCMMainService.ts rename to src/vs/platform/void/electron-main/voidSCMMainService.ts diff --git a/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts b/src/vs/platform/void/electron-main/voidUpdateMainService.ts similarity index 56% rename from src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts rename to src/vs/platform/void/electron-main/voidUpdateMainService.ts index eafcf108b6e..d1c331ee1b1 100644 --- a/src/vs/workbench/contrib/void/electron-main/voidUpdateMainService.ts +++ b/src/vs/platform/void/electron-main/voidUpdateMainService.ts @@ -3,20 +3,16 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IEnvironmentMainService } from '../../../../platform/environment/electron-main/environmentMainService.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; -import { IUpdateService, StateType } from '../../../../platform/update/common/update.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { IEnvironmentMainService } from '../../../platform/environment/electron-main/environmentMainService.js'; +import { IUpdateService, StateType } from '../../../platform/update/common/update.js'; import { IVoidUpdateService } from '../common/voidUpdateService.js'; import { VoidCheckUpdateRespose } from '../common/voidUpdateServiceTypes.js'; - - export class VoidMainUpdateService extends Disposable implements IVoidUpdateService { _serviceBrand: undefined; constructor( - @IProductService private readonly _productService: IProductService, @IEnvironmentMainService private readonly _envMainService: IEnvironmentMainService, @IUpdateService private readonly _updateService: IUpdateService, ) { @@ -32,12 +28,6 @@ export class VoidMainUpdateService extends Disposable implements IVoidUpdateServ return { message: null } as const } - // if disabled and not explicitly checking, return early - if (this._updateService.state.type === StateType.Disabled) { - if (!explicit) - return { message: null } as const - } - this._updateService.checkForUpdates(false) // implicity check, then handle result ourselves console.log('updateState', this._updateService.state) @@ -83,69 +73,65 @@ export class VoidMainUpdateService extends Disposable implements IVoidUpdateServ } if (this._updateService.state.type === StateType.Disabled) { - return await this._manualCheckGHTagIfDisabled(explicit) + // Updates disabled: skip notification + return { message: null } as const } return null } - - - - - - private async _manualCheckGHTagIfDisabled(explicit: boolean): Promise { - try { - const response = await fetch('https://api.github.com/repos/voideditor/binaries/releases/latest'); - - const data = await response.json(); - const version = data.tag_name; - - const myVersion = this._productService.version - const latestVersion = version - - const isUpToDate = myVersion === latestVersion // only makes sense if response.ok - - let message: string | null - let action: 'reinstall' | undefined - - // explicit - if (explicit) { - if (response.ok) { - if (!isUpToDate) { - message = 'A new version of Void is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!' - action = 'reinstall' - } - else { - message = 'Void is up-to-date!' - } - } - else { - message = `An error occurred when fetching the latest GitHub release tag. Please try again in ~5 minutes, or reinstall.` - action = 'reinstall' - } - } - // not explicit - else { - if (response.ok && !isUpToDate) { - message = 'A new version of Void is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!' - action = 'reinstall' - } - else { - message = null - } - } - return { message, action } as const - } - catch (e) { - if (explicit) { - return { - message: `An error occurred when fetching the latest GitHub release tag: ${e}. Please try again in ~5 minutes.`, - action: 'reinstall', - } - } - else { - return { message: null } as const - } - } - } + // private async _manualCheckGHTagIfDisabled(explicit: boolean): Promise { + // try { + // const response = await fetch('https://api.github.com/repos/voideditor/binaries/releases/latest'); + + // const data = await response.json(); + // const version = data.tag_name; + + // const myVersion = this._productService.version + // const latestVersion = version + + // const isUpToDate = myVersion === latestVersion // only makes sense if response.ok + + // let message: string | null + // let action: 'reinstall' | undefined + + // // explicit + // if (explicit) { + // if (response.ok) { + // if (!isUpToDate) { + // message = 'A new version of Void is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!' + // action = 'reinstall' + // } + // else { + // message = 'Void is up-to-date!' + // } + // } + // else { + // message = `An error occurred when fetching the latest GitHub release tag. Please try again in ~5 minutes, or reinstall.` + // action = 'reinstall' + // } + // } + // // not explicit + // else { + // if (response.ok && !isUpToDate) { + // message = 'A new version of Void is available! Please reinstall (auto-updates are disabled on this OS) - it only takes a second!' + // action = 'reinstall' + // } + // else { + // message = null + // } + // } + // return { message, action } as const + // } + // catch (e) { + // if (explicit) { + // return { + // message: `An error occurred when fetching the latest GitHub release tag: ${e}. Please try again in ~5 minutes.`, + // action: 'reinstall', + // } + // } + // else { + // return { message: null } as const + // } + // } + // } } diff --git a/src/vs/platform/webContentExtractor/node/sharedWebContentExtractorService.ts b/src/vs/platform/webContentExtractor/node/sharedWebContentExtractorService.ts index 61ae28d1e9b..4aec8a15fba 100644 --- a/src/vs/platform/webContentExtractor/node/sharedWebContentExtractorService.ts +++ b/src/vs/platform/webContentExtractor/node/sharedWebContentExtractorService.ts @@ -28,7 +28,7 @@ export class SharedWebContentExtractorService implements ISharedWebContentExtrac return undefined; } - const content = VSBuffer.wrap(await response.bytes()); + const content = VSBuffer.wrap(new Uint8Array(await response.arrayBuffer())); return content; } catch (err) { console.log(err); diff --git a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts index a82377228dc..0a5af2bb9de 100644 --- a/src/vs/workbench/api/browser/mainThreadBulkEdits.ts +++ b/src/vs/workbench/api/browser/mainThreadBulkEdits.ts @@ -6,7 +6,7 @@ import { VSBuffer, decodeBase64 } from '../../../base/common/buffer.js'; import { revive } from '../../../base/common/marshalling.js'; import { IBulkEditService, ResourceFileEdit, ResourceTextEdit } from '../../../editor/browser/services/bulkEditService.js'; -import { WorkspaceEdit } from '../../../editor/common/languages.js'; +import { WorkspaceEdit } from '../../../editor/common/language/languages.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceCellEditDto, IWorkspaceEditDto, IWorkspaceFileEditDto, MainContext, MainThreadBulkEditsShape } from '../common/extHost.protocol.js'; diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 0da8d2a09f1..755e9cf654e 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -12,12 +12,12 @@ import { revive } from '../../../base/common/marshalling.js'; import { escapeRegExpCharacters } from '../../../base/common/strings.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { Position } from '../../../editor/common/core/position.js'; -import { Range } from '../../../editor/common/core/range.js'; -import { getWordAtText } from '../../../editor/common/core/wordHelper.js'; -import { CompletionContext, CompletionItem, CompletionItemKind, CompletionList } from '../../../editor/common/languages.js'; -import { ITextModel } from '../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../editor/common/services/languageFeatures.js'; +import { Position } from '../../../editor/common/language/core/position.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { getWordAtText } from '../../../editor/common/language/core/wordHelper.js'; +import { CompletionContext, CompletionItem, CompletionItemKind, CompletionList } from '../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/api/browser/mainThreadChatCodeMapper.ts b/src/vs/workbench/api/browser/mainThreadChatCodeMapper.ts index 547e3346694..4f1690c55ed 100644 --- a/src/vs/workbench/api/browser/mainThreadChatCodeMapper.ts +++ b/src/vs/workbench/api/browser/mainThreadChatCodeMapper.ts @@ -5,7 +5,7 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { Disposable, DisposableMap, IDisposable } from '../../../base/common/lifecycle.js'; import { URI } from '../../../base/common/uri.js'; -import { TextEdit } from '../../../editor/common/languages.js'; +import { TextEdit } from '../../../editor/common/language/languages.js'; import { ICodeMapperProvider, ICodeMapperRequest, ICodeMapperResponse, ICodeMapperService } from '../../contrib/chat/common/chatCodeMapperService.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { ExtHostCodeMapperShape, ExtHostContext, ICodeMapperProgressDto, ICodeMapperRequestDto, MainContext, MainThreadCodeMapperShape } from '../common/extHost.protocol.js'; diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index b13b12de203..8f4fccfec18 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -7,8 +7,8 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, DisposableMap, DisposableStore, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { IRange, Range } from '../../../editor/common/core/range.js'; -import * as languages from '../../../editor/common/languages.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; +import * as languages from '../../../editor/common/language/languages.js'; import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; diff --git a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts index 7ff4687fdf2..f98b59ce533 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentContentProviders.ts @@ -6,13 +6,13 @@ import { onUnexpectedError } from '../../../base/common/errors.js'; import { dispose, DisposableMap } from '../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { EditOperation } from '../../../editor/common/core/editOperation.js'; -import { Range } from '../../../editor/common/core/range.js'; -import { ITextModel } from '../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; -import { ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { EditOperation } from '../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { ExtHostContext, ExtHostDocumentContentProvidersShape, MainContext, MainThreadDocumentContentProvidersShape } from '../common/extHost.protocol.js'; import { CancellationTokenSource } from '../../../base/common/cancellation.js'; diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index 47925bd1c08..d8596dd29d7 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -7,9 +7,9 @@ import { toErrorMessage } from '../../../base/common/errorMessage.js'; import { IReference, dispose, Disposable } from '../../../base/common/lifecycle.js'; import { Schemas } from '../../../base/common/network.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { ITextModel, shouldSynchronizeModel } from '../../../editor/common/model.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { ITextModel, shouldSynchronizeModel } from '../../../editor/common/language/model.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { IFileService, FileOperation } from '../../../platform/files/common/files.js'; import { ExtHostContext, ExtHostDocumentsShape, MainThreadDocumentsShape } from '../common/extHost.protocol.js'; import { EncodingMode, ITextFileEditorModel, ITextFileService, TextFileResolveReason } from '../../services/textfile/common/textfiles.js'; diff --git a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts index 69f487ee521..1c8cbe9f6ee 100644 --- a/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadDocumentsAndEditors.ts @@ -8,9 +8,9 @@ import { combinedDisposable, DisposableStore, DisposableMap } from '../../../bas import { ICodeEditor, isCodeEditor, isDiffEditor, IActiveCodeEditor } from '../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../editor/browser/services/codeEditorService.js'; import { IEditor } from '../../../editor/common/editorCommon.js'; -import { ITextModel, shouldSynchronizeModel } from '../../../editor/common/model.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { ITextModel, shouldSynchronizeModel } from '../../../editor/common/language/model.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { IFileService } from '../../../platform/files/common/files.js'; import { extHostCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { MainThreadDocuments } from './mainThreadDocuments.js'; diff --git a/src/vs/workbench/api/browser/mainThreadEditor.ts b/src/vs/workbench/api/browser/mainThreadEditor.ts index 257e9985452..30702069ba8 100644 --- a/src/vs/workbench/api/browser/mainThreadEditor.ts +++ b/src/vs/workbench/api/browser/mainThreadEditor.ts @@ -7,12 +7,12 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../editor/browser/editorBrowser.js'; import { RenderLineNumbersType, TextEditorCursorStyle, cursorStyleToString, EditorOption } from '../../../editor/common/config/editorOptions.js'; -import { IRange, Range } from '../../../editor/common/core/range.js'; -import { ISelection, Selection } from '../../../editor/common/core/selection.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../editor/common/language/core/selection.js'; import { IDecorationOptions, ScrollType } from '../../../editor/common/editorCommon.js'; -import { ITextModel, ITextModelUpdateOptions } from '../../../editor/common/model.js'; -import { ISingleEditOperation } from '../../../editor/common/core/editOperation.js'; -import { IModelService } from '../../../editor/common/services/model.js'; +import { ITextModel, ITextModelUpdateOptions } from '../../../editor/common/language/model.js'; +import { ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; import { SnippetController2 } from '../../../editor/contrib/snippet/browser/snippetController2.js'; import { IApplyEditsOptions, IEditorPropertiesChangeData, IResolvedTextEditorConfiguration, ISnippetOptions, ITextEditorConfigurationUpdate, TextEditorRevealType } from '../common/extHost.protocol.js'; import { IEditorPane } from '../../common/editor.js'; diff --git a/src/vs/workbench/api/browser/mainThreadEditors.ts b/src/vs/workbench/api/browser/mainThreadEditors.ts index a61ba3c7801..b927966c974 100644 --- a/src/vs/workbench/api/browser/mainThreadEditors.ts +++ b/src/vs/workbench/api/browser/mainThreadEditors.ts @@ -8,10 +8,10 @@ import { IDisposable, dispose, DisposableStore } from '../../../base/common/life import { equals as objectEquals } from '../../../base/common/objects.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { ICodeEditorService } from '../../../editor/browser/services/codeEditorService.js'; -import { IRange } from '../../../editor/common/core/range.js'; -import { ISelection } from '../../../editor/common/core/selection.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { ISelection } from '../../../editor/common/language/core/selection.js'; import { IDecorationOptions, IDecorationRenderOptions } from '../../../editor/common/editorCommon.js'; -import { ISingleEditOperation } from '../../../editor/common/core/editOperation.js'; +import { ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; import { CommandsRegistry } from '../../../platform/commands/common/commands.js'; import { ITextEditorOptions, IResourceEditorInput, EditorActivation, EditorResolution, ITextEditorDiffInformation, isTextEditorDiffInformationEqual, ITextEditorChange } from '../../../platform/editor/common/editor.js'; import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; @@ -31,11 +31,11 @@ import { IConfigurationService } from '../../../platform/configuration/common/co import { IQuickDiffModelService } from '../../contrib/scm/browser/quickDiffModel.js'; import { autorun, constObservable, derived, derivedOpts, IObservable, observableFromEvent } from '../../../base/common/observable.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; -import { isITextModel } from '../../../editor/common/model.js'; +import { isITextModel } from '../../../editor/common/language/model.js'; import { LineRangeMapping } from '../../../editor/common/diff/rangeMapping.js'; import { equals } from '../../../base/common/arrays.js'; import { Event } from '../../../base/common/event.js'; -import { DiffAlgorithmName } from '../../../editor/common/services/editorWorker.js'; +import { DiffAlgorithmName } from '../../../editor/common/language/services/editorWorker.js'; export interface IMainThreadEditorLocator { getEditor(id: string): MainThreadTextEditor | undefined; diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 9aadd0d679a..18e8f3cb6b4 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -14,16 +14,16 @@ import { ResourceMap } from '../../../base/common/map.js'; import { revive } from '../../../base/common/marshalling.js'; import { mixin } from '../../../base/common/objects.js'; import { URI } from '../../../base/common/uri.js'; -import { Position as EditorPosition, IPosition } from '../../../editor/common/core/position.js'; -import { Range as EditorRange, IRange } from '../../../editor/common/core/range.js'; -import { Selection } from '../../../editor/common/core/selection.js'; -import * as languages from '../../../editor/common/languages.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; +import { Position as EditorPosition, IPosition } from '../../../editor/common/language/core/position.js'; +import { Range as EditorRange, IRange } from '../../../editor/common/language/core/range.js'; +import { Selection } from '../../../editor/common/language/core/selection.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { IndentationRule, LanguageConfiguration, OnEnterRule } from '../../../editor/common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../editor/common/languages/languageConfigurationRegistry.js'; -import { ITextModel } from '../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../editor/common/services/languageFeatures.js'; -import { decodeSemanticTokensDto } from '../../../editor/common/services/semanticTokensDto.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../editor/common/language/services/languageFeatures.js'; +import { decodeSemanticTokensDto } from '../../../editor/common/language/services/semanticTokensDto.js'; import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { reviveWorkspaceEditDto } from './mainThreadBulkEdits.js'; diff --git a/src/vs/workbench/api/browser/mainThreadLanguages.ts b/src/vs/workbench/api/browser/mainThreadLanguages.ts index 05e8176ed1f..2cddec3f302 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguages.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguages.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from '../../../base/common/uri.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; -import { IModelService } from '../../../editor/common/services/model.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; import { MainThreadLanguagesShape, MainContext, ExtHostContext, ExtHostLanguagesShape } from '../common/extHost.protocol.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { IPosition } from '../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../editor/common/core/range.js'; -import { StandardTokenType } from '../../../editor/common/encodedTokenAttributes.js'; -import { ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; +import { StandardTokenType } from '../../../editor/common/language/encodedTokenAttributes.js'; +import { ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { ILanguageStatus, ILanguageStatusService } from '../../services/languageStatus/common/languageStatusService.js'; import { DisposableMap, DisposableStore } from '../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index 8c38e14647e..035fd005b73 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -9,7 +9,7 @@ import { onUnexpectedError } from '../../../base/common/errors.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { DisposableMap, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; import { NotebookDto } from './mainThreadNotebookDto.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 2e33d6f89a2..7f57c309b0b 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -10,7 +10,7 @@ import { IObservable, observableValue, observableValueOpts, transaction } from ' import { IDisposable, DisposableStore, combinedDisposable, dispose, Disposable } from '../../../base/common/lifecycle.js'; import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from '../../contrib/scm/common/scm.js'; import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemDto, SCMHistoryItemRefsChangeEventDto, SCMHistoryItemRefDto } from '../common/extHost.protocol.js'; -import { Command } from '../../../editor/common/languages.js'; +import { Command } from '../../../editor/common/language/languages.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { CancellationToken } from '../../../base/common/cancellation.js'; import { MarshalledId } from '../../../base/common/marshallingIds.js'; @@ -22,11 +22,11 @@ import { ResourceTree } from '../../../base/common/resourceTree.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js'; import { basename } from '../../../base/common/resources.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { Schemas } from '../../../base/common/network.js'; -import { ITextModel } from '../../../editor/common/model.js'; +import { ITextModel } from '../../../editor/common/language/model.js'; import { structuralEquals } from '../../../base/common/equals.js'; import { historyItemBaseRefColor, historyItemRefColor, historyItemRemoteRefColor } from '../../contrib/scm/browser/scmHistory.js'; import { ColorIdentifier } from '../../../platform/theme/common/colorUtils.js'; diff --git a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts index 9070caa10dd..1a3f7e84481 100644 --- a/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/browser/mainThreadSaveParticipant.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../base/common/cancellation.js'; -import { shouldSynchronizeModel } from '../../../editor/common/model.js'; +import { shouldSynchronizeModel } from '../../../editor/common/language/model.js'; import { localize } from '../../../nls.js'; import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { IProgressStep, IProgress } from '../../../platform/progress/common/progress.js'; diff --git a/src/vs/workbench/api/browser/mainThreadStatusBar.ts b/src/vs/workbench/api/browser/mainThreadStatusBar.ts index 96059861a79..78c44e2c634 100644 --- a/src/vs/workbench/api/browser/mainThreadStatusBar.ts +++ b/src/vs/workbench/api/browser/mainThreadStatusBar.ts @@ -7,7 +7,7 @@ import { MainThreadStatusBarShape, MainContext, ExtHostContext, StatusBarItemDto import { ThemeColor } from '../../../base/common/themables.js'; import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; import { DisposableStore, toDisposable } from '../../../base/common/lifecycle.js'; -import { Command } from '../../../editor/common/languages.js'; +import { Command } from '../../../editor/common/language/languages.js'; import { IAccessibilityInformation } from '../../../platform/accessibility/common/accessibility.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; import { IExtensionStatusBarItemService, StatusBarUpdateKind } from './statusBarExtensionPoint.js'; diff --git a/src/vs/workbench/api/browser/mainThreadTesting.ts b/src/vs/workbench/api/browser/mainThreadTesting.ts index b2369b89fad..0ef3fa90d17 100644 --- a/src/vs/workbench/api/browser/mainThreadTesting.ts +++ b/src/vs/workbench/api/browser/mainThreadTesting.ts @@ -10,7 +10,7 @@ import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposab import { ISettableObservable, observableValue, transaction } from '../../../base/common/observable.js'; import { WellDefinedPrefixTree } from '../../../base/common/prefixTree.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { Range } from '../../../editor/common/core/range.js'; +import { Range } from '../../../editor/common/language/core/range.js'; import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; import { TestCoverage } from '../../contrib/testing/common/testCoverage.js'; import { TestId } from '../../contrib/testing/common/testId.js'; diff --git a/src/vs/workbench/api/browser/mainThreadVoid.ts b/src/vs/workbench/api/browser/mainThreadVoid.ts new file mode 100644 index 00000000000..f0982d2f3a1 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadVoid.ts @@ -0,0 +1,42 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { MainContext, MainThreadWindowShape } from '../../api/common/extHost.protocol.js'; +import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; +import { Emitter } from '../../../base/common/event.js'; +import { UriComponents } from '../../../base/common/uri.js'; + + +interface IOpenUriOptions { } + +@extHostNamedCustomer(MainContext.MainThreadWindow) +export class MainThreadVoid implements MainThreadWindowShape { + private readonly _onModelsChanged = new Emitter(); + public readonly onModelsChanged = this._onModelsChanged.event; + + constructor( + context: IExtHostContext, + ) { + } + + async $getInitialState(): Promise<{ isFocused: boolean; isActive: boolean }> { + return { isFocused: true, isActive: true }; + } + + async $openUri(uri: UriComponents, uriString: string | undefined, options: IOpenUriOptions): Promise { + return true; + } + + async $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise { + return uri; + } + + async $getOpenRouterModels(): Promise<{ [key: string]: any }> { return {}; } + $updateOpenRouterModels(_models: { [key: string]: any }): void { /* no-op */ } + + dispose(): void { + this._onModelsChanged.dispose(); + } +} diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 89b4f98c69e..1bfc8d82ec1 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -89,7 +89,10 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { const workspaceFoldersToAdd = foldersToAdd.map(f => ({ uri: URI.revive(f.uri), name: f.name })); // Indicate in status message - this._notificationService.status(this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), { hideAfter: 10 * 1000 /* 10s */ }); + this._toDispose.add(this._notificationService.status( + this.getStatusMessage(extensionName, workspaceFoldersToAdd.length, deleteCount), + { hideAfter: 10 * 1000 /* 10s */ } + )); return this._workspaceEditingService.updateFolders(index, deleteCount, workspaceFoldersToAdd, true); } diff --git a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts index fa0d7fd2307..987f70dd309 100644 --- a/src/vs/workbench/api/browser/statusBarExtensionPoint.ts +++ b/src/vs/workbench/api/browser/statusBarExtensionPoint.ts @@ -11,7 +11,7 @@ import { isProposedApiEnabled } from '../../services/extensions/common/extension import { ExtensionsRegistry } from '../../services/extensions/common/extensionsRegistry.js'; import { IStatusbarService, StatusbarAlignment as MainThreadStatusBarAlignment, IStatusbarEntryAccessor, IStatusbarEntry, StatusbarAlignment, IStatusbarEntryPriority, StatusbarEntryKind } from '../../services/statusbar/browser/statusbar.js'; import { ThemeColor } from '../../../base/common/themables.js'; -import { Command } from '../../../editor/common/languages.js'; +import { Command } from '../../../editor/common/language/languages.js'; import { IAccessibilityInformation, isAccessibilityInformation } from '../../../platform/accessibility/common/accessibility.js'; import { IMarkdownString, isMarkdownString } from '../../../base/common/htmlContent.js'; import { getCodiconAriaLabel } from '../../../base/common/iconLabels.js'; diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f13affa3aad..3ab1c942eed 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -12,9 +12,9 @@ import { Schemas, matchesScheme } from '../../../base/common/network.js'; import Severity from '../../../base/common/severity.js'; import { URI } from '../../../base/common/uri.js'; import { TextEditorCursorStyle } from '../../../editor/common/config/editorOptions.js'; -import { score, targetsNotebooks } from '../../../editor/common/languageSelector.js'; +import { score, targetsNotebooks } from '../../../editor/common/language/languageSelector.js'; import * as languageConfiguration from '../../../editor/common/languages/languageConfiguration.js'; -import { OverviewRulerLane } from '../../../editor/common/model.js'; +import { OverviewRulerLane } from '../../../editor/common/language/model.js'; import { ExtensionError, ExtensionIdentifierSet, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import * as files from '../../../platform/files/common/files.js'; import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; @@ -1544,7 +1544,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } }; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { version: initData.version, // namespaces diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 51f8c0cfc3b..82129fd5eb0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -15,18 +15,18 @@ import Severity from '../../../base/common/severity.js'; import { ThemeColor, ThemeIcon } from '../../../base/common/themables.js'; import { URI, UriComponents, UriDto } from '../../../base/common/uri.js'; import { RenderLineNumbersType, TextEditorCursorStyle } from '../../../editor/common/config/editorOptions.js'; -import { ISingleEditOperation } from '../../../editor/common/core/editOperation.js'; -import { IPosition } from '../../../editor/common/core/position.js'; -import { IRange } from '../../../editor/common/core/range.js'; -import { ISelection, Selection } from '../../../editor/common/core/selection.js'; +import { ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../editor/common/language/core/selection.js'; import { IChange } from '../../../editor/common/diff/legacyLinesDiffComputer.js'; import * as editorCommon from '../../../editor/common/editorCommon.js'; -import { StandardTokenType } from '../../../editor/common/encodedTokenAttributes.js'; -import * as languages from '../../../editor/common/languages.js'; -import { CompletionItemLabel } from '../../../editor/common/languages.js'; +import { StandardTokenType } from '../../../editor/common/language/encodedTokenAttributes.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { CompletionItemLabel } from '../../../editor/common/language/languages.js'; import { CharacterPair, CommentRule, EnterAction } from '../../../editor/common/languages/languageConfiguration.js'; -import { EndOfLineSequence } from '../../../editor/common/model.js'; -import { IModelChangedEvent } from '../../../editor/common/model/mirrorTextModel.js'; +import { EndOfLineSequence } from '../../../editor/common/language/model.js'; +import { IModelChangedEvent } from '../../../editor/common/language/model/mirrorTextModel.js'; import { IAccessibilityInformation } from '../../../platform/accessibility/common/accessibility.js'; import { ILocalizedString } from '../../../platform/action/common/action.js'; import { ConfigurationTarget, IConfigurationChange, IConfigurationData, IConfigurationOverrides } from '../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 1d08ef21082..9f0bdc148c0 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -7,11 +7,11 @@ import { isFalsyOrEmpty } from '../../../base/common/arrays.js'; import { VSBuffer } from '../../../base/common/buffer.js'; import { Schemas, matchesSomeScheme } from '../../../base/common/network.js'; import { URI } from '../../../base/common/uri.js'; -import { IPosition } from '../../../editor/common/core/position.js'; -import { IRange } from '../../../editor/common/core/range.js'; -import { ISelection } from '../../../editor/common/core/selection.js'; -import * as languages from '../../../editor/common/languages.js'; -import { decodeSemanticTokensDto } from '../../../editor/common/services/semanticTokensDto.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { ISelection } from '../../../editor/common/language/core/selection.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { decodeSemanticTokensDto } from '../../../editor/common/language/services/semanticTokensDto.js'; import { validateWhenClauses } from '../../../platform/contextkey/common/contextkey.js'; import { ITextEditorOptions } from '../../../platform/editor/common/editor.js'; import { ICallHierarchyItemDto, IIncomingCallDto, IInlineValueContextDto, IOutgoingCallDto, IRawColorInfo, ITypeHierarchyItemDto, IWorkspaceEditDto } from './extHost.protocol.js'; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 09e03940e11..aeb1ac0bfeb 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -16,7 +16,7 @@ import { StopWatch } from '../../../base/common/stopwatch.js'; import { assertType } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import { generateUuid } from '../../../base/common/uuid.js'; -import { Location } from '../../../editor/common/languages.js'; +import { Location } from '../../../editor/common/language/languages.js'; import { ExtensionIdentifier, IExtensionDescription, IRelaxedExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; import { isChatViewTitleActionContext } from '../../contrib/chat/common/chatActions.js'; diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 0c170ec48a9..da81c831a9b 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -12,17 +12,17 @@ import * as extHostTypeConverter from './extHostTypeConverters.js'; import { cloneAndChange } from '../../../base/common/objects.js'; import { MainContext, MainThreadCommandsShape, ExtHostCommandsShape, ICommandDto, ICommandMetadataDto, MainThreadTelemetryShape } from './extHost.protocol.js'; import { isNonEmptyArray } from '../../../base/common/arrays.js'; -import * as languages from '../../../editor/common/languages.js'; +import * as languages from '../../../editor/common/language/languages.js'; import type * as vscode from 'vscode'; import { ILogService } from '../../../platform/log/common/log.js'; import { revive } from '../../../base/common/marshalling.js'; -import { IRange, Range } from '../../../editor/common/core/range.js'; -import { IPosition, Position } from '../../../editor/common/core/position.js'; +import { IRange, Range } from '../../../editor/common/language/core/range.js'; +import { IPosition, Position } from '../../../editor/common/language/core/position.js'; import { URI } from '../../../base/common/uri.js'; import { DisposableStore, toDisposable } from '../../../base/common/lifecycle.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { IExtHostRpcService } from './extHostRpcService.js'; -import { ISelection } from '../../../editor/common/core/selection.js'; +import { ISelection } from '../../../editor/common/language/core/selection.js'; import { TestItemImpl } from './extHostTestItem.js'; import { VSBuffer } from '../../../base/common/buffer.js'; import { SerializableObjectWithBuffers } from '../../services/extensions/common/proxyIdentifier.js'; diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 908d094ec00..f4ef5851fe5 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -10,8 +10,8 @@ import { Emitter } from '../../../base/common/event.js'; import { DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { IRange } from '../../../editor/common/core/range.js'; -import * as languages from '../../../editor/common/languages.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import * as languages from '../../../editor/common/language/languages.js'; import { ExtensionIdentifierMap, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ExtHostDocuments } from './extHostDocuments.js'; import * as extHostTypeConverter from './extHostTypeConverters.js'; diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index 410c189527c..9ec2291aee2 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -7,8 +7,8 @@ import { ok } from '../../../base/common/assert.js'; import { Schemas } from '../../../base/common/network.js'; import { regExpLeadsToEndlessLoop } from '../../../base/common/strings.js'; import { URI } from '../../../base/common/uri.js'; -import { MirrorTextModel } from '../../../editor/common/model/mirrorTextModel.js'; -import { ensureValidWordDefinition, getWordAtText } from '../../../editor/common/core/wordHelper.js'; +import { MirrorTextModel } from '../../../editor/common/language/model/mirrorTextModel.js'; +import { ensureValidWordDefinition, getWordAtText } from '../../../editor/common/language/core/wordHelper.js'; import { MainThreadDocumentsShape } from './extHost.protocol.js'; import { EndOfLine, Position, Range } from './extHostTypes.js'; import type * as vscode from 'vscode'; diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index 4e7dd522c51..fa55391426d 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from '../../../base/common/event.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import { IModelChangedEvent } from '../../../editor/common/model/mirrorTextModel.js'; +import { IModelChangedEvent } from '../../../editor/common/language/model/mirrorTextModel.js'; import { ExtHostDocumentsShape, IMainContext, MainContext, MainThreadDocumentsShape } from './extHost.protocol.js'; import { ExtHostDocumentData, setWordDefinitionFor } from './extHostDocumentData.js'; import { ExtHostDocumentsAndEditors } from './extHostDocumentsAndEditors.js'; diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index b8122b07faa..8f74ba02463 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -18,12 +18,12 @@ import { assertType, isObject } from '../../../base/common/types.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { IURITransformer } from '../../../base/common/uriIpc.js'; import { generateUuid } from '../../../base/common/uuid.js'; -import { IPosition } from '../../../editor/common/core/position.js'; -import { Range as EditorRange, IRange } from '../../../editor/common/core/range.js'; -import { ISelection, Selection } from '../../../editor/common/core/selection.js'; -import * as languages from '../../../editor/common/languages.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import { Range as EditorRange, IRange } from '../../../editor/common/language/core/range.js'; +import { ISelection, Selection } from '../../../editor/common/language/core/selection.js'; +import * as languages from '../../../editor/common/language/languages.js'; import { IAutoClosingPairConditional } from '../../../editor/common/languages/languageConfiguration.js'; -import { encodeSemanticTokensDto } from '../../../editor/common/services/semanticTokensDto.js'; +import { encodeSemanticTokensDto } from '../../../editor/common/language/services/semanticTokensDto.js'; import { localize } from '../../../nls.js'; import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/api/common/extHostTestItem.ts b/src/vs/workbench/api/common/extHostTestItem.ts index c45beb1c9c7..44b1dd765db 100644 --- a/src/vs/workbench/api/common/extHostTestItem.ts +++ b/src/vs/workbench/api/common/extHostTestItem.ts @@ -5,7 +5,7 @@ import type * as vscode from 'vscode'; import { URI } from '../../../base/common/uri.js'; -import * as editorRange from '../../../editor/common/core/range.js'; +import * as editorRange from '../../../editor/common/language/core/range.js'; import { TestId, TestIdPathParts } from '../../contrib/testing/common/testId.js'; import { createTestItemChildren, ExtHostTestItemEvent, ITestChildrenLike, ITestItemApi, ITestItemChildren, TestItemCollection, TestItemEventOp } from '../../contrib/testing/common/testItemCollection.js'; import { denamespaceTestTag, ITestItem, ITestItemContext } from '../../contrib/testing/common/testTypes.js'; diff --git a/src/vs/workbench/api/common/extHostTesting.ts b/src/vs/workbench/api/common/extHostTesting.ts index 1cef0298f32..8d6715e18ce 100644 --- a/src/vs/workbench/api/common/extHostTesting.ts +++ b/src/vs/workbench/api/common/extHostTesting.ts @@ -17,7 +17,7 @@ import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { isDefined } from '../../../base/common/types.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { generateUuid } from '../../../base/common/uuid.js'; -import { IPosition } from '../../../editor/common/core/position.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 86a322348a8..8d402dac84d 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -7,8 +7,8 @@ import { ok } from '../../../base/common/assert.js'; import { ReadonlyError, illegalArgument } from '../../../base/common/errors.js'; import { IdGenerator } from '../../../base/common/idGenerator.js'; import { TextEditorCursorStyle } from '../../../editor/common/config/editorOptions.js'; -import { IRange } from '../../../editor/common/core/range.js'; -import { ISingleEditOperation } from '../../../editor/common/core/editOperation.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; +import { ISingleEditOperation } from '../../../editor/common/language/core/editOperation.js'; import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from './extHost.protocol.js'; import * as TypeConverters from './extHostTypeConverters.js'; import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from './extHostTypes.js'; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 4179deb5ad3..8b821bfbecb 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -21,7 +21,7 @@ import { IExtensionDescription } from '../../../platform/extensions/common/exten import { MarkdownString, ViewBadge, DataTransfer } from './extHostTypeConverters.js'; import { IMarkdownString, isMarkdownString } from '../../../base/common/htmlContent.js'; import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js'; -import { ITreeViewsDnDService, TreeViewsDnDService } from '../../../editor/common/services/treeViewsDnd.js'; +import { ITreeViewsDnDService, TreeViewsDnDService } from '../../../editor/common/language/services/treeViewsDnd.js'; import { IAccessibilityInformation } from '../../../platform/accessibility/common/accessibility.js'; import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js'; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 75a62af117c..8b61cb36fa2 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -25,14 +25,14 @@ import { URI, UriComponents, isUriComponents } from '../../../base/common/uri.js import { IURITransformer } from '../../../base/common/uriIpc.js'; import { generateUuid } from '../../../base/common/uuid.js'; import { RenderLineNumbersType } from '../../../editor/common/config/editorOptions.js'; -import { IPosition } from '../../../editor/common/core/position.js'; -import * as editorRange from '../../../editor/common/core/range.js'; -import { ISelection } from '../../../editor/common/core/selection.js'; +import { IPosition } from '../../../editor/common/language/core/position.js'; +import * as editorRange from '../../../editor/common/language/core/range.js'; +import { ISelection } from '../../../editor/common/language/core/selection.js'; import { IContentDecorationRenderOptions, IDecorationOptions, IDecorationRenderOptions, IThemeDecorationRenderOptions } from '../../../editor/common/editorCommon.js'; -import * as encodedTokenAttributes from '../../../editor/common/encodedTokenAttributes.js'; -import * as languageSelector from '../../../editor/common/languageSelector.js'; -import * as languages from '../../../editor/common/languages.js'; -import { EndOfLineSequence, TrackedRangeStickiness } from '../../../editor/common/model.js'; +import * as encodedTokenAttributes from '../../../editor/common/language/encodedTokenAttributes.js'; +import * as languageSelector from '../../../editor/common/language/languageSelector.js'; +import * as languages from '../../../editor/common/language/languages.js'; +import { EndOfLineSequence, TrackedRangeStickiness } from '../../../editor/common/language/model.js'; import { ITextEditorOptions } from '../../../platform/editor/common/editor.js'; import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from '../../../platform/markers/common/markers.js'; diff --git a/src/vs/workbench/api/common/extHostUriOpener.ts b/src/vs/workbench/api/common/extHostUriOpener.ts index 7e97bb4fba0..c615f238e1f 100644 --- a/src/vs/workbench/api/common/extHostUriOpener.ts +++ b/src/vs/workbench/api/common/extHostUriOpener.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../base/common/cancellation.js'; import { toDisposable } from '../../../base/common/lifecycle.js'; import { Schemas } from '../../../base/common/network.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; -import * as languages from '../../../editor/common/languages.js'; +import * as languages from '../../../editor/common/language/languages.js'; import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js'; import type * as vscode from 'vscode'; import { ExtHostUriOpenersShape, IMainContext, MainContext, MainThreadUriOpenersShape } from './extHost.protocol.js'; diff --git a/src/vs/workbench/api/node/extHostConsoleForwarder.ts b/src/vs/workbench/api/node/extHostConsoleForwarder.ts index aa2dbca286c..241904a6ea4 100644 --- a/src/vs/workbench/api/node/extHostConsoleForwarder.ts +++ b/src/vs/workbench/api/node/extHostConsoleForwarder.ts @@ -47,7 +47,11 @@ export class ExtHostConsoleForwarder extends AbstractExtHostConsoleForwarder { Object.defineProperty(stream, 'write', { set: () => { }, - get: () => (chunk: Uint8Array | string, encoding?: BufferEncoding, callback?: (err?: Error) => void) => { + get: () => ( + chunk: Uint8Array | string, + encoding?: BufferEncoding, + callback?: (err?: Error | null) => void + ) => { if (!this._isMakingConsoleCall) { buf += (chunk as any).toString(encoding); const eol = buf.length > MAX_STREAM_BUFFER_LENGTH ? buf.length : buf.lastIndexOf('\n'); diff --git a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts index 2e10b368a64..7bcabada3e0 100644 --- a/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts +++ b/src/vs/workbench/api/test/browser/extHostApiCommands.test.ts @@ -27,7 +27,7 @@ import { TestRPCProtocol } from '../common/testRPCProtocol.js'; import { MarkerService } from '../../../../platform/markers/common/markerService.js'; import { IMarkerService } from '../../../../platform/markers/common/markers.js'; import { ICommandService, CommandsRegistry } from '../../../../platform/commands/common/commands.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ExtHostLanguageFeatures } from '../../common/extHostLanguageFeatures.js'; import { MainThreadLanguageFeatures } from '../../browser/mainThreadLanguageFeatures.js'; import { ExtHostApiCommands } from '../../common/extHostApiCommands.js'; @@ -40,21 +40,21 @@ import { ExtHostDiagnostics } from '../../common/extHostDiagnostics.js'; import type * as vscode from 'vscode'; import '../../../contrib/search/browser/search.contribution.js'; import { ILogService, NullLogService } from '../../../../platform/log/common/log.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { nullExtensionDescription, IExtensionService } from '../../../services/extensions/common/extensions.js'; import { dispose, ImmortalReference } from '../../../../base/common/lifecycle.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { mock } from '../../../../base/test/common/mock.js'; import { NullApiDeprecationService } from '../../common/extHostApiDeprecationService.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IExtHostFileSystemInfo } from '../../common/extHostFileSystemInfo.js'; import { URITransformerService } from '../../common/extHostUriTransformerService.js'; import { IOutlineModelService, OutlineModelService } from '../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; -import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../../editor/common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../editor/common/services/languageFeaturesService.js'; +import { ILanguageFeatureDebounceService, LanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../editor/common/language/services/languageFeaturesService.js'; import { assertType } from '../../../../base/common/types.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IExtHostTelemetry } from '../../common/extHostTelemetry.js'; diff --git a/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts b/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts index 5755dc3e1e8..64e03cee14d 100644 --- a/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts +++ b/src/vs/workbench/api/test/browser/extHostDocumentData.test.ts @@ -7,12 +7,12 @@ import assert from 'assert'; import { URI } from '../../../../base/common/uri.js'; import { ExtHostDocumentData } from '../../common/extHostDocumentData.js'; import { Position } from '../../common/extHostTypes.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { MainThreadDocumentsShape } from '../../common/extHost.protocol.js'; -import { IModelChangedEvent } from '../../../../editor/common/model/mirrorTextModel.js'; +import { IModelChangedEvent } from '../../../../editor/common/language/model/mirrorTextModel.js'; import { mock } from '../../../../base/test/common/mock.js'; import * as perfData from './extHostDocumentData.test.perf-data.js'; -import { setDefaultGetWordAtTextConfig } from '../../../../editor/common/core/wordHelper.js'; +import { setDefaultGetWordAtTextConfig } from '../../../../editor/common/language/core/wordHelper.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; suite('ExtHostDocumentData', () => { diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 294a8286dbb..a9321961019 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -9,8 +9,8 @@ import { setUnexpectedErrorHandler, errorHandler } from '../../../../base/common import { URI } from '../../../../base/common/uri.js'; import * as types from '../../common/extHostTypes.js'; import { createTextModel } from '../../../../editor/test/common/testTextModel.js'; -import { Position as EditorPosition, Position } from '../../../../editor/common/core/position.js'; -import { Range as EditorRange } from '../../../../editor/common/core/range.js'; +import { Position as EditorPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range as EditorRange } from '../../../../editor/common/language/core/range.js'; import { TestRPCProtocol } from '../common/testRPCProtocol.js'; import { IMarkerService } from '../../../../platform/markers/common/markers.js'; import { MarkerService } from '../../../../platform/markers/common/markerService.js'; @@ -20,7 +20,7 @@ import { ExtHostCommands } from '../../common/extHostCommands.js'; import { MainThreadCommands } from '../../browser/mainThreadCommands.js'; import { ExtHostDocuments } from '../../common/extHostDocuments.js'; import { ExtHostDocumentsAndEditors } from '../../common/extHostDocumentsAndEditors.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { getCodeLensModel } from '../../../../editor/contrib/codelens/browser/codelens.js'; import { getDefinitionsAtPosition, getImplementationsAtPosition, getTypeDefinitionsAtPosition, getDeclarationsAtPosition, getReferencesAtPosition } from '../../../../editor/contrib/gotoSymbol/browser/goToSymbol.js'; import { getHoversPromise } from '../../../../editor/contrib/hover/browser/getHover.js'; @@ -37,21 +37,21 @@ import { ExtHostDiagnostics } from '../../common/extHostDiagnostics.js'; import type * as vscode from 'vscode'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { NullLogService } from '../../../../platform/log/common/log.js'; -import { ITextModel, EndOfLineSequence } from '../../../../editor/common/model.js'; +import { ITextModel, EndOfLineSequence } from '../../../../editor/common/language/model.js'; import { getColors } from '../../../../editor/contrib/colorPicker/browser/color.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { nullExtensionDescription as defaultExtension } from '../../../services/extensions/common/extensions.js'; import { provideSelectionRanges } from '../../../../editor/contrib/smartSelect/browser/smartSelect.js'; import { mock } from '../../../../base/test/common/mock.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { NullApiDeprecationService } from '../../common/extHostApiDeprecationService.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; import { IExtHostFileSystemInfo } from '../../common/extHostFileSystemInfo.js'; import { URITransformerService } from '../../common/extHostUriTransformerService.js'; import { OutlineModel } from '../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../editor/common/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../editor/common/language/services/languageFeaturesService.js'; import { CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IExtHostTelemetry } from '../../common/extHostTelemetry.js'; diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index 3b45e1073b7..277222fbedb 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -13,7 +13,7 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { URI } from '../../../../base/common/uri.js'; import { mock, mockObject, MockObject } from '../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import * as editorRange from '../../../../editor/common/core/range.js'; +import * as editorRange from '../../../../editor/common/language/core/range.js'; import { ExtensionIdentifier, IExtensionDescription } from '../../../../platform/extensions/common/extensions.js'; import { NullLogService } from '../../../../platform/log/common/log.js'; import { MainThreadTestingShape } from '../../common/extHost.protocol.js'; diff --git a/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts b/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts index 9ee4d3316ee..56eb8b4f899 100644 --- a/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadBulkEdits.test.ts @@ -11,7 +11,7 @@ import { URI } from '../../../../base/common/uri.js'; import { FileSystemProviderCapabilities, IFileService } from '../../../../platform/files/common/files.js'; import { reviveWorkspaceEditDto } from '../../browser/mainThreadBulkEdits.js'; import { UriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentityService.js'; -import { IWorkspaceTextEdit } from '../../../../editor/common/languages.js'; +import { IWorkspaceTextEdit } from '../../../../editor/common/language/languages.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; suite('MainThreadBulkEdits', function () { diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts index c999d333938..998864cabc4 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentContentProviders.test.ts @@ -8,10 +8,10 @@ import { URI } from '../../../../base/common/uri.js'; import { MainThreadDocumentContentProviders } from '../../browser/mainThreadDocumentContentProviders.js'; import { createTextModel } from '../../../../editor/test/common/testTextModel.js'; import { mock } from '../../../../base/test/common/mock.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { TestRPCProtocol } from '../common/testRPCProtocol.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; suite('MainThreadDocumentContentProviders', function () { diff --git a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts index 26dd5eaf8f2..17f3071c25f 100644 --- a/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadDocumentsAndEditors.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { MainThreadDocumentsAndEditors } from '../../browser/mainThreadDocumentsAndEditors.js'; import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js'; import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js'; -import { ModelService } from '../../../../editor/common/services/modelService.js'; +import { ModelService } from '../../../../editor/common/language/services/modelService.js'; import { TestCodeEditorService } from '../../../../editor/test/browser/editorTestServices.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { IDocumentsAndEditorsDelta } from '../../common/extHost.protocol.js'; @@ -15,7 +15,7 @@ import { createTestCodeEditor, ITestCodeEditor } from '../../../../editor/test/b import { mock } from '../../../../base/test/common/mock.js'; import { TestEditorService, TestEditorGroupsService, TestEnvironmentService, TestPathService } from '../../../test/browser/workbenchTestServices.js'; import { Event } from '../../../../base/common/event.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IFileService } from '../../../../platform/files/common/files.js'; @@ -27,12 +27,12 @@ import { TestTextResourcePropertiesService, TestWorkingCopyFileService } from '. import { UriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentityService.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; -import { TextModel } from '../../../../editor/common/model/textModel.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { TestInstantiationService } from '../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { LanguageService } from '../../../../editor/common/services/languageService.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { LanguageService } from '../../../../editor/common/language/services/languageService.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js'; import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; diff --git a/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts b/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts index 552b0df1f32..1dd8cc76b9a 100644 --- a/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadEditors.test.ts @@ -11,14 +11,14 @@ import { mock } from '../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ITextSnapshot } from '../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ModelService } from '../../../../editor/common/services/modelService.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextSnapshot } from '../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../../editor/common/language/services/modelService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { TestCodeEditorService } from '../../../../editor/test/browser/editorTestServices.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../platform/configuration/test/common/testConfigurationService.js'; @@ -55,8 +55,8 @@ import { ICopyOperation, ICreateFileOperation, ICreateOperation, IDeleteOperatio import { IWorkingCopyService } from '../../../services/workingCopy/common/workingCopyService.js'; import { TestEditorGroupsService, TestEditorService, TestEnvironmentService, TestFileService, TestLifecycleService, TestWorkingCopyService } from '../../../test/browser/workbenchTestServices.js'; import { TestContextService, TestTextResourcePropertiesService } from '../../../test/common/workbenchTestServices.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { LanguageService } from '../../../../editor/common/services/languageService.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { LanguageService } from '../../../../editor/common/language/services/languageService.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { TestLanguageConfigurationService } from '../../../../editor/test/common/modes/testLanguageConfigurationService.js'; diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 2fb9c8edbb3..1bfe705df95 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -16,11 +16,11 @@ import { IQuickInputButton, IQuickInputService, IQuickPickSeparator, IKeyMods, I import { IWorkspaceContextService, IWorkspaceIdentifier } from '../../../platform/workspace/common/workspace.js'; import { ILabelService, Verbosity } from '../../../platform/label/common/label.js'; import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { IRecent, isRecentFolder, isRecentWorkspace, IWorkspacesService } from '../../../platform/workspaces/common/workspaces.js'; import { URI } from '../../../base/common/uri.js'; -import { getIconClasses } from '../../../editor/common/services/getIconClasses.js'; +import { getIconClasses } from '../../../editor/common/language/services/getIconClasses.js'; import { FileKind } from '../../../platform/files/common/files.js'; import { splitRecentLabel } from '../../../base/common/labels.js'; import { isMacintosh, isWeb, isWindows } from '../../../base/common/platform.js'; diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 5d45d2f2cfa..075fe44564d 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -14,9 +14,9 @@ import { FileKind } from '../../../platform/files/common/files.js'; import { ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../platform/label/common/label.js'; import { IQuickInputService, IPickOptions, IQuickPickItem } from '../../../platform/quickinput/common/quickInput.js'; -import { getIconClasses } from '../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; import { IFileDialogService, IPickAndOpenOptions } from '../../../platform/dialogs/common/dialogs.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { Schemas } from '../../../base/common/network.js'; diff --git a/src/vs/workbench/browser/codeeditor.ts b/src/vs/workbench/browser/codeeditor.ts index 437e01b37a6..4a232b59449 100644 --- a/src/vs/workbench/browser/codeeditor.ts +++ b/src/vs/workbench/browser/codeeditor.ts @@ -11,11 +11,11 @@ import { URI } from '../../base/common/uri.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference, isCodeEditor, isCompositeEditor } from '../../editor/browser/editorBrowser.js'; import { EmbeddedCodeEditorWidget } from '../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption } from '../../editor/common/config/editorOptions.js'; -import { IRange } from '../../editor/common/core/range.js'; +import { IRange } from '../../editor/common/language/core/range.js'; import { CursorChangeReason, ICursorPositionChangedEvent } from '../../editor/common/cursorEvents.js'; import { IEditorContribution } from '../../editor/common/editorCommon.js'; -import { IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../editor/common/model/textModel.js'; +import { IModelDecorationsChangeAccessor, TrackedRangeStickiness } from '../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../editor/common/language/model/textModel.js'; import { AbstractFloatingClickMenu, FloatingClickWidget } from '../../platform/actions/browser/floatingMenu.js'; import { IMenuService, MenuId } from '../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/browser/labels.ts b/src/vs/workbench/browser/labels.ts index 9f3885d69e7..964a2b60dfc 100644 --- a/src/vs/workbench/browser/labels.ts +++ b/src/vs/workbench/browser/labels.ts @@ -7,23 +7,23 @@ import { localize } from '../../nls.js'; import { URI } from '../../base/common/uri.js'; import { dirname, isEqual, basenameOrAuthority } from '../../base/common/resources.js'; import { IconLabel, IIconLabelValueOptions, IIconLabelCreationOptions } from '../../base/browser/ui/iconLabel/iconLabel.js'; -import { ILanguageService } from '../../editor/common/languages/language.js'; +import { ILanguageService } from '../../editor/common/language/language.js'; import { IWorkspaceContextService } from '../../platform/workspace/common/workspace.js'; import { IConfigurationService } from '../../platform/configuration/common/configuration.js'; -import { IModelService } from '../../editor/common/services/model.js'; +import { IModelService } from '../../editor/common/language/services/model.js'; import { ITextFileService } from '../services/textfile/common/textfiles.js'; import { IDecoration, IDecorationsService, IResourceDecorationChangeEvent } from '../services/decorations/common/decorations.js'; import { Schemas } from '../../base/common/network.js'; import { FileKind, FILES_ASSOCIATIONS_CONFIG } from '../../platform/files/common/files.js'; -import { ITextModel } from '../../editor/common/model.js'; +import { ITextModel } from '../../editor/common/language/model.js'; import { IThemeService } from '../../platform/theme/common/themeService.js'; import { Event, Emitter } from '../../base/common/event.js'; import { ILabelService } from '../../platform/label/common/label.js'; -import { getIconClasses } from '../../editor/common/services/getIconClasses.js'; +import { getIconClasses } from '../../editor/common/language/services/getIconClasses.js'; import { Disposable, dispose, IDisposable, MutableDisposable } from '../../base/common/lifecycle.js'; import { IInstantiationService } from '../../platform/instantiation/common/instantiation.js'; import { normalizeDriveLetter } from '../../base/common/labels.js'; -import { IRange } from '../../editor/common/core/range.js'; +import { IRange } from '../../editor/common/language/core/range.js'; import { ThemeIcon } from '../../base/common/themables.js'; import { INotebookDocumentService, extractCellOutputDetails } from '../services/notebook/common/notebookDocumentService.js'; diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 4a83f3b2d90..cffa2d577a8 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -7,6 +7,55 @@ @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } +/* JetBrains Mono bundled with the app for editor.fontFamily */ +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Regular.woff2") format("woff2"); + font-style: normal; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Italic.woff2") format("woff2"); + font-style: italic; + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Medium.woff2") format("woff2"); + font-style: normal; + font-weight: 500; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-MediumItalic.woff2") format("woff2"); + font-style: italic; + font-weight: 500; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-Bold.woff2") format("woff2"); + font-style: normal; + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "JetBrains Mono"; + src: url("../../../code/electron-sandbox/workbench/assets/fonts/JetBrainsMono-BoldItalic.woff2") format("woff2"); + font-style: italic; + font-weight: 700; + font-display: swap; +} + /* Font Families (with CJK support) */ .monaco-workbench.mac { font-family: -apple-system, BlinkMacSystemFont, sans-serif; } diff --git a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts index 894e4b51e22..4c2e0be721a 100644 --- a/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryDiffEditor.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { BaseBinaryResourceEditor } from './binaryEditor.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 351252cecda..2d76e9e649c 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -17,7 +17,7 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { combinedDisposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { basename, extUri } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { DocumentSymbol } from '../../../../editor/common/languages.js'; +import { DocumentSymbol } from '../../../../editor/common/language/languages.js'; import { OutlineElement } from '../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index e01631d1552..2c050534a04 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -30,7 +30,7 @@ import { localize } from '../../../../nls.js'; import { IOutline, IOutlineComparator } from '../../../services/outline/browser/outline.js'; import { IEditorOptions } from '../../../../platform/editor/common/editor.js'; import { IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; interface ILayoutInfo { maxHeight: number; diff --git a/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts b/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts index dc3f83afe6c..da25f226cbe 100644 --- a/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/diffEditorCommands.ts @@ -6,7 +6,7 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize, localize2 } from '../../../../nls.js'; import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 9f0b51ba1c2..d502fd90e60 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -25,8 +25,8 @@ import { EditorInputCapabilities, IEditorIdentifier, IUntypedEditorInput } from import { EDITOR_DRAG_AND_DROP_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BACKGROUND, EDITOR_DROP_INTO_PROMPT_BORDER, EDITOR_DROP_INTO_PROMPT_FOREGROUND } from '../../../common/theme.js'; import { GroupDirection, IEditorDropTargetDelegate, IEditorGroup, IEditorGroupsService, IMergeGroupOptions, MergeGroupMode } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { ITreeViewsDnDService } from '../../../../editor/common/services/treeViewsDndService.js'; -import { DraggedTreeItemsIdentifier } from '../../../../editor/common/services/treeViewsDnd.js'; +import { ITreeViewsDnDService } from '../../../../editor/common/language/services/treeViewsDndService.js'; +import { DraggedTreeItemsIdentifier } from '../../../../editor/common/language/services/treeViewsDnd.js'; interface IDropOperation { splitDirection?: GroupDirection; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 1a6788573ec..465e8c0ba38 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -38,7 +38,7 @@ import { getActionBarActions, PrimaryAndSecondaryActions } from '../../../../pla import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { hash } from '../../../../base/common/hash.js'; -import { getMimeTypes } from '../../../../editor/common/services/languagesAssociations.js'; +import { getMimeTypes } from '../../../../editor/common/language/services/languagesAssociations.js'; import { extname, isEqual } from '../../../../base/common/resources.js'; import { Schemas } from '../../../../base/common/network.js'; import { EditorActivation, IEditorOptions } from '../../../../platform/editor/common/editor.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index 120223e43c1..eb0c2c8361f 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -87,6 +87,7 @@ export class EditorGroupWatermark extends Disposable { // private enabled: boolean = false; private workbenchState: WorkbenchState; private currentDisposables = new Set(); + private isDisposed = false; constructor( container: HTMLElement, @@ -169,10 +170,16 @@ export class EditorGroupWatermark extends Disposable { const update = async () => { + if (this.isDisposed) { + return; + } // put async at top so don't need to wait (this prevents a jitter on load) const recentlyOpened = await this.workspacesService.getRecentlyOpened() .catch(() => ({ files: [], workspaces: [] })).then(w => w.workspaces); + if (this.isDisposed) { + return; + } clearNode(voidIconBox); clearNode(recentsBox); @@ -282,6 +289,9 @@ export class EditorGroupWatermark extends Disposable { } else { + if (this.isDisposed) { + return; + } // show them Void keybindings const keys = this.keybindingService.lookupKeybinding(VOID_CTRL_L_ACTION_ID); @@ -326,7 +336,9 @@ export class EditorGroupWatermark extends Disposable { }; update(); - this.transientDisposables.add(this.keybindingService.onDidUpdateKeybindings(update)); + if (!this.isDisposed) { + this.transientDisposables.add(this.keybindingService.onDidUpdateKeybindings(update)); + } } private clear(): void { @@ -335,9 +347,11 @@ export class EditorGroupWatermark extends Disposable { } override dispose(): void { - super.dispose(); - this.clear(); + this.isDisposed = true; this.currentDisposables.forEach(label => label.dispose()); + this.currentDisposables.clear(); + this.clear(); + super.dispose(); } } diff --git a/src/vs/workbench/browser/parts/editor/editorPane.ts b/src/vs/workbench/browser/parts/editor/editorPane.ts index bb868efd2f1..8fa48c4f7c1 100644 --- a/src/vs/workbench/browser/parts/editor/editorPane.ts +++ b/src/vs/workbench/browser/parts/editor/editorPane.ts @@ -22,7 +22,7 @@ import { indexOfPath } from '../../../../base/common/extpath.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IEditorOptions } from '../../../../platform/editor/common/editor.js'; -import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IBoundarySashes } from '../../../../base/browser/ui/sash/sash.js'; import { getWindowById } from '../../../../base/browser/dom.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts index bbab2a62d6e..8ce5686e0c9 100644 --- a/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts +++ b/src/vs/workbench/browser/parts/editor/editorQuickAccess.ts @@ -10,9 +10,9 @@ import { PickerQuickAccessProvider, IPickerQuickAccessItem, TriggerAction } from import { IEditorGroupsService, GroupsOrder } from '../../../services/editor/common/editorGroupsService.js'; import { EditorsOrder, IEditorIdentifier, EditorResourceAccessor, SideBySideEditor, GroupIdentifier } from '../../../common/editor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; import { prepareQuery, scoreItemFuzzy, compareItemsByFuzzyScore, FuzzyScorerCache } from '../../../../base/common/fuzzyScorer.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index 52c6462f761..c8141084855 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -17,7 +17,7 @@ import { IFileEditorInput, EditorResourceAccessor, IEditorPane, SideBySideEditor import { EditorInput } from '../../../common/editor/editorInput.js'; import { Disposable, MutableDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { IEditorAction } from '../../../../editor/common/editorCommon.js'; -import { EndOfLineSequence } from '../../../../editor/common/model.js'; +import { EndOfLineSequence } from '../../../../editor/common/language/model.js'; import { TrimTrailingWhitespaceAction } from '../../../../editor/contrib/linesOperations/browser/linesOperations.js'; import { IndentUsingSpaces, IndentUsingTabs, ChangeTabDisplaySize, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from '../../../../editor/contrib/indentation/browser/indentation.js'; import { BaseBinaryResourceEditor } from './binaryEditor.js'; @@ -25,22 +25,22 @@ import { BinaryResourceDiffEditor } from './binaryDiffEditor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IFileService, FILES_ASSOCIATIONS_CONFIG } from '../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { ILanguageService, ILanguageSelection } from '../../../../editor/common/languages/language.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { ILanguageService, ILanguageSelection } from '../../../../editor/common/language/language.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICommandService, CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { IExtensionGalleryService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { EncodingMode, IEncodingSupport, ILanguageSupport, ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { SUPPORTED_ENCODINGS } from '../../../services/textfile/common/encoding.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { deepClone } from '../../../../base/common/objects.js'; import { ICodeEditor, getCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { Schemas } from '../../../../base/common/network.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js'; -import { getIconClassesForLanguageId } from '../../../../editor/common/services/getIconClasses.js'; +import { getIconClassesForLanguageId } from '../../../../editor/common/language/services/getIconClasses.js'; import { Promises, timeout } from '../../../../base/common/async.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 4d4573776a7..2fefc5efeba 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -34,7 +34,7 @@ import { isCancellationError } from '../../../../base/common/errors.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; import { WorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { LocalSelectionTransfer } from '../../../../platform/dnd/browser/dnd.js'; -import { DraggedTreeItemsIdentifier } from '../../../../editor/common/services/treeViewsDnd.js'; +import { DraggedTreeItemsIdentifier } from '../../../../editor/common/language/services/treeViewsDnd.js'; import { IEditorResolverService } from '../../../services/editor/common/editorResolverService.js'; import { IEditorTitleControlDimensions } from './editorTitleControl.js'; import { IReadonlyEditorGroupModel } from '../../../common/editor/editorGroupModel.js'; diff --git a/src/vs/workbench/browser/parts/editor/editorWithViewState.ts b/src/vs/workbench/browser/parts/editor/editorWithViewState.ts index 8cfd84b64c1..7c039f2a765 100644 --- a/src/vs/workbench/browser/parts/editor/editorWithViewState.ts +++ b/src/vs/workbench/browser/parts/editor/editorWithViewState.ts @@ -11,7 +11,7 @@ import { IStorageService } from '../../../../platform/storage/common/storage.js' import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IEditorGroupsService, IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtUri } from '../../../../base/common/resources.js'; diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 88f8e4f9842..2ddf063def1 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -49,8 +49,8 @@ import { equals } from '../../../../base/common/objects.js'; import { EditorActivation, IEditorOptions } from '../../../../platform/editor/common/editor.js'; import { UNLOCK_GROUP_COMMAND_ID } from './editorCommands.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; -import { ITreeViewsDnDService } from '../../../../editor/common/services/treeViewsDndService.js'; -import { DraggedTreeItemsIdentifier } from '../../../../editor/common/services/treeViewsDnd.js'; +import { ITreeViewsDnDService } from '../../../../editor/common/language/services/treeViewsDndService.js'; +import { DraggedTreeItemsIdentifier } from '../../../../editor/common/language/services/treeViewsDnd.js'; import { IEditorResolverService } from '../../../services/editor/common/editorResolverService.js'; import { IEditorTitleControlDimensions } from './editorTitleControl.js'; import { StickyEditorGroupModel, UnstickyEditorGroupModel } from '../../../common/editor/filteredEditorGroupModel.js'; diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index 691a716cba6..d979203ea8e 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -27,7 +27,7 @@ import { DEFAULT_EDITOR_MIN_DIMENSIONS } from './editor.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { SIDE_BY_SIDE_EDITOR_HORIZONTAL_BORDER, SIDE_BY_SIDE_EDITOR_VERTICAL_BORDER } from '../../../common/theme.js'; import { AbstractEditorWithViewState } from './editorWithViewState.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 63b7b0d6f70..7b5b44e25cf 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -16,7 +16,7 @@ import { DiffEditorInput } from '../../../common/editor/diffEditorInput.js'; import { TextDiffEditorModel } from '../../../common/editor/textDiffEditorModel.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; -import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { TextFileOperationError, TextFileOperationResult } from '../../../services/textfile/common/textfiles.js'; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index e0a45755595..a04668501db 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -15,12 +15,12 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { computeEditorAriaLabel } from '../../editor.js'; import { AbstractEditorWithViewState } from './editorWithViewState.js'; import { IEditorViewState } from '../../../../editor/common/editorCommon.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationChangeEvent, ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IEditorOptions as ICodeEditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; diff --git a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts index f828e558722..f3ceadff68e 100644 --- a/src/vs/workbench/browser/parts/editor/textResourceEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textResourceEditor.ts @@ -14,18 +14,18 @@ import { UntitledTextEditorInput } from '../../../services/untitled/common/untit import { AbstractTextCodeEditor } from './textCodeEditor.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ScrollType, ICodeEditorViewState } from '../../../../editor/common/editorCommon.js'; import { IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; import { EditorOption, IEditorOptions as ICodeEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { ModelConstants } from '../../../../editor/common/model.js'; +import { ModelConstants } from '../../../../editor/common/language/model.js'; import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; import { IFileService } from '../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index 241526727af..59ed43d61ed 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts @@ -16,7 +16,7 @@ import { isThemeColor } from '../../../../editor/common/editorCommon.js'; import { addDisposableListener, EventType, hide, show, append, EventHelper, $ } from '../../../../base/browser/dom.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { assertIsDefined } from '../../../../base/common/types.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { renderIcon, renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9a152cef023..6b86c9f6889 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -68,15 +68,15 @@ import { CheckboxStateHandler, TreeItemCheckbox } from './checkbox.js'; import { setTimeout0 } from '../../../../base/common/platform.js'; import { AriaRole } from '../../../../base/browser/ui/aria/aria.js'; import { TelemetryTrustedValue } from '../../../../platform/telemetry/common/telemetryUtils.js'; -import { ITreeViewsDnDService } from '../../../../editor/common/services/treeViewsDndService.js'; -import { DraggedTreeItemsIdentifier } from '../../../../editor/common/services/treeViewsDnd.js'; +import { ITreeViewsDnDService } from '../../../../editor/common/language/services/treeViewsDndService.js'; +import { DraggedTreeItemsIdentifier } from '../../../../editor/common/language/services/treeViewsDnd.js'; import { IMarkdownRenderResult, MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import type { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; import { parseLinkedText } from '../../../../base/common/linkedText.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IAccessibleViewInformationService } from '../../../services/accessibility/common/accessibleViewInformationService.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; export class TreeViewPane extends ViewPane { diff --git a/src/vs/workbench/common/comments.ts b/src/vs/workbench/common/comments.ts index 4f4d00de478..83feda1508f 100644 --- a/src/vs/workbench/common/comments.ts +++ b/src/vs/workbench/common/comments.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { MarshalledId } from '../../base/common/marshallingIds.js'; -import { CommentThread } from '../../editor/common/languages.js'; +import { CommentThread } from '../../editor/common/language/languages.js'; export interface MarshalledCommentThread { $mid: MarshalledId.CommentThread; diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index d11f9b168aa..ac7e837f2fe 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -8,9 +8,9 @@ import { URI } from '../../base/common/uri.js'; import { localize } from '../../nls.js'; import { IContextKeyService, IContextKey, RawContextKey } from '../../platform/contextkey/common/contextkey.js'; import { basename, dirname, extname, isEqual } from '../../base/common/resources.js'; -import { ILanguageService } from '../../editor/common/languages/language.js'; +import { ILanguageService } from '../../editor/common/language/language.js'; import { IFileService } from '../../platform/files/common/files.js'; -import { IModelService } from '../../editor/common/services/model.js'; +import { IModelService } from '../../editor/common/language/services/model.js'; import { Schemas } from '../../base/common/network.js'; import { EditorInput } from './editor/editorInput.js'; import { IEditorResolverService } from '../services/editor/common/editorResolverService.js'; diff --git a/src/vs/workbench/common/editor/editorOptions.ts b/src/vs/workbench/common/editor/editorOptions.ts index e1dc1e8688e..15c6d16e4b9 100644 --- a/src/vs/workbench/common/editor/editorOptions.ts +++ b/src/vs/workbench/common/editor/editorOptions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange } from '../../../editor/common/core/range.js'; +import { IRange } from '../../../editor/common/language/core/range.js'; import { ICodeEditorViewState, IDiffEditorViewState, IEditor, ScrollType } from '../../../editor/common/editorCommon.js'; import { ITextEditorOptions, TextEditorSelectionRevealType, TextEditorSelectionSource } from '../../../platform/editor/common/editor.js'; import { isTextEditorViewState } from '../editor.js'; diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 360b534bdc9..f379b1c7b91 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -12,7 +12,7 @@ import { dirname, isEqual } from '../../../base/common/resources.js'; import { IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js'; import { IMarkdownString } from '../../../base/common/htmlContent.js'; import { isConfigured } from '../../../platform/configuration/common/configuration.js'; -import { ITextResourceConfigurationService } from '../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../editor/common/language/services/textResourceConfiguration.js'; import { ICustomEditorLabelService } from '../../services/editor/common/customEditorLabelService.js'; /** diff --git a/src/vs/workbench/common/editor/textEditorModel.ts b/src/vs/workbench/common/editor/textEditorModel.ts index 53b900ec6ed..7beab457a8f 100644 --- a/src/vs/workbench/common/editor/textEditorModel.ts +++ b/src/vs/workbench/common/editor/textEditorModel.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextModel, ITextBufferFactory, ITextSnapshot, ModelConstants } from '../../../editor/common/model.js'; +import { ITextModel, ITextBufferFactory, ITextSnapshot, ModelConstants, } from '../../../editor/common/language/model.js'; import { EditorModel } from './editorModel.js'; import { ILanguageSupport } from '../../services/textfile/common/textfiles.js'; import { URI } from '../../../base/common/uri.js'; -import { ITextEditorModel, IResolvedTextEditorModel } from '../../../editor/common/services/resolverService.js'; -import { ILanguageService, ILanguageSelection } from '../../../editor/common/languages/language.js'; -import { IModelService } from '../../../editor/common/services/model.js'; +import { ITextEditorModel, IResolvedTextEditorModel } from '../../../editor/common/language/services/resolverService.js'; +import { ILanguageService, ILanguageSelection } from '../../../editor/common/language/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; import { MutableDisposable } from '../../../base/common/lifecycle.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../editor/common/languages/modesRegistry.js'; import { ILanguageDetectionService, LanguageDetectionLanguageEventSource } from '../../services/languageDetection/common/languageDetectionWorkerService.js'; diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index ad5369aebdc..8a2ab14ee4d 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -13,12 +13,12 @@ import { IFileService } from '../../../platform/files/common/files.js'; import { ILabelService } from '../../../platform/label/common/label.js'; import { Schemas } from '../../../base/common/network.js'; import { isEqual } from '../../../base/common/resources.js'; -import { ITextEditorModel, ITextModelService } from '../../../editor/common/services/resolverService.js'; +import { ITextEditorModel, ITextModelService } from '../../../editor/common/language/services/resolverService.js'; import { TextResourceEditorModel } from './textResourceEditorModel.js'; import { IReference } from '../../../base/common/lifecycle.js'; -import { createTextBufferFactory } from '../../../editor/common/model/textModel.js'; +import { createTextBufferFactory } from '../../../editor/common/language/model/textModel.js'; import { IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js'; -import { ITextResourceConfigurationService } from '../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../editor/common/language/services/textResourceConfiguration.js'; import { ICustomEditorLabelService } from '../../services/editor/common/customEditorLabelService.js'; /** diff --git a/src/vs/workbench/common/editor/textResourceEditorModel.ts b/src/vs/workbench/common/editor/textResourceEditorModel.ts index 703848412c6..c1c730baf8f 100644 --- a/src/vs/workbench/common/editor/textResourceEditorModel.ts +++ b/src/vs/workbench/common/editor/textResourceEditorModel.ts @@ -5,8 +5,8 @@ import { BaseTextEditorModel } from './textEditorModel.js'; import { URI } from '../../../base/common/uri.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; -import { IModelService } from '../../../editor/common/services/model.js'; +import { ILanguageService } from '../../../editor/common/language/language.js'; +import { IModelService } from '../../../editor/common/language/services/model.js'; import { ILanguageDetectionService } from '../../services/languageDetection/common/languageDetectionWorkerService.js'; import { IAccessibilityService } from '../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 37e1b8f620c..b425a9c2b71 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Command } from '../../editor/common/languages.js'; +import { Command } from '../../editor/common/language/languages.js'; import { UriComponents, URI } from '../../base/common/uri.js'; import { Event, Emitter } from '../../base/common/event.js'; import { ContextKeyExpression } from '../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index 6e000778220..823609e391a 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -18,9 +18,9 @@ import { URI } from '../../../../base/common/uri.js'; import { IEditorConstructionOptions } from '../../../../editor/browser/config/editorConfiguration.js'; import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { AccessibilityHelpNLS } from '../../../../editor/common/standaloneStrings.js'; import { CodeActionController } from '../../../../editor/contrib/codeAction/browser/codeActionController.js'; import { localize } from '../../../../nls.js'; @@ -47,7 +47,7 @@ import { IChatCodeBlockContextProviderService } from '../../chat/browser/chat.js import { ICodeBlockActionContext } from '../../chat/browser/codeBlockPart.js'; import { getSimpleEditorOptions } from '../../codeEditor/browser/simpleEditorOptions.js'; import { Schemas } from '../../../../base/common/network.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; const enum DIMENSIONS { MAX_WIDTH = 600 diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts index 28d6fe3edef..a5905872df0 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts +++ b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts @@ -8,9 +8,9 @@ import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.j import { IReader, autorun, autorunWithStore, derived, observableFromEvent, observableFromPromise, observableFromValueWithChangeEvent, observableSignalFromEvent, wasEventTriggeredRecently } from '../../../../base/common/observable.js'; import { isDefined } from '../../../../base/common/types.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { CursorChangeReason } from '../../../../editor/common/cursorEvents.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { FoldingController } from '../../../../editor/contrib/folding/browser/folding.js'; import { AccessibilityModality, AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/acp/browser/AcpHostCallbacksService.ts b/src/vs/workbench/contrib/acp/browser/AcpHostCallbacksService.ts new file mode 100644 index 00000000000..72c026a1f2a --- /dev/null +++ b/src/vs/workbench/contrib/acp/browser/AcpHostCallbacksService.ts @@ -0,0 +1,1036 @@ +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { URI } from '../../../../base/common/uri.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { ITerminalToolService } from '../../void/browser/terminalToolService.js'; +import { IChatThreadService } from '../../void/browser/chatThreadService.js'; +import { normalizeAcpToolName } from '../../void/browser/ChatAcpHandler.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { defaultGlobalSettings } from '../../../../platform/void/common/voidSettingsTypes.js'; +import { approvalTypeOfToolName } from '../../../../platform/void/common/toolsServiceTypes.js'; +import { isAToolName } from '../../void/common/prompt/prompts.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { computeTruncatedToolOutput } from '../../../../platform/void/common/toolOutputTruncation.js'; + +import { + toolOutputFileName, + normalizeMetaLogFilePath, + stableToolOutputsRelPath, +} from '../../../../platform/void/common/toolOutputFileNames.js'; + +type EnvVar = { name: string; value: string }; +type TerminalExitStatus = { exitCode: number | null; signal: string | null }; + +export class AcpHostCallbacksService { + + constructor( + private readonly instantiationService: IInstantiationService, + private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + ) { } + + private _terminalSuggestedFileName(terminalId: string): string { + // stable: .void/tool_outputs/terminal_<8hex>.log (keyed by terminalId) + return stableToolOutputsRelPath({ toolName: 'run_command', terminalId }); + } + + private _getTerminalToolService(): ITerminalToolService | null { + try { return this.instantiationService.invokeFunction(accessor => accessor.get(ITerminalToolService)); } + catch { return null; } + } + + private _getWorkspaceRoot(): URI | null { + try { + const ws = this.instantiationService.invokeFunction(a => a.get(IWorkspaceContextService)); + const w = ws.getWorkspace(); + return w.folders?.length ? w.folders[0].uri : null; + } catch { + return null; + } + } + + private _unwrapDeepRunResult(res: any): any { + // Some terminal services return { result, resolveReason } (and sometimes nested result.result...) + let cur = res; + for (let i = 0; i < 4; i++) { + if (!cur || typeof cur !== 'object') break; + if ('result' in cur) { + cur = (cur as any).result; + continue; + } + break; + } + return cur; + } + + private _getMaxToolOutputLength(): number { + try { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const raw = (vss?.state as any)?.globalSettings?.maxToolOutputLength; + const n = typeof raw === 'number' ? raw : (typeof raw === 'string' ? Number(raw) : NaN); + if (Number.isFinite(n) && n > 0) return n; + } catch { /* ignore */ } + return 16000; + } + + private _getReadFileChunkLines(): number { + try { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const raw = (vss?.state as any)?.globalSettings?.readFileChunkLines; + const n = typeof raw === 'number' ? raw : (typeof raw === 'string' ? Number(raw) : NaN); + if (Number.isFinite(n) && n > 0) return n; + } catch { /* ignore */ } + return defaultGlobalSettings.readFileChunkLines; + } + + private _countLines(text: string): number { + if (!text) return 0; + return text.split(/\r?\n/).length; + } + + private _asUriFromPathOrUri(pathOrUri: unknown): URI { + const s = String(pathOrUri ?? '').trim(); + + // Real URI with scheme (file://, vscode-remote://, etc) + // Important: don't treat "C:foo" as a scheme URI + const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s) && !/^[a-zA-Z]:[\\/]/.test(s); + if (hasScheme) return URI.parse(s); + + // Absolute paths + const isWindowsDriveAbs = /^[a-zA-Z]:[\\/]/.test(s); + const isPosixAbs = s.startsWith('/'); + if (isWindowsDriveAbs || isPosixAbs) return URI.file(s); + + // Workspace-relative paths (e.g. ".void/tool_outputs/x.log") + const root = this._getWorkspaceRoot(); + if (root) { + let rel = s; + if (rel.startsWith('./')) rel = rel.slice(2); + else if (rel.startsWith('.\\')) rel = rel.slice(2); + + const parts = rel.split(/[/\\]/).filter(Boolean); + if (parts.length) return URI.joinPath(root, ...parts); + } + + // Fallback + return URI.file(s); + } + + private _shellQuoteArg(arg: string): string { + if (!/[ \t\r\n"]/.test(arg)) return arg; + return `"${arg.replace(/"/g, '\\"')}"`; + } + + private _buildCommandLine(command: string, args: unknown): string { + const arr = Array.isArray(args) ? args.map(a => String(a ?? '')) : []; + if (!arr.length) return command; + return `${command} ${arr.map(a => this._shellQuoteArg(a)).join(' ')}`; + } + + private async _saveToToolOutputs(fullText: string, suggestedFileNameOrPath?: string): Promise { + try { + const root = this._getWorkspaceRoot(); + if (!root) return null; + + const outputDir = URI.joinPath(root, '.void', 'tool_outputs'); + if (!(await this.fileService.exists(outputDir))) { + await this.fileService.createFolder(outputDir); + } + + // normalize suggested to ".void/tool_outputs/" and take basename + let fileName: string | null = null; + if (suggestedFileNameOrPath) { + const norm = normalizeMetaLogFilePath(suggestedFileNameOrPath); + if (norm) fileName = norm.split('/').filter(Boolean).slice(-1)[0] ?? null; + } + + // fallback: output_<8hex>.log + if (!fileName) { + fileName = toolOutputFileName('output', String(fullText ?? '')); + } + + const fileUri = URI.joinPath(outputDir, fileName); + + // IMPORTANT: overwrite always (otherwise polling produces stale/partial logs) + await this.fileService.writeFile(fileUri, VSBuffer.fromString(String(fullText ?? ''))); + + // IMPORTANT: return workspace-relative path + return `.void/tool_outputs/${fileName}`; + } catch { + return null; + } + } + + private async _finalizeTerminalIfNeeded(terminalId: string): Promise { + const st = this._terminalStateById.get(terminalId); + if (!st || !st.done) return; + if (st.finalized) return; + + const max = this._getMaxToolOutputLength(); + const effectiveMax = + (st.outputByteLimit && Number.isFinite(st.outputByteLimit) && st.outputByteLimit > 0) + ? Math.min(max, st.outputByteLimit) + : max; + + const full = String(st.finalOutput ?? ''); + const suggested = this._terminalSuggestedFileName(terminalId); + + if (full.length > effectiveMax) { + const t = await this._truncateWithMetaIfNeeded( + full, + effectiveMax, + suggested, + { includeMeta: true, saveToFile: true } + ); + st.finalResponseText = t.text; + st.finalResponseTruncated = true; + st.finalSavedPath = t.savedPath; + } else { + st.finalResponseText = full; + st.finalResponseTruncated = false; + st.finalSavedPath = null; + } + + st.finalized = true; + } + + private async _truncateReadTextFileWithMeta( + fullText: string, + maxChars: number, + pathOrUri: string, + requestedLine?: number, + _requestedLimit?: number, + fileTotalLines?: number + ): Promise<{ text: string; didTruncateForMax: boolean }> { + const s = String(fullText ?? ''); + if (!s) return { text: '', didTruncateForMax: false }; + if (!Number.isFinite(maxChars) || maxChars <= 0) return { text: '', didTruncateForMax: true }; + if (s.length <= maxChars) return { text: s, didTruncateForMax: false }; + + const originalLength = s.length; + + const requestedStartLine = + (typeof requestedLine === 'number' && Number.isFinite(requestedLine) && requestedLine > 0) + ? requestedLine + : 1; + + const chunk = this._getReadFileChunkLines(); + const normalizedFileTotalLines = + (typeof fileTotalLines === 'number' && Number.isFinite(fileTotalLines) && fileTotalLines > 0) + ? Math.floor(fileTotalLines) + : undefined; + + // fit suffix into maxChars by shrinking body + let bodyMax = maxChars; + + for (let iter = 0; iter < 4; iter++) { + // computeTruncatedToolOutput(max<=0) returns the full string. + // For footer-fitting loops we need an empty body when no body budget remains. + const { truncatedBody, lineAfterTruncation } = + bodyMax > 0 + ? computeTruncatedToolOutput(s, bodyMax) + : { truncatedBody: '', lineAfterTruncation: 0 }; + const startLineExclusive = lineAfterTruncation > 0 ? lineAfterTruncation : 0; + const nextStartLine = requestedStartLine + startLineExclusive; + const suggestedEndLine = nextStartLine + chunk - 1; + + const headerLines = [ + `[VOID] TOOL OUTPUT TRUNCATED, SEE TRUNCATION_META BELOW.`, + `Only the first ${maxChars} characters are included in this message.`, + `Display limit: maxToolOutputLength = ${maxChars} characters.`, + ]; + + const uriStr = String(pathOrUri ?? ''); + + const instructionsLines = [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. Continue by calling read_file on the ORIGINAL uri (NOT on a tool-output log):`, + ` read_file({ uri: ${JSON.stringify(uriStr)}, startLine: ${nextStartLine}, endLine: ${suggestedEndLine} })`, + ` 3. IMPORTANT: endLine above is a chunk boundary, NOT the end of file.`, + ` 4. Recommended next chunk size: readFileChunkLines = ${chunk}.`, + ...(normalizedFileTotalLines !== undefined + ? [` Known total file lines: ${normalizedFileTotalLines}.`] + : []), + ` 5. If still truncated, increase startLine by about ${chunk} and repeat.`, + ]; + + const meta = { + tool: 'read_file', + uri: uriStr, + requestedStartLine, + nextStartLine, + suggested: { + startLine: nextStartLine, + endLine: suggestedEndLine, + chunkLines: chunk, + endLineIsFileEnd: false, + }, + ...(normalizedFileTotalLines !== undefined ? { fileTotalLines: normalizedFileTotalLines } : {}), + maxChars, + originalLength, + }; + + const metaLine = `TRUNCATION_META: ${JSON.stringify(meta)}`; + const suffix = `...\n\n${headerLines.join('\n')}\n${instructionsLines.join('\n')}\n${metaLine}`; + const nextBodyMax = Math.max(0, maxChars - suffix.length); + + if (nextBodyMax === bodyMax || iter === 3) { + const finalText = `${truncatedBody}${suffix}`; + return { text: finalText.slice(0, maxChars), didTruncateForMax: true }; + } + bodyMax = nextBodyMax; + } + return { text: s.slice(0, maxChars), didTruncateForMax: true }; + } + + private async _truncateWithMetaIfNeeded( + fullText: string, + maxChars: number, + suggestedFileNameOrPath?: string, + opts?: { includeMeta?: boolean; saveToFile?: boolean } + ): Promise<{ text: string; didTruncateForMax: boolean; savedPath: string | null }> { + + const s = String(fullText ?? ''); + const includeMeta = opts?.includeMeta !== false; + const saveToFile = opts?.saveToFile === true; + + if (!s) return { text: '', didTruncateForMax: false, savedPath: null }; + if (!Number.isFinite(maxChars) || maxChars <= 0) return { text: '', didTruncateForMax: true, savedPath: null }; + if (s.length <= maxChars) return { text: s, didTruncateForMax: false, savedPath: null }; + + if (!includeMeta) { + return { text: s.slice(0, maxChars), didTruncateForMax: true, savedPath: null }; + } + + let savedPath: string | null = null; + const originalLength = s.length; + + if (saveToFile) { + savedPath = await this._saveToToolOutputs(s, suggestedFileNameOrPath); + } + + let bodyMax = maxChars; + for (let iter = 0; iter < 4; iter++) { + // computeTruncatedToolOutput(max<=0) returns the full string. + // For footer-fitting loops we need an empty body when no body budget remains. + const { truncatedBody, lineAfterTruncation } = + bodyMax > 0 + ? computeTruncatedToolOutput(s, bodyMax) + : { truncatedBody: '', lineAfterTruncation: 0 }; + const startLineExclusive = lineAfterTruncation > 0 ? lineAfterTruncation : 0; + + const headerLines = [ + `[VOID] TOOL OUTPUT TRUNCATED, SEE TRUNCATION_META BELOW.`, + `Only the first ${maxChars} characters are included in this message.`, + `Display limit: maxToolOutputLength = ${maxChars} characters.`, + ]; + + const instructionsLines = savedPath + ? [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. To see the rest of this tool output, call read_file on logFilePath, starting from line startLineExclusive + 1.`, + ] + : [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output when the missing tail is critical.`, + ` 2. In this environment the full log file path is not available; you can only work with the visible part.`, + ]; + + const meta = { logFilePath: savedPath, startLineExclusive, maxChars, originalLength }; + const metaLine = `TRUNCATION_META: ${JSON.stringify(meta)}`; + + const suffix = `...\n\n${headerLines.join('\n')}\n${instructionsLines.join('\n')}\n${metaLine}`; + const nextBodyMax = Math.max(0, maxChars - suffix.length); + + if (nextBodyMax === bodyMax || iter === 3) { + const finalText = `${truncatedBody}${suffix}`; + return { text: finalText.slice(0, maxChars), didTruncateForMax: true, savedPath }; + } + + bodyMax = nextBodyMax; + } + + return { text: s.slice(0, maxChars), didTruncateForMax: true, savedPath }; + } + + private readonly _terminalStateById = new Map; + outputPumpPromise?: Promise; + outputByteLimit?: number; + commandLine?: string; + runType?: 'ephemeral' | 'persistent'; + finalOutput?: string; + + startedAt?: number; + lastOutputAt?: number; + inactivityTimeoutSeconds?: number; + + finalized?: boolean; + finalResponseText?: string; + finalResponseTruncated?: boolean; + finalSavedPath?: string | null; + }>(); + + private _markTerminalDone(terminalId: string, exitCode: number | null, signal: string | null) { + const st = this._terminalStateById.get(terminalId); + if (!st) return; + st.done = true; + st.exitCode = exitCode; + st.signal = signal; + } + + private _extractExitStatusFromRunResult(res: any): TerminalExitStatus { + try { + const r = this._unwrapDeepRunResult(res) ?? {}; + let exitCode: number | null = null; + let signal: string | null = null; + + // ACP-ish nested shape: { exitStatus: { exitCode, signal } } + const es = (r && typeof r === 'object') ? (r as any).exitStatus : undefined; + if (es && typeof es === 'object') { + const ec = (es as any).exitCode; + const sg = (es as any).signal; + if (typeof ec === 'number' && Number.isFinite(ec)) exitCode = ec; + else if (ec === null) exitCode = null; + if (typeof sg === 'string') signal = sg; + else if (sg === null) signal = null; + return { exitCode, signal }; + } + + // Common direct shapes + if (typeof (r as any).exitCode === 'number' && Number.isFinite((r as any).exitCode)) exitCode = (r as any).exitCode; + else if ((r as any).exitCode === null) exitCode = null; + + else if (typeof (r as any).code === 'number' && Number.isFinite((r as any).code)) exitCode = (r as any).code; + else if (typeof (r as any).status === 'number' && Number.isFinite((r as any).status)) exitCode = (r as any).status; + + if (typeof (r as any).signal === 'string') signal = (r as any).signal; + else if ((r as any).signal === null) signal = null; + + return { exitCode, signal }; + } catch { + return { exitCode: null, signal: null }; + } + } + + private _extractOutputFromRunResult(res: any): string { + try { + const r0 = this._unwrapDeepRunResult(res); + + if (typeof r0 === 'string') return r0; + if (!r0 || typeof r0 !== 'object') return ''; + + const r: any = r0; + + // Common shapes + if (typeof r.output === 'string') return String(r.output); + if (typeof r.combinedOutput === 'string') return String(r.combinedOutput); + + // stdout/stderr pairs + const stdout = typeof r.stdout === 'string' ? String(r.stdout) : ''; + const stderr = typeof r.stderr === 'string' ? String(r.stderr) : ''; + if (stdout || stderr) return `${stdout}${stderr}`; + + // sometimes "text" / "value" / "result" is used + if (typeof r.text === 'string') return String(r.text); + if (typeof r.value === 'string') return String(r.value); + if (typeof r.result === 'string') return String(r.result); + + return ''; + } catch { + return ''; + } + } + + private _normalizeReadTerminalValue(v: any): string { + try { + if (typeof v === 'string') return v; + if (!v || typeof v !== 'object') return ''; + + if (typeof (v as any).output === 'string') return String((v as any).output); + if (typeof (v as any).value === 'string') return String((v as any).value); + + // some services return { lines: string[] } + if (Array.isArray((v as any).lines)) return (v as any).lines.map((x: any) => String(x ?? '')).join('\n'); + + return ''; + } catch { + return ''; + } + } + + private async _readTerminalBestEffort(termSvc: any, terminalId: string): Promise { + // Long-running commands often don't produce output immediately. + // Bump retries a bit so external agents don't see "empty" too often. + let out = ''; + for (let i = 0; i < 10; i++) { + try { + const v = await termSvc.readTerminal(terminalId).catch(() => undefined); + const s = this._normalizeReadTerminalValue(v); + if (s && s.length >= out.length) out = s; + } catch { /* ignore */ } + + await new Promise(r => setTimeout(r, 50)); + } + return out; + } + + private async _awaitFinalRunResult(startRes: any): Promise { + try { + const p = (startRes && typeof startRes === 'object') ? (startRes as any).resPromise : undefined; + if (p && typeof p.then === 'function') { + return await p; + } + } catch { /* ignore */ } + return startRes; + } + + private _startTerminalOutputPump(termSvc: any, terminalId: string): void { + const st = this._terminalStateById.get(terminalId); + if (!st) return; + if (st.outputPumpPromise) return; + + st.outputPumpPromise = (async () => { + try { + for (let i = 0; i < 1000000; i++) { + const cur = this._terminalStateById.get(terminalId); + if (!cur) return; + + try { + const fresh = await this._readTerminalBestEffort(termSvc, terminalId); + if (fresh) { + const prev = String(cur.finalOutput ?? ''); + + // IMPORTANT: + // only accept monotonic extension, otherwise DO NOT overwrite + // (readTerminal can return a "different window" not starting from the beginning). + if (!prev || fresh.startsWith(prev)) { + if (fresh.length > prev.length) { + cur.finalOutput = fresh; + cur.lastOutputAt = Date.now(); + } + } + } + } catch { /* ignore */ } + + if (cur.done) break; + await new Promise(r => setTimeout(r, 250)); + } + + // final refresh after done (still monotonic) + try { + const fresh2 = await this._readTerminalBestEffort(termSvc, terminalId); + const cur2 = this._terminalStateById.get(terminalId); + if (cur2 && fresh2) { + const prev2 = String(cur2.finalOutput ?? ''); + if (!prev2 || fresh2.startsWith(prev2)) { + if (fresh2.length > prev2.length) { + cur2.finalOutput = fresh2; + cur2.lastOutputAt = Date.now(); + } + } + } + } catch { /* ignore */ } + } catch { /* ignore */ } + })(); + } + + async handle(kind: string, params: any, threadId: string | undefined): Promise { + if (kind !== 'terminalOutput') { + this.logService.debug(`[AcpHostCallbacksService] handle INPUT: kind="${kind}" threadId="${threadId}"`); + this.logService.debug(`[AcpHostCallbacksService] handle PARAMS:`, JSON.stringify(params, null, 2)); + } + + const p = params ?? {}; + + // ------------------------- + // Permissions + // ------------------------- + if (kind === 'requestPermission') { + try { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const autos = vss.state.globalSettings.autoApprove ?? {}; + const mcpAuto = vss.state.globalSettings.mcpAutoApprove === true; + const chat = this.instantiationService.invokeFunction(a => a.get(IChatThreadService)); + + const toolCallId: string | undefined = + p?.toolCall?.toolCallId ?? p?.toolCallId; + + let rawName: string = + p?.toolCall?.rawInput?.name + ?? p?.toolCall?.name + ?? p?.toolCall?.function?.name + ?? p?.toolCall?.title + ?? p?.title + ?? 'tool'; + + let rawArgs: Record = + (p?.toolCall?.rawInput?.args && typeof p.toolCall.rawInput.args === 'object') + ? p.toolCall.rawInput.args + : (p?.toolCall?.rawInput && typeof p.toolCall.rawInput === 'object') + ? p.toolCall.rawInput + : {}; + + // External ACP often stores name in llmInfo.toolCallSoFar + if (threadId) { + const st = chat.streamState[threadId]; + const toolCallSoFar = (st as any)?.llmInfo?.toolCallSoFar; + if ((rawName === 'tool' || !rawName) && toolCallId && toolCallSoFar && String(toolCallSoFar.id) === String(toolCallId)) { + rawName = String(toolCallSoFar.name ?? rawName); + rawArgs = (toolCallSoFar.rawParams && typeof toolCallSoFar.rawParams === 'object') ? toolCallSoFar.rawParams : rawArgs; + } + } + + const normName = normalizeAcpToolName(String(rawName)); + + if (!threadId || !toolCallId) { + return { outcome: { outcome: 'selected', optionId: 'reject_once' } }; + } + + const isBuiltin = isAToolName(normName); + const approvalType = isBuiltin ? approvalTypeOfToolName[normName] : undefined; + + if (isBuiltin && !approvalType) { + return { outcome: { outcome: 'selected', optionId: 'allow_once' } }; + } + + if (isBuiltin && approvalType) { + const autoApprove = !!(autos as any)[approvalType]; + if (autoApprove) { + return { outcome: { outcome: 'selected', optionId: 'allow_once' } }; + } + } + + if (!isBuiltin && mcpAuto) { + return { outcome: { outcome: 'selected', optionId: 'allow_once' } }; + } + + chat.enqueueToolRequestFromAcp(threadId, { + id: toolCallId, + name: normName, + rawParams: rawArgs + }); + + const decision = await new Promise<'approved' | 'rejected' | 'skipped'>((resolve) => { + const disposable = chat.onExternalToolDecision(({ threadId: t, toolCallId: id, decision }) => { + if (t === threadId && id === toolCallId) { + disposable.dispose(); + resolve(decision); + } + }); + }); + + return decision === 'approved' + ? { outcome: { outcome: 'selected', optionId: 'allow_once' } } + : { outcome: { outcome: 'selected', optionId: 'reject_once' } }; + + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] requestPermission ERROR:`, err); + return { outcome: { outcome: 'selected', optionId: 'reject_once' } }; + } + } + + // ------------------------- + // FS + // ------------------------- + if (kind === 'readTextFile') { + try { + const path = p?.path ?? p?.uri; + this.logService.debug(`[AcpHostCallbacksService] fs/read_text_file: Reading "${path}"`); + + if (!path) throw new Error('fs/read_text_file: missing path'); + const uri = this._asUriFromPathOrUri(path); + const file = await this.fileService.readFile(uri); + const fullText = file.value.toString(); + + const line = typeof p?.line === 'number' && Number.isFinite(p.line) ? p.line : undefined; + const limit = typeof p?.limit === 'number' && Number.isFinite(p.limit) ? p.limit : undefined; + + let content: string; + let totalFileLines: number; + if (!line && !limit) { + content = fullText; + totalFileLines = this._countLines(fullText); + } else { + const rawLines = fullText.split(/\r?\n/); + totalFileLines = rawLines.length; + const startIdx = Math.max(0, (line ?? 1) - 1); + const endIdx = limit ? Math.min(rawLines.length, startIdx + Math.max(0, limit)) : rawLines.length; + const sliced = rawLines.slice(startIdx, endIdx).join('\n'); + const hadTrailingNl = /\r?\n$/.test(fullText); + content = hadTrailingNl ? (sliced + '\n') : sliced; + } + + const max = this._getMaxToolOutputLength(); + + // IMPORTANT: for read_file, NEVER point model to .void/tool_outputs. + // Continue by re-calling read_file on the ORIGINAL path/uri with increased line. + const pathOrUriStr = String(path ?? ''); + + const t = await this._truncateReadTextFileWithMeta(content, max, pathOrUriStr, line ?? 1, limit, totalFileLines); + return { content: t.text }; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] fs/read_text_file ERROR:`, err); + throw err; + } + } + + if (kind === 'writeTextFile') { + try { + const path = p?.path ?? p?.uri; + const content = p?.content; + this.logService.debug(`[AcpHostCallbacksService] fs/write_text_file: Writing to "${path}" (${content?.length ?? 0} chars)`); + + if (!path) throw new Error('fs/write_text_file: missing path'); + if (typeof content !== 'string') throw new Error('fs/write_text_file: missing content'); + + // Ensure .void/tool_outputs exists when agent writes logs there + try { + const s = String(path ?? '').trim(); + const isToolOutputsRel = + /^\.\/?\.void[\\/]+tool_outputs[\\/]+/i.test(s) || + /^\.void[\\/]+tool_outputs[\\/]+/i.test(s); + + if (isToolOutputsRel) { + const root = this._getWorkspaceRoot(); + if (root) { + const outputDir = URI.joinPath(root, '.void', 'tool_outputs'); + if (!(await this.fileService.exists(outputDir))) { + await this.fileService.createFolder(outputDir); + } + } + } + } catch { /* ignore */ } + + const uri = this._asUriFromPathOrUri(path); + await this.fileService.writeFile(uri, VSBuffer.fromString(content)); + return null; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] fs/write_text_file ERROR:`, err); + throw err; + } + } + + if (kind === 'createTerminal') { + try { + const termSvc = this._getTerminalToolService(); + if (!termSvc) throw new Error('Terminal service unavailable'); + + const command = String(p?.command ?? ''); + this.logService.debug(`[AcpHostCallbacksService] terminal/create: Command="${command}"`); + if (!command) throw new Error('terminal/create: missing command'); + + const args = p?.args; + const cwd: string | null = (p?.cwd ?? null) === null ? null : String(p?.cwd); + + const envArr: EnvVar[] = Array.isArray(p?.env) ? p.env : []; + const envObj: Record = {}; + for (const item of envArr) { + if (!item) continue; + const name = String((item as any).name ?? ''); + if (!name) continue; + envObj[name] = String((item as any).value ?? ''); + } + + const terminalId = String(p?.terminalId ?? generateUuid()); + const cmdLine = this._buildCommandLine(command, args); + + const existing = this._terminalStateById.get(terminalId); + if (existing && existing.done === false) { + if (existing.commandLine === cmdLine) { + this.logService.debug(`[AcpHostCallbacksService] terminal/create: DEDUP running terminalId=${terminalId}`); + return { terminalId }; + } + this._terminalStateById.delete(terminalId); + } + + const outputByteLimit = + (typeof p?.outputByteLimit === 'number' && Number.isFinite(p.outputByteLimit) && p.outputByteLimit > 0) + ? p.outputByteLimit + : undefined; + + // IMPORTANT: external agents + our TerminalToolService API => use EPHEMERAL deterministically + const runType: 'ephemeral' = 'ephemeral'; + + const inactivityTimeoutSeconds = + (typeof p?.inactivityTimeoutSeconds === 'number' && Number.isFinite(p.inactivityTimeoutSeconds) && p.inactivityTimeoutSeconds > 0) + ? p.inactivityTimeoutSeconds + : 600; + + // Create state BEFORE starting run so onOutput can append immediately + const stObj = { + done: false, + exitCode: null, + signal: null, + runPromise: Promise.resolve(), + outputByteLimit, + commandLine: cmdLine, + runType, + finalOutput: '', + startedAt: Date.now(), + lastOutputAt: Date.now(), + inactivityTimeoutSeconds, + finalized: false, + finalResponseText: undefined, + finalResponseTruncated: undefined, + finalSavedPath: undefined, + }; + this._terminalStateById.set(terminalId, stObj); + + const appendOutput = (chunk: string) => { + const st = this._terminalStateById.get(terminalId); + if (!st || st.done) return; + const s = String(chunk ?? ''); + if (!s) return; + st.finalOutput = (st.finalOutput ?? '') + s; + st.lastOutputAt = Date.now(); + }; + + const runPromise = (async () => { + try { + const startRes = await (termSvc as any).runCommand(cmdLine, { + type: 'ephemeral', + cwd, + terminalId, + env: Object.keys(envObj).length ? envObj : undefined, + inactivityTimeoutSeconds, + onOutput: appendOutput, + }); + + const finalRes = await this._awaitFinalRunResult(startRes); + const payload = this._unwrapDeepRunResult(finalRes); + + // merge final output (prefer longer) + let out = this._extractOutputFromRunResult(payload); + const st = this._terminalStateById.get(terminalId); + if (st) { + const prev = String(st.finalOutput ?? ''); + if (out && out.length >= prev.length) st.finalOutput = out; + else if (!out) out = prev; + } + + // resolveReason mapping (timeout vs done) + const rr = payload && typeof payload === 'object' ? (payload as any).resolveReason : undefined; + const rrType = rr && typeof rr === 'object' ? String((rr as any).type ?? '') : ''; + + if (rrType === 'timeout') { + // Host-side inactivity timeout + this._markTerminalDone(terminalId, 124, 'VOID_INACTIVITY_TIMEOUT'); + } else if (rrType === 'done' && typeof (rr as any)?.exitCode === 'number') { + this._markTerminalDone(terminalId, (rr as any).exitCode, null); + } else { + const { exitCode, signal } = this._extractExitStatusFromRunResult(payload); + this._markTerminalDone(terminalId, exitCode, signal); + } + } catch { + this._markTerminalDone(terminalId, 1, null); + } + })(); + + stObj.runPromise = runPromise; + + return { terminalId }; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] terminal/create ERROR:`, err); + throw err; + } + } + + if (kind === 'terminalOutput') { + try { + const termSvc = this._getTerminalToolService(); + if (!termSvc) throw new Error('Terminal service unavailable'); + + const terminalId = String(p?.terminalId ?? ''); + if (!terminalId) throw new Error('terminal/output: missing terminalId'); + + const st = this._terminalStateById.get(terminalId); + + // If unknown terminalId, best-effort read (no file writes here) + if (!st) { + let out = ''; + try { out = await this._readTerminalBestEffort(termSvc as any, terminalId); } catch { /* ignore */ } + const max = this._getMaxToolOutputLength(); + const t = await this._truncateWithMetaIfNeeded(out, max, undefined, { includeMeta: false, saveToFile: false }); + return { output: t.text, truncated: t.didTruncateForMax, isRunning: true }; + } + + // Don't start pump if we already have accumulated output (onOutput should be the source of truth). + if (!(st.finalOutput && st.finalOutput.length > 0)) { + this._startTerminalOutputPump(termSvc as any, terminalId); + } + + // Refresh (best-effort) but NEVER overwrite accumulated output with non-prefix snapshots + { + try { + const fresh = await this._readTerminalBestEffort(termSvc as any, terminalId); + if (fresh) { + const prev = String(st.finalOutput ?? ''); + // Only accept fresh if it extends previous (monotonic, same start). + if (!prev || fresh.startsWith(prev)) { + st.finalOutput = fresh; + st.lastOutputAt = Date.now(); + } + } + } catch { /* ignore */ } + } + + const byteLimitFromState = st.outputByteLimit; + const byteLimitFromCall = + (typeof p?.outputByteLimit === 'number' && Number.isFinite(p.outputByteLimit) && p.outputByteLimit > 0) + ? p.outputByteLimit + : undefined; + + const byteLimit = + (byteLimitFromState && byteLimitFromCall) + ? Math.min(byteLimitFromState, byteLimitFromCall) + : (byteLimitFromCall ?? byteLimitFromState); + + const max = this._getMaxToolOutputLength(); + const effectiveMax = + (byteLimit && Number.isFinite(byteLimit) && byteLimit > 0) + ? Math.min(max, byteLimit) + : max; + + // ------------------------- + // DONE: finalize ONCE (this is the ONLY place that may write the log file) + // ------------------------- + if (st.done) { + // If already finalized, return cached stable result + if (!st.finalized) { + // One last best-effort read before finalization, but only if monotonic + try { + const fresh2 = await this._readTerminalBestEffort(termSvc as any, terminalId); + if (fresh2) { + const prev2 = String(st.finalOutput ?? ''); + if (!prev2 || fresh2.startsWith(prev2)) { + st.finalOutput = fresh2; + st.lastOutputAt = Date.now(); + } + } + } catch { /* ignore */ } + + await this._finalizeTerminalIfNeeded(terminalId); + } + + const output = String(st.finalResponseText ?? st.finalOutput ?? ''); + const truncated = !!st.finalResponseTruncated; + + const exitStatus: TerminalExitStatus = { exitCode: st.exitCode, signal: st.signal }; + + return { + output, + truncated, + isRunning: false, + exitStatus + }; + } + + // ------------------------- + // RUNNING: never write a file; never TRUNCATION_META + // Show from start; if too long – show start + footer. + // ------------------------- + const outputFull = String(st.finalOutput ?? ''); + if (outputFull.length <= effectiveMax) { + return { output: outputFull, truncated: false, isRunning: true }; + } + + const footer = + `\n\n[VOID] OUTPUT EXCEEDS DISPLAY LIMIT DURING RUN.\n` + + `Current captured length: ${outputFull.length} chars.\n` + + `Full output (and TRUNCATION_META + log file) will be produced after completion if needed.\n`; + + const bodyMax = Math.max(0, effectiveMax - footer.length); + const output = `${outputFull.slice(0, bodyMax)}${footer}`; + + return { output, truncated: true, isRunning: true }; + + } catch (err) { + throw err; + } + } + + if (kind === 'waitForTerminalExit') { + try { + const terminalId = String(p?.terminalId ?? ''); + if (!terminalId) throw new Error('terminal/wait_for_exit: missing terminalId'); + + const st = this._terminalStateById.get(terminalId); + if (st) { + await st.runPromise.catch(() => { }); + return { exitCode: st.exitCode, signal: st.signal }; + } + return { exitCode: null, signal: null }; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] terminal/wait_for_exit ERROR:`, err); + throw err; + } + } + + if (kind === 'killTerminal') { + try { + const termSvc = this._getTerminalToolService(); + if (!termSvc) throw new Error('Terminal service unavailable'); + + const terminalId = String(p?.terminalId ?? ''); + if (!terminalId) throw new Error('terminal/kill: missing terminalId'); + + try { + const anySvc = termSvc as any; + if (typeof anySvc.killTemporaryTerminal === 'function') await anySvc.killTemporaryTerminal(terminalId); + else if (typeof anySvc.killTerminal === 'function') await anySvc.killTerminal(terminalId); + else if (typeof anySvc.killPersistentTerminal === 'function') await anySvc.killPersistentTerminal(terminalId); + } catch { } + + this._markTerminalDone(terminalId, null, 'SIGTERM'); + return null; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] terminal/kill ERROR:`, err); + throw err; + } + } + + if (kind === 'releaseTerminal') { + try { + const termSvc = this._getTerminalToolService(); + if (!termSvc) throw new Error('Terminal service unavailable'); + + const terminalId = String(p?.terminalId ?? ''); + if (!terminalId) throw new Error('terminal/release: missing terminalId'); + + try { + const out = await this._readTerminalBestEffort(termSvc as any, terminalId); + const st = this._terminalStateById.get(terminalId); + if (st && out && out.length >= (st.finalOutput?.length ?? 0)) st.finalOutput = out; + } catch { /* ignore */ } + + try { + const anySvc = termSvc as any; + if (typeof anySvc.releaseTemporaryTerminal === 'function') await anySvc.releaseTemporaryTerminal(terminalId); + else if (typeof anySvc.releaseTerminal === 'function') await anySvc.releaseTerminal(terminalId); + else if (typeof anySvc.killTemporaryTerminal === 'function') await anySvc.killTemporaryTerminal(terminalId); + else if (typeof anySvc.killPersistentTerminal === 'function') await anySvc.killPersistentTerminal(terminalId); + } catch { } + + this._markTerminalDone( + terminalId, + (this._terminalStateById.get(terminalId)?.exitCode ?? null), + (this._terminalStateById.get(terminalId)?.signal ?? null) + ); + return null; + } catch (err) { + this.logService.error(`[AcpHostCallbacksService] terminal/release ERROR:`, err); + throw err; + } + } + + throw new Error(`Unknown host callback: ${kind}`); + } +} diff --git a/src/vs/workbench/contrib/acp/browser/AcpInternalExtMethodService.ts b/src/vs/workbench/contrib/acp/browser/AcpInternalExtMethodService.ts new file mode 100644 index 00000000000..376d73ca7dd --- /dev/null +++ b/src/vs/workbench/contrib/acp/browser/AcpInternalExtMethodService.ts @@ -0,0 +1,1113 @@ +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { removeMCPToolNamePrefix } from '../../void/common/mcpServiceTypes.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IPtyHostService } from '../../../../platform/terminal/common/terminal.js'; + +import { IToolsService } from '../../void/common/toolsService.js'; +import { ILanguageModelToolsService, IToolData } from '../../chat/common/languageModelToolsService.js'; + +import { IDynamicProviderRegistryService } from '../../../../platform/void/common/providerReg.js'; +import { getModelApiConfiguration, getModelCapabilities } from '../../../../platform/void/common/modelInference.js'; + +import { + FeatureName, + ModelSelection, + ModelSelectionOptions, + ProviderName, + type ChatMode, + type specialToolFormat +} from '../../../../platform/void/common/voidSettingsTypes.js'; + +import { + ToolName, + isAToolName, + availableTools, + chat_systemMessageForAcp, +} from '../../void/common/prompt/prompts.js'; + +import type { + RawToolParamsObj, + RequestParamsConfig, + ProviderRouting, + DynamicRequestConfig, + AdditionalToolInfo +} from '../../../../platform/void/common/sendLLMMessageTypes.js'; + +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { timeout } from '../../../../base/common/async.js'; +import { removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; +import { ITerminalService, ITerminalInstance, ICreateTerminalOptions } from '../../../../workbench/contrib/terminal/browser/terminal.js'; +import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js'; +import { MAX_TERMINAL_CHARS } from '../../../../platform/void/common/prompt/constants.js'; +import { URI } from '../../../../base/common/uri.js'; + +const IMCPServiceId = createDecorator('mcpConfigService'); + +type _TerminalRunState = { + terminalId: string; + terminal: ITerminalInstance; + + // tail buffer (fast for streaming) + outputRaw: string; + truncated: boolean; + + // full buffer (used once at the end) + outputRawFull: string; + fullTruncated: boolean; + + done: boolean; + exitCode: number | null; + + resolveExit: (code: number) => void; + exitPromise: Promise<{ exitCode: number }>; + disposables: IDisposable[]; +}; + +export class AcpInternalExtMethodService { + constructor( + private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService + ) { } + + + private _getMcpService(): any | null { + try { return this.instantiationService.invokeFunction(accessor => accessor.get(IMCPServiceId)); } + catch { return null; } + } + + private _mcpSafePrefixFromServerName(serverName: string): string { + const s = String(serverName ?? '').trim(); + const safe = s.replace(/[^a-zA-Z0-9_]/g, '_'); + return safe || 'mcp'; + } + + private _mcpInputSchemaToAdditionalToolParams(inputSchema: any): Record | undefined { + const props = inputSchema?.properties; + if (!props || typeof props !== 'object') return undefined; + + return Object.fromEntries( + Object.entries(props).map(([key, schema]: [string, any]) => [ + key, + { + description: schema?.description || `Parameter: ${key}`, + type: schema?.type, + enum: schema?.enum, + items: schema?.items, + properties: schema?.properties, + required: schema?.required, + default: schema?.default, + minimum: schema?.minimum, + maximum: schema?.maximum, + minLength: schema?.minLength, + maxLength: schema?.maxLength, + }, + ]) + ); + } + + private _collectMcpJsonAdditionalTools(): AdditionalToolInfo[] { + const mcp = this._getMcpService(); + if (!mcp) return []; + + const servers = (mcp.state?.mcpServerOfName ?? {}) as Record; + const out: AdditionalToolInfo[] = []; + + for (const [serverName, server] of Object.entries(servers)) { + if (!server || server.status !== 'success') continue; + + const tools = Array.isArray(server.tools) ? server.tools : []; + if (!tools.length) continue; + + const safePrefix = this._mcpSafePrefixFromServerName(serverName); + + for (const t of tools) { + const fullName = String(t?.name ?? '').trim(); + if (!fullName) continue; + + const baseName = removeMCPToolNamePrefix(fullName) || fullName; + + out.push({ + + name: `${safePrefix}__${baseName}`, + description: String(t?.description ?? ''), + params: this._mcpInputSchemaToAdditionalToolParams(t?.inputSchema), + }); + } + } + + return out; + } + + private _resolveMcpJsonToolByAcpName(acpToolName: string): { serverName: string; toolNamePrefixed: string } | null { + + const idx = acpToolName.indexOf('__'); + if (idx <= 0) return null; + + const safePrefix = acpToolName.slice(0, idx).trim(); + const baseName = acpToolName.slice(idx + 2).trim(); + if (!safePrefix || !baseName) return null; + + const mcp = this._getMcpService(); + if (!mcp) return null; + + const servers = (mcp.state?.mcpServerOfName ?? {}) as Record; + + + let matchedServerName: string | null = null; + for (const serverName of Object.keys(servers)) { + if (this._mcpSafePrefixFromServerName(serverName).toLowerCase() === safePrefix.toLowerCase()) { + matchedServerName = serverName; + break; + } + } + if (!matchedServerName) return null; + + const server = servers[matchedServerName]; + if (!server || server.status !== 'success') return null; + + const tools = Array.isArray(server.tools) ? server.tools : []; + for (const t of tools) { + const fullName = String(t?.name ?? '').trim(); + if (!fullName) continue; + + const bn = removeMCPToolNamePrefix(fullName) || fullName; + if (bn === baseName) { + + + return { serverName: matchedServerName, toolNamePrefixed: fullName }; + } + } + + return null; + } + + private _getToolsService(): IToolsService | null { + try { return this.instantiationService.invokeFunction(accessor => accessor.get(IToolsService)); } + catch { return null; } + } + + private _getDisabledToolNamesSet(): Set { + try { + const vss = this.instantiationService.invokeFunction(accessor => accessor.get(IVoidSettingsService)); + const arr = vss.state.globalSettings.disabledToolNames; + if (!Array.isArray(arr)) return new Set(); + return new Set(arr.map(v => String(v ?? '').trim()).filter(Boolean)); + } catch { + return new Set(); + } + } + + private _disabledToolError(name: string): string { + return `Tool "${name}" is disabled in Void settings.`; + } + + private readonly _terminalRuns = new Map(); + + private _getTerminalService(): ITerminalService | null { + try { return this.instantiationService.invokeFunction(accessor => accessor.get(ITerminalService)); } + catch { return null; } + } + + private async _waitForCommandDetectionCapability(terminal: ITerminalInstance): Promise { + const existing = terminal.capabilities.get(TerminalCapability.CommandDetection); + if (existing) return existing; + + const disposables: IDisposable[] = []; + const waitTimeout = timeout(10_000); + + const waitForCapability = new Promise((res) => { + disposables.push( + terminal.capabilities.onDidAddCapability((e: any) => { + if (e?.id === TerminalCapability.CommandDetection) { + res(e.capability); + } + }) + ); + }); + + const cap = await Promise.any([waitTimeout, waitForCapability]) + .finally(() => { disposables.forEach(d => d.dispose()); }); + + return cap ?? undefined; + } + + private _quoteShellArg(s: string): string { + // simple POSIX-safe single-quote escaping + if (s === '') return '\'\''; + if (/^[a-zA-Z0-9_\/\.\-=:]+$/.test(s)) return s; + return `'${s.replace(/'/g, `'\\''`)}'`; + } + + private async _handleTerminalExtMethod(method: string, p: any): Promise { + const rawMethod = String(method ?? ''); + const isTerminal = rawMethod.startsWith('terminal/') || rawMethod.startsWith('_terminal/'); + if (!isTerminal) return undefined; + + const m = rawMethod.startsWith('_') ? rawMethod.slice(1) : rawMethod; + + const safeJson = (v: any): string => { + try { + const seen = new WeakSet(); + return JSON.stringify(v, (_k, val) => { + if (val && typeof val === 'object') { + if (seen.has(val)) return '[Circular]'; + seen.add(val); + } + if (typeof val === 'function') return '[Function]'; + return val; + }); + } catch (e: any) { + try { return JSON.stringify({ _stringifyError: String(e?.message ?? e) }); } catch { return '"[Unstringifiable]"'; } + } + }; + const dbg = (tag: string, obj: any) => this.logService.debug('[ACP debug terminal]', tag, safeJson(obj)); + + const safeErrMsg = (e: any): string => { + if (e instanceof Error) return e.message; + if (typeof e?.message === 'string') return e.message; + try { return JSON.stringify(e); } catch { return String(e); } + }; + + dbg('incoming', { + method: rawMethod, + normalized: m, + paramsKeys: p && typeof p === 'object' ? Object.keys(p) : null, + }); + + const terminalService = this._getTerminalService(); + if (!terminalService) throw new Error(`terminal/*: ITerminalService not available`); + + try { + try { + const wc = (terminalService as any).whenConnected; + if (wc && typeof wc.then === 'function') await wc; + } catch { /* ignore */ } + + if (m === 'terminal/create') { + const type = String(p?.type ?? 'ephemeral'); + if (type !== 'ephemeral') throw new Error(`terminal/create: only type="ephemeral" is supported`); + + const terminalId = String(p?.terminalId ?? '').trim(); + if (!terminalId) throw new Error('terminal/create: terminalId is required'); + + const command = String(p?.command ?? '').trim(); + if (!command) throw new Error('terminal/create: command is required'); + + const args: string[] = Array.isArray(p?.args) + ? (p.args as unknown[]).map((x: unknown): string => String(x ?? '')) + : []; + + const cwdRaw = (p?.cwd === null || p?.cwd === undefined) ? null : String(p.cwd); + const outputByteLimit = + typeof p?.outputByteLimit === 'number' && Number.isFinite(p.outputByteLimit) && p.outputByteLimit > 0 + ? Math.floor(p.outputByteLimit) + : undefined; + + const commandLine = args.length + ? `${command} ${args.map((a: string) => this._quoteShellArg(a)).join(' ')}` + : command; + + // release old + const prev = this._terminalRuns.get(terminalId); + if (prev) { + try { prev.disposables.forEach(d => d.dispose()); } catch { } + try { prev.terminal.dispose(); } catch { } + this._terminalRuns.delete(terminalId); + } + + const ws = this.instantiationService.invokeFunction(a => a.get(IWorkspaceContextService)); + const cwd: URI | string | undefined = + (cwdRaw && cwdRaw.trim().length > 0) + ? cwdRaw.trim() + : ws.getWorkspace().folders[0]?.uri; + + const options: ICreateTerminalOptions = { + cwd, + location: undefined, + config: { + forceShellIntegration: true, + hideFromUser: true, + } as any, + skipContributedProfileCheck: true, + }; + + const terminal = await terminalService.createTerminal(options); + + let resolveExit!: (code: number) => void; + const exitPromise = new Promise<{ exitCode: number }>((res) => { + resolveExit = (code: number) => res({ exitCode: code }); + }); + + const st: _TerminalRunState = { + terminalId, + terminal, + + // tail buffer (fast for streaming) + outputRaw: '', + truncated: false, + + // full buffer (used once at the end) + outputRawFull: '', + fullTruncated: false, + + done: false, + exitCode: null, + + resolveExit, + exitPromise, + disposables: [] + }; + + // Tail buffer limit (for fast polling) + const TAIL_MAX = + typeof outputByteLimit === 'number' + ? Math.max(4096, outputByteLimit) + : Math.max(50_000, MAX_TERMINAL_CHARS * 4); + + // Full buffer limit + // IMPORTANT: Make FULL_MAX >= outputByteLimit so "full:true" can actually be full. + // (We still keep a hard cap to avoid OOM.) + const FULL_MAX = Math.max( + 50 * 1024 * 1024, // 50MB baseline + typeof outputByteLimit === 'number' ? outputByteLimit * 4 : 0 // best-effort chars vs bytes + ); + + st.disposables.push( + terminal.onData((data: string) => { + if (st.done) return; + const chunk = String(data ?? ''); + + // full + if (!st.fullTruncated) { + if (st.outputRawFull.length + chunk.length <= FULL_MAX) { + st.outputRawFull += chunk; + } else { + st.outputRawFull += chunk.slice(0, Math.max(0, FULL_MAX - st.outputRawFull.length)); + st.fullTruncated = true; + dbg('full.truncated', { terminalId, FULL_MAX }); + } + } + + // tail + st.outputRaw += chunk; + if (st.outputRaw.length > TAIL_MAX) { + st.outputRaw = st.outputRaw.slice(st.outputRaw.length - TAIL_MAX); + st.truncated = true; + } + }) + ); + + const cmdCap = await this._waitForCommandDetectionCapability(terminal); + if (!cmdCap) throw new Error('terminal/create: CommandDetection not ready'); + + st.disposables.push( + cmdCap.onCommandFinished((cmd: any) => { + if (st.done) return; + st.done = true; + + const exitCode = (typeof cmd?.exitCode === 'number') ? cmd.exitCode : 0; + st.exitCode = exitCode; + + // Prefer structured output ONLY if it is not shorter than what we already captured + // (some implementations return only tail/limited output). + const out = cmd?.getOutput?.(); + if (typeof out === 'string' && out.length) { + const shouldReplaceFull = + st.outputRawFull.length === 0 || + st.fullTruncated || + out.length > st.outputRawFull.length; + + if (shouldReplaceFull) { + if (out.length <= FULL_MAX) { + st.outputRawFull = out; + st.fullTruncated = false; + } else { + st.outputRawFull = out.slice(0, FULL_MAX); + st.fullTruncated = true; + } + } + + // Recompute tail from whatever we consider "full truth" + const src = st.outputRawFull; + st.outputRaw = src.slice(Math.max(0, src.length - TAIL_MAX)); + st.truncated = src.length > TAIL_MAX; + } + + dbg('command.finished', { + terminalId, + exitCode, + fullLen: st.outputRawFull.length, + tailLen: st.outputRaw.length, + fullTruncated: st.fullTruncated, + tailTruncated: st.truncated + }); + try { st.resolveExit(exitCode); } catch { } + }) + ); + + this._terminalRuns.set(terminalId, st); + + dbg('create.sendText', { terminalId, commandLinePreview: commandLine.slice(0, 200) }); + await terminal.sendText(commandLine, true); + + return { terminalId }; + } + + if (m === 'terminal/output') { + const terminalId = String(p?.terminalId ?? '').trim(); + if (!terminalId) throw new Error('terminal/output: terminalId is required'); + + const st = this._terminalRuns.get(terminalId); + if (!st) throw new Error(`terminal/output: terminal "${terminalId}" does not exist`); + + const wantFull = !!p?.full; + + let outputRaw = wantFull ? st.outputRawFull : st.outputRaw; + let output = removeAnsiEscapeCodes(outputRaw ?? ''); + + // clamp only in non-full mode (stream polling) + if (!wantFull && output.length > MAX_TERMINAL_CHARS) { + const half = Math.floor(MAX_TERMINAL_CHARS / 2); + output = output.slice(0, half) + '\n...\n' + output.slice(output.length - half); + } + + const exitStatus = st.done + ? { exitCode: st.exitCode ?? 0, signal: null as string | null } + : undefined; + + dbg('output.read', { + terminalId, + full: wantFull, + outputLen: output.length, + truncated: wantFull ? st.fullTruncated : st.truncated, + done: st.done, + exitCode: st.exitCode + }); + + return { + output, + truncated: wantFull ? !!st.fullTruncated : !!st.truncated, + ...(exitStatus ? { exitStatus } : {}) + }; + } + + if (m === 'terminal/wait_for_exit') { + const terminalId = String(p?.terminalId ?? '').trim(); + if (!terminalId) throw new Error('terminal/wait_for_exit: terminalId is required'); + + const st = this._terminalRuns.get(terminalId); + if (!st) throw new Error(`terminal/wait_for_exit: terminal "${terminalId}" does not exist`); + + const r = await st.exitPromise; + return { exitCode: r.exitCode, signal: null }; + } + + if (m === 'terminal/kill') { + const terminalId = String(p?.terminalId ?? '').trim(); + if (!terminalId) throw new Error('terminal/kill: terminalId is required'); + + const st = this._terminalRuns.get(terminalId); + if (!st) return {}; + + if (!st.done) { + st.done = true; + st.exitCode = 130; + try { st.resolveExit(130); } catch { } + } + try { st.disposables.forEach(d => d.dispose()); } catch { } + try { st.terminal.dispose(); } catch { } + this._terminalRuns.delete(terminalId); + return {}; + } + + if (m === 'terminal/release') { + const terminalId = String(p?.terminalId ?? '').trim(); + if (!terminalId) throw new Error('terminal/release: terminalId is required'); + + const st = this._terminalRuns.get(terminalId); + if (!st) return {}; + + try { st.disposables.forEach(d => d.dispose()); } catch { } + try { st.terminal.dispose(); } catch { } + this._terminalRuns.delete(terminalId); + return {}; + } + + throw new Error(`Unknown terminal extMethod: ${m}`); + } catch (e: any) { + const msg = `terminal extMethod failed (${rawMethod}): ${safeErrMsg(e)}`; + dbg('error', { rawMethod, normalized: m, message: msg }); + throw new Error(msg); + } + } + + public async handle(reqParams: any): Promise { + const method = reqParams?.method as string; + const p = reqParams?.params ?? {}; + const disabledToolNames = this._getDisabledToolNamesSet(); + + const terminalHandled = await this._handleTerminalExtMethod(method, p); + if (terminalHandled !== undefined) { + return terminalHandled; + } + + // IMPORTANT: void/settings/getLLMConfig must NOT depend on IToolsService. + // Tests (and some runtimes) may not register IToolsService, but ACP settings extMethod must still work. + if (method === 'void/settings/getLLMConfig') { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const st = vss.state; + const disabledToolNamesList = Array.from(disabledToolNames); + const disabledStaticTools = disabledToolNamesList.filter(name => isAToolName(name)); + const disabledDynamicTools = disabledToolNamesList.filter(name => !isAToolName(name)); + + type GetLLMCfgParams = { featureName: FeatureName }; + const paramsTyped = p as Partial | undefined; + const rawFeature = paramsTyped?.featureName; + + const isAllowedFeature = (val: unknown): val is Extract => + val === 'Chat' || val === 'Ctrl+K'; + + if (!isAllowedFeature(rawFeature)) { + throw new Error(`void/settings/getLLMConfig: featureName must be 'Chat' or 'Ctrl+K'`); + } + + const feature = rawFeature; + + const selected: ModelSelection | null = st.modelSelectionOfFeature[feature]; + + const providerName: ProviderName | null = selected ? selected.providerName : null; + const modelName: string | null = selected ? selected.modelName : null; + + let modelSelectionOptions: ModelSelectionOptions | null = null; + if (providerName && modelName) { + modelSelectionOptions = st.optionsOfModelSelection?.[feature]?.[providerName]?.[modelName] ?? null; + } + + // ---- Per-model requestParams/providerRouting from customProviders ---- + let requestParams: RequestParamsConfig | null = null; + let providerRouting: ProviderRouting | null = null; + + if (providerName && modelName) { + try { + const providerSlug = String(providerName).trim().toLowerCase(); + + // st.customProviders is usually a map keyed by slug, + // but be resilient: also search by `cp.slug`. + const cp: any = + (st.customProviders as any)?.[providerSlug] ?? + Object.values(st.customProviders ?? {}).find((x: any) => String(x?.slug ?? '').trim().toLowerCase() === providerSlug); + + // Some versions store overrides under cp.perModel, others under cp.models. + const perModel: Record = + (cp?.perModel && typeof cp.perModel === 'object') ? cp.perModel : + (cp?.models && typeof cp.models === 'object') ? cp.models : + {}; + + const fullKey = modelName.includes('/') ? modelName : `${providerSlug}/${modelName}`; + const shortKey = modelName.includes('/') ? modelName.split('/').slice(1).join('/') : modelName; + + const cfg = perModel[modelName] ?? perModel[fullKey] ?? perModel[shortKey]; + + const rp = cfg?.requestParams as RequestParamsConfig | undefined; + if (rp && (rp.mode === 'default' || rp.mode === 'off' || rp.mode === 'override')) { + requestParams = rp; + } + + const pr = cfg?.providerRouting as ProviderRouting | undefined; + if (pr && typeof pr === 'object') { + providerRouting = pr; + } + } catch { + // ignore + } + } + + this.logService.debug('[ACP getLLMConfig]', JSON.stringify({ + feature, + hasSelection: !!selected, + providerName, + modelName, + hasModelSelectionOptions: !!modelSelectionOptions, + chatMode: st.globalSettings.chatMode ?? null, + hasSystemPrompt: !!st.globalSettings.acpSystemPrompt, + hasRequestParams: !!requestParams, + hasProviderRouting: !!providerRouting, + }, null, 2)); + + const combinedSettings = { ...st.settingsOfProvider }; + if (st.customProviders) { + for (const [slug, cp] of Object.entries(st.customProviders)) { + combinedSettings[slug] = { + ...cp, + models: [], + _didFillInProviderSettings: true + } as any; + } + } + + const asToolFormat = (v: unknown): specialToolFormat | undefined => { + return (v === 'openai-style' || v === 'anthropic-style' || v === 'gemini-style' || v === 'disabled') + ? v + : undefined; + }; + + // ---- dynamicRequestConfig from dynamic provider registry ---- + let dynamicRequestConfig: DynamicRequestConfig | null = null; + if (providerName && modelName) { + try { + const registry = this.instantiationService.invokeFunction(a => a.get(IDynamicProviderRegistryService)); + await registry.initialize?.(); + + const providerSlug = String(providerName).trim().toLowerCase(); + + // Registry often expects a fully-qualified model id. + const fullModelId = modelName.includes('/') ? modelName : `${providerSlug}/${modelName}`; + + const baseCfg = registry.getRequestConfigForModel(fullModelId, providerSlug); + const caps = + await registry.getEffectiveModelCapabilities(providerSlug, modelName) + .catch(async () => registry.getEffectiveModelCapabilities(providerSlug, fullModelId as any)); + + dynamicRequestConfig = { + endpoint: baseCfg.endpoint, + apiStyle: baseCfg.apiStyle, + supportsSystemMessage: baseCfg.supportsSystemMessage, + specialToolFormat: baseCfg.specialToolFormat, + headers: { ...baseCfg.headers }, + ...(caps?.fimTransport ? { fimTransport: caps.fimTransport as any } : {}), + ...(caps?.reasoningCapabilities !== undefined ? { reasoningCapabilities: caps.reasoningCapabilities as any } : {}), + ...(caps?.supportCacheControl !== undefined ? { supportCacheControl: !!caps.supportCacheControl } : {}), + }; + } catch (e) { + this.logService.warn('[ACP getLLMConfig] Failed to build dynamicRequestConfig:', e); + dynamicRequestConfig = null; + } + } + + // separateSystemMessage here is for the LLM inside the builtin ACP agent. + let separateSystemMessage: string | null = null; + + const explicit = (st.globalSettings.acpSystemPrompt ?? '').trim(); + + if (explicit) { + separateSystemMessage = explicit + ` + ACP PLAN (builtin ACP agent; REQUIRED): + - Do NOT output any execution plan in plain text (no "..."). + - If a plan is needed, call the tool "acp_plan" with entries and keep it updated.`; + } else { + try { + const ws = this.instantiationService.invokeFunction(a => a.get(IWorkspaceContextService)); + const folders = ws.getWorkspace().folders.map(f => f.uri.fsPath); + + const dummyPty = {} as unknown as IPtyHostService; + + // IMPORTANT: for ACP prompt format, prefer dynamicRequestConfig from registry. + // This keeps ACP prompt style in sync with the actual transport/tool format + // used by sendChatRouter in main. + let toolFormat: specialToolFormat = 'openai-style'; + const dynFmt = asToolFormat((dynamicRequestConfig as any)?.specialToolFormat); + if (dynFmt) { + toolFormat = dynFmt; + } else if (providerName && modelName) { + const modelCapabilities = getModelCapabilities(providerName, modelName, st.overridesOfModel || undefined); + const capsFmt = asToolFormat((modelCapabilities as any)?.specialToolFormat); + if (capsFmt) { + toolFormat = capsFmt; + } else { + try { + const modelId = modelName.includes('/') ? modelName : `${providerName}/${modelName}`; + const apiCfg = getModelApiConfiguration(modelId); + const apiFmt = asToolFormat((apiCfg as any)?.specialToolFormat); + if (apiFmt) toolFormat = apiFmt; + } catch { /* ignore */ } + } + } + + separateSystemMessage = await chat_systemMessageForAcp({ + workspaceFolders: folders, + chatMode: st.globalSettings.chatMode, + toolFormat, + ptyHostService: dummyPty, + disabledStaticToolNames: disabledStaticTools, + }); + } catch { + separateSystemMessage = `You are an editor agent inside Void. + Use tools to read/search/edit. For plans, do NOT print a textual plan; use the tool "acp_plan".`; + } + } + + let additionalTools: AdditionalToolInfo[] | null = null; + try { + const byName = new Map(); + + + try { + const lmToolsService = this.instantiationService.invokeFunction(a => a.get(ILanguageModelToolsService)); + const registeredTools = lmToolsService.getTools(); + + const toolsArray: IToolData[] = []; + for (const toolData of registeredTools) toolsArray.push(toolData); + + const mcpTools = toolsArray.filter(toolData => toolData.source?.type === 'mcp'); + + for (const toolData of mcpTools) { + const baseName = toolData.toolReferenceName || toolData.displayName; + const source = toolData.source; + + let safePrefix = 'mcp'; + if (source && source.type === 'mcp') { + const rawId = source.definitionId || source.collectionId || 'mcp'; + const idParts = rawId.split('.'); + const serverName = idParts[idParts.length - 1] || rawId; + safePrefix = serverName.replace(/[^a-zA-Z0-9_]/g, '_'); + } + + const name = `${safePrefix}__${baseName}`; + if (disabledToolNames.has(name)) continue; + + byName.set(name, { + name, + description: toolData.modelDescription || toolData.userDescription || '', + params: toolData.inputSchema?.properties + ? Object.fromEntries( + Object.entries(toolData.inputSchema.properties).map(([key, schema]: [string, any]) => [ + key, + { + description: (schema as any).description || `Parameter: ${key}`, + type: (schema as any).type, + enum: (schema as any).enum, + items: (schema as any).items, + properties: (schema as any).properties, + required: (schema as any).required, + default: (schema as any).default, + minimum: (schema as any).minimum, + maximum: (schema as any).maximum, + minLength: (schema as any).minLength, + maxLength: (schema as any).maxLength, + }, + ]) + ) + : undefined, + }); + } + } catch (e) { + + this.logService.debug('[ACP getLLMConfig] ILanguageModelToolsService MCP tools unavailable:', e); + } + + + try { + for (const t of this._collectMcpJsonAdditionalTools()) { + if (disabledToolNames.has(t.name)) continue; + if (!byName.has(t.name)) byName.set(t.name, t); + } + } catch (e) { + this.logService.warn('[ACP getLLMConfig] Failed to collect MCP tools from IMCPService:', e); + } + + additionalTools = byName.size ? Array.from(byName.values()) : null; + } catch (e) { + this.logService.error('[ACP getLLMConfig] Failed to collect dynamic tools:', e); + additionalTools = null; + } + + return { + providerName, + modelName, + settingsOfProvider: combinedSettings, + modelSelectionOptions, + overridesOfModel: st.overridesOfModel || null, + separateSystemMessage, + chatMode: st.globalSettings.chatMode ?? null, + loopGuard: { + maxTurnsPerPrompt: st.globalSettings.loopGuardMaxTurnsPerPrompt, + maxSameAssistantPrefix: st.globalSettings.loopGuardMaxSameAssistantPrefix, + maxSameToolCall: st.globalSettings.loopGuardMaxSameToolCall, + }, + requestParams, + providerRouting, + dynamicRequestConfig, + additionalTools, + disabledStaticTools, + disabledDynamicTools, + }; + } + + // Everything below is tool-related and needs IToolsService. + const tools = this._getToolsService(); + if (!tools) return {}; + + const normalizeToolAlias = (nameStr: string): string => { + const n = nameStr.trim().toLowerCase(); + if (n === 'edit' || n === 'apply_patch') return 'edit_file'; + if (n === 'write' || n === 'write_file') return 'rewrite_file'; + if (n === 'read' || n === 'cat') return 'read_file'; + if (n === 'search' || n === 'ripgrep') return 'grep'; + return nameStr; + }; + + const getMcpSafePrefix = (t: IToolData): string | undefined => { + if (!t.source || t.source.type !== 'mcp') return undefined; + const rawId = t.source.definitionId || t.source.collectionId || 'mcp'; + const idParts = String(rawId).split('.'); + const serverName = idParts[idParts.length - 1] || rawId; + return String(serverName).replace(/[^a-zA-Z0-9_]/g, '_'); + }; + + const toMcpPrefixedName = (t: IToolData): string => { + const baseName = t.toolReferenceName || t.displayName || t.id; + const prefix = getMcpSafePrefix(t) || 'mcp'; + return `${prefix}__${baseName}`; + }; + + const resolveMcpToolByName = (nameStr: string): IToolData | undefined => { + const lmToolsService = this.instantiationService.invokeFunction(accessor => accessor.get(ILanguageModelToolsService)); + const allTools = Array.from(lmToolsService.getTools()); + + let tool: IToolData | undefined = lmToolsService.getToolByName(nameStr); + if (tool) return tool; + + const findCandidates = (baseName: string) => + allTools.filter(t => + t.source?.type === 'mcp' && + (t.toolReferenceName === baseName || t.displayName === baseName) + ); + + if (nameStr.includes('__')) { + const idx = nameStr.indexOf('__'); + const prefix = nameStr.slice(0, idx); + const baseName = nameStr.slice(idx + 2); + + if (baseName) { + const candidates = findCandidates(baseName); + if (candidates.length) { + const byPrefix = candidates.find(c => getMcpSafePrefix(c) === prefix); + return byPrefix || candidates[0]; + } + } + + const fallbackBase = nameStr.split('__').pop(); + if (fallbackBase) { + const candidates = findCandidates(fallbackBase); + if (candidates.length) { + const byPrefix = candidates.find(c => getMcpSafePrefix(c) === prefix); + return byPrefix || candidates[0]; + } + } + } + + const candidates2 = findCandidates(nameStr); + if (candidates2.length) return candidates2[0]; + + return undefined; + }; + + // Bridge validate/call/stringify for builtin tools + const bindTool = (name: K) => { + const validate = (tools.validateParams as Record unknown>)[name]; + const call = (tools.callTool as Record Promise<{ result: unknown }>>)[name]; + const stringify = (tools.stringOfResult as Partial string>>)[name]; + return { validate, call, stringify }; + }; + + if (method === 'void/tools/list') { + const fallbackBuiltinNames = (): ToolName[] => + Object.keys(tools.callTool).filter(isAToolName) as ToolName[]; + let builtinNames: ToolName[] = fallbackBuiltinNames(); + try { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const rawChatMode = vss.state.globalSettings.chatMode; + const chatMode: ChatMode | null = + rawChatMode === 'agent' || rawChatMode === 'gather' || rawChatMode === 'normal' + ? rawChatMode + : null; + if (chatMode) { + const names = (availableTools(chatMode) ?? []).map(tool => tool.name); + builtinNames = Array.from(new Set(names)).filter(isAToolName) as ToolName[]; + } + } catch { + builtinNames = fallbackBuiltinNames(); + } + + let mcpNames: string[] = []; + try { + const lmToolsService = this.instantiationService.invokeFunction(accessor => accessor.get(ILanguageModelToolsService)); + const allTools = Array.from(lmToolsService.getTools()); + mcpNames = allTools + .filter(t => t.source?.type === 'mcp') + .map(t => toMcpPrefixedName(t)); + } catch { + mcpNames = []; + } + + const all = Array.from(new Set([...builtinNames, ...mcpNames])) + .filter(name => !disabledToolNames.has(name)) + .sort(); + return { tools: all }; + } + + if (method === 'void/tools/describe') { + const raw = String(p?.name ?? '').trim(); + if (!raw) throw new Error('void/tools/describe: missing name'); + + let nameStr = normalizeToolAlias(raw); + + // Built-in tools + if (isAToolName(nameStr)) { + if (disabledToolNames.has(nameStr)) { + throw new Error(this._disabledToolError(nameStr)); + } + + const modes: Array<'agent' | 'gather' | 'normal'> = ['agent', 'gather', 'normal']; + const all = new Map(); + + for (const m of modes) { + const arr = availableTools(m) ?? []; + for (const t of arr) { + if (t?.name && !all.has(t.name)) all.set(t.name, t); + } + } + + const info = all.get(nameStr); + return { + name: nameStr, + description: String(info?.description ?? ''), + inputSchema: { + type: 'object', + properties: (info?.params ?? {}) as any + } + }; + } + + // MCP/dynamic tools + const tool = resolveMcpToolByName(nameStr); + if (!tool) throw new Error(`Unknown tool: ${nameStr}`); + const prefixedName = toMcpPrefixedName(tool); + if (disabledToolNames.has(prefixedName)) { + throw new Error(this._disabledToolError(prefixedName)); + } + + return { + name: prefixedName, + description: tool.modelDescription || tool.userDescription || '', + inputSchema: tool.inputSchema ?? null + }; + } + + if (method === 'void/tools/execute') { + let nameStr = normalizeToolAlias(String(p?.name ?? '')); + if (!isAToolName(nameStr)) throw new Error(`Unknown tool: ${nameStr}`); + if (disabledToolNames.has(nameStr)) throw new Error(this._disabledToolError(nameStr)); + + const name: ToolName = nameStr; + const rawParams: RawToolParamsObj = (p?.params ?? {}) as RawToolParamsObj; + + const { validate, call } = bindTool(name); + const validated = validate(rawParams); + const { result } = await call(validated); + const resolved = await result; + return { ok: true, result: resolved }; + } + + if (method === 'void/tools/execute_with_text') { + let nameStr = normalizeToolAlias(String(p?.name ?? '')); + const rawParams: RawToolParamsObj = (p?.params ?? {}) as RawToolParamsObj; + + // 1) Built-in tools + if (isAToolName(nameStr)) { + if (disabledToolNames.has(nameStr)) throw new Error(this._disabledToolError(nameStr)); + + const name: ToolName = nameStr; + const { validate, call, stringify } = bindTool(name); + + const validated = validate(rawParams); + const { result } = await call(validated); + const resolved = await result; + + let text: string; + try { + text = stringify + ? stringify(validated, resolved) + : (typeof resolved === 'string' ? resolved : JSON.stringify(resolved)); + } catch { + text = typeof resolved === 'string' ? resolved : JSON.stringify(resolved); + } + + return { ok: true, result: resolved, text }; + } + + + try { + const lmToolsService = this.instantiationService.invokeFunction(accessor => accessor.get(ILanguageModelToolsService)); + const tool = resolveMcpToolByName(nameStr); + + if (tool) { + const prefixedName = toMcpPrefixedName(tool); + if (disabledToolNames.has(prefixedName)) { + throw new Error(this._disabledToolError(prefixedName)); + } + + const invocation = { + callId: generateUuid(), + toolId: tool.id, + parameters: rawParams ?? {}, + context: undefined, + skipConfirmation: true, + }; + + const res = await lmToolsService.invokeTool(invocation, async () => 0, CancellationToken.None); + + const textParts = (res.content ?? []).filter(part => part.kind === 'text').map(part => part.value as string); + let value: any; + if (textParts.length > 0) value = textParts.join('\n'); + else if (res.toolResultDetails) value = res.toolResultDetails; + else if (res.toolResultMessage) value = res.toolResultMessage; + else value = {}; + + const text = typeof value === 'string' ? value : JSON.stringify(value); + return { ok: true, result: value, text }; + } + } catch { + + } + + + const mcp = this._getMcpService(); + if (mcp) { + const resolved = this._resolveMcpJsonToolByAcpName(nameStr); + if (resolved) { + if (disabledToolNames.has(nameStr)) { + throw new Error(this._disabledToolError(nameStr)); + } + + const { serverName, toolNamePrefixed } = resolved; + + const callRes = await mcp.callMCPTool({ + serverName, + toolName: toolNamePrefixed, + params: rawParams ?? {} + }); + + const rawResult = callRes?.result ?? callRes; + + let text: string; + try { + text = typeof mcp.stringifyResult === 'function' + ? String(mcp.stringifyResult(rawResult)) + : (typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult)); + } catch { + text = typeof rawResult === 'string' ? rawResult : JSON.stringify(rawResult); + } + + return { ok: true, result: rawResult, text }; + } + } + throw new Error(`Unknown tool: ${nameStr}`); + } + return {}; + } +} diff --git a/src/vs/workbench/contrib/acp/browser/acpService.ts b/src/vs/workbench/contrib/acp/browser/acpService.ts new file mode 100644 index 00000000000..08c892e773f --- /dev/null +++ b/src/vs/workbench/contrib/acp/browser/acpService.ts @@ -0,0 +1,174 @@ +import { IAcpService, IAcpStream, IAcpChatMessage, IAcpUserMessage, IAcpSendOptions } from '../../../../platform/acp/common/iAcpService.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { AcpChannelClient, AcpChannelName, AcpHostCallbackRequest } from '../../../../platform/acp/common/acpIpc.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { FeatureName } from '../../../../platform/void/common/voidSettingsTypes.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; + +import { AcpHostCallbacksService } from './AcpHostCallbacksService.js'; +import { AcpInternalExtMethodService } from './AcpInternalExtMethodService.js'; + +export class AcpService extends Disposable implements IAcpService { + declare readonly _serviceBrand: undefined; + + private readonly client: IAcpService; + + // Remember ACP mode per thread to separate builtin vs external behaviors + private readonly _modeByThreadId = new Map(); + private readonly _acpThreadPrefix = generateUuid(); + private readonly _hostCallbacks: AcpHostCallbacksService; + private readonly _internalExtMethods: AcpInternalExtMethodService; + private readonly _acpThreadIdByUiThreadId = new Map(); + private readonly _uiThreadIdByAcpThreadId = new Map(); + + constructor( + @IMainProcessService mainProcessService: IMainProcessService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, + ) { + super(); + const channel = mainProcessService.getChannel(AcpChannelName); + this.client = new AcpChannelClient(channel); + + this._hostCallbacks = new AcpHostCallbacksService(this.instantiationService, this.fileService, this.logService); + this._internalExtMethods = new AcpInternalExtMethodService(this.instantiationService, this.logService); + + // host callbacks from main + const onHostCallback = channel.listen('onHostCallback'); + const disposable = onHostCallback(async (req) => { + // IMPORTANT: multi-window routing guard. + // Do NOT respond if this window does not own the thread, otherwise it can race and return wrong workspace. + if (!this._shouldHandleHostCallback(req)) { + this.logService.debug?.('[ACP Browser] ignore hostCallback for foreign thread', { + requestId: req.requestId, + kind: req.kind, + threadId: req.threadId + }); + return; + } + + try { + const result = await this.handleHostCallback(req); + await channel.call('hostCallbackResult', { requestId: req.requestId, result }); + } catch (e: any) { + await channel.call('hostCallbackResult', { requestId: req.requestId, error: e?.message ?? String(e) }); + } + }); + this._register(disposable); + } + + isConnected(): boolean { return this.client.isConnected(); } + + connect(opts?: IAcpSendOptions): Promise { + this.logService.debug?.('[ACP Browser] AcpService.connect called with opts:', opts); + return this.client.connect(opts); + } + + disconnect(): Promise { return this.client.disconnect(); } + + sendChatMessage( + threadId: string, + history: IAcpChatMessage[], + message: IAcpUserMessage, + opts?: IAcpSendOptions + ): Promise { + // IMPORTANT: + // UI threadId can be identical across windows (shared StorageScope.APPLICATION), + // so we must use a per-window unique ACP threadId on the wire. + const acpThreadId = this._toAcpThreadId(threadId); + + // remember mode for routing host callbacks (keyed by ACP threadId) + const mode = opts?.mode || 'builtin'; + this._modeByThreadId.set(acpThreadId, mode); + + return this._sendChatMessageWithResolvedSystem(acpThreadId, history, message, opts); + } + + private _toAcpThreadId(uiThreadId: string): string { + const tid = String(uiThreadId ?? '').trim(); + if (!tid) return tid; + + const existing = this._acpThreadIdByUiThreadId.get(tid); + if (existing) return existing; + + const acpTid = `${this._acpThreadPrefix}:${tid}`; + this._acpThreadIdByUiThreadId.set(tid, acpTid); + this._uiThreadIdByAcpThreadId.set(acpTid, tid); + return acpTid; + } + + private _toUiThreadId(acpThreadId: string | undefined): string | undefined { + if (!acpThreadId) return undefined; + return this._uiThreadIdByAcpThreadId.get(acpThreadId) ?? acpThreadId; + } + + private _shouldHandleHostCallback(req: AcpHostCallbackRequest): boolean { + const acpTid = req.threadId; + + // If no threadId, keep legacy behavior. + if (!acpTid) return true; + + // Only the window that initiated this ACP threadId should answer. + return this._modeByThreadId.has(acpTid); + } + + private async _sendChatMessageWithResolvedSystem( + threadId: string, + history: IAcpChatMessage[], + message: IAcpUserMessage, + opts?: IAcpSendOptions + ): Promise { + const feature = (opts?.featureName === 'Ctrl+K') ? 'Ctrl+K' : 'Chat'; + let system = (opts?.system ?? '').trim(); + if (!system) { + system = (await this._computeDefaultAcpSystemPrompt(feature))?.trim() ?? ''; + } + const nextOpts: IAcpSendOptions | undefined = system + ? { ...(opts ?? {}), system } + : opts; + return this.client.sendChatMessage(threadId, history, message, nextOpts); + } + + private async _computeDefaultAcpSystemPrompt(_feature: FeatureName): Promise { + const vss = this.instantiationService.invokeFunction(a => a.get(IVoidSettingsService)); + const st = vss.state; + + const explicit = (st.globalSettings.acpSystemPrompt ?? '').trim(); + //It doesn't make any sense + if (explicit) return explicit; + + // The ACP protocol does not support the transfer of system prompt for external agents + return ''; + } + + private async handleHostCallback(req: AcpHostCallbackRequest): Promise { + const kind = req.kind; + const params = req.params ?? {}; + + // threadId on the wire is ACP-threadId (prefixed) + const acpThreadId: string | undefined = req.threadId; + // threadId for UI services must be original UI threadId + const uiThreadId: string | undefined = this._toUiThreadId(acpThreadId); + + // extMethod is INTERNAL-only (builtin agent) + if (kind === 'extMethod') { + const mode = (acpThreadId ? this._modeByThreadId.get(acpThreadId) : undefined) || 'builtin'; + if (mode !== 'builtin') { + throw new Error(`ACP extMethod is only supported in builtin mode. Current mode: ${mode}`); + } + return this._internalExtMethods.handle(params); + } + + // all other host callbacks are common (permission/fs/terminal) + // IMPORTANT: pass uiThreadId so tool permission/results route to the correct chat thread in this window. + return this._hostCallbacks.handle(kind, params, uiThreadId); + } +} + +registerSingleton(IAcpService, AcpService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/acp/browser/voidToolsAdapter.ts b/src/vs/workbench/contrib/acp/browser/voidToolsAdapter.ts new file mode 100644 index 00000000000..84f5d6cb01d --- /dev/null +++ b/src/vs/workbench/contrib/acp/browser/voidToolsAdapter.ts @@ -0,0 +1,96 @@ +import type { IFileService } from '../../../../platform/files/common/files.js'; +import { IToolsService } from '../../void/common/toolsService.js'; +import { URI } from '../../../../base/common/uri.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; + +export interface IAcpToolCall { + id: string; + name: string; + args: any; +} + +export interface IAcpToolResult { + id: string; // toolCallId + name: string; + result?: any; + error?: string; +} + +export class VoidToolsAdapter { + constructor( + private readonly fileService: IFileService, + private readonly toolsService: IToolsService + ) { } + + + async readTextFile(path: string): Promise { + + try { + const validate = (this.toolsService.validateParams as any)?.read_file as ((p: any) => any) | undefined; + const caller = (this.toolsService.callTool as any)?.read_file as ((p: any) => Promise<{ result: any }>) | undefined; + if (validate && caller) { + const validated = validate({ uri: path }); + const { result } = await caller(validated); + const resolved = await result; + const text: string = resolved?.fileContents ?? ''; + return text; + } + } catch { + + } + + + const uri = URI.parse(path); + const content = await this.fileService.readFile(uri); + return content.value.toString(); + } + + + async writeTextFile(path: string, text: string): Promise { + + try { + const validate = (this.toolsService.validateParams as any)?.rewrite_file as ((p: any) => any) | undefined; + const caller = (this.toolsService.callTool as any)?.rewrite_file as ((p: any) => Promise<{ result: any }>) | undefined; + if (validate && caller) { + const validated = validate({ uri: path, new_content: text }); + const { result } = await caller(validated); + await result; + return; + } + } catch { + + } + + + const uri = URI.parse(path); + await this.fileService.writeFile(uri, VSBuffer.fromString(text)); + } + + + async dispatchToolCall(call: IAcpToolCall): Promise { + try { + if (call.name === 'readTextFile') { + const text = await this.readTextFile(call.args?.path); + return { id: call.id, name: call.name, result: { text } }; + } + if (call.name === 'writeTextFile') { + await this.writeTextFile(call.args?.path, call.args?.text ?? ''); + return { id: call.id, name: call.name, result: { ok: true } }; + } + + + const validate = (this.toolsService.validateParams as any)?.[call.name] as ((p: any) => any) | undefined; + const caller = (this.toolsService.callTool as any)?.[call.name] as ((p: any) => Promise<{ result: any }>) | undefined; + if (!validate || !caller) { + throw new Error(`Unknown tool: ${call.name}`); + } + const validated = validate(call.args ?? {}); + const { result } = await caller(validated); + const resolved = await result; + + return { id: call.id, name: call.name, result: resolved }; + } catch (e: any) { + return { id: call.id, name: call.name, error: e?.message ?? String(e) }; + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/acp/test/browser/AcpHostCallbacksService.test.ts b/src/vs/workbench/contrib/acp/test/browser/AcpHostCallbacksService.test.ts new file mode 100644 index 00000000000..950713a2d37 --- /dev/null +++ b/src/vs/workbench/contrib/acp/test/browser/AcpHostCallbacksService.test.ts @@ -0,0 +1,188 @@ +import assert from 'assert'; +import { URI } from '../../../../../base/common/uri.js'; +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { isWindows } from '../../../../../base/common/platform.js'; +import { IVoidSettingsService } from '../../../../../platform/void/common/voidSettingsService.js'; +import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; +import { AcpHostCallbacksService } from '../../../../../workbench/contrib/acp/browser/AcpHostCallbacksService.js'; + +suite('AcpHostCallbacksService readTextFile truncation', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + const rootPath = isWindows ? 'C:\\ws' : '/ws'; + const workspaceRoot = URI.file(rootPath); + + const fakeLogService: any = { + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { }, + }; + + function parseMeta(content: string): any { + const tail = content.slice(-4000); + const m = tail.match(/TRUNCATION_META:\s*(\{[\s\S]*\})\s*$/); + assert.ok(m, 'Expected TRUNCATION_META at end of content'); + return JSON.parse(m[1]); + } + + function makeInstantiationService(opts: { maxToolOutputLength: number; readFileChunkLines?: number }) { + const fakeVss: any = { + state: { + globalSettings: { + maxToolOutputLength: opts.maxToolOutputLength, + ...(opts.readFileChunkLines !== undefined ? { readFileChunkLines: opts.readFileChunkLines } : {}) + } + } + }; + + const fakeWs: any = { + getWorkspace() { + return { folders: [{ uri: workspaceRoot }] }; + } + }; + + return { + invokeFunction(fn: (accessor: { get: (id: unknown) => unknown }) => T): T { + return fn({ + get(id: unknown) { + if (id === IVoidSettingsService) return fakeVss; + if (id === IWorkspaceContextService) return fakeWs; + throw new Error('Unexpected service token'); + }, + }); + }, + }; + } + + function makeFileService(fileText: string) { + let writeCount = 0; + + const fileService: any = { + async readFile(_uri: URI) { + return { value: VSBuffer.fromString(fileText) }; + }, + async writeFile(_uri: URI, _buf: VSBuffer) { + writeCount++; + }, + async exists(_uri: URI) { return true; }, + async createFolder(_uri: URI) { /* noop */ }, + + __debug: { + writeCount: () => writeCount, + } + }; + + return fileService; + } + + test('readTextFile: truncation footer matches unified read_file meta (uri + nextStartLine + suggested) and does NOT write tool_outputs', async () => { + const maxToolOutputLength = 1200; + + const absPath = + isWindows + ? 'C:\\abs\\path\\file.ts' + : '/abs/path/file.ts'; + + const big = Array.from({ length: 500 }, (_, i) => `LINE_${i + 1} ${'X'.repeat(80)}`).join('\n'); + + + const instantiationService = makeInstantiationService({ maxToolOutputLength }); + const fileService = makeFileService(big); + + const svc: any = new (AcpHostCallbacksService as any)(instantiationService, fileService, fakeLogService); + + const line = 10; + const limit = 50; + + const res = await svc.handle('readTextFile', { path: absPath, line, limit }, undefined); + assert.ok(res && typeof res.content === 'string'); + + const text: string = res.content; + + assert.ok(text.includes('[VOID] TOOL OUTPUT TRUNCATED'), 'must include truncation header'); + assert.ok(text.includes('Continue by calling read_file on the ORIGINAL uri'), 'must include unified read_file instruction'); + assert.ok(!text.includes('.void/tool_outputs'), 'readTextFile must NOT point to tool_outputs'); + + const meta = parseMeta(text); + + assert.strictEqual(meta.tool, 'read_file'); + assert.strictEqual(meta.uri, absPath); + assert.strictEqual(meta.requestedStartLine, line); + + assert.ok(typeof meta.nextStartLine === 'number' && meta.nextStartLine > line, 'nextStartLine must advance'); + assert.ok(meta.suggested && typeof meta.suggested.startLine === 'number' && typeof meta.suggested.endLine === 'number'); + + assert.strictEqual(meta.suggested.startLine, meta.nextStartLine); + // The code uses a fixed chunk size of 200 for suggestions, not the requested limit + assert.strictEqual(meta.suggested.endLine, meta.nextStartLine + 200 - 1); + + // ensure instruction matches meta values + assert.ok( + text.includes(`read_file({ uri: ${JSON.stringify(absPath)}, startLine: ${meta.nextStartLine}, endLine: ${meta.suggested.endLine} })`), + 'instruction must match TRUNCATION_META' + ); + }); + + test('readTextFile: truncation uses readFileChunkLines setting (700) for suggested range', async () => { + const maxToolOutputLength = 1200; + const readFileChunkLines = 700; + + const absPath = + isWindows + ? 'C:\\abs\\path\\file.ts' + : '/abs/path/file.ts'; + + const big = Array.from({ length: 500 }, (_, i) => `LINE_${i + 1} ${'X'.repeat(80)}`).join('\n'); + + const instantiationService = makeInstantiationService({ maxToolOutputLength, readFileChunkLines }); + const fileService = makeFileService(big); + + const svc: any = new (AcpHostCallbacksService as any)(instantiationService, fileService, fakeLogService); + + const line = 10; + const limit = 50; + + const res = await svc.handle('readTextFile', { path: absPath, line, limit }, undefined); + assert.ok(res && typeof res.content === 'string'); + + const text: string = res.content; + const meta = parseMeta(text); + + assert.strictEqual(meta.tool, 'read_file'); + assert.strictEqual(meta.uri, absPath); + assert.strictEqual(meta.requestedStartLine, line); + assert.ok(typeof meta.nextStartLine === 'number' && meta.nextStartLine > line); + assert.ok(meta.suggested); + assert.strictEqual(meta.suggested.startLine, meta.nextStartLine); + // Key assertion: suggested.endLine should be nextStartLine + 700 - 1 + assert.strictEqual(meta.suggested.endLine, meta.nextStartLine + readFileChunkLines - 1); + + // no file writes for readTextFile + assert.strictEqual(fileService.__debug.writeCount(), 0); + }); + + test('readTextFile: no truncation returns plain content without footer', async () => { + const maxToolOutputLength = 5000; + + const absPath = + isWindows + ? 'C:\\abs\\path\\small.ts' + : '/abs/path/small.ts'; + + const small = `A\nB\nC\n`; + + const instantiationService = makeInstantiationService({ maxToolOutputLength }); + const fileService = makeFileService(small); + + const svc: any = new (AcpHostCallbacksService as any)(instantiationService, fileService, fakeLogService); + + const res = await svc.handle('readTextFile', { path: absPath }, undefined); + assert.ok(res && typeof res.content === 'string'); + + assert.strictEqual(res.content, small); + assert.ok(!res.content.includes('TRUNCATION_META'), 'should not include footer'); + assert.strictEqual(fileService.__debug.writeCount(), 0); + }); +}); diff --git a/src/vs/workbench/contrib/acp/test/browser/acpInternalExtMethodService.getLLMConfigSwitch.test.ts b/src/vs/workbench/contrib/acp/test/browser/acpInternalExtMethodService.getLLMConfigSwitch.test.ts new file mode 100644 index 00000000000..4f31c82c18a --- /dev/null +++ b/src/vs/workbench/contrib/acp/test/browser/acpInternalExtMethodService.getLLMConfigSwitch.test.ts @@ -0,0 +1,427 @@ +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { NullLogService } from '../../../../../platform/log/common/log.js'; +import { AcpInternalExtMethodService } from '../../browser/AcpInternalExtMethodService.js'; +import { IDynamicProviderRegistryService } from '../../../../../platform/void/common/providerReg.js'; +import { IVoidSettingsService } from '../../../../../platform/void/common/voidSettingsService.js'; +import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; +import { URI } from '../../../../../base/common/uri.js'; + +suite('ACP getLLMConfig - config switches with settings', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + test('void/settings/getLLMConfig returns fresh model + dynamicRequestConfig after settings change', async () => { + const logService = new NullLogService(); + + const settingsService: any = { + state: { + settingsOfProvider: { + provA: { endpoint: 'https://provider-a.example/v1', apiKey: 'provKeyA', _didFillInProviderSettings: true }, + provB: { endpoint: 'https://provider-b.example/v1', apiKey: 'provKeyB', _didFillInProviderSettings: true }, + }, + customProviders: {}, + overridesOfModel: {}, + + modelSelectionOfFeature: { + Chat: { providerName: 'provA', modelName: 'provA/modelA' }, + 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null, 'SCM': null, + }, + optionsOfModelSelection: { + Chat: { + provA: { 'provA/modelA': { temperature: 0.1 } }, + provB: { 'provB/modelB': { temperature: 0.9 } }, + }, + 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {}, 'SCM': {}, + }, + + globalSettings: { + chatMode: 'normal', + useAcp: false, + acpMode: 'builtin', + acpAgentUrl: '', + acpProcessCommand: '', + acpProcessArgs: [], + acpProcessEnv: {}, + acpModel: null, + + acpSystemPrompt: 'SYS', + showAcpPlanInChat: true, + + autoRefreshModels: false, + aiInstructions: '', + enableAutocomplete: false, + syncApplyToChat: false, + syncSCMToChat: false, + enableFastApply: false, + autoApprove: {}, + mcpAutoApprove: false, + showInlineSuggestions: false, + includeToolLintErrors: false, + loopGuardMaxTurnsPerPrompt: 25, + loopGuardMaxSameAssistantPrefix: 10, + loopGuardMaxSameToolCall: 10, + isOnboardingComplete: true, + disableTelemetry: true, + + chatRetries: 0, + retryDelay: 0, + maxToolOutputLength: 40000, + }, + mcpUserStateOfName: {}, + } + }; + + const registry: any = { + initialize: async () => { }, + getRequestConfigForModel: (fullModelId: string, _providerSlug: string) => { + if (fullModelId === 'provA/modelA') { + return { + apiStyle: 'openai-compatible', + endpoint: 'https://api-a.example/v1', + headers: { Authorization: 'Bearer keyA' }, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }; + } + if (fullModelId === 'provB/modelB') { + return { + apiStyle: 'openai-compatible', + endpoint: 'https://api-b.example/v1', + headers: { Authorization: 'Bearer keyB' }, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }; + } + throw new Error('unexpected model id in registry: ' + fullModelId); + }, + getEffectiveModelCapabilities: async () => ({ supportCacheControl: false }), + }; + + const workspace: any = { + getWorkspace: () => ({ folders: [{ uri: URI.file('/workspace/root') }] }), + }; + + const instantiationService: any = { + invokeFunction: (fn: any) => fn({ + get: (id: any) => { + if (id === IVoidSettingsService) return settingsService; + if (id === IDynamicProviderRegistryService) return registry; + if (id === IWorkspaceContextService) return workspace; + + return { getTools: () => new Set() }; + } + }) + }; + + const svc = new AcpInternalExtMethodService(instantiationService, logService); + + // ----- BEFORE ----- + const res1 = await svc.handle({ method: 'void/settings/getLLMConfig', params: { featureName: 'Chat' } }); + assert.strictEqual(res1.providerName, 'provA'); + assert.strictEqual(res1.modelName, 'provA/modelA'); + assert.strictEqual(res1.chatMode, 'normal'); + assert.strictEqual(res1.dynamicRequestConfig.endpoint, 'https://api-a.example/v1'); + assert.strictEqual(res1.dynamicRequestConfig.headers.Authorization, 'Bearer keyA'); + assert.ok(String(res1.separateSystemMessage || '').includes('SYS')); + + // ----- AFTER: switch to agent ----- + settingsService.state = { + ...settingsService.state, + modelSelectionOfFeature: { + ...settingsService.state.modelSelectionOfFeature, + Chat: { providerName: 'provB', modelName: 'provB/modelB' }, + }, + globalSettings: { + ...settingsService.state.globalSettings, + useAcp: true, + acpMode: 'builtin', + chatMode: 'agent', + acpSystemPrompt: 'SYS2', + } + }; + + const res2 = await svc.handle({ method: 'void/settings/getLLMConfig', params: { featureName: 'Chat' } }); + assert.strictEqual(res2.providerName, 'provB'); + assert.strictEqual(res2.modelName, 'provB/modelB'); + assert.strictEqual(res2.chatMode, 'agent'); + assert.strictEqual(res2.dynamicRequestConfig.endpoint, 'https://api-b.example/v1'); + assert.strictEqual(res2.dynamicRequestConfig.headers.Authorization, 'Bearer keyB'); + assert.ok(String(res2.separateSystemMessage || '').includes('SYS2')); + }); + + test('void/settings/getLLMConfig splits disabled tools into static and dynamic lists', async () => { + const logService = new NullLogService(); + + const settingsService: any = { + state: { + settingsOfProvider: { + openAI: { apiKey: 'k', _didFillInProviderSettings: true }, + }, + customProviders: {}, + overridesOfModel: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openAI', modelName: 'gpt-4o-mini' }, + 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null, 'SCM': null, + }, + optionsOfModelSelection: { + Chat: {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {}, 'SCM': {}, + }, + globalSettings: { + chatMode: 'agent', + useAcp: true, + acpMode: 'builtin', + acpAgentUrl: '', + acpProcessCommand: '', + acpProcessArgs: [], + acpProcessEnv: {}, + acpModel: null, + acpSystemPrompt: 'SYS', + showAcpPlanInChat: true, + autoRefreshModels: false, + aiInstructions: '', + enableAutocomplete: false, + syncApplyToChat: false, + syncSCMToChat: false, + enableFastApply: false, + autoApprove: {}, + mcpAutoApprove: false, + showInlineSuggestions: false, + includeToolLintErrors: false, + loopGuardMaxTurnsPerPrompt: 25, + loopGuardMaxSameAssistantPrefix: 10, + loopGuardMaxSameToolCall: 10, + isOnboardingComplete: true, + disableTelemetry: true, + chatRetries: 0, + retryDelay: 0, + maxToolOutputLength: 40000, + disabledToolNames: ['read_file', 'myServer__toolA'], + }, + mcpUserStateOfName: {}, + } + }; + + const registry: any = { + initialize: async () => { }, + getRequestConfigForModel: () => ({ + apiStyle: 'openai-compatible', + endpoint: 'https://api.openai.com/v1', + headers: {}, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }), + getEffectiveModelCapabilities: async () => ({ supportCacheControl: false }), + }; + + const workspace: any = { + getWorkspace: () => ({ folders: [{ uri: URI.file('/workspace/root') }] }), + }; + + const instantiationService: any = { + invokeFunction: (fn: any) => fn({ + get: (id: any) => { + if (id === IVoidSettingsService) return settingsService; + if (id === IDynamicProviderRegistryService) return registry; + if (id === IWorkspaceContextService) return workspace; + return { getTools: () => new Set() }; + } + }) + }; + + const svc = new AcpInternalExtMethodService(instantiationService, logService); + const res = await svc.handle({ method: 'void/settings/getLLMConfig', params: { featureName: 'Chat' } }); + + assert.deepStrictEqual(res.disabledStaticTools, ['read_file']); + assert.deepStrictEqual(res.disabledDynamicTools, ['myServer__toolA']); + }); + + test('void/settings/getLLMConfig prefers dynamicRequestConfig.specialToolFormat for ACP prompt style', async () => { + const logService = new NullLogService(); + + const settingsService: any = { + state: { + settingsOfProvider: { + openAI: { apiKey: 'k', _didFillInProviderSettings: true }, + }, + customProviders: {}, + overridesOfModel: { + openAI: { + 'gpt-4o-mini': { + specialToolFormat: 'disabled', + }, + }, + }, + modelSelectionOfFeature: { + Chat: { providerName: 'openAI', modelName: 'gpt-4o-mini' }, + 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null, 'SCM': null, + }, + optionsOfModelSelection: { + Chat: {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {}, 'SCM': {}, + }, + globalSettings: { + chatMode: 'agent', + useAcp: true, + acpMode: 'builtin', + acpAgentUrl: '', + acpProcessCommand: '', + acpProcessArgs: [], + acpProcessEnv: {}, + acpModel: null, + acpSystemPrompt: '', + showAcpPlanInChat: true, + autoRefreshModels: false, + aiInstructions: '', + enableAutocomplete: false, + syncApplyToChat: false, + syncSCMToChat: false, + enableFastApply: false, + autoApprove: {}, + mcpAutoApprove: false, + showInlineSuggestions: false, + includeToolLintErrors: false, + loopGuardMaxTurnsPerPrompt: 25, + loopGuardMaxSameAssistantPrefix: 10, + loopGuardMaxSameToolCall: 10, + isOnboardingComplete: true, + disableTelemetry: true, + chatRetries: 0, + retryDelay: 0, + maxToolOutputLength: 40000, + disabledToolNames: ['read_file'], + }, + mcpUserStateOfName: {}, + } + }; + + const registry: any = { + initialize: async () => { }, + getRequestConfigForModel: () => ({ + apiStyle: 'openai-compatible', + endpoint: 'https://api.openai.com/v1', + headers: {}, + specialToolFormat: 'openai-style', + supportsSystemMessage: 'developer-role', + }), + getEffectiveModelCapabilities: async () => ({ supportCacheControl: false }), + }; + + const workspace: any = { + getWorkspace: () => ({ folders: [{ uri: URI.file('/workspace/root') }] }), + }; + + const instantiationService: any = { + invokeFunction: (fn: any) => fn({ + get: (id: any) => { + if (id === IVoidSettingsService) return settingsService; + if (id === IDynamicProviderRegistryService) return registry; + if (id === IWorkspaceContextService) return workspace; + return { getTools: () => new Set() }; + } + }) + }; + + const svc = new AcpInternalExtMethodService(instantiationService, logService); + const res = await svc.handle({ method: 'void/settings/getLLMConfig', params: { featureName: 'Chat' } }); + const msg = String(res.separateSystemMessage ?? ''); + + assert.ok( + !msg.includes('!!!CRITICAL: YOU MUST USE XML TOOLS - NO EXCEPTIONS!!!'), + 'ACP prompt must be native when dynamicRequestConfig.specialToolFormat=openai-style' + ); + assert.ok( + msg.includes('Core execution rules (MUST, Native tools):'), + 'native ACP prompt marker must be present' + ); + }); + + test('void/settings/getLLMConfig excludes disabled static tools from ACP XML prompt when specialToolFormat=disabled', async () => { + const logService = new NullLogService(); + + const settingsService: any = { + state: { + settingsOfProvider: { + openAI: { apiKey: 'k', _didFillInProviderSettings: true }, + }, + customProviders: {}, + overridesOfModel: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openAI', modelName: 'gpt-4o-mini' }, + 'Ctrl+K': null, 'Autocomplete': null, 'Apply': null, 'SCM': null, + }, + optionsOfModelSelection: { + Chat: {}, 'Ctrl+K': {}, 'Autocomplete': {}, 'Apply': {}, 'SCM': {}, + }, + globalSettings: { + chatMode: 'agent', + useAcp: true, + acpMode: 'builtin', + acpAgentUrl: '', + acpProcessCommand: '', + acpProcessArgs: [], + acpProcessEnv: {}, + acpModel: null, + acpSystemPrompt: '', + showAcpPlanInChat: true, + autoRefreshModels: false, + aiInstructions: '', + enableAutocomplete: false, + syncApplyToChat: false, + syncSCMToChat: false, + enableFastApply: false, + autoApprove: {}, + mcpAutoApprove: false, + showInlineSuggestions: false, + includeToolLintErrors: false, + loopGuardMaxTurnsPerPrompt: 25, + loopGuardMaxSameAssistantPrefix: 10, + loopGuardMaxSameToolCall: 10, + isOnboardingComplete: true, + disableTelemetry: true, + chatRetries: 0, + retryDelay: 0, + maxToolOutputLength: 40000, + disabledToolNames: ['read_file'], + }, + mcpUserStateOfName: {}, + } + }; + + const registry: any = { + initialize: async () => { }, + getRequestConfigForModel: () => ({ + apiStyle: 'openai-compatible', + endpoint: 'https://api.openai.com/v1', + headers: {}, + specialToolFormat: 'disabled', + supportsSystemMessage: 'developer-role', + }), + getEffectiveModelCapabilities: async () => ({ supportCacheControl: false }), + }; + + const workspace: any = { + getWorkspace: () => ({ folders: [{ uri: URI.file('/workspace/root') }] }), + }; + + const instantiationService: any = { + invokeFunction: (fn: any) => fn({ + get: (id: any) => { + if (id === IVoidSettingsService) return settingsService; + if (id === IDynamicProviderRegistryService) return registry; + if (id === IWorkspaceContextService) return workspace; + return { getTools: () => new Set() }; + } + }) + }; + + const svc = new AcpInternalExtMethodService(instantiationService, logService); + const res = await svc.handle({ method: 'void/settings/getLLMConfig', params: { featureName: 'Chat' } }); + const msg = String(res.separateSystemMessage ?? ''); + + assert.ok( + msg.includes('!!!CRITICAL: YOU MUST USE XML TOOLS - NO EXCEPTIONS!!!'), + 'ACP prompt must switch to XML mode when specialToolFormat=disabled' + ); + assert.ok(msg.includes('- run_command:'), 'enabled static tools should stay in ACP XML tools list'); + assert.ok(!msg.includes('- read_file:'), 'disabled static tools must be excluded from ACP XML tools list'); + }); +}); diff --git a/src/vs/workbench/contrib/acp/test/browser/acpService.test.ts b/src/vs/workbench/contrib/acp/test/browser/acpService.test.ts new file mode 100644 index 00000000000..e62117e98e7 --- /dev/null +++ b/src/vs/workbench/contrib/acp/test/browser/acpService.test.ts @@ -0,0 +1,213 @@ +import assert from 'assert'; +import { IVoidSettingsService } from '../../../../../platform/void/common/voidSettingsService.js'; +import type { RequestParamsConfig, ProviderRouting, DynamicRequestConfig } from '../../../../../platform/void/common/sendLLMMessageTypes.js'; +import { IDynamicProviderRegistryService } from '../../../../../platform/void/common/providerReg.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { AcpInternalExtMethodService } from '../../../../../workbench/contrib/acp/browser/AcpInternalExtMethodService.js'; + +function pickMethod(obj: T, names: string[]): (...args: any[]) => any { + for (const n of names) { + const fn = (obj as any)?.[n]; + if (typeof fn === 'function') return fn.bind(obj); + } + throw new Error(`None of the methods exist on object: ${names.join(', ')}`); +} + +suite('AcpService.getLLMConfig', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + const fakeLogService: ILogService = { + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { }, + + } as any; + + const makeInstantiationService = (services: { vss: any; registry?: any; logService?: ILogService }) => { + return { + invokeFunction(fn: (accessor: { get: (id: unknown) => unknown }) => T): T { + return fn({ + get(id: unknown) { + if (id === IVoidSettingsService) return services.vss; + if (id === IDynamicProviderRegistryService) return services.registry; + if (id === ILogService) return services.logService ?? fakeLogService; + throw new Error('Unexpected service token'); + }, + }); + }, + }; + }; + + const callGetLLMConfig = async (svc: any, featureName: string) => { + const fn = pickMethod(svc, ['handle', 'handleExtMethod', '_handle', '_handleExtMethod', 'call', '_call', 'execute', '_execute']); + return await fn({ + method: 'void/settings/getLLMConfig', + params: { featureName }, + }); + }; + + test('includes per-model requestParams from customProviders', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'openrouter/test-model' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { + Chat: {}, + 'Ctrl+K': {}, + Autocomplete: {}, + Apply: {}, + }, + overridesOfModel: {}, + globalSettings: { + acpSystemPrompt: 'dummy', + chatMode: null, + }, + customProviders: { + openrouter: { + perModel: { + 'openrouter/test-model': { + requestParams: { + mode: 'override', + params: { max_tokens: 99 }, + } satisfies RequestParamsConfig, + }, + }, + }, + }, + }; + + const fakeVss = { state: fakeState } as any; + const instantiationService = makeInstantiationService({ vss: fakeVss, logService: fakeLogService }); + + + const svc: any = new (AcpInternalExtMethodService as any)(instantiationService as any, fakeLogService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + const rp = res.requestParams as RequestParamsConfig | null; + assert.ok(rp, 'requestParams should be present'); + assert.strictEqual(rp!.mode, 'override'); + assert.strictEqual((rp!.params as any).max_tokens, 99); + } finally { + try { svc.dispose?.(); } catch { } + } + }); + + test('includes per-model providerRouting from customProviders when present', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'openrouter/test-model' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { + Chat: {}, + 'Ctrl+K': {}, + Autocomplete: {}, + Apply: {}, + }, + overridesOfModel: {}, + globalSettings: { + acpSystemPrompt: 'dummy', + chatMode: null, + }, + customProviders: { + openrouter: { + perModel: { + 'openrouter/test-model': { + providerRouting: { + order: ['openai'], + allow_fallbacks: false, + } satisfies ProviderRouting, + }, + }, + }, + }, + }; + + const fakeVss = { state: fakeState } as any; + const instantiationService = makeInstantiationService({ vss: fakeVss, logService: fakeLogService }); + + const svc: any = new (AcpInternalExtMethodService as any)(instantiationService as any, fakeLogService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + const pr = res.providerRouting as ProviderRouting | null; + assert.ok(pr, 'providerRouting should be present'); + assert.deepStrictEqual(pr, { order: ['openai'], allow_fallbacks: false }); + } finally { + try { svc.dispose?.(); } catch { } + } + }); + + test('includes dynamicRequestConfig from dynamic provider registry when available', async () => { + const fakeState: any = { + settingsOfProvider: {}, + modelSelectionOfFeature: { + Chat: { providerName: 'openrouter', modelName: 'tngtech/deepseek-r1t-chimera:free' }, + 'Ctrl+K': null, + Autocomplete: null, + Apply: null, + }, + optionsOfModelSelection: { + Chat: {}, + 'Ctrl+K': {}, + Autocomplete: {}, + Apply: {}, + }, + overridesOfModel: {}, + globalSettings: { + acpSystemPrompt: 'dummy', + chatMode: null, + }, + customProviders: {}, + }; + + const fakeVss = { state: fakeState } as any; + + const dynamicCfg: DynamicRequestConfig = { + endpoint: 'https://openrouter.ai/api/v1', + apiStyle: 'openai-compatible', + supportsSystemMessage: false, + specialToolFormat: 'disabled', + headers: { + Accept: 'application/json', + Authorization: 'Bearer test-key', + }, + }; + + const fakeRegistry: any = { + async initialize() { /* no-op */ }, + getRequestConfigForModel(modelId: string, slug?: string) { + assert.strictEqual(slug, 'openrouter'); + assert.strictEqual(modelId, 'tngtech/deepseek-r1t-chimera:free'); + return dynamicCfg; + }, + async getEffectiveModelCapabilities(_slug: string, _modelId: string) { + return {}; + }, + }; + + const instantiationService = makeInstantiationService({ vss: fakeVss, registry: fakeRegistry, logService: fakeLogService }); + + const svc: any = new (AcpInternalExtMethodService as any)(instantiationService as any, fakeLogService as any); + try { + const res = await callGetLLMConfig(svc, 'Chat'); + + const drc = res.dynamicRequestConfig as DynamicRequestConfig | null; + assert.ok(drc, 'dynamicRequestConfig should be present'); + assert.strictEqual(drc!.endpoint, 'https://openrouter.ai/api/v1'); + assert.strictEqual(drc!.supportsSystemMessage, false); + assert.strictEqual(drc!.specialToolFormat, 'disabled'); + assert.strictEqual(drc!.headers['Authorization'], 'Bearer test-key'); + } finally { + try { svc.dispose?.(); } catch { } + } + }); +}); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts index ad46b6e8b89..28e001a0527 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkCellEdits.ts @@ -9,7 +9,7 @@ import { compare } from '../../../../base/common/strings.js'; import { isObject } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { ResourceEdit } from '../../../../editor/browser/services/bulkEditService.js'; -import { WorkspaceEditMetadata } from '../../../../editor/common/languages.js'; +import { WorkspaceEditMetadata } from '../../../../editor/common/language/languages.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; import { UndoRedoGroup, UndoRedoSource } from '../../../../platform/undoRedo/common/undoRedo.js'; import { getNotebookEditorFromEditorPane } from '../../notebook/browser/notebookBrowser.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index b20903fbab3..03a8c603f4e 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -11,7 +11,7 @@ import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; import { IBulkEditOptions, IBulkEditPreviewHandler, IBulkEditResult, IBulkEditService, ResourceEdit, ResourceFileEdit, ResourceTextEdit } from '../../../../editor/browser/services/bulkEditService.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { WorkspaceEdit } from '../../../../editor/common/languages.js'; +import { WorkspaceEdit } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { Extensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts index ce82d4983b7..db9b0da19f6 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkFileEdits.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ -import { WorkspaceFileEditOptions } from '../../../../editor/common/languages.js'; +import { WorkspaceFileEditOptions } from '../../../../editor/common/language/languages.js'; import { IFileService, FileSystemProviderCapabilities, IFileContent, IFileStatWithMetadata } from '../../../../platform/files/common/files.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index 4e4ec817f8b..50cdd3c85ce 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -6,17 +6,17 @@ import { dispose, IDisposable, IReference } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; -import { EndOfLineSequence, ITextModel } from '../../../../editor/common/model.js'; -import { ITextModelService, IResolvedTextEditorModel } from '../../../../editor/common/services/resolverService.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { EndOfLineSequence, ITextModel } from '../../../../editor/common/language/model.js'; +import { ITextModelService, IResolvedTextEditorModel } from '../../../../editor/common/language/services/resolverService.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { IUndoRedoService, UndoRedoGroup, UndoRedoSource } from '../../../../platform/undoRedo/common/undoRedo.js'; -import { SingleModelEditStackElement, MultiModelEditStackElement } from '../../../../editor/common/model/editStack.js'; +import { SingleModelEditStackElement, MultiModelEditStackElement } from '../../../../editor/common/language/model/editStack.js'; import { ResourceMap } from '../../../../base/common/map.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ResourceTextEdit } from '../../../../editor/browser/services/bulkEditService.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/snippetController2.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts b/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts index 23371d20aff..26bc3c4bbc9 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/conflicts.ts @@ -5,11 +5,11 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { URI } from '../../../../base/common/uri.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from '../../../../editor/browser/services/bulkEditService.js'; import { ResourceNotebookCellEdit } from './bulkCellEdits.js'; import { ILogService } from '../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/opaqueEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/opaqueEdits.ts index a8615ee859e..3a3b4e862f1 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/opaqueEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/opaqueEdits.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { isObject } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { ResourceEdit } from '../../../../editor/browser/services/bulkEditService.js'; -import { ICustomEdit, WorkspaceEditMetadata } from '../../../../editor/common/languages.js'; +import { ICustomEdit, WorkspaceEditMetadata } from '../../../../editor/common/language/languages.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; import { IUndoRedoService, UndoRedoElementType, UndoRedoGroup, UndoRedoSource } from '../../../../platform/undoRedo/common/undoRedo.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 267b9be32f5..28c773aa359 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -15,8 +15,8 @@ import { URI } from '../../../../../base/common/uri.js'; import './bulkEdit.css'; import { ResourceEdit } from '../../../../../editor/browser/services/bulkEditService.js'; import { IMultiDiffEditorOptions, IMultiDiffResourceId } from '../../../../../editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index ad2d7ebb97e..92d6e9fa571 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/model/textModel.js'; -import { WorkspaceEditMetadata } from '../../../../../editor/common/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/language/model/textModel.js'; +import { WorkspaceEditMetadata } from '../../../../../editor/common/language/languages.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { coalesceInPlace } from '../../../../../base/common/arrays.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/core/editOperation.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/language/core/editOperation.js'; import { ServicesAccessor, IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts index 6c78277c3d6..62833bd9475 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { IAsyncDataSource, ITreeRenderer, ITreeNode, ITreeSorter } from '../../../../../base/browser/ui/tree/tree.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { FuzzyScore, createMatches } from '../../../../../base/common/filters.js'; import { IResourceLabel, ResourceLabels } from '../../../../browser/labels.js'; import { HighlightedLabel, IHighlight } from '../../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; import { IIdentityProvider, IListVirtualDelegate, IKeyboardNavigationLabelProvider } from '../../../../../base/browser/ui/list/list.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import * as dom from '../../../../../base/browser/dom.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { IDisposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; -import { TextModel } from '../../../../../editor/common/model/textModel.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { BulkFileOperations, BulkFileOperation, BulkFileOperationType, BulkTextEdit, BulkCategory } from './bulkEditPreview.js'; import { FileKind } from '../../../../../platform/files/common/files.js'; import { localize } from '../../../../../nls.js'; diff --git a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts index d4aaa7577f3..4d0a61a42c1 100644 --- a/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts +++ b/src/vs/workbench/contrib/bulkEdit/test/browser/bulkEditPreview.test.ts @@ -10,10 +10,10 @@ import { mock } from '../../../../test/common/workbenchTestServices.js'; import { InstantiationService } from '../../../../../platform/instantiation/common/instantiationService.js'; import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { URI } from '../../../../../base/common/uri.js'; import { BulkFileOperations } from '../../browser/preview/bulkEditPreview.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ResourceFileEdit, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts index fb48c5cd1bb..cf40e853b24 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution.ts @@ -20,8 +20,8 @@ import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.j import { PeekContext } from '../../../../editor/contrib/peekView/browser/peekView.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; import { MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 75da6466a36..9cd172c1872 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -14,18 +14,18 @@ import * as callHTree from './callHierarchyTree.js'; import { IAsyncDataTreeViewState } from '../../../../base/browser/ui/tree/asyncDataTree.js'; import { localize } from '../../../../nls.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { SplitView, Orientation, Sizing } from '../../../../base/browser/ui/splitview/splitview.js'; import { Dimension, isKeyboardEvent } from '../../../../base/browser/dom.js'; import { Event } from '../../../../base/common/event.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { toDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from '../../../../editor/common/model.js'; +import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions, OverviewRulerLane } from '../../../../editor/common/language/model.js'; import { themeColorFromId, IThemeService, IColorTheme } from '../../../../platform/theme/common/themeService.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { Color } from '../../../../base/common/color.js'; import { TreeMouseEventTarget, ITreeNode } from '../../../../base/browser/ui/tree/tree.js'; diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index 160d73e72b8..2c6a899cea2 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -9,9 +9,9 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { FuzzyScore, createMatches } from '../../../../base/common/filters.js'; import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js'; -import { SymbolKinds, Location, SymbolTag } from '../../../../editor/common/languages.js'; +import { SymbolKinds, Location, SymbolTag } from '../../../../editor/common/language/languages.js'; import { compare } from '../../../../base/common/strings.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; import { localize } from '../../../../nls.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; diff --git a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts index 795a96fd018..244c636f0f8 100644 --- a/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/common/callHierarchy.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange } from '../../../../editor/common/core/range.js'; -import { SymbolKind, ProviderResult, SymbolTag } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { SymbolKind, ProviderResult, SymbolTag } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { LanguageFeatureRegistry } from '../../../../editor/common/languageFeatureRegistry.js'; import { URI } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { isNonEmptyArray } from '../../../../base/common/arrays.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { IDisposable, RefCountedDisposable } from '../../../../base/common/lifecycle.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { assertType } from '../../../../base/common/types.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; export const enum CallHierarchyDirection { CallsTo = 'incomingCalls', diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 4fb4ed8e71d..34fd919aac0 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -16,7 +16,7 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { EditorAction2 } from '../../../../../editor/browser/editorExtensions.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { SuggestController } from '../../../../../editor/contrib/suggest/browser/suggestController.js'; import { localize, localize2 } from '../../../../../nls.js'; import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index a627d291bac..dd96f6a30ec 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -15,9 +15,9 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { WithUriValue } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { IRange, Range } from '../../../../../editor/common/core/range.js'; -import { Command } from '../../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; +import { Command } from '../../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { AbstractGotoSymbolQuickAccessProvider, IGotoSymbolQuickPickItem } from '../../../../../editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.js'; import { localize, localize2 } from '../../../../../nls.js'; import { Action2, IAction2Options, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index f5f9343bed8..c256203af65 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -5,7 +5,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { localize, localize2 } from '../../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 71bfc203f7d..17c601fbbf4 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -13,7 +13,7 @@ import { basename } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { IBulkEditService } from '../../../../../editor/browser/services/bulkEditService.js'; -import { isLocation } from '../../../../../editor/common/languages.js'; +import { isLocation } from '../../../../../editor/common/language/languages.js'; import { localize, localize2 } from '../../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts index f6d1050cbd5..08582acb3b7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/codeBlockOperations.ts @@ -13,10 +13,10 @@ import { URI } from '../../../../../base/common/uri.js'; import { getCodeEditor, IActiveCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { localize } from '../../../../../nls.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts index 73ab2960823..a32f134e904 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/implicitContextAttachment.ts @@ -11,8 +11,8 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { basename, dirname } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { localize } from '../../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts index ac4fcefcd20..634994fcd51 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts @@ -15,10 +15,10 @@ import { Button } from '../../../../../../base/browser/ui/button/button.js'; import { basename, dirname } from '../../../../../../base/common/resources.js'; import { ILabelService } from '../../../../../../platform/label/common/label.js'; import { StandardMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../../../editor/common/language/services/model.js'; import { IHoverService } from '../../../../../../platform/hover/browser/hover.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { FileKind, IFileService } from '../../../../../../platform/files/common/files.js'; import { IMenuService, MenuId } from '../../../../../../platform/actions/common/actions.js'; import { getCleanPromptName } from '../../../../../../platform/prompts/common/constants.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 45a3078e6b7..748ba1328d6 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -7,7 +7,7 @@ import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts index a61ab242d16..cc47b56d75a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts @@ -6,7 +6,7 @@ import { URI } from '../../../../base/common/uri.js'; import { Emitter } from '../../../../base/common/event.js'; import { basename } from '../../../../base/common/resources.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { IChatRequestVariableEntry } from '../common/chatModel.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts index 1873df4b042..51796162cdb 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts @@ -12,7 +12,7 @@ import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate. import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { URI } from '../../../../base/common/uri.js'; import { localize } from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts index aaf5e335bcd..ba4be290916 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts @@ -13,14 +13,14 @@ import { Disposable, DisposableStore, IDisposable } from '../../../../../base/co import { basename, dirname } from '../../../../../base/common/path.js'; import { URI } from '../../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { IRange, Range } from '../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; import { LanguageFeatureRegistry } from '../../../../../editor/common/languageFeatureRegistry.js'; -import { Location, SymbolKind } from '../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { Location, SymbolKind } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts index b78f30375ed..ca42e79589f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatCollapsibleContentPart.ts @@ -15,7 +15,7 @@ import { IChatContentPart, IChatContentPartRenderContext } from './chatContentPa import { $ } from './chatReferencesContentPart.js'; import { EditorPool } from './chatMarkdownContentPart.js'; import { CodeBlockPart, ICodeBlockData, ICodeBlockRenderOptions } from '../codeBlockPart.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { IDisposableReference } from './chatCollections.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { autorun, IObservable, observableValue } from '../../../../../base/common/observable.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts index b939a1ad578..f6a9d91eaa5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts @@ -15,12 +15,12 @@ import { equalsIgnoreCase } from '../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { getIconClasses } from '../../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { getIconClasses } from '../../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts index f9f5da9713d..7548af3bb48 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts @@ -12,12 +12,12 @@ import { isEqual } from '../../../../../base/common/resources.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; -import { ISingleEditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/model/textModel.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { DefaultModelSHA1Computer } from '../../../../../editor/common/services/modelService.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ISingleEditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/language/model/textModel.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { DefaultModelSHA1Computer } from '../../../../../editor/common/language/services/modelService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { InstantiationType, registerSingleton } from '../../../../../platform/instantiation/common/extensions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts index 5c3ef465a0b..c0dbe424bcd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatToolInvocationPart.ts @@ -11,9 +11,9 @@ import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../.. import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { Location } from '../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { Location } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { localize } from '../../../../../nls.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts index ba80693ce0c..7c1fd40159c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts @@ -14,9 +14,9 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../base/common/mime.js'; import { basename } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { SymbolKinds } from '../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { SymbolKinds } from '../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { CodeDataTransfers, containsDragType, DocumentSymbolTransferData, extractEditorsDropData, extractMarkerDropData, extractSymbolDropData, IDraggedResourceEditorInput, MarkerTransferData } from '../../../../platform/dnd/browser/dnd.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEdinputInputContentProvider.ts b/src/vs/workbench/contrib/chat/browser/chatEdinputInputContentProvider.ts index cbc084b3b82..c3fe134785b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEdinputInputContentProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEdinputInputContentProvider.ts @@ -5,10 +5,10 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ChatInputPart } from './chatInputPart.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index fa505f7d3cb..d446417f5c5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -10,12 +10,12 @@ import { basename } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { isCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; -import { isLocation, Location } from '../../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { isLocation, Location } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize, localize2 } from '../../../../../nls.js'; import { Action2, IAction2Options, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts index 3b7237f1769..772c7162150 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts @@ -17,14 +17,14 @@ import { AccessibleDiffViewer, IAccessibleDiffViewerModel } from '../../../../.. import { RenderOptions, LineSource, renderLines } from '../../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { diffAddDecoration, diffWholeLineAddDecoration, diffDeleteDecoration } from '../../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; import { EditorOption, IEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { IDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; -import { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../editor/common/model/textModel.js'; +import { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../editor/common/language/model/textModel.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../editor/common/viewModel.js'; import { localize } from '../../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts index 48a0038e739..d8e58ef7de1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts @@ -12,21 +12,21 @@ import { themeColorFromId } from '../../../../../base/common/themables.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { getCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { ISingleEditOperation, EditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { ISingleEditOperation, EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { OffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IDocumentDiff, nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { OverviewRulerLane, MinimapPosition, ITextModel, IModelDeltaDecoration } from '../../../../../editor/common/model.js'; -import { SingleModelEditStackElement } from '../../../../../editor/common/model/editStack.js'; -import { ModelDecorationOptions, createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/model/textModel.js'; -import { OffsetEdits } from '../../../../../editor/common/model/textModelOffsetEdit.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { OverviewRulerLane, MinimapPosition, ITextModel, IModelDeltaDecoration } from '../../../../../editor/common/language/model.js'; +import { SingleModelEditStackElement } from '../../../../../editor/common/language/model/editStack.js'; +import { ModelDecorationOptions, createTextBufferFactoryFromSnapshot } from '../../../../../editor/common/language/model/textModel.js'; +import { OffsetEdits } from '../../../../../editor/common/language/model/textModelOffsetEdit.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { localize } from '../../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index eb68edac8ff..3bc476440ff 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -9,8 +9,8 @@ import { Schemas } from '../../../../../base/common/network.js'; import { clamp } from '../../../../../base/common/numbers.js'; import { autorun, derived, IObservable, ITransaction, observableValue } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; +import { OffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts index d687d403583..9e5cd71debd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -14,15 +14,15 @@ import { isEqual } from '../../../../../base/common/resources.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; -import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; +import { OffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts index 498740b82a4..98400d1ff07 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts @@ -18,8 +18,8 @@ import { compare } from '../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index 98941d4cd1d..957919b2e10 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -16,13 +16,13 @@ import { asyncTransaction, autorun, derived, derivedOpts, derivedWithStore, IObs import { isEqual, joinPath } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { IBulkEditService } from '../../../../../editor/browser/services/bulkEditService.js'; -import { IOffsetEdit, ISingleOffsetEdit, OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IOffsetEdit, ISingleOffsetEdit, OffsetEdit } from '../../../../../editor/common/language/core/offsetEdit.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { EditorActivation } from '../../../../../platform/editor/common/editor.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingTextModelContentProviders.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingTextModelContentProviders.ts index 20b3d767623..c29650f8eef 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingTextModelContentProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingTextModelContentProviders.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelContentProvider } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider } from '../../../../../editor/common/language/services/resolverService.js'; import { chatEditingSnapshotScheme, IChatEditingService } from '../../common/chatEditingService.js'; import { ChatEditingSession } from './chatEditingSession.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts index b516adce76e..ff00fff099f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from '../../../../../../base/common/buffer.js'; -import { TextEdit } from '../../../../../../editor/common/languages.js'; +import { TextEdit } from '../../../../../../editor/common/language/languages.js'; import { NotebookTextModel } from '../../../../notebook/common/model/notebookTextModel.js'; import { CellEditType, ICellEditOperation } from '../../../../notebook/common/notebookCommon.js'; import { INotebookService } from '../../../../notebook/common/notebookService.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts index 9418dc7c6bd..c01f970ce27 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts @@ -9,17 +9,17 @@ import { ITransaction, IObservable, observableValue, autorun, transaction } from import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js'; import { themeColorFromId } from '../../../../../../base/common/themables.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { EditOperation, ISingleEditOperation } from '../../../../../../editor/common/core/editOperation.js'; -import { OffsetEdit } from '../../../../../../editor/common/core/offsetEdit.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { EditOperation, ISingleEditOperation } from '../../../../../../editor/common/language/core/editOperation.js'; +import { OffsetEdit } from '../../../../../../editor/common/language/core/offsetEdit.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { IDocumentDiff, nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; -import { TextEdit } from '../../../../../../editor/common/languages.js'; -import { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; -import { OffsetEdits } from '../../../../../../editor/common/model/textModelOffsetEdit.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; +import { TextEdit } from '../../../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../../editor/common/language/model/textModel.js'; +import { OffsetEdits } from '../../../../../../editor/common/language/model/textModelOffsetEdit.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; +import { IModelContentChangedEvent } from '../../../../../../editor/common/language/textModelEvents.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { observableConfigValue } from '../../../../../../platform/observable/common/platformObservableUtils.js'; import { editorSelectionBackground } from '../../../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts index ced1613cf14..dd46dec9a3b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts @@ -8,8 +8,8 @@ import { autorun, IObservable, ISettableObservable, observableFromEvent, observa import { debouncedObservable } from '../../../../../../base/common/observableInternal/utils.js'; import { basename } from '../../../../../../base/common/resources.js'; import { assertType } from '../../../../../../base/common/types.js'; -import { LineRange } from '../../../../../../editor/common/core/lineRange.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { LineRange } from '../../../../../../editor/common/language/core/lineRange.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { localize } from '../../../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts index 376d0ed8d5f..20b3905482b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts @@ -6,7 +6,7 @@ import { ISettableObservable, ObservablePromise } from '../../../../../../base/common/observable.js'; import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; /** diff --git a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts index 3752bff4c6b..5849f5768d5 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts @@ -10,12 +10,12 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { Location, SymbolKinds } from '../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { Location, SymbolKinds } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { DefinitionAction } from '../../../../editor/contrib/gotoSymbol/browser/goToCommands.js'; import * as nls from '../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index 43b06df7a22..fac26435613 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -28,12 +28,12 @@ import { IEditorConstructionOptions } from '../../../../editor/browser/config/ed import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js'; import { DropIntoEditorController } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js'; import { ContentHoverController } from '../../../../editor/contrib/hover/browser/contentHoverController.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts index 6e78f992eeb..9a87ede56e6 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts @@ -9,7 +9,7 @@ import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { IMarkdownRendererOptions, IMarkdownRenderResult, MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts b/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts index 72911840c5f..28f7b52cc43 100644 --- a/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts @@ -11,11 +11,11 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../base/common/mime.js'; import { basename, joinPath } from '../../../../base/common/resources.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteEditsSession } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteEditsSession } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { localize } from '../../../../nls.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { IFileService } from '../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 137fc14757b..df414b1027d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -9,7 +9,7 @@ import { disposableTimeout } from '../../../../base/common/async.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus.ts b/src/vs/workbench/contrib/chat/browser/chatStatus.ts index d4d6718a816..071af5ea675 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -25,7 +25,7 @@ import { Gesture, EventType as TouchEventType } from '../../../../base/browser/t import { IEditorService } from '../../../services/editor/common/editorService.js'; import product from '../../../../platform/product/common/product.js'; import { isObject } from '../../../../base/common/types.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; @@ -35,7 +35,7 @@ import { Link } from '../../../../platform/opener/browser/link.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IChatStatusItemService, ChatStatusEntry } from './chatStatusItemService.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; import { getCodeEditor } from '../../../../editor/browser/editorBrowser.js'; diff --git a/src/vs/workbench/contrib/chat/browser/chatVariables.ts b/src/vs/workbench/contrib/chat/browser/chatVariables.ts index f822997f5ae..b2760c6ecdd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/chatVariables.ts @@ -5,7 +5,7 @@ import { coalesce } from '../../../../base/common/arrays.js'; import { URI } from '../../../../base/common/uri.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IChatRequestVariableData, IChatRequestVariableEntry } from '../common/chatModel.js'; diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 399a2f9e36f..c9152b10ed0 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -23,14 +23,14 @@ import { ICodeEditorService } from '../../../../editor/browser/services/codeEdit import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { DiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; import { EDITOR_FONT_DEFAULTS, EditorOption, IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; -import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; -import { TextModelText } from '../../../../editor/common/model/textModelText.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { DefaultModelSHA1Computer } from '../../../../editor/common/services/modelService.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; +import { EndOfLinePreference, ITextModel } from '../../../../editor/common/language/model.js'; +import { TextModelText } from '../../../../editor/common/language/model/textModelText.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { DefaultModelSHA1Computer } from '../../../../editor/common/language/services/modelService.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { BracketMatchingController } from '../../../../editor/contrib/bracketMatching/browser/bracketMatching.js'; import { ColorDetector } from '../../../../editor/contrib/colorPicker/browser/colorDetector.js'; import { ContextMenuController } from '../../../../editor/contrib/contextmenu/browser/contextmenu.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts index 48ba9a62047..b1b2b993fb2 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts @@ -15,10 +15,10 @@ import { ResourceSet } from '../../../../../base/common/map.js'; import { basename, dirname, joinPath, relativePath } from '../../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; -import { IRange, Range } from '../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { IDecorationOptions } from '../../../../../editor/common/editorCommon.js'; -import { Command, isLocation } from '../../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { Command, isLocation } from '../../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts index cb6ce3b9d2d..383b5fc63e3 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables/chatFileReference.ts @@ -6,7 +6,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { assert } from '../../../../../../base/common/assert.js'; import { IDynamicVariable } from '../../../common/chatVariables.js'; -import { IRange } from '../../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../../editor/common/language/core/range.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; import { FilePromptParser } from '../../../common/promptSyntax/parsers/filePromptParser.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts index afcd62f2218..b03c2dc2786 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts @@ -12,7 +12,7 @@ import { basename } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; -import { Location } from '../../../../../editor/common/languages.js'; +import { Location } from '../../../../../editor/common/language/languages.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { EditorsOrder } from '../../../../common/editor.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 804fd5765d6..35a41e702c5 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -13,12 +13,12 @@ import { dirname } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { isCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IWordAtPosition, getWordAtText } from '../../../../../editor/common/core/wordHelper.js'; -import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, DocumentSymbol, Location, ProviderResult, SymbolKind, SymbolKinds } from '../../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IWordAtPosition, getWordAtText } from '../../../../../editor/common/language/core/wordHelper.js'; +import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, DocumentSymbol, Location, ProviderResult, SymbolKind, SymbolKinds } from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { IOutlineModelService } from '../../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; import { localize } from '../../../../../nls.js'; import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 06957dd285a..91a9a85ffe0 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -6,7 +6,7 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IDecorationOptions } from '../../../../../editor/common/editorCommon.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { inputPlaceholderForeground } from '../../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts index 51808206d1a..817a1309e94 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts @@ -5,8 +5,8 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IModelDecoration } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IModelDecoration } from '../../../../../editor/common/language/model.js'; import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from '../../../../../editor/contrib/hover/browser/hoverTypes.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 4c3465c0c8e..85be064cd9d 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -259,7 +259,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } model.acceptResponseProgress(request, toolInvocation); - if (prepared?.confirmationMessages) { + if (!dto.skipConfirmation && prepared?.confirmationMessages) { this._accessibilityService.alert(localize('toolConfirmationMessage', "Action required: {0}", prepared.confirmationMessages.title)); const userConfirmed = await toolInvocation.confirmed.p; if (!userConfirmed) { @@ -275,7 +275,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } } else { const prepared = await this.prepareToolInvocation(tool, dto, token); - if (prepared?.confirmationMessages) { + if (!dto.skipConfirmation && prepared?.confirmationMessages) { const result = await this._dialogService.confirm({ message: prepared.confirmationMessages.title, detail: renderStringAsPlaintext(prepared.confirmationMessages.message) }); if (!result.confirmed) { throw new CancellationError(); diff --git a/src/vs/workbench/contrib/chat/common/annotations.ts b/src/vs/workbench/contrib/chat/common/annotations.ts index fa770db98d8..13f33e38dbb 100644 --- a/src/vs/workbench/contrib/chat/common/annotations.ts +++ b/src/vs/workbench/contrib/chat/common/annotations.ts @@ -5,7 +5,7 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { basename } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IChatProgressRenderableResponseContent, IChatProgressResponseContent, appendMarkdownString, canMergeMarkdownStrings } from './chatModel.js'; import { IChatAgentVulnerabilityDetails, IChatMarkdownContent } from './chatService.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 6ef8775d594..edaa68482ff 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -15,7 +15,7 @@ import { IObservable, observableValue } from '../../../../base/common/observable import { equalsIgnoreCase } from '../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; -import { Command, ProviderResult } from '../../../../editor/common/languages.js'; +import { Command, ProviderResult } from '../../../../editor/common/language/languages.js'; import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts b/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts index e761fc19358..5836e872ff8 100644 --- a/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts +++ b/src/vs/workbench/contrib/chat/common/chatCodeMapperService.ts @@ -6,7 +6,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index 629bbd1cdc7..69d68fa6e20 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -8,7 +8,7 @@ import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable, IReader, ITransaction } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index c393e9a849b..09305811e49 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -17,9 +17,9 @@ import { basename, isEqual } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI, UriComponents, UriDto, isUriComponents } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IOffsetRange, OffsetRange } from '../../../../editor/common/core/offsetRange.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { Location, SymbolKind, TextEdit } from '../../../../editor/common/languages.js'; +import { IOffsetRange, OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { Location, SymbolKind, TextEdit } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { IMarker, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; @@ -1368,7 +1368,6 @@ export class ChatModel extends Disposable implements IChatModel { // Port entries from old format const result = 'responseErrorDetails' in raw ? - // eslint-disable-next-line local/code-no-dangerous-type-assertions { errorDetails: raw.responseErrorDetails } as IChatAgentResult : raw.result; request.response = new ChatResponseModel(raw.response ?? [new MarkdownString(raw.response)], this, agent, raw.slashCommand, request.id, true, raw.isCanceled, raw.vote, raw.voteDownReason, result, raw.followups, undefined, undefined, raw.responseId); request.response.shouldBeRemovedOnSend = raw.isHidden ? { requestId: raw.requestId } : raw.shouldBeRemovedOnSend; diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index 2542fa4ca67..802601bbf47 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -5,8 +5,8 @@ import { revive } from '../../../../base/common/marshalling.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; -import { IOffsetRange, OffsetRange } from '../../../../editor/common/core/offsetRange.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IOffsetRange, OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IChatAgentCommand, IChatAgentData, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; import { IChatRequestVariableEntry, IDiagnosticVariableEntryFilterData } from './chatModel.js'; import { IChatSlashData } from './chatSlashCommands.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index ecf72050af0..082b9efd0d2 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OffsetRange } from '../../../../editor/common/core/offsetRange.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { OffsetRange } from '../../../../editor/common/language/core/offsetRange.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IChatAgentData, IChatAgentService } from './chatAgents.js'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicVariablePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestToolPart, IParsedChatRequest, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from './chatParserTypes.js'; import { IChatSlashCommandService } from './chatSlashCommands.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index 4ba63bfd57e..4d549868312 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -9,9 +9,9 @@ import { Event } from '../../../../base/common/event.js'; import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { ISelection } from '../../../../editor/common/core/selection.js'; -import { Command, Location, TextEdit } from '../../../../editor/common/languages.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ISelection } from '../../../../editor/common/language/core/selection.js'; +import { Command, Location, TextEdit } from '../../../../editor/common/language/languages.js'; import { FileType } from '../../../../platform/files/common/files.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index f6b2c371941..0e1f7379757 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -15,7 +15,7 @@ import { Disposable, DisposableMap, IDisposable } from '../../../../base/common/ import { revive } from '../../../../base/common/marshalling.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; import { URI } from '../../../../base/common/uri.js'; -import { isLocation } from '../../../../editor/common/languages.js'; +import { isLocation } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index 809d5637c53..891e83c3123 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -6,8 +6,8 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IChatModel, IChatRequestVariableData, IChatRequestVariableEntry, IDiagnosticVariableEntryFilterData } from './chatModel.js'; import { IParsedChatRequest } from './chatParserTypes.js'; diff --git a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts index 7ce74db646a..aae392a983c 100644 --- a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts +++ b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts @@ -7,10 +7,10 @@ import { Iterable } from '../../../../base/common/iterator.js'; import { Disposable, IReference } from '../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../base/common/network.js'; import { URI } from '../../../../base/common/uri.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { EndOfLinePreference, ITextModel } from '../../../../editor/common/language/model.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { extractCodeblockUrisFromText, extractVulnerabilitiesFromText, IMarkdownVulnerability } from './annotations.js'; import { IChatRequestViewModel, IChatResponseViewModel, isResponseVM } from './chatViewModel.js'; diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 9453dc07614..86e64700ba9 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -13,7 +13,7 @@ import { URI } from '../../../../base/common/uri.js'; import { ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { IChatTerminalToolInvocationData, IChatToolInputInvocationData } from './chatService.js'; import { Schemas } from '../../../../base/common/network.js'; import { PromptElementJSON, stringifyPromptElementJSON } from './tools/promptTsxTypes.js'; @@ -73,6 +73,7 @@ export interface IToolInvocation { chatInteractionId?: string; toolSpecificData?: IChatTerminalToolInvocationData | IChatToolInputInvocationData; modelId?: string; + skipConfirmation?: boolean; } export interface IToolInvocationContext { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/parsers/promptVariableParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/parsers/promptVariableParser.ts index d7fddaf9a06..ffed605eb27 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/parsers/promptVariableParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/parsers/promptVariableParser.ts @@ -5,7 +5,7 @@ import { pick } from '../../../../../../../base/common/arrays.js'; import { assert } from '../../../../../../../base/common/assert.js'; -import { Range } from '../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { PromptVariable, PromptVariableWithData } from '../tokens/promptVariable.js'; import { Tab } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/tab.js'; import { Hash } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/hash.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts index fd04befbc57..93115d91445 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts @@ -6,7 +6,7 @@ import { PromptVariableWithData } from './promptVariable.js'; import { assert } from '../../../../../../../base/common/assert.js'; -import { IRange, Range } from '../../../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../../../editor/common/language/core/range.js'; import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts index 449926faa3e..91d2a2a8afa 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts @@ -5,7 +5,7 @@ import { PromptToken } from './promptToken.js'; import { assert } from '../../../../../../../base/common/assert.js'; -import { IRange, Range } from '../../../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../../../editor/common/language/core/range.js'; import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; import { INVALID_NAME_CHARACTERS, STOP_CHARACTERS } from '../parsers/promptVariableParser.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts index 7d5d89d8ca3..aa6bd355c7f 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/constants.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { LanguageFilter } from '../../../../../editor/common/languageSelector.js'; +import { LanguageFilter } from '../../../../../editor/common/language/languageSelector.js'; import { COPILOT_CUSTOM_INSTRUCTIONS_FILENAME, PROMPT_FILE_EXTENSION } from '../../../../../platform/prompts/common/constants.js'; /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts index 71907911652..d4944caf56f 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts @@ -6,15 +6,15 @@ import { IPromptContentsProvider } from './types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; import { FilePromptContentProvider } from './filePromptContentsProvider.js'; import { PromptContentsProviderBase } from './promptContentsProviderBase.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { newWriteableStream, ReadableStream } from '../../../../../../base/common/stream.js'; -import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; +import { IModelContentChangedEvent } from '../../../../../../editor/common/language/textModelEvents.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { TextModel } from '../../../../../../editor/common/model/textModel.js'; +import { TextModel } from '../../../../../../editor/common/language/model/textModel.js'; /** * Prompt contents provider for a {@link ITextModel} instance. diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts index 71a659c4d4f..a79f939566a 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts @@ -4,10 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { ResolveError } from '../../promptFileReferenceErrors.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js'; +/** + * Structural shape of a prompt resolve error. + */ +export interface IPromptResolveError extends Error { + readonly uri: URI; + readonly errorType: string; + sameTypeAs(other: unknown): other is IPromptResolveError; + equal(other: unknown): boolean; +} + /** * Interface for a prompt contents provider. Prompt contents providers are * responsible for providing contents of a prompt as a byte streams and @@ -27,10 +36,10 @@ export interface IPromptContentsProvider extends IDisposable { /** * Event that fires when the prompt contents change. The event is either a * {@linkcode VSBufferReadableStream} stream with changed contents or - * an instance of the {@linkcode ResolveError} error. + * an instance of a prompt resolve error. */ onContentChanged( - callback: (streamOrError: VSBufferReadableStream | ResolveError) => void, + callback: (streamOrError: VSBufferReadableStream | IPromptResolveError) => void, ): IDisposable; /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts index 7d46c72dbbe..6d1b48863b2 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts @@ -7,7 +7,7 @@ import { IPromptsService } from '../service/types.js'; import { IPromptFileReference } from '../parsers/types.js'; import { assert } from '../../../../../../base/common/assert.js'; import { NotPromptFile } from '../../promptFileReferenceErrors.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { assertDefined } from '../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { IEditor } from '../../../../../../editor/common/editorCommon.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts index 9b40572a7af..46a3e4b03f8 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts @@ -6,7 +6,7 @@ import { LANGUAGE_SELECTOR } from '../constants.js'; import { IPromptsService } from '../service/types.js'; import { assert } from '../../../../../../base/common/assert.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { assertDefined } from '../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; @@ -14,9 +14,9 @@ import { CancellationToken } from '../../../../../../base/common/cancellation.js import { Registry } from '../../../../../../platform/registry/common/platform.js'; import { FolderReference, NotPromptFile } from '../../promptFileReferenceErrors.js'; import { LifecyclePhase } from '../../../../../services/lifecycle/common/lifecycle.js'; -import { ILink, ILinksList, LinkProvider } from '../../../../../../editor/common/languages.js'; +import { ILink, ILinksList, LinkProvider } from '../../../../../../editor/common/language/languages.js'; import { IWorkbenchContributionsRegistry, Extensions } from '../../../../../common/contributions.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; /** * Provides link references for prompt files. diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts index 26f630845d5..90109de8357 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts @@ -19,10 +19,10 @@ import { IPromptsService } from '../service/types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { assertOneOf } from '../../../../../../base/common/types.js'; import { isWindows } from '../../../../../../base/common/platform.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; import { IPromptFileReference, IPromptReference } from '../parsers/types.js'; import { dirname, extUri } from '../../../../../../base/common/resources.js'; import { assert, assertNever } from '../../../../../../base/common/assert.js'; @@ -30,9 +30,9 @@ import { IFileService } from '../../../../../../platform/files/common/files.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { Registry } from '../../../../../../platform/registry/common/platform.js'; import { LifecyclePhase } from '../../../../../services/lifecycle/common/lifecycle.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../../../common/contributions.js'; -import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList } from '../../../../../../editor/common/languages.js'; +import { CompletionContext, CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList } from '../../../../../../editor/common/language/languages.js'; /** * Type for a filesystem completion item - the one that has its {@link CompletionItem.kind kind} set diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/types.d.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/types.d.ts index 85e7f645b75..42d93045635 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/types.d.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/types.d.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange } from '../../../../../../editor/common/core/range.js'; -import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; +import { IRange } from '../../../../../../editor/common/language/core/range.js'; +import { IModelDecorationOptions } from '../../../../../../editor/common/language/model.js'; /** * Decoration object. @@ -18,7 +18,7 @@ export interface ITextModelDecoration { /** * Associated decoration options. */ - options: ModelDecorationOptions; + options: IModelDecorationOptions; } /** diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index bd5dc68c831..64333a0fb09 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -9,7 +9,7 @@ import { ChatPromptCodec } from '../codecs/chatPromptCodec.js'; import { Emitter } from '../../../../../../base/common/event.js'; import { FileReference } from '../codecs/tokens/fileReference.js'; import { ChatPromptDecoder } from '../codecs/chatPromptDecoder.js'; -import { IRange } from '../../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../../editor/common/language/core/range.js'; import { assertDefined } from '../../../../../../base/common/types.js'; import { IPromptContentsProvider } from '../contentProviders/types.js'; import { IPromptReference, IResolveError, ITopError } from './types.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts index e0cf7edddac..daad54cefe4 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/textModelPromptParser.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { BasePromptParser } from './basePromptParser.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; import { TextModelContentsProvider } from '../contentProviders/textModelContentsProvider.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts index 096ada44af7..324e66cbf25 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts @@ -4,9 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { ResolveError } from '../../promptFileReferenceErrors.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; -import { IRange, Range } from '../../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../../editor/common/language/core/range.js'; + +/** + * Structural shape of a prompt resolve error. + */ +export interface IPromptResolveError extends Error { + readonly uri: URI; + readonly errorType: string; + sameTypeAs(other: unknown): other is IPromptResolveError; + equal(other: unknown): boolean; +} /** * A resolve error with a parent prompt URI, if any. @@ -15,7 +24,7 @@ export interface IResolveError { /** * Original error instance. */ - readonly originalError: ResolveError; + readonly originalError: IPromptResolveError; /** * URI of the parent that references this error. @@ -73,7 +82,7 @@ interface IPromptReferenceBase extends IDisposable { * including the {@link linkRange} and any additional * parts the reference may contain (e.g., the `#file:` prefix). */ - readonly range: Range; + readonly range: IRange; /** * Range of the link part that the reference points to. @@ -110,12 +119,12 @@ interface IPromptReferenceBase extends IDisposable { * * See also {@link resolveFailed}. */ - readonly errorCondition: ResolveError | undefined; + readonly errorCondition: IPromptResolveError | undefined; /** * Get list of errors for the direct links of the current reference. */ - readonly errors: readonly ResolveError[]; + readonly errors: readonly IPromptResolveError[]; /** * List of all errors that occurred while resolving the current diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 97297a4d720..25b4e612b65 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -7,7 +7,7 @@ import { IPromptPath, IPromptsService } from './types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { assert } from '../../../../../../base/common/assert.js'; import { PromptFilesLocator } from '../utils/promptFilesLocator.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { ObjectCache } from '../../../../../../base/common/objectCache.js'; import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts index 1f29a5811b5..93dcf1b45dd 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; import { createDecorator } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts index 42270fb0be7..3e75ae49fe0 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts @@ -10,7 +10,7 @@ import { ICellDiffInfo } from '../../browser/chatEditing/notebook/notebookCellCh import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; import { ObservablePromise, observableValue } from '../../../../../base/common/observable.js'; import { CellEditType, CellKind, ICell, ICellEditOperation, NotebookCellsChangeType } from '../../../notebook/common/notebookCommon.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { URI } from '../../../../../base/common/uri.js'; import { hash } from '../../../../../base/common/hash.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; diff --git a/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts index f7612029ae7..664c9e1db2e 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts @@ -23,14 +23,14 @@ import { IWorkbenchAssignmentService } from '../../../../services/assignment/com import { NullWorkbenchAssignmentService } from '../../../../services/assignment/test/common/nullAssignmentService.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { URI } from '../../../../../base/common/uri.js'; import { assertType } from '../../../../../base/common/types.js'; import { isEqual } from '../../../../../base/common/resources.js'; import { waitForState } from '../../../../../base/common/observable.js'; import { INotebookService } from '../../../notebook/common/notebookService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ChatAgentLocation } from '../../common/constants.js'; import { NotebookTextModel } from '../../../notebook/common/model/notebookTextModel.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index 768f7892ded..5aa444bb0a2 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -9,8 +9,8 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { URI } from '../../../../../base/common/uri.js'; import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { OffsetRange } from '../../../../../editor/common/core/offsetRange.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { OffsetRange } from '../../../../../editor/common/language/core/offsetRange.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index be6d91e9c26..94491a09bf7 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -10,7 +10,7 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { URI } from '../../../../../base/common/uri.js'; import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts index 57bc91080ed..a4a38ffaf06 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts @@ -34,7 +34,6 @@ export class MockChatService implements IChatService { this.sessions.set(session.sessionId, session); } getSession(sessionId: string): IChatModel | undefined { - // eslint-disable-next-line local/code-no-dangerous-type-assertions return this.sessions.get(sessionId) ?? {} as IChatModel; } async getOrRestoreSession(sessionId: string): Promise { diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts index b063215ec7d..499ea1d8365 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from '../../../../../../../base/common/buffer.js'; -import { Range } from '../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { newWriteableStream } from '../../../../../../../base/common/stream.js'; import { TestDecoder } from '../../../../../../../editor/test/common/utils/testDecoder.js'; import { ChatPromptCodec } from '../../../../common/promptSyntax/codecs/chatPromptCodec.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts index 6f009d57468..59b3b9514d8 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { VSBuffer } from '../../../../../../../base/common/buffer.js'; -import { Range } from '../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { newWriteableStream } from '../../../../../../../base/common/stream.js'; import { TestDecoder } from '../../../../../../../editor/test/common/utils/testDecoder.js'; import { FileReference } from '../../../../common/promptSyntax/codecs/tokens/fileReference.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts index 3478fd7d34d..61c20ff6d11 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { randomInt } from '../../../../../../../../base/common/numbers.js'; -import { Range } from '../../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../../editor/common/language/core/range.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; import { BaseToken } from '../../../../../../../../editor/common/codecs/baseToken.js'; import { PromptToken } from '../../../../../common/promptSyntax/codecs/tokens/promptToken.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/markdownLink.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/markdownLink.test.ts index bfb7e3672d7..466f0491c8b 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/markdownLink.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/markdownLink.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { randomInt } from '../../../../../../../../base/common/numbers.js'; -import { Range } from '../../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../../editor/common/language/core/range.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../../base/test/common/utils.js'; import { MarkdownLink } from '../../../../../../../../editor/common/codecs/markdownCodec/tokens/markdownLink.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts index 3f4767456c1..66ebe146b31 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts @@ -8,7 +8,7 @@ import { createURI } from '../testUtils/createUri.js'; import { URI } from '../../../../../../../base/common/uri.js'; import { Schemas } from '../../../../../../../base/common/network.js'; import { ExpectedReference } from '../testUtils/expectedReference.js'; -import { ITextModel } from '../../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../../editor/common/language/model.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { OpenFailed } from '../../../../common/promptFileReferenceErrors.js'; import { IFileService } from '../../../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts index 1677ae1921c..bcc3f7eed8b 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts @@ -8,7 +8,7 @@ import { URI } from '../../../../../../base/common/uri.js'; import { Schemas } from '../../../../../../base/common/network.js'; import { extUri } from '../../../../../../base/common/resources.js'; import { isWindows } from '../../../../../../base/common/platform.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { IMockFolder, MockFilesystem } from './testUtils/mockFilesystem.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts index bced4a5e45e..2631d222a3a 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/service/promptsService.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { createURI } from '../testUtils/createUri.js'; import { URI } from '../../../../../../../base/common/uri.js'; -import { Range } from '../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { assertDefined } from '../../../../../../../base/common/types.js'; import { waitRandom } from '../../../../../../../base/test/common/testUtils.js'; import { IPromptsService } from '../../../../common/promptSyntax/service/types.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts index 2c91a7f730d..876013a7a5a 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { URI } from '../../../../../../../base/common/uri.js'; -import { Range } from '../../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../../editor/common/language/core/range.js'; import { assertDefined } from '../../../../../../../base/common/types.js'; import { ResolveError } from '../../../../common/promptFileReferenceErrors.js'; import { IPromptReference } from '../../../../common/promptSyntax/parsers/types.js'; diff --git a/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts b/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts index 4e95ff4b118..c6267be4f81 100644 --- a/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts @@ -8,7 +8,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/ import { Emitter, Event } from '../../../../../base/common/event.js'; import { DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { ProviderResult } from '../../../../../editor/common/languages.js'; +import { ProviderResult } from '../../../../../editor/common/language/languages.js'; import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; import { nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; diff --git a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts index a7a7ce4c176..bd075a10b2c 100644 --- a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts +++ b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts @@ -8,7 +8,7 @@ import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { IJSONSchema, IJSONSchemaMap } from '../../../../base/common/jsonSchema.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { codeActionCommandId, refactorCommandId, sourceActionCommandId } from '../../../../editor/contrib/codeAction/browser/codeAction.js'; import { CodeActionKind } from '../../../../editor/contrib/codeAction/common/types.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts b/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts index 9a24f11cbb4..cfdc10dc465 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts @@ -20,10 +20,10 @@ import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; -import { EditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { assertIsDefined } from '../../../../../base/common/types.js'; import { ActionBar } from '../../../../../base/browser/ui/actionbar/actionbar.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts index a2637cab038..1cff1dca9a9 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/diffEditorHelper.ts @@ -9,7 +9,7 @@ import { IDiffEditor } from '../../../../editor/browser/editorBrowser.js'; import { registerDiffEditorContribution } from '../../../../editor/browser/editorExtensions.js'; import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js'; import { IDiffEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize } from '../../../../nls.js'; import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index 4bfaeb312fb..21dfcb45f09 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -12,13 +12,13 @@ import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../../editor/browser/editorBrowser.js'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution, EditorContributionInstantiation } from '../../../../../editor/browser/editorExtensions.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../../editor/common/editorCommon.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { SemanticTokensLegend, SemanticTokens, TreeSitterTokenizationRegistry } from '../../../../../editor/common/languages.js'; -import { FontStyle, ColorId, StandardTokenType, TokenMetadata } from '../../../../../editor/common/encodedTokenAttributes.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { SemanticTokensLegend, SemanticTokens, TreeSitterTokenizationRegistry } from '../../../../../editor/common/language/languages.js'; +import { FontStyle, ColorId, StandardTokenType, TokenMetadata } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; import { findMatchingThemeRule } from '../../../../services/textMate/common/TMHelper.js'; import { ITextMateTokenizationService } from '../../../../services/textMate/browser/textMateTokenizationFeature.js'; @@ -30,8 +30,8 @@ import { SemanticTokenRule, TokenStyleData, TokenStyle } from '../../../../../pl import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { SEMANTIC_HIGHLIGHTING_SETTING_ID, IEditorSemanticHighlightingOptions } from '../../../../../editor/contrib/semanticTokens/common/semanticTokensConfig.js'; import { Schemas } from '../../../../../base/common/network.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { ITextModelTreeSitter, ITreeSitterParserService } from '../../../../../editor/common/services/treeSitterParserService.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelTreeSitter, ITreeSitterParserService } from '../../../../../editor/common/language/services/treeSitterParserService.js'; import type * as Parser from '@vscode/tree-sitter-wasm'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts index a0f1261b5d2..acc86bc23ed 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsOutline.ts @@ -17,22 +17,22 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/ import { raceCancellation, TimeoutTimer, timeout, Barrier } from '../../../../../base/common/async.js'; import { onUnexpectedError } from '../../../../../base/common/errors.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { IPosition } from '../../../../../editor/common/core/position.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; import { ScrollType } from '../../../../../editor/common/editorCommon.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IEditorOptions, TextEditorSelectionRevealType } from '../../../../../platform/editor/common/editor.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; -import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { IDataSource } from '../../../../../base/browser/ui/tree/tree.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { localize } from '../../../../../nls.js'; -import { IMarkerDecorationsService } from '../../../../../editor/common/services/markerDecorations.js'; +import { IMarkerDecorationsService } from '../../../../../editor/common/language/services/markerDecorations.js'; import { MarkerSeverity } from '../../../../../platform/markers/common/markers.js'; import { isEqual } from '../../../../../base/common/resources.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; type DocumentSymbolItem = OutlineGroup | OutlineElement; diff --git a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts index 192a7529c1d..459b9ece5a3 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/outline/documentSymbolsTree.ts @@ -15,9 +15,9 @@ import { mainWindow } from '../../../../../base/browser/window.js'; import { createMatches, FuzzyScore } from '../../../../../base/common/filters.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { DocumentSymbol, getAriaLabelForSymbol, SymbolKind, symbolKindNames, SymbolKinds, SymbolTag } from '../../../../../editor/common/languages.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { DocumentSymbol, getAriaLabelForSymbol, SymbolKind, symbolKindNames, SymbolKinds, SymbolTag } from '../../../../../editor/common/language/languages.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { OutlineElement, OutlineGroup, OutlineModel } from '../../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; import '../../../../../editor/contrib/symbolIcons/browser/symbolIcons.js'; // The codicon symbol colors are defined here and must be loaded to get colors import { localize } from '../../../../../nls.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts index 972fd363b90..b36fd61c24e 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts @@ -6,7 +6,7 @@ import { localize, localize2 } from '../../../../../nls.js'; import { IKeyMods, IQuickInputService } from '../../../../../platform/quickinput/common/quickInput.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { AbstractGotoLineQuickAccessProvider } from '../../../../../editor/contrib/quickAccess/browser/gotoLineQuickAccess.js'; import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IQuickAccessRegistry, Extensions as QuickaccesExtensions } from '../../../../../platform/quickinput/common/quickAccess.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts index 037908fa8db..534fb1e44f8 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts @@ -6,20 +6,20 @@ import { localize, localize2 } from '../../../../../nls.js'; import { IKeyMods, IQuickPickSeparator, IQuickInputService, IQuickPick, ItemActivation } from '../../../../../platform/quickinput/common/quickInput.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IQuickAccessRegistry, Extensions as QuickaccessExtensions } from '../../../../../platform/quickinput/common/quickAccess.js'; import { AbstractGotoSymbolQuickAccessProvider, IGotoSymbolQuickPickItem } from '../../../../../editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IWorkbenchEditorConfiguration } from '../../../../common/editor.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { DisposableStore, IDisposable, toDisposable, Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { timeout } from '../../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { registerAction2, Action2, MenuId } from '../../../../../platform/actions/common/actions.js'; import { KeyMod, KeyCode } from '../../../../../base/common/keyCodes.js'; import { prepareQuery } from '../../../../../base/common/fuzzyScorer.js'; -import { SymbolKind } from '../../../../../editor/common/languages.js'; +import { SymbolKind } from '../../../../../editor/common/language/languages.js'; import { fuzzyScore } from '../../../../../base/common/filters.js'; import { onUnexpectedError } from '../../../../../base/common/errors.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; @@ -30,7 +30,7 @@ import { isCompositeEditor } from '../../../../../editor/browser/editorBrowser.j import { ITextEditorOptions } from '../../../../../platform/editor/common/editor.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { IOutlineModelService } from '../../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { accessibilityHelpIsShown, accessibleViewIsShown } from '../../../accessibility/browser/accessibilityConfiguration.js'; import { matchesFuzzyIconAware, parseLabelWithIcons } from '../../../../../base/common/iconLabels.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index d7ccb452f4b..d221780b40c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -10,13 +10,13 @@ import * as strings from '../../../../base/common/strings.js'; import { IActiveCodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { trimTrailingWhitespace } from '../../../../editor/common/commands/trimTrailingWhitespaceCommand.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; -import { CodeActionProvider, CodeActionTriggerType } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { CodeActionProvider, CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { ApplyCodeActionReason, applyCodeAction, getCodeActions } from '../../../../editor/contrib/codeAction/browser/codeAction.js'; import { CodeActionKind, CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js'; import { FormattingMode, formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider } from '../../../../editor/contrib/format/browser/format.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 2bea83e98e1..07131d605af 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -18,14 +18,14 @@ import { IEditorConstructionOptions } from '../../../../../editor/browser/config import { EditorExtensionsRegistry } from '../../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget } from '../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; -import { EditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ensureValidWordDefinition, getWordAtText } from '../../../../../editor/common/core/wordHelper.js'; -import * as languages from '../../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ensureValidWordDefinition, getWordAtText } from '../../../../../editor/common/language/core/wordHelper.js'; +import * as languages from '../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { ContextMenuController } from '../../../../../editor/contrib/contextmenu/browser/contextmenu.js'; import { SnippetController2 } from '../../../../../editor/contrib/snippet/browser/snippetController2.js'; import { SuggestController } from '../../../../../editor/contrib/suggest/browser/suggestController.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts index 913b5548e82..9aca28af755 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleColumnSelection.ts @@ -11,8 +11,8 @@ import { ICodeEditorService } from '../../../../editor/browser/services/codeEdit import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { CoreNavigationCommands } from '../../../../editor/browser/coreCommands.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; export class ToggleColumnSelectionAction extends Action2 { diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 524d3457777..02ae6b330cf 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -16,7 +16,7 @@ import { findDiffEditorContainingCodeEditor } from '../../../../editor/browser/w import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { IDiffEditorContribution, IEditorContribution } from '../../../../editor/common/editorCommon.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts b/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts index b74d233147b..e5b9bf06f56 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/workbenchEditorWorkerService.ts @@ -7,9 +7,9 @@ import { WebWorkerDescriptor } from '../../../../base/browser/webWorkerFactory.j import { FileAccess } from '../../../../base/common/network.js'; import { EditorWorkerService } from '../../../../editor/browser/services/editorWorkerService.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ILogService } from '../../../../platform/log/common/log.js'; export class WorkbenchEditorWorkerService extends EditorWorkerService { @@ -20,7 +20,8 @@ export class WorkbenchEditorWorkerService extends EditorWorkerService { @ILanguageConfigurationService languageConfigurationService: ILanguageConfigurationService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { - const workerDescriptor = new WebWorkerDescriptor(FileAccess.asBrowserUri('vs/editor/common/services/editorWebWorkerMain.js'), 'TextEditorWorker'); + + const workerDescriptor = new WebWorkerDescriptor(FileAccess.asBrowserUri(`vs/editor/common/language/services/editorWebWorkerMain.js`), 'TextEditorWorker'); super(workerDescriptor, modelService, configurationService, logService, languageConfigurationService, languageFeaturesService); } } diff --git a/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts b/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts index 80aa77d2019..d9296513815 100644 --- a/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts +++ b/src/vs/workbench/contrib/codeEditor/common/languageConfigurationExtensionPoint.ts @@ -10,7 +10,7 @@ import * as types from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { CharacterPair, CommentRule, EnterAction, ExplicitLanguageConfiguration, FoldingMarkers, FoldingRules, IAutoClosingPair, IAutoClosingPairConditional, IndentAction, IndentationRule, OnEnterRule } from '../../../../editor/common/languages/languageConfiguration.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { Extensions, IJSONContributionRegistry } from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts index 0516a684364..9ea9e4408ec 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/selectionClipboard.ts @@ -11,9 +11,9 @@ import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { registerEditorContribution, EditorAction, ServicesAccessor, registerEditorAction, EditorContributionInstantiation } from '../../../../editor/browser/editorExtensions.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { ICursorSelectionChangedEvent } from '../../../../editor/common/cursorEvents.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, Handler } from '../../../../editor/common/editorCommon.js'; -import { EndOfLinePreference } from '../../../../editor/common/model.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { SelectionClipboardContributionID } from '../browser/selectionClipboard.js'; import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; diff --git a/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts b/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts index 645eabd6384..06e02356a19 100644 --- a/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts +++ b/src/vs/workbench/contrib/codeEditor/electron-sandbox/startDebugTextMate.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from '../../../../nls.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { ITextMateTokenizationService } from '../../../services/textMate/browser/textMateTokenizationFeature.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { Constants } from '../../../../base/common/uint.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-sandbox/environmentService.js'; diff --git a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts index 1186af19416..c6e7b45939e 100644 --- a/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/browser/saveParticipant.test.ts @@ -9,8 +9,8 @@ import { FinalNewLineParticipant, TrimFinalNewLinesParticipant, TrimWhitespacePa import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { workbenchInstantiationService, TestServiceAccessor } from '../../../../test/browser/workbenchTestServices.js'; import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { TextFileEditorModel } from '../../../../services/textfile/common/textFileEditorModel.js'; import { IResolvedTextFileEditorModel, snapshotToString } from '../../../../services/textfile/common/textfiles.js'; import { SaveReason } from '../../../../common/editor.js'; diff --git a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts index 59ec549cbf0..313c145cbd2 100644 --- a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts @@ -14,15 +14,15 @@ import { IRelaxedTextModelCreationOptions, createModelServices, instantiateTextM import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { ILanguageConfiguration, LanguageConfigurationFileHandler } from '../../common/languageConfigurationExtensionPoint.js'; import { parse } from '../../../../../base/common/json.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; -import { ISingleEditOperation } from '../../../../../editor/common/core/editOperation.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; +import { ISingleEditOperation } from '../../../../../editor/common/language/core/editOperation.js'; import { trimTrailingWhitespace } from '../../../../../editor/common/commands/trimTrailingWhitespaceCommand.js'; import { execSync } from 'child_process'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../../editor/common/languages.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRegistry } from '../../../../../editor/common/language/languages.js'; import { NullState } from '../../../../../editor/common/languages/nullTokenize.js'; -import { MetadataConsts, StandardTokenType } from '../../../../../editor/common/encodedTokenAttributes.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { MetadataConsts, StandardTokenType } from '../../../../../editor/common/language/encodedTokenAttributes.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { FileAccess } from '../../../../../base/common/network.js'; function getIRange(range: IRange): IRange { diff --git a/src/vs/workbench/contrib/comments/browser/commentColors.ts b/src/vs/workbench/contrib/comments/browser/commentColors.ts index 6151312d878..482c0d80163 100644 --- a/src/vs/workbench/contrib/comments/browser/commentColors.ts +++ b/src/vs/workbench/contrib/comments/browser/commentColors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from '../../../../base/common/color.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { peekViewTitleBackground } from '../../../../editor/contrib/peekView/browser/peekView.js'; import * as nls from '../../../../nls.js'; import { contrastBorder, disabledForeground, listFocusOutline, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts index e5cecf7c684..fdbac28b761 100644 --- a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts @@ -6,12 +6,12 @@ import * as nls from '../../../../nls.js'; import { Color } from '../../../../base/common/color.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition } from '../../../../editor/browser/editorBrowser.js'; -import { IModelDecorationOptions, OverviewRulerLane } from '../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; +import { IModelDecorationOptions, OverviewRulerLane } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { darken, editorBackground, editorForeground, listInactiveSelectionBackground, opaque, registerColor } from '../../../../platform/theme/common/colorRegistry.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { CommentThreadState } from '../../../../editor/common/languages.js'; +import { CommentThreadState } from '../../../../editor/common/language/languages.js'; import { Disposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { Emitter } from '../../../../base/common/event.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentMenus.ts b/src/vs/workbench/contrib/comments/browser/commentMenus.ts index 6cd59dc3007..4afa3a388de 100644 --- a/src/vs/workbench/contrib/comments/browser/commentMenus.ts +++ b/src/vs/workbench/contrib/comments/browser/commentMenus.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { Comment } from '../../../../editor/common/languages.js'; +import { Comment } from '../../../../editor/common/language/languages.js'; import { IMenu, IMenuActionOptions, IMenuCreateOptions, IMenuService, MenuId, MenuItemAction, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 77f5e4117a2..dff140f6f3e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -5,7 +5,7 @@ import * as nls from '../../../../nls.js'; import * as dom from '../../../../base/browser/dom.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { ActionsOrientation, ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { Action, IAction, Separator, ActionRunner } from '../../../../base/common/actions.js'; import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable, dispose } from '../../../../base/common/lifecycle.js'; @@ -34,7 +34,7 @@ import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { TimestampWidget } from './timestamp.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IMarkdownString } from '../../../../base/common/htmlContent.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { ICellRange } from '../../notebook/common/notebookRange.js'; import { CommentMenus } from './commentMenus.js'; import { Scrollable, ScrollbarVisibility } from '../../../../base/common/scrollable.js'; @@ -48,8 +48,8 @@ import { IAccessibilityService } from '../../../../platform/accessibility/common import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { MarshalledCommentThread } from '../../../common/comments.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; class CommentsActionRunner extends ActionRunner { protected override async runAction(action: IAction, context: any[]): Promise { diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 917b6a64239..753bddb91c6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -13,10 +13,10 @@ import { Schemas } from '../../../../base/common/network.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import * as languages from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; @@ -31,7 +31,7 @@ import { ICellRange } from '../../notebook/common/notebookRange.js'; import { LayoutableEditor, MIN_EDITOR_HEIGHT, SimpleCommentEditor, calculateEditorHeight } from './simpleCommentEditor.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; let INMEM_MODEL_ID = 0; export const COMMENTEDITOR_DECORATION_KEY = 'commenteditordecoration'; diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 550288cb0de..6d360b7037e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread, CommentingRangeResourceHint } from '../../../../editor/common/languages.js'; +import { CommentThreadChangedEvent, CommentInfo, Comment, CommentReaction, CommentingRanges, CommentThread, CommentOptions, PendingCommentThread, CommentingRangeResourceHint } from '../../../../editor/common/language/languages.js'; import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { Range, IRange } from '../../../../editor/common/core/range.js'; +import { Range, IRange } from '../../../../editor/common/language/core/range.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ICommentThreadChangedEvent } from '../common/commentModel.js'; import { CommentMenus } from './commentMenus.js'; @@ -21,7 +21,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { CommentContextKeys } from '../common/commentContextKeys.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { CommentsModel, ICommentsModel } from './commentsModel.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { Schemas } from '../../../../base/common/network.js'; export const ICommentService = createDecorator('commentService'); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadAdditionalActions.ts b/src/vs/workbench/contrib/comments/browser/commentThreadAdditionalActions.ts index 95ea3147997..15785d6c2df 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadAdditionalActions.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadAdditionalActions.ts @@ -9,8 +9,8 @@ import { IAction } from '../../../../base/common/actions.js'; import { IMenu, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../../base/common/marshallingIds.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import * as languages from '../../../../editor/common/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { CommentFormActions } from './commentFormActions.js'; import { CommentMenus } from './commentMenus.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts index 7daa727eddc..28df3f2637c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts @@ -6,7 +6,7 @@ import * as dom from '../../../../base/browser/dom.js'; import * as nls from '../../../../nls.js'; import { Disposable, DisposableMap, DisposableStore } from '../../../../base/common/lifecycle.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { Emitter } from '../../../../base/common/event.js'; import { ICommentService } from './commentService.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; @@ -17,9 +17,9 @@ import { URI } from '../../../../base/common/uri.js'; import { ICommentThreadWidget } from '../common/commentThreadWidget.js'; import { IMarkdownRendererOptions, MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ICellRange } from '../../notebook/common/notebookRange.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { LayoutableEditor } from './simpleCommentEditor.js'; export class CommentThreadBody extends Disposable { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts b/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts index 91a30f2fafb..c44d46bf5d1 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts @@ -9,8 +9,8 @@ import { Action, ActionRunner } from '../../../../base/common/actions.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Disposable, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import * as strings from '../../../../base/common/strings.js'; -import * as languages from '../../../../editor/common/languages.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import * as nls from '../../../../nls.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenu, MenuItemAction, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index 1b083f5b847..035848e7ae6 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -5,10 +5,10 @@ import { Disposable, dispose, IDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { CommentThread, CommentThreadCollapsibleState } from '../../../../editor/common/languages.js'; -import { IModelDecorationOptions, IModelDeltaDecoration } from '../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { CommentThread, CommentThreadCollapsibleState } from '../../../../editor/common/language/languages.js'; +import { IModelDecorationOptions, IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { ICommentInfo, ICommentService } from './commentService.js'; class CommentThreadRangeDecoration implements IModelDeltaDecoration { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index f1efc0001da..20a8a5df4d7 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -9,7 +9,7 @@ import * as domStylesheets from '../../../../base/browser/domStylesheets.js'; import { Emitter } from '../../../../base/common/event.js'; import { Disposable, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { IMarkdownRendererOptions } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -24,7 +24,7 @@ import { ICommentThreadWidget } from '../common/commentThreadWidget.js'; import { IColorTheme } from '../../../../platform/theme/common/themeService.js'; import { contrastBorder, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { PANEL_BORDER } from '../../../common/theme.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { commentThreadStateBackgroundColorVar, commentThreadStateColorVar } from './commentColors.js'; import { ICellRange } from '../../notebook/common/notebookRange.js'; import { FontInfo } from '../../../../editor/common/config/fontInfo.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 0039a4030ea..97e2c96f2be 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -8,9 +8,9 @@ import { Color } from '../../../../base/common/color.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor, IEditorMouseEvent, isCodeEditor, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import * as languages from '../../../../editor/common/languages.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts index 79dc09225cf..105410f8c8c 100644 --- a/src/vs/workbench/contrib/comments/browser/comments.contribution.ts +++ b/src/vs/workbench/contrib/comments/browser/comments.contribution.ts @@ -15,7 +15,7 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { Extensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from '../../../common/contributions.js'; import { IActivityService, NumberBadge } from '../../../services/activity/common/activity.js'; import { COMMENTS_VIEW_ID } from './commentsTreeViewer.js'; -import { CommentThreadState } from '../../../../editor/common/languages.js'; +import { CommentThreadState } from '../../../../editor/common/language/languages.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { CONTEXT_KEY_HAS_COMMENTS, CONTEXT_KEY_SOME_COMMENTS_EXPANDED, CommentsPanel } from './commentsView.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts b/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts index 3efc58ca953..3718dfaabde 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts @@ -21,8 +21,8 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { URI } from '../../../../base/common/uri.js'; -import { CommentThread, Comment } from '../../../../editor/common/languages.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { CommentThread, Comment } from '../../../../editor/common/language/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IAction } from '../../../../base/common/actions.js'; export class CommentsAccessibleView extends Disposable implements IAccessibleViewImplementation { diff --git a/src/vs/workbench/contrib/comments/browser/commentsController.ts b/src/vs/workbench/contrib/comments/browser/commentsController.ts index 2036275fe84..3ce45fbc93f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsController.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsController.ts @@ -12,11 +12,11 @@ import { DisposableStore, dispose, IDisposable } from '../../../../base/common/l import './media/review.css'; import { ICodeEditor, IEditorMouseEvent, isCodeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { EditorType, IDiffEditor, IEditor, IEditorContribution, IModelChangedEvent } from '../../../../editor/common/editorCommon.js'; -import { IModelDecorationOptions, IModelDeltaDecoration } from '../../../../editor/common/model.js'; -import { ModelDecorationOptions, TextModel } from '../../../../editor/common/model/textModel.js'; -import * as languages from '../../../../editor/common/languages.js'; +import { IModelDecorationOptions, IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions, TextModel } from '../../../../editor/common/language/model/textModel.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -34,7 +34,7 @@ import { COMMENTS_SECTION, ICommentsConfiguration } from '../common/commentsConf import { COMMENTEDITOR_DECORATION_KEY } from './commentReply.js'; import { Emitter } from '../../../../base/common/event.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { CommentThreadRangeDecorator } from './commentThreadRangeDecorator.js'; import { ICursorSelectionChangedEvent } from '../../../../editor/common/cursorEvents.js'; import { CommentsPanel } from './commentsView.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 9c44f25179a..17abcaa9c40 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -17,7 +17,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { CommentController, ID } from './commentsController.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { CommentContextKeys } from '../common/commentContextKeys.js'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsInputContentProvider.ts b/src/vs/workbench/contrib/comments/browser/commentsInputContentProvider.ts index 58dc41ddd29..91a2cb60092 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsInputContentProvider.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsInputContentProvider.ts @@ -9,10 +9,10 @@ import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IEditorContribution, ScrollType } from '../../../../editor/common/editorCommon.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ITextResourceEditorInput } from '../../../../platform/editor/common/editor.js'; import { applyTextEditorOptions } from '../../../common/editor/editorOptions.js'; import { SimpleCommentEditor } from './simpleCommentEditor.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsModel.ts b/src/vs/workbench/contrib/comments/browser/commentsModel.ts index a2191238f6e..bced992949a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsModel.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsModel.ts @@ -5,7 +5,7 @@ import { groupBy } from '../../../../base/common/arrays.js'; import { URI } from '../../../../base/common/uri.js'; -import { CommentThread } from '../../../../editor/common/languages.js'; +import { CommentThread } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { ResourceWithCommentThreads, ICommentThreadChangedEvent } from '../common/commentModel.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index f692f467181..d81bdaf1160 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -22,7 +22,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { commentViewThreadStateColorVar, getCommentThreadStateIconColor } from './commentColors.js'; -import { CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/languages.js'; +import { CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/language/languages.js'; import { Color } from '../../../../base/common/color.js'; import { IMatch } from '../../../../base/common/filters.js'; import { FilterOptions } from './commentsFilterOptions.js'; diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index e9dcf952046..33164647582 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -27,7 +27,7 @@ import { CommentsFilters, CommentsFiltersChangeEvent, CommentsSortOrder } from ' import { Memento, MementoObject } from '../../../common/memento.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { FilterOptions } from './commentsFilterOptions.js'; -import { CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/languages.js'; +import { CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/language/languages.js'; import { revealCommentThread } from './commentsController.js'; import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; import { CommentsModel, threadHasMeaningfulComments, type ICommentsModel } from './commentsModel.js'; @@ -38,7 +38,7 @@ import type { ITreeElement } from '../../../../base/browser/ui/tree/tree.js'; import { IPathService } from '../../../services/path/common/pathService.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; export const CONTEXT_KEY_HAS_COMMENTS = new RawContextKey('commentsView.hasComments', false); export const CONTEXT_KEY_SOME_COMMENTS_EXPANDED = new RawContextKey('commentsView.someCommentsExpanded', false); diff --git a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts index f11377c917a..ebba6aada95 100644 --- a/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts +++ b/src/vs/workbench/contrib/comments/browser/simpleCommentEditor.ts @@ -24,7 +24,7 @@ import { IAccessibilityService } from '../../../../platform/accessibility/common import { ICommentThreadWidget } from '../common/commentThreadWidget.js'; import { CommentContextKeys } from '../common/commentContextKeys.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { clamp } from '../../../../base/common/numbers.js'; diff --git a/src/vs/workbench/contrib/comments/common/commentModel.ts b/src/vs/workbench/contrib/comments/common/commentModel.ts index 87eb513d7cc..fc7562aa6f8 100644 --- a/src/vs/workbench/contrib/comments/common/commentModel.ts +++ b/src/vs/workbench/contrib/comments/common/commentModel.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { Comment, CommentThread, CommentThreadChangedEvent, CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/languages.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { Comment, CommentThread, CommentThreadChangedEvent, CommentThreadApplicability, CommentThreadState } from '../../../../editor/common/language/languages.js'; export interface ICommentThreadChangedEvent extends CommentThreadChangedEvent { uniqueOwner: string; diff --git a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts index 2b2216c8971..6925841de19 100644 --- a/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts +++ b/src/vs/workbench/contrib/comments/test/browser/commentsView.test.ts @@ -5,10 +5,10 @@ import assert from 'assert'; import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; -import { IRange, Range } from '../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; import { CommentsPanel } from '../../browser/commentsView.js'; import { CommentService, ICommentController, ICommentInfo, ICommentService, INotebookCommentInfo } from '../../browser/commentService.js'; -import { Comment, CommentInput, CommentReaction, CommentThread, CommentThreadCollapsibleState, CommentThreadState } from '../../../../../editor/common/languages.js'; +import { Comment, CommentInput, CommentReaction, CommentThread, CommentThreadCollapsibleState, CommentThreadState } from '../../../../../editor/common/language/languages.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IViewContainerModel, IViewDescriptor, IViewDescriptorService, ViewContainer, ViewContainerLocation } from '../../../../common/views.js'; diff --git a/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts index 38648ff3dfd..d2fa14658bd 100644 --- a/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customTextEditorModel.ts @@ -9,7 +9,7 @@ import { Disposable, IReference } from '../../../../base/common/lifecycle.js'; import { basename } from '../../../../base/common/path.js'; import { isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 929118f46c0..d0d69ae2b73 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -22,10 +22,10 @@ import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationOverviewRulerOptions, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationOverviewRulerOptions, IModelDecorationsChangeAccessor, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index efa3cd9fa68..b02683c07cb 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -18,16 +18,16 @@ import { EditorCommand, ServicesAccessor, registerEditorCommand } from '../../.. import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EditorOption, IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { IDecorationOptions } from '../../../../editor/common/editorCommon.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { CompletionContext, CompletionItemKind, CompletionList } from '../../../../editor/common/languages.js'; +import { CompletionContext, CompletionItemKind, CompletionList } from '../../../../editor/common/language/languages.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { CompletionOptions, provideSuggestionItems } from '../../../../editor/contrib/suggest/browser/suggest.js'; import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 0965dcaf636..727bc1f1563 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -26,7 +26,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { Constants } from '../../../../base/common/uint.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { localize, localize2 } from '../../../../nls.js'; import { getActionBarActions, getContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index cc2513d24a3..06080c80df0 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -8,9 +8,9 @@ import { Event } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { Constants } from '../../../../base/common/uint.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { GlyphMarginLane, IModelDecorationOptions, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { GlyphMarginLane, IModelDecorationOptions, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { localize } from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { registerColor } from '../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/debug/browser/callStackWidget.ts b/src/vs/workbench/contrib/debug/browser/callStackWidget.ts index 2948bbb715b..5ee74f3020d 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackWidget.ts @@ -22,12 +22,12 @@ import { EditorContributionCtor, EditorContributionInstantiation, IEditorContrib import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { IWordAtPosition } from '../../../../editor/common/core/wordHelper.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { Location } from '../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { Location } from '../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ClickLinkGesture, ClickLinkMouseEvent } from '../../../../editor/contrib/gotoSymbol/browser/link/clickLinkGesture.js'; import { localize, localize2 } from '../../../../nls.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts index 0a130630a40..ea36c436d66 100644 --- a/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugAdapterManager.ts @@ -11,8 +11,8 @@ import Severity from '../../../../base/common/severity.js'; import * as strings from '../../../../base/common/strings.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IEditorModel } from '../../../../editor/common/editorCommon.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { IMenuService, MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 92b79ee572b..0790ea95959 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -22,7 +22,7 @@ import { InputFocusedContext } from '../../../../platform/contextkey/common/cont import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { ActiveEditorContext, PanelFocusContext, ResourceContextKey } from '../../../common/contextkeys.js'; import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js'; -import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourcePropertiesService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index f6a5af3713a..bf08e063844 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,9 +9,9 @@ import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorAction, IActionOptions, registerEditorAction } from '../../../../editor/browser/editorExtensions.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { MessageController } from '../../../../editor/contrib/message/browser/messageController.js'; import * as nls from '../../../../nls.js'; import { ILocalizedString } from '../../../../platform/action/common/action.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 8f67f94b32e..a1db37737fe 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -26,17 +26,17 @@ import { URI } from '../../../../base/common/uri.js'; import { CoreEditingCommands } from '../../../../editor/browser/coreCommands.js'; import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { EditorOption, IEditorHoverOptions } from '../../../../editor/common/config/editorOptions.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { DEFAULT_WORD_REGEXP } from '../../../../editor/common/core/wordHelper.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { DEFAULT_WORD_REGEXP } from '../../../../editor/common/language/core/wordHelper.js'; import { IEditorDecorationsCollection, ScrollType } from '../../../../editor/common/editorCommon.js'; -import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; -import { InlineValue, InlineValueContext } from '../../../../editor/common/languages.js'; -import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops } from '../../../../editor/common/model.js'; -import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/services/languageFeatureDebounce.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; +import { InlineValue, InlineValueContext } from '../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops } from '../../../../editor/common/language/model.js'; +import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../../editor/common/language/services/languageFeatureDebounce.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ContentHoverController } from '../../../../editor/contrib/hover/browser/contentHoverController.js'; import { HoverStartMode, HoverStartSource } from '../../../../editor/contrib/hover/browser/hoverOperation.js'; import * as nls from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 2d2a3ea5f63..05719ce81dd 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -20,12 +20,12 @@ import { isMacintosh } from '../../../../base/common/platform.js'; import { ScrollbarVisibility } from '../../../../base/common/scrollable.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../editor/browser/editorBrowser.js'; import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import * as nls from '../../../../nls.js'; import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 9a359f4f9d8..f83a9e97949 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -17,7 +17,7 @@ import severity from '../../../../base/common/severity.js'; import { URI, URI as uri } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 3e63e489167..4033e2def66 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -17,7 +17,7 @@ import * as resources from '../../../../base/common/resources.js'; import Severity from '../../../../base/common/severity.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index c98e35153df..a675d55488c 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -17,10 +17,10 @@ import { URI } from '../../../../base/common/uri.js'; import { applyFontInfo } from '../../../../editor/browser/config/domFontInfo.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { BareFontInfo } from '../../../../editor/common/config/fontInfo.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { StringBuilder } from '../../../../editor/common/core/stringBuilder.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { StringBuilder } from '../../../../editor/common/language/core/stringBuilder.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 930812de0f9..40cb63e4112 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -28,15 +28,15 @@ import { EditorAction, registerEditorAction } from '../../../../editor/browser/e import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EDITOR_FONT_DEFAULTS, EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IDecorationOptions } from '../../../../editor/common/editorCommon.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { CompletionContext, CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemKinds, CompletionList } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { CompletionContext, CompletionItem, CompletionItemInsertTextRule, CompletionItemKind, CompletionItemKinds, CompletionList } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextResourcePropertiesService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js'; import { localize, localize2 } from '../../../../nls.js'; import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; diff --git a/src/vs/workbench/contrib/debug/browser/replAccessibleView.ts b/src/vs/workbench/contrib/debug/browser/replAccessibleView.ts index 638dd70b513..058a4da2073 100644 --- a/src/vs/workbench/contrib/debug/browser/replAccessibleView.ts +++ b/src/vs/workbench/contrib/debug/browser/replAccessibleView.ts @@ -13,7 +13,7 @@ import { IViewsService } from '../../../services/views/common/viewsService.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { Position } from '../../../../editor/common/core/position.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; export class ReplAccessibleView implements IAccessibleViewImplementation { priority = 70; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 6ce5e3e38a9..b4291eff53c 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -12,10 +12,10 @@ import { IJSONSchemaSnippet } from '../../../../base/common/jsonSchema.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import severity from '../../../../base/common/severity.js'; import { URI, UriComponents, URI as uri } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import * as editorCommon from '../../../../editor/common/editorCommon.js'; -import { ITextModel as EditorIModel } from '../../../../editor/common/model.js'; +import { ITextModel as EditorIModel } from '../../../../editor/common/language/model.js'; import * as nls from '../../../../nls.js'; import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index 5ad05e1cb7d..139c169ee17 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -5,17 +5,17 @@ import { URI as uri } from '../../../../base/common/uri.js'; import { localize } from '../../../../nls.js'; -import { getMimeTypes } from '../../../../editor/common/services/languagesAssociations.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; +import { getMimeTypes } from '../../../../editor/common/language/services/languagesAssociations.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { DEBUG_SCHEME, IDebugService, IDebugSession } from './debug.js'; import { Source } from './debugSource.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; import { ErrorNoTelemetry } from '../../../../base/common/errors.js'; diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 75794f9dfd4..f96f50b7c46 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -17,7 +17,7 @@ import * as resources from '../../../../base/common/resources.js'; import { isString, isUndefinedOrNull } from '../../../../base/common/types.js'; import { URI, URI as uri } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import * as nls from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; diff --git a/src/vs/workbench/contrib/debug/common/debugSource.ts b/src/vs/workbench/contrib/debug/common/debugSource.ts index 7791782bb5e..1c77d5f8a20 100644 --- a/src/vs/workbench/contrib/debug/common/debugSource.ts +++ b/src/vs/workbench/contrib/debug/common/debugSource.ts @@ -8,7 +8,7 @@ import { URI } from '../../../../base/common/uri.js'; import { normalize, isAbsolute } from '../../../../base/common/path.js'; import * as resources from '../../../../base/common/resources.js'; import { DEBUG_SCHEME } from './debug.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; import { Schemas } from '../../../../base/common/network.js'; import { isUri } from './debugUtils.js'; diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 3a3791f7622..c0bf187bea7 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -11,12 +11,12 @@ import { deepClone } from '../../../../base/common/objects.js'; import { Schemas } from '../../../../base/common/network.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { coalesce } from '../../../../base/common/arrays.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; const _formatPIIRegexp = /{([^}]+)}/g; diff --git a/src/vs/workbench/contrib/debug/common/debugger.ts b/src/vs/workbench/contrib/debug/common/debugger.ts index ac7c966538a..787c7e0cbad 100644 --- a/src/vs/workbench/contrib/debug/common/debugger.ts +++ b/src/vs/workbench/contrib/debug/common/debugger.ts @@ -11,7 +11,7 @@ import { IConfig, IDebuggerContribution, IDebugAdapter, IDebugger, IDebugSession import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js'; import * as ConfigurationResolverUtils from '../../../services/configurationResolver/common/configurationResolverUtils.js'; -import { ITextResourcePropertiesService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourcePropertiesService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { URI } from '../../../../base/common/uri.js'; import { Schemas } from '../../../../base/common/network.js'; import { isDebuggerMainContribution } from './debugUtils.js'; diff --git a/src/vs/workbench/contrib/debug/common/loadedScriptsPicker.ts b/src/vs/workbench/contrib/debug/common/loadedScriptsPicker.ts index 680ba4579ab..b411869d9cb 100644 --- a/src/vs/workbench/contrib/debug/common/loadedScriptsPicker.ts +++ b/src/vs/workbench/contrib/debug/common/loadedScriptsPicker.ts @@ -8,9 +8,9 @@ import { Source } from './debugSource.js'; import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; import { IDebugService, IDebugSession } from './debug.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { dirname } from '../../../../base/common/resources.js'; diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index 61e2ff90693..df67b908205 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -8,10 +8,10 @@ import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { dispose } from '../../../../../base/common/lifecycle.js'; import { URI as uri } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { OverviewRulerLane } from '../../../../../editor/common/model.js'; -import { LanguageService } from '../../../../../editor/common/services/languageService.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { OverviewRulerLane } from '../../../../../editor/common/language/model.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; import { createTextModel } from '../../../../../editor/test/common/testTextModel.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; diff --git a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts index 59658c31ab6..b85abb79a14 100644 --- a/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/callStack.test.ts @@ -9,7 +9,7 @@ import { ThemeIcon } from '../../../../../base/common/themables.js'; import { Constants } from '../../../../../base/common/uint.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { TestAccessibilityService } from '../../../../../platform/accessibility/test/common/testAccessibilityService.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index 8d3c433daac..4ca34360403 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -7,8 +7,8 @@ import { DeferredPromise } from '../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Event } from '../../../../../base/common/event.js'; import { URI as uri } from '../../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../../../editor/common/core/position.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { IPosition, Position } from '../../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { NullLogService } from '../../../../../platform/log/common/log.js'; import { IStorageService } from '../../../../../platform/storage/common/storage.js'; import { IWorkspaceFolder } from '../../../../../platform/workspace/common/workspace.js'; diff --git a/src/vs/workbench/contrib/dropOrPasteInto/browser/configurationSchema.ts b/src/vs/workbench/contrib/dropOrPasteInto/browser/configurationSchema.ts index 648270466ac..8c478c3049a 100644 --- a/src/vs/workbench/contrib/dropOrPasteInto/browser/configurationSchema.ts +++ b/src/vs/workbench/contrib/dropOrPasteInto/browser/configurationSchema.ts @@ -8,7 +8,7 @@ import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { pasteAsCommandId } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteContribution.js'; import { pasteAsPreferenceConfig } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js'; import { dropAsPreferenceConfig } from '../../../../editor/contrib/dropOrPasteInto/browser/dropIntoEditorController.js'; diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts b/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts index 4ec70193efc..9f436178a25 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessionsViews.ts @@ -44,7 +44,6 @@ export class EditSessionsDataViews extends Disposable { treeView.dataProvider = this.instantiationService.createInstance(EditSessionDataViewDataProvider); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); - // eslint-disable-next-line local/code-no-dangerous-type-assertions viewsRegistry.registerViews([{ id: viewId, name: EDIT_SESSIONS_TITLE, diff --git a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts index 9fd1cf7eae3..26fbc3f5f8d 100644 --- a/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts +++ b/src/vs/workbench/contrib/editSessions/test/browser/editSessions.test.ts @@ -33,7 +33,7 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { Event } from '../../../../../base/common/event.js'; import { IViewDescriptorService } from '../../../../common/views.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; import { IDialogService, IPrompt } from '../../../../../platform/dialogs/common/dialogs.js'; import { IEditorService, ISaveAllEditorsOptions } from '../../../../services/editor/common/editorService.js'; diff --git a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts index 356cacea1b9..e1050ecbaf5 100644 --- a/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts +++ b/src/vs/workbench/contrib/emmet/test/browser/emmetAction.test.ts @@ -7,7 +7,7 @@ import { IGrammarContributions, EmmetEditorAction } from '../../browser/emmetAct import { withTestCodeEditor } from '../../../../../editor/test/browser/testCodeEditor.js'; import assert from 'assert'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; class MockGrammarContributions implements IGrammarContributions { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index aaaf4865076..b6b796a443e 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -23,8 +23,8 @@ import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import './media/extensionEditor.css'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { TokenizationRegistry } from '../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { TokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { generateTokensCSSForColorMap } from '../../../../editor/common/languages/supports/tokenization.js'; import { localize } from '../../../../nls.js'; import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 572bb2605fe..6cdb7bb44fa 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -32,7 +32,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { buttonBackground, buttonForeground, buttonHoverBackground, registerColor, editorWarningForeground, editorInfoForeground, editorErrorForeground, buttonSeparator } from '../../../../platform/theme/common/colorRegistry.js'; import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js'; import { ITextEditorSelection } from '../../../../platform/editor/common/editor.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { MenuId, IMenuService, MenuItemAction, SubmenuItemAction } from '../../../../platform/actions/common/actions.js'; import { PICK_WORKSPACE_FOLDER_COMMAND_ID } from '../../../browser/actions/workspaceCommands.js'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts b/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts index 1eee95203bd..752406d2114 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsCompletionItemsProvider.ts @@ -7,13 +7,13 @@ import { localize } from '../../../../nls.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { getLocation, parse } from '../../../../base/common/json.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { CompletionContext, CompletionList, CompletionItemKind, CompletionItem } from '../../../../editor/common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CompletionContext, CompletionList, CompletionItemKind, CompletionItem } from '../../../../editor/common/language/languages.js'; import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; export class ExtensionsCompletionItemsProvider extends Disposable implements IWorkbenchContribution { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index bcf99d677c2..f4a799db724 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -43,7 +43,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, TargetPlatform, ExtensionIdentifier, IExtensionIdentifier, IExtensionDescription, isApplicationScopedExtension } from '../../../../platform/extensions/common/extensions.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { FileAccess } from '../../../../base/common/network.js'; import { IIgnoredExtensionsManagementService } from '../../../../platform/userDataSync/common/ignoredExtensions.js'; diff --git a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts index 6d98e545b6e..36910e0977b 100644 --- a/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/fileBasedRecommendations.ts @@ -12,13 +12,13 @@ import { StorageScope, IStorageService, StorageTarget } from '../../../../platfo import { IProductService } from '../../../../platform/product/common/productService.js'; import { IFileContentCondition, IFilePathCondition, IFileLanguageCondition, IFileOpenCondition } from '../../../../base/common/product.js'; import { IStringDictionary } from '../../../../base/common/collections.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { Schemas } from '../../../../base/common/network.js'; import { basename, extname } from '../../../../base/common/resources.js'; import { match } from '../../../../base/common/glob.js'; import { URI } from '../../../../base/common/uri.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from '../../../../platform/extensionRecommendations/common/extensionRecommendations.js'; import { distinct } from '../../../../base/common/arrays.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts index 65e957e8802..71291a40f8e 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/debugExtensionHostAction.ts @@ -105,7 +105,6 @@ export class DebugExtensionsContribution extends Disposable implements IWorkbenc location: ProgressLocation.Notification, title: nls.localize('debugExtensionHost.progress', "Attaching Debugger To Extension Host"), }, async p => { - // eslint-disable-next-line local/code-no-dangerous-type-assertions await this._debugService.startDebugging(undefined, { type: 'node', name: nls.localize('debugExtensionHost.launch.name', "Attach Extension Host"), diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts index a380a714200..234cfcab4f4 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionRecommendationsService.test.ts @@ -30,8 +30,8 @@ import { IEnvironmentService } from '../../../../../platform/environment/common/ import { ConfigurationKey, IExtensionsWorkbenchService } from '../../common/extensions.js'; import { TestExtensionEnablementService } from '../../../../services/extensionManagement/test/browser/extensionEnablementService.test.js'; import { IURLService } from '../../../../../platform/url/common/url.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; import { INotificationService, Severity, IPromptChoice, IPromptOptions } from '../../../../../platform/notification/common/notification.js'; import { NativeURLService } from '../../../../../platform/url/common/urlService.js'; diff --git a/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts b/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts index 68495c2158d..c3907a83325 100644 --- a/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts +++ b/src/vs/workbench/contrib/externalUriOpener/common/externalUriOpenerService.ts @@ -9,7 +9,7 @@ import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { LinkedList } from '../../../../base/common/linkedList.js'; import { isWeb } from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; -import * as languages from '../../../../editor/common/languages.js'; +import * as languages from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts index 81aee12e962..e552e341d1e 100644 --- a/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts +++ b/src/vs/workbench/contrib/externalUriOpener/test/common/externalUriOpenerService.test.ts @@ -8,7 +8,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { ExternalUriOpenerPriority } from '../../../../../editor/common/languages.js'; +import { ExternalUriOpenerPriority } from '../../../../../editor/common/language/languages.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts index d7157206c93..0b070f20a2d 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorInput.ts @@ -13,7 +13,7 @@ import { IFileService } from '../../../../../platform/files/common/files.js'; import { ITextFileService, TextFileEditorModelState, TextFileResolveReason, TextFileOperationError, TextFileOperationResult, ITextFileEditorModel, EncodingMode } from '../../../../services/textfile/common/textfiles.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IReference, dispose, DisposableStore } from '../../../../../base/common/lifecycle.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { FILE_EDITOR_INPUT_ID, TEXT_FILE_EDITOR_ID, BINARY_FILE_EDITOR_ID } from '../../common/files.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; @@ -21,9 +21,9 @@ import { IEditorService } from '../../../../services/editor/common/editorService import { isEqual } from '../../../../../base/common/resources.js'; import { Event } from '../../../../../base/common/event.js'; import { Schemas } from '../../../../../base/common/network.js'; -import { createTextBufferFactory } from '../../../../../editor/common/model/textModel.js'; +import { createTextBufferFactory } from '../../../../../editor/common/language/model/textModel.js'; import { IPathService } from '../../../../services/path/common/pathService.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { ICustomEditorLabelService } from '../../../../services/editor/common/customEditorLabelService.js'; diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts index bff940f4e0b..6498431c994 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileEditor.ts @@ -20,7 +20,7 @@ import { FileOperationError, FileOperationResult, FileChangesEvent, IFileService import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { IStorageService } from '../../../../../platform/storage/common/storage.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { ICodeEditorViewState, ScrollType } from '../../../../../editor/common/editorCommon.js'; diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index c8c1f5e3314..8d3427c433a 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -13,7 +13,7 @@ import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, ITextFileSav import { ServicesAccessor, IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IDisposable, dispose, Disposable } from '../../../../../base/common/lifecycle.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { DiffEditorInput } from '../../../../common/editor/diffEditorInput.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 50f786ea2f7..9eacf650495 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -16,14 +16,14 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; import { IQuickInputService, ItemActivation } from '../../../../platform/quickinput/common/quickInput.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_IN_GROUP_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from './fileConstants.js'; -import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ICommandService, CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { Schemas } from '../../../../base/common/network.js'; diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 267b609b778..158219fa63a 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -21,7 +21,7 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { KeyMod, KeyCode, KeyChord } from '../../../../base/common/keyCodes.js'; import { isWeb, isWindows } from '../../../../base/common/platform.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection, IExplorerService } from './files.js'; import { IWorkspaceEditingService } from '../../../services/workspaces/common/workspaceEditing.js'; import { resolveCommandsContext } from '../../../browser/parts/editor/editorCommandsContext.js'; diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 0eb091699a5..c1b3735a3d8 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -9,11 +9,11 @@ import { IWorkbenchEditorConfiguration, IEditorIdentifier, EditorResourceAccesso import { EditorInput } from '../../../common/editor/editorInput.js'; import { IFilesConfiguration as PlatformIFilesConfiguration, FileChangeType, IFileService } from '../../../../platform/files/common/files.js'; import { ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; -import { ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService, ILanguageSelection } from '../../../../editor/common/languages/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService, ILanguageSelection } from '../../../../editor/common/language/language.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { InputFocusedContextKey } from '../../../../platform/contextkey/common/contextkeys.js'; import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; diff --git a/src/vs/workbench/contrib/folding/browser/folding.contribution.ts b/src/vs/workbench/contrib/folding/browser/folding.contribution.ts index 338e0c1fb2a..8760e25a96e 100644 --- a/src/vs/workbench/contrib/folding/browser/folding.contribution.ts +++ b/src/vs/workbench/contrib/folding/browser/folding.contribution.ts @@ -12,8 +12,8 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '. import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { FoldingRangeProvider } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { FoldingRangeProvider } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js'; diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index 2eec08fd35a..37aecc4a0dd 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -6,14 +6,14 @@ import { getCodeEditor, ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorAction, registerEditorAction } from '../../../../editor/browser/editorExtensions.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider } from '../../../../editor/common/languages.js'; +import { DocumentFormattingEditProvider, DocumentRangeFormattingEditProvider } from '../../../../editor/common/language/languages.js'; import * as nls from '../../../../nls.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { formatDocumentRangesWithProvider, formatDocumentWithProvider, getRealAndSyntheticDocumentFormattersOrdered, FormattingConflicts, FormattingMode, FormattingKind } from '../../../../editor/contrib/format/browser/format.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js'; @@ -22,13 +22,13 @@ import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js' import { IExtensionService, toExtension } from '../../../services/extensions/common/extensions.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { INotificationService, NotificationPriority, Severity } from '../../../../platform/notification/common/notification.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { editorConfigurationBaseNode } from '../../../../editor/common/config/editorConfigurationSchema.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts index e12080faaf8..d2cf55175ea 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsNone.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsNone.ts @@ -14,7 +14,7 @@ import { ICommandService } from '../../../../platform/commands/common/commands.j import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { diff --git a/src/vs/workbench/contrib/format/browser/formatModified.ts b/src/vs/workbench/contrib/format/browser/formatModified.ts index d0fce02f1c4..5006b2be300 100644 --- a/src/vs/workbench/contrib/format/browser/formatModified.ts +++ b/src/vs/workbench/contrib/format/browser/formatModified.ts @@ -7,11 +7,11 @@ import { isNonEmptyArray } from '../../../../base/common/arrays.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorAction, registerEditorAction, ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { formatDocumentRangesWithSelectedProvider, FormattingMode } from '../../../../editor/contrib/format/browser/format.js'; import * as nls from '../../../../nls.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 6ce57174001..04b1fd97e2c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -21,14 +21,14 @@ import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrow import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { ISelection, Selection, SelectionDirection } from '../../../../editor/common/core/selection.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ISelection, Selection, SelectionDirection } from '../../../../editor/common/language/core/selection.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { TextEdit } from '../../../../editor/common/languages.js'; -import { IValidEditOperation } from '../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; -import { DefaultModelSHA1Computer } from '../../../../editor/common/services/modelService.js'; +import { TextEdit } from '../../../../editor/common/language/languages.js'; +import { IValidEditOperation } from '../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { DefaultModelSHA1Computer } from '../../../../editor/common/language/services/modelService.js'; import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; import { MessageController } from '../../../../editor/contrib/message/browser/messageController.js'; import { localize } from '../../../../nls.js'; @@ -744,7 +744,6 @@ export class InlineChatController1 implements IEditorContribution { this._log(`${this._session?.textModelN.uri.toString()} received ${newEdits.length} edits`); - // NEW changes lastLength = edits.length; progressiveEditsAvgDuration.update(progressiveEditsClock.elapsed()); progressiveEditsClock.reset(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index c17f655ba9f..19265078e4e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -11,15 +11,15 @@ import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from ' import { InlineChatController } from './inlineChatController.js'; import { ACTION_START, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_VISIBLE, InlineChatConfigKeys } from '../common/inlineChat.js'; import { EditorAction2, ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { AbstractInline1ChatAction } from './inlineChatActions.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { URI } from '../../../../base/common/uri.js'; import { isEqual } from '../../../../base/common/resources.js'; -import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; +import { StandardTokenType } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { autorun, derivedWithStore, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; @@ -28,7 +28,7 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; import { IChatAgentService } from '../../chat/common/chatAgents.js'; -import { IMarkerDecorationsService } from '../../../../editor/common/services/markerDecorations.js'; +import { IMarkerDecorationsService } from '../../../../editor/common/language/services/markerDecorations.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { toAction } from '../../../../base/common/actions.js'; import { IMouseEvent } from '../../../../base/browser/mouseEvent.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index c153bb89559..9282944de4f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -5,18 +5,18 @@ import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IIdentifiedSingleEditOperation, IModelDecorationOptions, IModelDeltaDecoration, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { CTX_INLINE_CHAT_HAS_STASHED_SESSION } from '../common/inlineChat.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; -import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { IInlineChatSessionService } from './inlineChatSessionService.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { coalesceInPlace } from '../../../../base/common/arrays.js'; import { Iterable } from '../../../../base/common/iterator.js'; -import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts index a6e06ad4b36..8870970588b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionService.ts @@ -7,9 +7,9 @@ import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { IActiveCodeEditor, ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { IValidEditOperation } from '../../../../editor/common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { IValidEditOperation } from '../../../../editor/common/language/model.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IChatEditingSession } from '../../chat/common/chatEditingService.js'; import { IChatModel } from '../../chat/common/chatModel.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 97b36199564..e2941993b79 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -13,13 +13,13 @@ import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { IActiveCodeEditor, ICodeEditor, isCodeEditor, isCompositeEditor, isDiffEditor } from '../../../../editor/browser/editorBrowser.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { IValidEditOperation } from '../../../../editor/common/model.js'; -import { createTextBufferFactoryFromSnapshot } from '../../../../editor/common/model/textModel.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { IValidEditOperation } from '../../../../editor/common/language/model.js'; +import { createTextBufferFactoryFromSnapshot } from '../../../../editor/common/language/model/textModel.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 1c19f39cd02..c93e4c91f10 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -11,14 +11,14 @@ import { themeColorFromId, ThemeIcon } from '../../../../base/common/themables.j import { ICodeEditor, IViewZone, IViewZoneChangeAccessor } from '../../../../editor/browser/editorBrowser.js'; import { StableEditorScrollState } from '../../../../editor/browser/stableEditorScroll.js'; import { LineSource, RenderOptions, renderLines } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; -import { ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, IValidEditOperation, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { IModelDecorationsChangeAccessor, IModelDeltaDecoration, IValidEditOperation, MinimapPosition, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { InlineDecoration, InlineDecorationType } from '../../../../editor/common/viewModel.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index d3d0b420edf..5f7ee4d95f3 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -14,14 +14,14 @@ import { constObservable, derived, IObservable, ISettableObservable, observableV import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { AccessibleDiffViewer, IAccessibleDiffViewerModel } from '../../../../editor/browser/widget/diffEditor/components/accessibleDiffViewer.js'; import { EditorOption, IComputedEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { LineRange } from '../../../../editor/common/core/lineRange.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { LineRange } from '../../../../editor/common/language/core/lineRange.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { DetailedLineRangeMapping, RangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { ICodeEditorViewState, ScrollType } from '../../../../editor/common/editorCommon.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IAccessibleViewService } from '../../../../platform/accessibility/browser/accessibleView.js'; import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 83836042939..5249392a596 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -11,8 +11,8 @@ import { assertType } from '../../../../base/common/types.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { StableEditorBottomScrollState } from '../../../../editor/browser/stableEditorScroll.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; import { IOptions, ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWidget.js'; import { localize } from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/inlineChat/browser/utils.ts b/src/vs/workbench/contrib/inlineChat/browser/utils.ts index 020cd9e0e58..09a0a6b3f1f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/utils.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/utils.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { IIdentifiedSingleEditOperation, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { IIdentifiedSingleEditOperation, ITextModel, IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { IEditObserver } from './inlineChatStrategies.js'; import { IProgress } from '../../../../platform/progress/common/progress.js'; import { IntervalTimer, AsyncIterableSource } from '../../../../base/common/async.js'; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 49aa40744ee..3c6f716f534 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -12,11 +12,11 @@ import { mock } from '../../../../../base/test/common/mock.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; import { IActiveCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { IDiffProviderFactoryService } from '../../../../../editor/browser/widget/diffEditor/diffProviderFactoryService.js'; -import { EditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { EndOfLineSequence, ITextModel } from '../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { EndOfLineSequence, ITextModel } from '../../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { TestDiffProviderFactoryService } from '../../../../../editor/test/browser/diff/testDiffProviderFactoryService.js'; import { instantiateTestCodeEditor } from '../../../../../editor/test/browser/testCodeEditor.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; @@ -65,7 +65,7 @@ import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionSer import { TestWorkerService } from './testWorkerService.js'; import { ILanguageModelsService, LanguageModelsService } from '../../../chat/common/languageModels.js'; import { IChatEditingService, IChatEditingSession } from '../../../chat/common/chatEditingService.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { TextModelResolverService } from '../../../../services/textmodelResolver/common/textModelResolverService.js'; import { ChatInputBoxContentProvider } from '../../../chat/browser/chatEdinputInputContentProvider.js'; import { constObservable, IObservable } from '../../../../../base/common/observable.js'; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index 5e2024222e7..776a51a5e36 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -10,9 +10,9 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/tes import { TestDiffProviderFactoryService } from '../../../../../editor/test/browser/diff/testDiffProviderFactoryService.js'; import { IActiveCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { IDiffProviderFactoryService } from '../../../../../editor/browser/widget/diffEditor/diffProviderFactoryService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { instantiateTestCodeEditor } from '../../../../../editor/test/browser/testCodeEditor.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; @@ -32,9 +32,9 @@ import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionSer import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { assertType } from '../../../../../base/common/types.js'; -import { EditOperation } from '../../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; import { TestWorkerService } from './testWorkerService.js'; import { IExtensionService, nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts index 416e3cf3a47..e8320284893 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/testWorkerService.ts @@ -4,16 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { assertType } from '../../../../../base/common/types.js'; -import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from '../../../../../editor/common/services/editorWorker.js'; +import { DiffAlgorithmName, IEditorWorkerService, ILineChange } from '../../../../../editor/common/language/services/editorWorker.js'; import { IDocumentDiff, IDocumentDiffProviderOptions } from '../../../../../editor/common/diff/documentDiffProvider.js'; -import { EditorWorker } from '../../../../../editor/common/services/editorWebWorker.js'; -import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { EditorWorker } from '../../../../../editor/common/language/services/editorWebWorker.js'; +import { LineRange } from '../../../../../editor/common/language/core/lineRange.js'; import { MovedText } from '../../../../../editor/common/diff/linesDiffComputer.js'; import { LineRangeMapping, DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; -import { TextEdit } from '../../../../../editor/common/languages.js'; +import { TextEdit } from '../../../../../editor/common/language/languages.js'; export class TestWorkerService extends mock() { diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 47fb9d3d0d2..692f9b82d84 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -12,11 +12,11 @@ import { extname, isEqual } from '../../../../base/common/resources.js'; import { isFalsyOrWhitespace } from '../../../../base/common/strings.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; import { IBulkEditService } from '../../../../editor/browser/services/bulkEditService.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { peekViewBorder } from '../../../../editor/contrib/peekView/browser/peekView.js'; import { Context as SuggestContext } from '../../../../editor/contrib/suggest/browser/suggest.js'; import { localize, localize2 } from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts index caa2ab618cc..c7ff3110c56 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditor.ts @@ -29,7 +29,7 @@ import { GroupsOrder, IEditorGroup, IEditorGroupsService } from '../../../servic import { ExecutionStateCellStatusBarContrib, TimerCellStatusBarContrib } from '../../notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.js'; import { INotebookKernelService } from '../../notebook/common/notebookKernelService.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ReplEditorSettings, INTERACTIVE_INPUT_CURSOR_BOUNDARY } from './interactiveCommon.js'; @@ -48,7 +48,7 @@ import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/s import { TabCompletionController } from '../../snippets/browser/tabCompletion.js'; import { MarkerController } from '../../../../editor/contrib/gotoError/browser/gotoError.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ITextEditorOptions, TextEditorSelectionSource } from '../../../../platform/editor/common/editor.js'; import { INotebookExecutionStateService, NotebookExecutionType } from '../../notebook/common/notebookExecutionStateService.js'; import { NOTEBOOK_KERNEL } from '../../notebook/common/notebookContextKeys.js'; diff --git a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts index 122dd42bfaa..52bea4a75c4 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactiveEditorInput.ts @@ -9,7 +9,7 @@ import * as paths from '../../../../base/common/path.js'; import { isEqual, joinPath } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts index 729a6932d6e..35ec3cee293 100644 --- a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -13,7 +13,7 @@ import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js' import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; import { ILanguageDetectionService, LanguageDetectionHintConfig, LanguageDetectionLanguageEventSource } from '../../../services/languageDetection/common/languageDetectionWorkerService.js'; import { ThrottledDelayer } from '../../../../base/common/async.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { registerAction2, Action2 } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts b/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts index 2666528ab82..af8212e730f 100644 --- a/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts +++ b/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts @@ -24,9 +24,9 @@ import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ActiveEditorContext, ResourceContextKey } from '../../../common/contextkeys.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { coalesce } from '../../../../base/common/arrays.js'; import { getLocalHistoryDateFormatter, LOCAL_HISTORY_ICON_RESTORE, LOCAL_HISTORY_MENU_CONTEXT_KEY } from './localHistory.js'; diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index e80722d7993..456a96e42a2 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -10,7 +10,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import * as marked from '../../../../base/common/marked/marked.js'; import { Schemas } from '../../../../base/common/network.js'; import { escape } from '../../../../base/common/strings.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { tokenizeToString } from '../../../../editor/common/languages/textToHtmlTokenizer.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { markedGfmHeadingIdPlugin } from './markedGfmHeadingIdPlugin.js'; diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index 91c033d4e18..0f40ea6c1a9 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -11,7 +11,7 @@ import { ResourceMap } from '../../../../base/common/map.js'; import { basename, extUri } from '../../../../base/common/resources.js'; import { splitLines } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { IMarker, IMarkerData, IRelatedInformation, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { unsupportedSchemas } from '../../../../platform/markers/common/markerService.js'; diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index 3a10bafb327..633c54e8913 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -27,7 +27,7 @@ import Messages from './messages.js'; import { isUndefinedOrNull } from '../../../../base/common/types.js'; import { IProblemsWidget } from './markersView.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { unsupportedSchemas } from '../../../../platform/markers/common/markerService.js'; import Severity from '../../../../base/common/severity.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 4ed27e35584..27ad0b0f4f3 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -29,21 +29,21 @@ import { URI } from '../../../../base/common/uri.js'; import { Action, IAction, toAction } from '../../../../base/common/actions.js'; import { localize } from '../../../../nls.js'; import { CancelablePromise, createCancelablePromise, Delayer } from '../../../../base/common/async.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { applyCodeAction, ApplyCodeActionReason, getCodeActions } from '../../../../editor/contrib/codeAction/browser/codeAction.js'; import { CodeActionKind, CodeActionSet, CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IEditorService, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; -import { CodeActionTriggerType } from '../../../../editor/common/languages.js'; +import { CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { Link } from '../../../../platform/opener/browser/link.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { MarkersContextKeys, MarkersViewMode } from '../common/markers.js'; import { unsupportedSchemas } from '../../../../platform/markers/common/markerService.js'; diff --git a/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts b/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts index 8f81edd834f..180e6b2d993 100644 --- a/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts +++ b/src/vs/workbench/contrib/mcp/browser/mcpLanguageFeatures.ts @@ -10,10 +10,10 @@ import { findNodeAtLocation, Node, parseTree } from '../../../../base/common/jso import { Disposable, DisposableStore, dispose, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable } from '../../../../base/common/observable.js'; import { isEqual } from '../../../../base/common/resources.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { CodeLensList, CodeLensProvider, InlayHint, InlayHintList } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { CodeLensList, CodeLensProvider, InlayHint, InlayHintList } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { localize } from '../../../../nls.js'; import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; @@ -23,7 +23,7 @@ import { IMcpConfigPath, IMcpConfigPathsService } from '../common/mcpConfigPaths import { mcpConfigurationSection } from '../common/mcpConfiguration.js'; import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; import { IMcpService, McpConnectionState } from '../common/mcpTypes.js'; -import { EditStoredInput, RemoveStoredInput, RestartServer, ShowOutput, StartServer, StopServer } from './mcpCommands.js'; +import { EditStoredInput, RemoveStoredInput, ResetMcpCachedTools, RestartServer, ShowOutput, StartServer, StopServer } from './mcpCommands.js'; const diagnosticOwner = 'vscode.mcp'; @@ -259,6 +259,13 @@ export class McpLanguageFeatures extends Disposable implements IWorkbenchContrib id: '', title: localize('server.toolCountCached', '{0} cached tools', toolCount), } + }, { + range, + command: { + id: ResetMcpCachedTools.ID, + title: localize('mcp.clearCache', "Clear Cache"), + arguments: [], + }, }); } } diff --git a/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts index 70969d39a39..d875c09dc2f 100644 --- a/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts +++ b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts @@ -8,8 +8,8 @@ import { Throttler } from '../../../../../base/common/async.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { autorunDelta, ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Location } from '../../../../../editor/common/languages.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { Location } from '../../../../../editor/common/language/languages.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { getMcpServerMapping } from '../mcpConfigFileUtils.js'; import { IMcpConfigPath, IMcpConfigPathsService } from '../mcpConfigPathsService.js'; @@ -118,6 +118,9 @@ export class ConfigMcpDiscovery extends Disposable implements IMcpDiscovery { const nextDefinitions = Object.entries(value?.servers || {}).map(([name, value]): McpServerDefinition => ({ id: `${collectionId}.${name}`, label: name, + excludeTools: Array.isArray((value as any).excludeTools) + ? (value as any).excludeTools.filter((t: unknown) => typeof t === 'string' && t.trim().length > 0).map((t: string) => t.trim()) + : undefined, launch: 'type' in value && value.type === 'sse' ? { type: McpServerTransportType.SSE, uri: URI.parse(value.url), diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts b/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts index 641782c5894..bfd8b25f924 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpConfigFileUtils.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { findNodeAtLocation, parseTree as jsonParseTree } from '../../../../base/common/json.js'; -import { Location } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { Location } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export const getMcpServerMapping = (opts: { model: ITextModel; diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts index cea678906e3..84e583b64ce 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -91,6 +91,13 @@ export const mcpStdioServerSchema: IJSONSchema = { ] } }, + excludeTools: { + type: 'array', + description: localize('app.mcp.excludeTools.command', "Tool names to exclude for this server."), + items: { + type: 'string' + } + }, } }; @@ -129,6 +136,11 @@ export const mcpServerSchema: IJSONSchema = { description: localize('app.mcp.json.headers', "Additional headers sent to the server."), additionalProperties: { type: 'string' }, }, + excludeTools: { + type: 'array', + description: localize('app.mcp.excludeTools.command', "Tool names to exclude for this server."), + items: { type: 'string' }, + }, } }] } diff --git a/src/vs/workbench/contrib/mcp/common/mcpServer.ts b/src/vs/workbench/contrib/mcp/common/mcpServer.ts index e565f38cab3..4d9289d0d73 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpServer.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpServer.ts @@ -70,6 +70,9 @@ export class McpServerMetadataCache extends Disposable { private readonly cache = new LRUCache(128); private readonly extensionServers = new Map(); + private readonly _changeCounter = observableValue(this, 0); + public readonly changeCounter: IObservable = this._changeCounter; + constructor( scope: StorageScope, @IStorageService storageService: IStorageService, @@ -101,6 +104,11 @@ export class McpServerMetadataCache extends Disposable { } } + private _bump(): void { + this.didChange = true; + this._changeCounter.set(this._changeCounter.get() + 1, undefined); + } + /** Resets the cache for tools and extension servers */ reset() { this.cache.clear(); @@ -116,7 +124,7 @@ export class McpServerMetadataCache extends Disposable { /** Sets cached tools for a server */ storeTools(definitionId: string, tools: readonly IValidatedMcpTool[]): void { this.cache.set(definitionId, { ...this.cache.get(definitionId), tools }); - this.didChange = true; + this._bump(); } /** Gets cached servers for a collection (used for extensions, before the extension activates) */ @@ -124,6 +132,12 @@ export class McpServerMetadataCache extends Disposable { return this.extensionServers.get(collectionId); } + clearTools(definitionId: string): void { + if (this.cache.delete(definitionId)) { + this._bump(); + } + } + /** Sets cached servers for a collection */ storeServers(collectionId: string, entry: IServerCacheEntry | undefined): void { if (entry) { @@ -131,7 +145,7 @@ export class McpServerMetadataCache extends Disposable { } else { this.extensionServers.delete(collectionId); } - this.didChange = true; + this._bump(); } } @@ -160,6 +174,7 @@ export class McpServer extends Disposable implements IMcpServer { public readonly tools: IObservable; public readonly toolsState = derived(reader => { + this._toolCache.changeCounter.read(reader); const fromServer = this.toolsFromServerPromise.read(reader); const connectionState = this.connectionState.read(reader); const isIdle = McpConnectionState.canBeStarted(connectionState.state) && !fromServer; @@ -247,6 +262,7 @@ export class McpServer extends Disposable implements IMcpServer { // 4. Publish tools const toolPrefix = this._mcpRegistry.collectionToolPrefix(this.collection); this.tools = derived(reader => { + this._toolCache.changeCounter.read(reader); const serverTools = this.toolsFromServer.read(reader); const definitions = serverTools ?? this.toolsFromCache ?? []; const prefix = toolPrefix.read(reader); @@ -314,6 +330,11 @@ export class McpServer extends Disposable implements IMcpServer { return this._connection.get()?.stop() || Promise.resolve(); } + public resetToolCache(): void { + this._toolCache.clearTools(this.definition.id); + this.resetLiveData(); + } + private resetLiveData() { transaction(tx => { this.toolsFromServerPromise.set(undefined, tx); diff --git a/src/vs/workbench/contrib/mcp/common/mcpService.ts b/src/vs/workbench/contrib/mcp/common/mcpService.ts index 38666d83ca1..3adcbfed846 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpService.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpService.ts @@ -15,10 +15,13 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILogService } from '../../../../platform/log/common/log.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../../chat/common/languageModelToolsService.js'; import { IMcpRegistry } from './mcpRegistryTypes.js'; import { McpServer, McpServerMetadataCache } from './mcpServer.js'; import { IMcpServer, IMcpService, IMcpTool, McpCollectionDefinition, McpServerDefinition, McpServerToolsState } from './mcpTypes.js'; +import type { InternalToolInfo } from '../../void/common/prompt/prompts.js'; +import type { AdditionalToolInfo } from '../../../../platform/void/common/sendLLMMessageTypes.js'; interface ISyncedToolData { toolData: IToolData; @@ -39,11 +42,13 @@ export class McpService extends Disposable implements IMcpService { protected readonly userCache: McpServerMetadataCache; protected readonly workspaceCache: McpServerMetadataCache; + private readonly _toolFilterVersion = observableValue(this, 0); constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, @ILanguageModelToolsService private readonly _toolsService: ILanguageModelToolsService, + @IVoidSettingsService private readonly _voidSettingsService: IVoidSettingsService, @ILogService private readonly _logService: ILogService, ) { super(); @@ -61,11 +66,39 @@ export class McpService extends Disposable implements IMcpService { } updateThrottle.schedule(500); })); + + this._register(this._voidSettingsService.onDidChangeState(() => { + this._toolFilterVersion.set(this._toolFilterVersion.get() + 1, undefined); + })); + } + + private _mcpSafePrefixFromDefinitionId(definitionId: string | undefined, collectionId: string | undefined): string { + const rawId = definitionId || collectionId || 'mcp'; + const idParts = String(rawId).split('.'); + const serverName = idParts[idParts.length - 1] || rawId; + const safePrefix = String(serverName).replace(/[^a-zA-Z0-9_]/g, '_'); + return safePrefix || 'mcp'; + } + + private _getUserDisabledToolNamesSet(): Set { + const arr = this._voidSettingsService.state.globalSettings.disabledToolNames; + if (!Array.isArray(arr)) return new Set(); + return new Set(arr.map(v => String(v ?? '').trim()).filter(Boolean)); } public resetCaches(): void { this.userCache.reset(); this.workspaceCache.reset(); + + // Reset tool cache for all servers + for (const serverRef of this._servers.get()) { + if (serverRef.object instanceof McpServer) { + serverRef.object.resetToolCache(); + } + } + + // Force update servers to clear cached tools from UI + this._updateCollectedServers(); } public async activateCollections(): Promise { @@ -88,12 +121,28 @@ export class McpService extends Disposable implements IMcpService { await Promise.all(todo); } - private _syncTools(server: McpServer, store: DisposableStore) { + private async _syncTools(server: McpServer, store: DisposableStore) { const tools = new Map(); store.add(autorun(reader => { + this._toolFilterVersion.read(reader); + const toDelete = new Set(tools.keys()); + const disabledByUser = this._getUserDisabledToolNamesSet(); + const excludedByConfig = new Set( + (((server.definition as unknown as { excludeTools?: readonly string[] }).excludeTools) ?? []) + .map(v => String(v ?? '').trim()) + .filter(Boolean) + ); + const safePrefix = this._mcpSafePrefixFromDefinitionId(server.definition.id, server.collection.id); + for (const tool of server.tools.read(reader)) { + const baseName = String(tool.definition.name ?? '').trim(); + const prefixedName = `${safePrefix}__${baseName}`; + if (excludedByConfig.has(baseName) || disabledByUser.has(prefixedName)) { + continue; + } + const existing = tools.get(tool.id); const collection = this._mcpRegistry.collections.get().find(c => c.id === server.collection.id); const toolData: IToolData = { @@ -125,6 +174,9 @@ export class McpService extends Disposable implements IMcpService { implDispose: this._toolsService.registerToolImplementation(tool.id, this._instantiationService.createInstance(McpToolImplementation, tool, server)), }); } + + // Sync with dynamicVoidTools registry for LLM payload + void this._syncToolToDynamicRegistry(tool); } for (const id of toDelete) { @@ -133,6 +185,9 @@ export class McpService extends Disposable implements IMcpService { tool.toolDispose.dispose(); tool.implDispose.dispose(); tools.delete(id); + + // Remove from dynamicVoidTools registry + void this._removeToolFromDynamicRegistry(tool.toolData.displayName); } } })); @@ -142,9 +197,81 @@ export class McpService extends Disposable implements IMcpService { tool.toolDispose.dispose(); tool.implDispose.dispose(); } + + // Clean up dynamic tools on disposal + void this._cleanupDynamicTools(tools); })); } + private async _syncToolToDynamicRegistry(tool: IMcpTool): Promise { + try { + const { dynamicVoidTools } = await import('../../void/common/prompt/prompts.js'); + const params: NonNullable = {}; + + // Extract parameters from input schema if available + if (tool.definition.inputSchema && typeof tool.definition.inputSchema === 'object') { + const schema = tool.definition.inputSchema as any; + this._logService.debug('[MCP DEBUG] Tool input schema for', tool.definition.name, ':', JSON.stringify(schema, null, 2)); + if (schema.properties && typeof schema.properties === 'object') { + for (const [paramName, paramSchema] of Object.entries(schema.properties)) { + if (typeof paramSchema === 'object' && paramSchema !== null) { + this._logService.debug('[MCP DEBUG] Processing parameter:', paramName, 'schema:', paramSchema); + params[paramName] = { + description: (paramSchema as any).description || `Parameter: ${paramName}`, + type: (paramSchema as any).type, + enum: (paramSchema as any).enum, + items: (paramSchema as any).items, + properties: (paramSchema as any).properties, + required: (paramSchema as any).required, + default: (paramSchema as any).default, + minimum: (paramSchema as any).minimum, + maximum: (paramSchema as any).maximum, + minLength: (paramSchema as any).minLength, + maxLength: (paramSchema as any).maxLength + }; + } + } + } + } + + const dynamicToolInfo: InternalToolInfo = { + name: tool.definition.name, + description: tool.definition.description || '', + params: params as InternalToolInfo['params'] + }; + + dynamicVoidTools.set(tool.definition.name, dynamicToolInfo); + this._logService.debug(`[MCP] Registered dynamic tool: ${tool.definition.name}, total dynamic tools: ${dynamicVoidTools.size}`); + this._logService.debug(`[MCP DEBUG] Dynamic tools after registration:`, Array.from(dynamicVoidTools.keys())); + } catch (error) { + this._logService.error(`[MCP] Failed to sync tool ${tool.definition.name} to dynamic registry:`, error); + this._logService.error(`[MCP DEBUG] Error syncing tool:`, error); + } + } + + private async _removeToolFromDynamicRegistry(toolName: string): Promise { + try { + const { dynamicVoidTools } = await import('../../void/common/prompt/prompts.js'); + if (dynamicVoidTools.has(toolName)) { + dynamicVoidTools.delete(toolName); + this._logService.debug(`[MCP] Unregistered dynamic tool: ${toolName}`); + } + } catch (error) { + this._logService.error(`[MCP] Failed to remove tool from dynamic registry:`, error); + } + } + + private async _cleanupDynamicTools(tools: Map): Promise { + try { + const { dynamicVoidTools } = await import('../../void/common/prompt/prompts.js'); + for (const [, tool] of tools) { + dynamicVoidTools.delete(tool.toolData.displayName); + } + } catch (error) { + this._logService.error(`[MCP] Failed to cleanup dynamic tools registry:`, error); + } + } + private _updateCollectedServers() { const definitions = this._mcpRegistry.collections.get().flatMap(collectionDefinition => collectionDefinition.serverDefinitions.get().map(serverDefinition => ({ diff --git a/src/vs/workbench/contrib/mcp/common/mcpTypes.ts b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts index 6c4a2540878..d0aaf3535cd 100644 --- a/src/vs/workbench/contrib/mcp/common/mcpTypes.ts +++ b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts @@ -10,7 +10,7 @@ import { equals as objectsEqual } from '../../../../base/common/objects.js'; import { equals as arraysEqual } from '../../../../base/common/arrays.js'; import { IObservable } from '../../../../base/common/observable.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; @@ -99,6 +99,8 @@ export interface McpServerDefinition { readonly roots?: URI[] | undefined; /** If set, allows configuration variables to be resolved in the {@link launch} with the given context */ readonly variableReplacement?: McpServerDefinitionVariableReplacement; + /** Optional list of tool names to exclude for this server. */ + readonly excludeTools?: readonly string[]; readonly presentation?: { /** Sort order of the definition. */ @@ -114,6 +116,7 @@ export namespace McpServerDefinition { readonly label: string; readonly launch: McpServerLaunch.Serialized; readonly variableReplacement?: McpServerDefinitionVariableReplacement.Serialized; + readonly excludeTools?: readonly string[]; } export function toSerialized(def: McpServerDefinition): McpServerDefinition.Serialized { @@ -126,6 +129,7 @@ export namespace McpServerDefinition { label: def.label, launch: McpServerLaunch.fromSerialized(def.launch), variableReplacement: def.variableReplacement ? McpServerDefinitionVariableReplacement.fromSerialized(def.variableReplacement) : undefined, + excludeTools: def.excludeTools ? [...def.excludeTools] : undefined, }; } @@ -134,6 +138,7 @@ export namespace McpServerDefinition { && a.label === b.label && arraysEqual(a.roots, b.roots, (a, b) => a.toString() === b.toString()) && objectsEqual(a.launch, b.launch) + && arraysEqual(a.excludeTools, b.excludeTools, (a, b) => a === b) && objectsEqual(a.presentation, b.presentation) && objectsEqual(a.variableReplacement, b.variableReplacement); } @@ -177,6 +182,7 @@ export interface IMcpService { readonly lazyCollectionState: IObservable; /** Activatese extensions and runs their MCP servers. */ activateCollections(): Promise; + } export const enum LazyCollectionState { diff --git a/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts index 582e69eaf93..d44e29e7825 100644 --- a/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts +++ b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts @@ -3,9 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable @stylistic/ts/member-delimiter-style */ -/* eslint-disable local/code-no-unexternalized-strings */ - /** * Schema updated from the Model Context Protocol repository at * https://github.com/modelcontextprotocol/specification/tree/main/schema @@ -20,8 +17,8 @@ export namespace MCP { | JSONRPCResponse | JSONRPCError; - export const LATEST_PROTOCOL_VERSION = "2024-11-05"; - export const JSONRPC_VERSION = "2.0"; + export const LATEST_PROTOCOL_VERSION = '2024-11-05'; + export const JSONRPC_VERSION = '2.0'; /** * A progress token, used to associate progress notifications with the original request. @@ -140,7 +137,7 @@ export namespace MCP { * A client MUST NOT attempt to cancel its `initialize` request. */ export interface CancelledNotification extends Notification { - method: "notifications/cancelled"; + method: 'notifications/cancelled'; params: { /** * The ID of the request to cancel. @@ -161,7 +158,7 @@ export namespace MCP { * This request is sent from the client to the server when it first connects, asking it to begin initialization. */ export interface InitializeRequest extends Request { - method: "initialize"; + method: 'initialize'; params: { /** * The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. @@ -185,7 +182,7 @@ export namespace MCP { /** * Instructions describing how to use the server and its features. * - * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a 'hint" to the model. For example, this information MAY be added to the system prompt. */ instructions?: string; } @@ -194,7 +191,7 @@ export namespace MCP { * This notification is sent from the client to the server after initialization has finished. */ export interface InitializedNotification extends Notification { - method: "notifications/initialized"; + method: 'notifications/initialized'; } /** @@ -278,7 +275,7 @@ export namespace MCP { * A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected. */ export interface PingRequest extends Request { - method: "ping"; + method: 'ping'; } /* Progress notifications */ @@ -286,7 +283,7 @@ export namespace MCP { * An out-of-band notification used to inform the receiver of a progress update for a long-running request. */ export interface ProgressNotification extends Notification { - method: "notifications/progress"; + method: 'notifications/progress'; params: { /** * The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. @@ -331,7 +328,7 @@ export namespace MCP { * Sent from the client to request a list of resources the server has. */ export interface ListResourcesRequest extends PaginatedRequest { - method: "resources/list"; + method: 'resources/list'; } /** @@ -345,7 +342,7 @@ export namespace MCP { * Sent from the client to request a list of resource templates the server has. */ export interface ListResourceTemplatesRequest extends PaginatedRequest { - method: "resources/templates/list"; + method: 'resources/templates/list'; } /** @@ -359,7 +356,7 @@ export namespace MCP { * Sent from the client to the server, to read a specific resource URI. */ export interface ReadResourceRequest extends Request { - method: "resources/read"; + method: 'resources/read'; params: { /** * The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. @@ -381,14 +378,14 @@ export namespace MCP { * An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client. */ export interface ResourceListChangedNotification extends Notification { - method: "notifications/resources/list_changed"; + method: 'notifications/resources/list_changed'; } /** * Sent from the client to request resources/updated notifications from the server whenever a particular resource changes. */ export interface SubscribeRequest extends Request { - method: "resources/subscribe"; + method: 'resources/subscribe'; params: { /** * The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it. @@ -403,7 +400,7 @@ export namespace MCP { * Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request. */ export interface UnsubscribeRequest extends Request { - method: "resources/unsubscribe"; + method: 'resources/unsubscribe'; params: { /** * The URI of the resource to unsubscribe from. @@ -418,7 +415,7 @@ export namespace MCP { * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. */ export interface ResourceUpdatedNotification extends Notification { - method: "notifications/resources/updated"; + method: 'notifications/resources/updated'; params: { /** * The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. @@ -450,7 +447,7 @@ export namespace MCP { /** * A description of what this resource represents. * - * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a 'hint' to the model. */ description?: string; @@ -488,7 +485,7 @@ export namespace MCP { /** * A description of what this template is for. * - * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a 'hint' to the model. */ description?: string; @@ -535,7 +532,7 @@ export namespace MCP { * Sent from the client to request a list of prompts and prompt templates the server has. */ export interface ListPromptsRequest extends PaginatedRequest { - method: "prompts/list"; + method: 'prompts/list'; } /** @@ -549,7 +546,7 @@ export namespace MCP { * Used by the client to get a prompt provided by the server. */ export interface GetPromptRequest extends Request { - method: "prompts/get"; + method: 'prompts/get'; params: { /** * The name of the prompt or prompt template. @@ -612,7 +609,7 @@ export namespace MCP { /** * The sender or recipient of messages and data in a conversation. */ - export type Role = "user" | "assistant"; + export type Role = 'user' | 'assistant'; /** * Describes a message returned as part of a prompt. @@ -632,7 +629,7 @@ export namespace MCP { * of the LLM and/or the user. */ export interface EmbeddedResource extends Annotated { - type: "resource"; + type: 'resource'; resource: TextResourceContents | BlobResourceContents; } @@ -640,7 +637,7 @@ export namespace MCP { * An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client. */ export interface PromptListChangedNotification extends Notification { - method: "notifications/prompts/list_changed"; + method: 'notifications/prompts/list_changed'; } /* Tools */ @@ -648,7 +645,7 @@ export namespace MCP { * Sent from the client to request a list of tools the server has. */ export interface ListToolsRequest extends PaginatedRequest { - method: "tools/list"; + method: 'tools/list'; } /** @@ -685,7 +682,7 @@ export namespace MCP { * Used by the client to invoke a tool provided by the server. */ export interface CallToolRequest extends Request { - method: "tools/call"; + method: 'tools/call'; params: { name: string; arguments?: { [key: string]: unknown }; @@ -696,7 +693,7 @@ export namespace MCP { * An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client. */ export interface ToolListChangedNotification extends Notification { - method: "notifications/tools/list_changed"; + method: 'notifications/tools/list_changed'; } /** @@ -715,7 +712,7 @@ export namespace MCP { * A JSON Schema object defining the expected parameters for the tool. */ inputSchema: { - type: "object"; + type: 'object'; properties?: { [key: string]: object }; required?: string[]; }; @@ -726,7 +723,7 @@ export namespace MCP { * A request from the client to the server, to enable or adjust logging. */ export interface SetLevelRequest extends Request { - method: "logging/setLevel"; + method: 'logging/setLevel'; params: { /** * The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message. @@ -739,7 +736,7 @@ export namespace MCP { * Notification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically. */ export interface LoggingMessageNotification extends Notification { - method: "notifications/message"; + method: 'notifications/message'; params: { /** * The severity of this log message. @@ -763,21 +760,21 @@ export namespace MCP { * https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 */ export type LoggingLevel = - | "debug" - | "info" - | "notice" - | "warning" - | "error" - | "critical" - | "alert" - | "emergency"; + | 'debug' + | 'info' + | 'notice' + | 'warning' + | 'error' + | 'critical' + | 'alert' + | 'emergency'; /* Sampling */ /** * A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. */ export interface CreateMessageRequest extends Request { - method: "sampling/createMessage"; + method: 'sampling/createMessage'; params: { messages: SamplingMessage[]; /** @@ -791,7 +788,7 @@ export namespace MCP { /** * A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request. */ - includeContext?: "none" | "thisServer" | "allServers"; + includeContext?: 'none' | 'thisServer' | 'allServers'; /** * @TJS-type number */ @@ -819,7 +816,7 @@ export namespace MCP { /** * The reason why sampling stopped, if known. */ - stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string; + stopReason?: 'endTurn' | 'stopSequence' | 'maxTokens' | string; } /** @@ -861,7 +858,7 @@ export namespace MCP { * Text provided to or from an LLM. */ export interface TextContent extends Annotated { - type: "text"; + type: 'text'; /** * The text content of the message. */ @@ -872,7 +869,7 @@ export namespace MCP { * An image provided to or from an LLM. */ export interface ImageContent extends Annotated { - type: "image"; + type: 'image'; /** * The base64-encoded image data. * @@ -970,7 +967,7 @@ export namespace MCP { * A request from the client to the server, to ask for completion options. */ export interface CompleteRequest extends Request { - method: "completion/complete"; + method: 'completion/complete'; params: { ref: PromptReference | ResourceReference; /** @@ -1013,7 +1010,7 @@ export namespace MCP { * A reference to a resource or resource template definition. */ export interface ResourceReference { - type: "ref/resource"; + type: 'ref/resource'; /** * The URI or URI template of the resource. * @@ -1026,7 +1023,7 @@ export namespace MCP { * Identifies a prompt. */ export interface PromptReference { - type: "ref/prompt"; + type: 'ref/prompt'; /** * The name of the prompt or prompt template */ @@ -1044,7 +1041,7 @@ export namespace MCP { * structure or access specific locations that the client has permission to read from. */ export interface ListRootsRequest extends Request { - method: "roots/list"; + method: 'roots/list'; } /** @@ -1082,7 +1079,7 @@ export namespace MCP { * The server should then request an updated list of roots using the ListRootsRequest. */ export interface RootsListChangedNotification extends Notification { - method: "notifications/roots/list_changed"; + method: 'notifications/roots/list_changed'; } /* Client messages */ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts index 2d2edc99e76..300b50f84c0 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts @@ -6,7 +6,7 @@ import { VSBuffer } from '../../../../../base/common/buffer.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { localize, localize2 } from '../../../../../nls.js'; import { ILocalizedString } from '../../../../../platform/action/common/action.js'; import { Action2 } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index 28217f06aa7..07e8968b340 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -8,7 +8,7 @@ import { autorun } from '../../../../base/common/observable.js'; import { isEqual } from '../../../../base/common/resources.js'; import { isDefined } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IFileService } from '../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index a110faf6a99..4c2552249e6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts @@ -11,8 +11,8 @@ import { derived, IObservable, observableFromEvent, observableValue } from '../. import { basename, isEqual } from '../../../../base/common/resources.js'; import Severity from '../../../../base/common/severity.js'; import { URI } from '../../../../base/common/uri.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { ConfirmResult, IDialogService, IPromptButton } from '../../../../platform/dialogs/common/dialogs.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; @@ -27,8 +27,8 @@ import { MergeEditorTelemetry } from './telemetry.js'; import { StorageCloseWithConflicts } from '../common/mergeEditor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ITextFileEditorModel, ITextFileSaveOptions, ITextFileService } from '../../../services/textfile/common/textfiles.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; export interface MergeEditorArgs { base: URI; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts index d2611d0b1f8..89ccf6435ab 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeMarkers/mergeMarkersController.ts @@ -7,7 +7,7 @@ import { h } from '../../../../../base/browser/dom.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { autorun, IObservable } from '../../../../../base/common/observable.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { LineRange } from '../model/lineRange.js'; import { MergeEditorViewModel } from '../view/viewModel.js'; import * as nls from '../../../../../nls.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts index 8bc16815852..62ba223e5db 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -6,13 +6,13 @@ import { assertFn, checkAdjacentItems } from '../../../../../base/common/assert.js'; import { IReader } from '../../../../../base/common/observable.js'; import { RangeMapping as DiffRangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { LineRange } from './lineRange.js'; import { DetailedLineRangeMapping, RangeMapping } from './mapping.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; -import { LineRange as DiffLineRange } from '../../../../../editor/common/core/lineRange.js'; +import { LineRange as DiffLineRange } from '../../../../../editor/common/language/core/lineRange.js'; export interface IMergeDiffComputer { computeDiff(textModel1: ITextModel, textModel2: ITextModel, reader: IReader): Promise; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts index 3bbd65333ff..334dc159f1b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { equals } from '../../../../../base/common/arrays.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IIdentifiedSingleEditOperation } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IIdentifiedSingleEditOperation } from '../../../../../editor/common/language/model.js'; import { LineRange } from './lineRange.js'; /** diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts index eff77556a78..a09874b198a 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts @@ -6,8 +6,8 @@ import { Comparator, compareBy, numberComparator } from '../../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Constants } from '../../../../../base/common/uint.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; export class LineRange { public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts index 3025d37f4be..8a2caa39bdb 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -7,9 +7,9 @@ import { compareBy, numberComparator } from '../../../../../base/common/arrays.j import { findLast } from '../../../../../base/common/arraysFind.js'; import { assertFn, checkAdjacentItems } from '../../../../../base/common/assert.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { concatArrays } from '../utils.js'; import { LineRangeEdit } from './editing.js'; import { LineRange } from './lineRange.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 26c5f44afd1..2ec10ba01f1 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -7,9 +7,9 @@ import { CompareResult, equals } from '../../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { autorunHandleChanges, derived, IObservable, IReader, ISettableObservable, ITransaction, keepObserved, observableValue, transaction, waitForState } from '../../../../../base/common/observable.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { localize } from '../../../../../nls.js'; import { IResourceUndoRedoElement, IUndoRedoService, UndoRedoElementType, UndoRedoGroup } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { EditorModel } from '../../../../common/editor/editorModel.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts index cd62b5944a1..45b186f6ca6 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts @@ -7,9 +7,9 @@ import { compareBy, equals, numberComparator, tieBreakComparators } from '../../ import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { splitLines } from '../../../../../base/common/strings.js'; import { Constants } from '../../../../../base/common/uint.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { LineRangeEdit, RangeEdit } from './editing.js'; import { LineRange } from './lineRange.js'; import { DetailedLineRangeMapping, MappingAlignment } from './mapping.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/rangeUtils.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/rangeUtils.ts index 0c6666af32b..a4a4aa08926 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/rangeUtils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/rangeUtils.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { TextLength } from '../../../../../editor/common/core/textLength.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; export function rangeContainsPosition(range: Range, position: Position): boolean { if (position.lineNumber < range.startLineNumber || position.lineNumber > range.endLineNumber) { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index 2741b6681d4..accc5f67371 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -6,7 +6,7 @@ import { compareBy, numberComparator } from '../../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Disposable, toDisposable } from '../../../../../base/common/lifecycle.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { DetailedLineRangeMapping } from './mapping.js'; import { LineRangeEdit } from './editing.js'; import { LineRange } from './lineRange.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index 308db886d5b..41bac5f7749 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -8,7 +8,7 @@ import { onUnexpectedError } from '../../../../base/common/errors.js'; import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable, autorunOpts } from '../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { IModelDeltaDecoration } from '../../../../editor/common/model.js'; +import { IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; export function setStyle( diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts index e43788e7ed1..e365d6c0251 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/baseCodeEditorView.ts @@ -7,7 +7,7 @@ import { h, reset } from '../../../../../../base/browser/dom.js'; import { renderLabelWithIcons } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { BugIndicatingError } from '../../../../../../base/common/errors.js'; import { IObservable, autorun, autorunWithStore, derived } from '../../../../../../base/common/observable.js'; -import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/model.js'; +import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/language/model.js'; import { localize } from '../../../../../../nls.js'; import { MenuId } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts index 5520e2a0d72..e09af9ac674 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts @@ -11,8 +11,8 @@ import { IObservable, autorun, derived, observableFromEvent } from '../../../../ import { EditorExtensionsRegistry, IEditorContributionDescription } from '../../../../../../editor/browser/editorExtensions.js'; import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../../editor/common/core/selection.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../../editor/common/language/core/selection.js'; import { CodeLensContribution } from '../../../../../../editor/contrib/codelens/browser/codelensController.js'; import { FoldingController } from '../../../../../../editor/contrib/folding/browser/folding.js'; import { MenuWorkbenchToolBar } from '../../../../../../platform/actions/browser/toolbar.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts index 4a3b0081cd3..a063a00d827 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts @@ -14,7 +14,7 @@ import { autorun, autorunOpts, derived, derivedOpts, IObservable, ISettableObser import { noBreakWhitespace } from '../../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { isDefined } from '../../../../../../base/common/types.js'; -import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/model.js'; +import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/language/model.js'; import { localize } from '../../../../../../nls.js'; import { MenuId } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts index c5730805fd3..3220318a792 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editors/resultCodeEditorView.ts @@ -10,7 +10,7 @@ import { CompareResult } from '../../../../../../base/common/arrays.js'; import { BugIndicatingError } from '../../../../../../base/common/errors.js'; import { toDisposable } from '../../../../../../base/common/lifecycle.js'; import { autorun, autorunWithStore, derived, IObservable } from '../../../../../../base/common/observable.js'; -import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/model.js'; +import { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/language/model.js'; import { localize } from '../../../../../../nls.js'; import { MenuId } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts index e607effbacd..be23c8e0c75 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/lineAlignment.ts @@ -6,9 +6,9 @@ import { compareBy } from '../../../../../base/common/arrays.js'; import { assertFn, checkAdjacentItems } from '../../../../../base/common/assert.js'; import { isDefined } from '../../../../../base/common/types.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { TextLength } from '../../../../../editor/common/core/textLength.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; import { RangeMapping } from '../model/mapping.js'; import { ModifiedBaseRange } from '../model/modifiedBaseRange.js'; import { addLength, lengthBetweenPositions, lengthOfRange } from '../model/rangeUtils.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 666ad3f373e..22be01e7101 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -20,8 +20,8 @@ import { ICodeEditor, IViewZoneChangeAccessor } from '../../../../../editor/brow import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; import { IEditorOptions as ICodeEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; import { ICodeEditorViewState, ScrollType } from '../../../../../editor/common/editorCommon.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize } from '../../../../../nls.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from '../../../../../platform/editor/common/editor.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts index 74322dc65a4..253ce5619e4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts @@ -6,9 +6,9 @@ import { findLast } from '../../../../../base/common/arraysFind.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { derived, derivedObservableWithWritableCache, IObservable, IReader, ITransaction, observableValue, transaction } from '../../../../../base/common/observable.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../../../editor/common/editorCommon.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { INotificationService } from '../../../../../platform/notification/common/notification.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts index 1456ead586b..a2b796734c4 100644 --- a/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts +++ b/src/vs/workbench/contrib/mergeEditor/electron-sandbox/devCommands.ts @@ -7,7 +7,7 @@ import { VSBuffer } from '../../../../base/common/buffer.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { randomPath } from '../../../../base/common/extpath.js'; import { URI } from '../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { localize, localize2 } from '../../../../nls.js'; import { ILocalizedString } from '../../../../platform/action/common/action.js'; import { Action2, IAction2Options } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/mergeEditor/test/browser/mapping.test.ts b/src/vs/workbench/contrib/mergeEditor/test/browser/mapping.test.ts index bef40313331..300e00043ec 100644 --- a/src/vs/workbench/contrib/mergeEditor/test/browser/mapping.test.ts +++ b/src/vs/workbench/contrib/mergeEditor/test/browser/mapping.test.ts @@ -5,9 +5,9 @@ import assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { TextLength } from '../../../../../editor/common/core/textLength.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { TextLength } from '../../../../../editor/common/language/core/textLength.js'; import { DocumentRangeMap, RangeMapping } from '../../browser/model/mapping.js'; suite('merge editor mapping', () => { diff --git a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts index 65b7b366c39..43563b022ac 100644 --- a/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts +++ b/src/vs/workbench/contrib/mergeEditor/test/browser/model.test.ts @@ -7,9 +7,9 @@ import assert from 'assert'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { IReader, transaction } from '../../../../../base/common/observable.js'; import { isDefined } from '../../../../../base/common/types.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { linesDiffComputers } from '../../../../../editor/common/diff/linesDiffComputers.js'; -import { EndOfLinePreference, ITextModel } from '../../../../../editor/common/model.js'; +import { EndOfLinePreference, ITextModel } from '../../../../../editor/common/language/model.js'; import { createModelServices, createTextModel } from '../../../../../editor/test/common/testTextModel.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js'; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts index 553d01befc7..4a80d57a47f 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts @@ -5,7 +5,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { URI } from '../../../../base/common/uri.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { localize2 } from '../../../../nls.js'; import { Action2, MenuId } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index 3a45ea35556..a33a1a1fad0 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -7,7 +7,7 @@ import * as DOM from '../../../../base/browser/dom.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { MultiDiffEditorWidget } from '../../../../editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.js'; import { IResourceLabel, IWorkbenchUIElementFactory } from '../../../../editor/browser/widget/multiDiffEditor/workbenchUIElementFactory.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { InstantiationService } from '../../../../platform/instantiation/common/instantiationService.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; @@ -26,7 +26,7 @@ import { MultiDiffEditorViewModel } from '../../../../editor/browser/widget/mult import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from '../../../../editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IDiffEditor } from '../../../../editor/common/editorCommon.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { MultiDiffEditorItem } from './multiDiffSourceResolverService.js'; import { IEditorProgressService } from '../../../../platform/progress/common/progress.js'; diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts index aa09311d401..4b44ca8a298 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts @@ -19,8 +19,8 @@ import { RefCounted } from '../../../../editor/browser/widget/diffEditor/utils.j import { IDocumentDiffItem, IMultiDiffEditorModel } from '../../../../editor/browser/widget/multiDiffEditor/model.js'; import { MultiDiffEditorViewModel } from '../../../../editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.js'; import { IDiffEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize } from '../../../../nls.js'; import { ConfirmResult } from '../../../../platform/dialogs/common/dialogs.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts index 584fc890a92..15c9e1cf6d5 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts @@ -5,7 +5,7 @@ import { Disposable, IDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { IMarkerData, IMarkerService } from '../../../../../../platform/markers/common/markers.js'; -import { IRange } from '../../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../../editor/common/language/core/range.js'; import { ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { CellKind, NotebookSetting } from '../../../common/notebookCommon.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts index d41ae0fcfd4..30aa7df71ca 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticsActions.ts @@ -5,7 +5,7 @@ import { KeyCode, KeyMod } from '../../../../../../base/common/keyCodes.js'; import { ServicesAccessor } from '../../../../../../editor/browser/editorExtensions.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { CodeActionController } from '../../../../../../editor/contrib/codeAction/browser/codeActionController.js'; import { CodeActionKind, CodeActionTriggerSource } from '../../../../../../editor/contrib/codeAction/common/types.js'; import { localize, localize2 } from '../../../../../../nls.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts index 4434091f38d..f452b785746 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/statusBarProviders.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../../../../base/common/cancellation.js import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../../base/common/map.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { localize } from '../../../../../../nls.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts index 43215bff942..52169dafeff 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/debug/notebookDebugDecorations.ts @@ -5,7 +5,7 @@ import { Delayer } from '../../../../../../base/common/async.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IRange, Range } from '../../../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../../../editor/common/language/core/range.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { debugIconBreakpointForeground } from '../../../../debug/browser/breakpointEditorContribution.js'; import { focusedStackFrameColor, topStackFrameColor } from '../../../../debug/browser/callStackEditorContribution.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts index db21d25ea27..d7e1eb505a7 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorStatusBar/editorStatusBar.ts @@ -6,7 +6,7 @@ import * as nls from '../../../../../../nls.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../../base/common/network.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts index 8a27b9eefb7..bf229afdc2b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IModelDeltaDecoration } from '../../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; +import { IModelDeltaDecoration } from '../../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../../editor/common/language/model/textModel.js'; import { FindDecorations } from '../../../../../../editor/contrib/find/browser/findDecorations.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { overviewRulerSelectionHighlightForeground, overviewRulerFindMatchForeground } from '../../../../../../platform/theme/common/colorRegistry.js'; import { CellFindMatchWithIndex, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, INotebookDeltaDecoration, INotebookEditor, NotebookOverviewRulerLane, } from '../../notebookBrowser.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts index 7858fef22dc..e85cf5eaa30 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -7,9 +7,9 @@ import { findFirstIdxMonotonousOrArrLen } from '../../../../../../base/common/ar import { CancelablePromise, createCancelablePromise, Delayer } from '../../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { FindMatch } from '../../../../../../editor/common/model.js'; -import { PrefixSumComputer } from '../../../../../../editor/common/model/prefixSumComputer.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { FindMatch } from '../../../../../../editor/common/language/model.js'; +import { PrefixSumComputer } from '../../../../../../editor/common/language/model/prefixSumComputer.js'; import { FindReplaceState, FindReplaceStateChangedEvent } from '../../../../../../editor/contrib/find/browser/findState.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { NotebookFindFilters } from './findFilters.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts index 94e37788a69..8c9b2e767c3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts @@ -12,7 +12,7 @@ import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../../../editor/browser/services/codeEditorService.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; import { EditorContextKeys } from '../../../../../../editor/common/editorContextKeys.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { FindStartFocusAction, getSelectionSearchString, IFindStartOptions, StartFindAction, StartFindReplaceAction } from '../../../../../../editor/contrib/find/browser/findController.js'; import { localize2 } from '../../../../../../nls.js'; import { Action2, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index f2de65d5bdc..307749e59e8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -25,7 +25,7 @@ import { KeyCode } from '../../../../../../base/common/keyCodes.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { isSafari } from '../../../../../../base/common/platform.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { FindReplaceState, FindReplaceStateChangedEvent } from '../../../../../../editor/contrib/find/browser/findState.js'; import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, findSelectionIcon, SimpleButton } from '../../../../../../editor/contrib/find/browser/findWidget.js'; import { parseReplaceString, ReplacePattern } from '../../../../../../editor/contrib/find/browser/replacePattern.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts index 6c57739806f..cd6862a1a04 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts @@ -10,8 +10,8 @@ import { KeyCode, KeyMod } from '../../../../../../base/common/keyCodes.js'; import { Lazy } from '../../../../../../base/common/lazy.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import * as strings from '../../../../../../base/common/strings.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { FindMatch } from '../../../../../../editor/common/model.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { FindMatch } from '../../../../../../editor/common/language/model.js'; import { MATCHES_LIMIT } from '../../../../../../editor/contrib/find/browser/findModel.js'; import { FindReplaceState } from '../../../../../../editor/contrib/find/browser/findState.js'; import { NLS_MATCHES_LOCATION, NLS_NO_RESULTS } from '../../../../../../editor/contrib/find/browser/findWidget.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts index 4b201578667..399b62b1e02 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/format/formatting.ts @@ -11,9 +11,9 @@ import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { EditorAction, registerEditorAction } from '../../../../../../editor/browser/editorExtensions.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../../../editor/browser/services/bulkEditService.js'; import { EditorContextKeys } from '../../../../../../editor/common/editorContextKeys.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { FormattingMode, formatDocumentWithSelectedProvider, getDocumentFormattingEditsWithSelectedProvider } from '../../../../../../editor/contrib/format/browser/format.js'; import { Action2, MenuId, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts index 116d5d3fab9..bbc24dd65ab 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts @@ -16,19 +16,19 @@ import { RedoCommand, UndoCommand } from '../../../../../../editor/browser/edito import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorConfiguration } from '../../../../../../editor/common/config/editorConfiguration.js'; import { cursorBlinkingStyleFromString, cursorStyleFromString, TextEditorCursorBlinkingStyle, TextEditorCursorStyle } from '../../../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { Selection, SelectionDirection } from '../../../../../../editor/common/core/selection.js'; -import { IWordAtPosition, USUAL_WORD_SEPARATORS } from '../../../../../../editor/common/core/wordHelper.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { Selection, SelectionDirection } from '../../../../../../editor/common/language/core/selection.js'; +import { IWordAtPosition, USUAL_WORD_SEPARATORS } from '../../../../../../editor/common/language/core/wordHelper.js'; import { CommandExecutor, CursorsController } from '../../../../../../editor/common/cursor/cursor.js'; import { DeleteOperations } from '../../../../../../editor/common/cursor/cursorDeleteOperations.js'; import { CursorConfiguration, ICursorSimpleModel } from '../../../../../../editor/common/cursorCommon.js'; import { CursorChangeReason } from '../../../../../../editor/common/cursorEvents.js'; import { CompositionTypePayload, Handler, ReplacePreviousCharPayload } from '../../../../../../editor/common/editorCommon.js'; import { ILanguageConfigurationService } from '../../../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { IModelDeltaDecoration, ITextModel, PositionAffinity } from '../../../../../../editor/common/model.js'; -import { indentOfLine } from '../../../../../../editor/common/model/textModel.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { IModelDeltaDecoration, ITextModel, PositionAffinity } from '../../../../../../editor/common/language/model.js'; +import { indentOfLine } from '../../../../../../editor/common/language/model/textModel.js'; +import { ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { ICoordinatesConverter } from '../../../../../../editor/common/viewModel.js'; import { ViewModelEventsCollector } from '../../../../../../editor/common/viewModelEventDispatcher.js'; import { IAccessibilityService } from '../../../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts index 305e4d8fde4..15121ddfe1b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookSelectionHighlight.ts @@ -6,9 +6,9 @@ import { Event } from '../../../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; -import { Selection, SelectionDirection } from '../../../../../../editor/common/core/selection.js'; +import { Selection, SelectionDirection } from '../../../../../../editor/common/language/core/selection.js'; import { CursorChangeReason } from '../../../../../../editor/common/cursorEvents.js'; -import { FindMatch, IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/model.js'; +import { FindMatch, IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/language/model.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IActiveNotebookEditor, ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts index 57103835244..ef673f9a5f2 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts @@ -10,12 +10,12 @@ import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle import { ResourceMap } from '../../../../../../base/common/map.js'; import { isEqual } from '../../../../../../base/common/resources.js'; import { format } from '../../../../../../base/common/strings.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { StandardTokenType } from '../../../../../../editor/common/encodedTokenAttributes.js'; -import { InlineValueContext, InlineValueText, InlineValueVariableLookup } from '../../../../../../editor/common/languages.js'; -import { IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { StandardTokenType } from '../../../../../../editor/common/language/encodedTokenAttributes.js'; +import { InlineValueContext, InlineValueText, InlineValueVariableLookup } from '../../../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; import { localize } from '../../../../../../nls.js'; import { registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts index a66112843ab..7c5a3ac5778 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/outline/notebookOutline.ts @@ -15,7 +15,7 @@ import { FuzzyScore, createMatches } from '../../../../../../base/common/filters import { Disposable, DisposableStore, IDisposable, toDisposable, type IReference } from '../../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { getIconClassesForLanguageId } from '../../../../../../editor/common/services/getIconClasses.js'; +import { getIconClassesForLanguageId } from '../../../../../../editor/common/language/services/getIconClasses.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from '../../../../../../platform/configuration/common/configurationRegistry.js'; import { IEditorOptions } from '../../../../../../platform/editor/common/editor.js'; @@ -36,8 +36,8 @@ import { LifecyclePhase } from '../../../../../services/lifecycle/common/lifecyc import { IBreadcrumbsDataSource, IOutline, IOutlineComparator, IOutlineCreator, IOutlineListConfig, IOutlineService, IQuickPickDataSource, IQuickPickOutlineElement, OutlineChangeEvent, OutlineConfigCollapseItemsValues, OutlineConfigKeys, OutlineTarget } from '../../../../../services/outline/browser/outline.js'; import { OutlineEntry } from '../../viewModel/OutlineEntry.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; -import { IModelDeltaDecoration } from '../../../../../../editor/common/model.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { IModelDeltaDecoration } from '../../../../../../editor/common/language/model.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { mainWindow } from '../../../../../../base/browser/window.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; import { Action2, IMenu, IMenuService, MenuId, MenuItemAction, MenuRegistry, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; @@ -53,7 +53,7 @@ import { NOTEBOOK_IS_ACTIVE_EDITOR } from '../../../common/notebookContextKeys.j import { NotebookOutlineConstants } from '../../viewModel/notebookOutlineEntryFactory.js'; import { INotebookCellOutlineDataSourceFactory } from '../../viewModel/notebookOutlineDataSourceFactory.js'; import { INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; class NotebookOutlineTemplate { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts index d8dd4e13090..11e2d8be8e6 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts @@ -10,14 +10,14 @@ import { isEqual } from '../../../../../../base/common/resources.js'; import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { IBulkEditService, ResourceEdit, ResourceTextEdit } from '../../../../../../editor/browser/services/bulkEditService.js'; import { trimTrailingWhitespace } from '../../../../../../editor/common/commands/trimTrailingWhitespaceCommand.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../../editor/common/core/selection.js'; -import { CodeActionProvider, CodeActionTriggerType, IWorkspaceTextEdit } from '../../../../../../editor/common/languages.js'; -import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../../editor/common/language/core/selection.js'; +import { CodeActionProvider, CodeActionTriggerType, IWorkspaceTextEdit } from '../../../../../../editor/common/language/languages.js'; +import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { ApplyCodeActionReason, applyCodeAction, getCodeActions } from '../../../../../../editor/contrib/codeAction/browser/codeAction.js'; import { CodeActionItem, CodeActionKind, CodeActionTriggerSource } from '../../../../../../editor/contrib/codeAction/common/types.js'; import { FormattingMode, getDocumentFormattingEditsWithSelectedProvider } from '../../../../../../editor/contrib/format/browser/format.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts index 0795f4536e7..cca538c87ad 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOperations.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { IBulkEditService, ResourceEdit, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; -import { IPosition, Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { EndOfLinePreference, IReadonlyTextBuffer } from '../../../../../editor/common/model.js'; +import { IPosition, Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { EndOfLinePreference, IReadonlyTextBuffer } from '../../../../../editor/common/language/model.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../../editor/common/languages/modesRegistry.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ResourceNotebookCellEdit } from '../../../bulkEdit/browser/bulkCellEdits.js'; import { INotebookActionContext, INotebookCellActionContext } from './coreActions.js'; import { CellEditState, CellFocusMode, expandCellRangesWithHiddenCells, IActiveNotebookEditor, ICellViewModel } from '../notebookBrowser.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts index 1875c7304c3..6e8ae29bebe 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts @@ -6,12 +6,12 @@ import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { codiconsLibrary } from '../../../../../../base/common/codiconsLibrary.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { IWordAtPosition } from '../../../../../../editor/common/core/wordHelper.js'; -import { CompletionContext, CompletionItemKind, CompletionList } from '../../../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { IWordAtPosition } from '../../../../../../editor/common/language/core/wordHelper.js'; +import { CompletionContext, CompletionItemKind, CompletionList } from '../../../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; import { localize } from '../../../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index 2296896d9d9..4badd57bd4c 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -17,14 +17,14 @@ import { assertType } from '../../../../../../base/common/types.js'; import { URI } from '../../../../../../base/common/uri.js'; import { IActiveCodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { ISingleEditOperation } from '../../../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../../../editor/common/core/position.js'; -import { Selection } from '../../../../../../editor/common/core/selection.js'; -import { TextEdit } from '../../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; -import { ICursorStateComputer, ITextModel } from '../../../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; +import { ISingleEditOperation } from '../../../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../../../editor/common/language/core/position.js'; +import { Selection } from '../../../../../../editor/common/language/core/selection.js'; +import { TextEdit } from '../../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; +import { ICursorStateComputer, ITextModel } from '../../../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; +import { IModelService } from '../../../../../../editor/common/language/services/model.js'; import { localize } from '../../../../../../nls.js'; import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts index 3fe9771b282..ce30b5767f5 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts @@ -7,14 +7,14 @@ import { KeyChord, KeyCode, KeyMod } from '../../../../../base/common/keyCodes.j import { Mimes } from '../../../../../base/common/mime.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { CommandExecutor } from '../../../../../editor/common/cursor/cursor.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { getIconClasses } from '../../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { getIconClasses } from '../../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { LineCommentCommand, Type } from '../../../../../editor/contrib/comment/browser/lineCommentCommand.js'; import { localize, localize2 } from '../../../../../nls.js'; import { MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index b3e1fcdacca..6dac1c08c03 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -9,7 +9,7 @@ import { isEqual } from '../../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI, UriComponents } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { localize, localize2 } from '../../../../../nls.js'; import { MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts index b7bdf36f347..2486901dac7 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/insertCellActions.ts @@ -5,7 +5,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { localize } from '../../../../../nls.js'; import { IAction2Options, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions.ts index 1172d9c0a39..5e0e6fb374a 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions.ts @@ -7,9 +7,9 @@ import * as nls from '../../../../../nls.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index a0a5557ddab..69bafb64d98 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -10,8 +10,8 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { DiffElementCellViewModelBase, getFormattedOutputJSON, OutputComparison, outputEqual, OUTPUT_EDITOR_HEIGHT_MAGIC, PropertyFoldingState, SideBySideDiffElementViewModel, SingleSideDiffElementViewModel, DiffElementPlaceholderViewModel, IDiffElementViewModelBase, NotebookDocumentMetadataViewModel } from './diffElementViewModel.js'; import { CellDiffSideBySideRenderTemplate, CellDiffSingleSideRenderTemplate, DiffSide, DIFF_CELL_MARGIN, INotebookTextDiffEditor, NOTEBOOK_DIFF_CELL_INPUT, NOTEBOOK_DIFF_CELL_PROPERTY, NOTEBOOK_DIFF_CELL_PROPERTY_EXPANDED, CellDiffPlaceholderRenderTemplate, IDiffCellMarginOverlay, NOTEBOOK_DIFF_CELL_IGNORE_WHITESPACE, NotebookDocumentDiffElementRenderTemplate, NOTEBOOK_DIFF_METADATA } from './notebookDiffEditorBrowser.js'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { CellEditType, CellUri, NotebookCellMetadata } from '../../common/notebookCommon.js'; import { ToolBar } from '../../../../../base/browser/ui/toolbar/toolbar.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; @@ -32,7 +32,7 @@ import { SelectionClipboardContributionID } from '../../../codeEditor/browser/se import { TabCompletionController } from '../../../snippets/browser/tabCompletion.js'; import { renderIcon, renderLabelWithIcons } from '../../../../../base/browser/ui/iconLabel/iconLabels.js'; import * as editorCommon from '../../../../../editor/common/editorCommon.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; @@ -45,7 +45,7 @@ import { ICommandService } from '../../../../../platform/commands/common/command import { DiffNestedCellViewModel } from './diffNestedCellViewModel.js'; import { localize } from '../../../../../nls.js'; import { Emitter } from '../../../../../base/common/event.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { getFormattedMetadataJSON } from '../../common/model/notebookCellTextModel.js'; import { IDiffEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; import { getUnchangedRegionSettings } from './unchangedEditorRegions.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel.ts index cccd9f4ee2e..ee911c95071 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel.ts @@ -6,7 +6,7 @@ import { Emitter } from '../../../../../base/common/event.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; -import { PrefixSumComputer } from '../../../../../editor/common/model/prefixSumComputer.js'; +import { PrefixSumComputer } from '../../../../../editor/common/language/model/prefixSumComputer.js'; import { IDiffNestedCellViewModel } from './notebookDiffEditorBrowser.js'; import { ICellOutputViewModel, IGenericCellViewModel } from '../notebookBrowser.js'; import { CellViewModelStateChangeEvent } from '../notebookViewEvents.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/editorHeightCalculator.ts b/src/vs/workbench/contrib/notebook/browser/diff/editorHeightCalculator.ts index 6c4bbaaa86a..fdce5183f61 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/editorHeightCalculator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/editorHeightCalculator.ts @@ -5,8 +5,8 @@ import { URI } from '../../../../../base/common/uri.js'; import { UnchangedRegion } from '../../../../../editor/browser/widget/diffEditor/diffEditorViewModel.js'; -import { IEditorWorkerService } from '../../../../../editor/common/services/editorWorker.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IEditorWorkerService } from '../../../../../editor/common/language/services/editorWorker.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { getEditorPadding } from './diffCellEditorOptions.js'; import { HeightOfHiddenLinesRegionInDiffEditor } from './diffElementViewModel.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts index 8da4d2c230d..564863cfea9 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts @@ -8,16 +8,16 @@ import { autorunWithStore, derived, observableFromEvent } from '../../../../../. import { INotebookEditor } from '../../notebookBrowser.js'; import { ThrottledDelayer } from '../../../../../../base/common/async.js'; import { ICodeEditor, IViewZone } from '../../../../../../editor/browser/editorBrowser.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; import { themeColorFromId } from '../../../../../../base/common/themables.js'; import { RenderOptions, LineSource, renderLines } from '../../../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { diffAddDecoration, diffWholeLineAddDecoration, diffDeleteDecoration } from '../../../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; -import { ITextModel, TrackedRangeStickiness, MinimapPosition, IModelDeltaDecoration, OverviewRulerLane } from '../../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; +import { ITextModel, TrackedRangeStickiness, MinimapPosition, IModelDeltaDecoration, OverviewRulerLane } from '../../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../../editor/common/language/model/textModel.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../editor/common/viewModel.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts index 540f3080980..1d8559fa527 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts @@ -7,7 +7,7 @@ import { createTrustedTypesPolicy } from '../../../../../../base/browser/trusted import { Disposable, DisposableStore, dispose, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { splitLines } from '../../../../../../base/common/strings.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { tokenizeToString } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts index fe62546116f..529f7d067fc 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts @@ -5,11 +5,11 @@ import { IReference, ReferenceCollection } from '../../../../../../base/common/lifecycle.js'; import { createDecorator, IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { CellKind } from '../../../common/notebookCommon.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; +import { IModelService } from '../../../../../../editor/common/language/services/model.js'; export const INotebookOriginalCellModelFactory = createDecorator('INotebookOriginalCellModelFactory'); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts index 8007f857b45..47cd0f4918f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts @@ -9,7 +9,7 @@ import { INotebookService } from '../../../common/notebookService.js'; import { bufferToStream, VSBuffer } from '../../../../../../base/common/buffer.js'; import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; import { createDecorator, IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; export const INotebookOriginalModelReferenceFactory = createDecorator('INotebookOriginalModelReferenceFactory'); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index e3438e83bd7..a5a91b2b66c 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -23,7 +23,7 @@ import { DEFAULT_EDITOR_ASSOCIATION } from '../../../../common/editor.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { CellEditType, ICellEditOperation, NOTEBOOK_DIFF_EDITOR_ID } from '../../common/notebookCommon.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { NotebookMultiTextDiffEditor } from './notebookMultiDiffEditor.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import type { URI } from '../../../../../base/common/uri.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts index c6d7549eb6e..c12bcef0776 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts @@ -31,7 +31,7 @@ import type { IMultiDiffEditorOptions } from '../../../../../editor/browser/widg import { INotebookDocumentService } from '../../../../services/notebook/common/notebookDocumentService.js'; import { localize } from '../../../../../nls.js'; import { Schemas } from '../../../../../base/common/network.js'; -import { getIconClassesForLanguageId } from '../../../../../editor/common/services/getIconClasses.js'; +import { getIconClassesForLanguageId } from '../../../../../editor/common/language/services/getIconClasses.js'; import { NotebookDiffViewModel } from './notebookDiffViewModel.js'; import { NotebookDiffEditorEventDispatcher } from './eventDispatcher.js'; import { NOTEBOOK_DIFF_CELLS_COLLAPSED, NOTEBOOK_DIFF_HAS_UNCHANGED_CELLS, NOTEBOOK_DIFF_UNCHANGED_CELLS_HIDDEN } from './notebookDiffEditorBrowser.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditorInput.ts index c84dcb0e3de..45a6f478583 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditorInput.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../base/common/uri.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; +import { ITextResourceConfigurationService } from '../../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiffEditorInput.js'; import { IMultiDiffSourceResolverService, IResolvedMultiDiffSource, type IMultiDiffSourceResolver } from '../../../multiDiffEditor/browser/multiDiffSourceResolverService.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 10a50d99cdf..c1c1191567b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -10,10 +10,10 @@ import { extname, isEqual } from '../../../../base/common/resources.js'; import { assertType } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { toFormattedString } from '../../../../base/common/jsonFormatter.js'; -import { ITextModel, ITextBufferFactory, DefaultEndOfLine, ITextBuffer } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageSelection, ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModel, ITextBufferFactory, DefaultEndOfLine, ITextBuffer } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageSelection, ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import * as nls from '../../../../nls.js'; import { Extensions, IConfigurationPropertySchema, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; @@ -114,7 +114,7 @@ import { INotebookKeymapService } from '../common/notebookKeymapService.js'; import { NotebookKeymapService } from './services/notebookKeymapServiceImpl.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modesRegistry.js'; import { INotebookExecutionStateService } from '../common/notebookExecutionStateService.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { NotebookInfo } from '../../../../editor/common/languageFeatureRegistry.js'; import { COMMENTEDITOR_DECORATION_KEY } from '../../comments/browser/commentReply.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index cf385680dfc..67d0c017108 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -11,10 +11,10 @@ import { URI } from '../../../../base/common/uri.js'; import { IEditorContributionDescription } from '../../../../editor/browser/editorExtensions.js'; import * as editorCommon from '../../../../editor/common/editorCommon.js'; import { FontInfo } from '../../../../editor/common/config/fontInfo.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; -import { FindMatch, IModelDeltaDecoration, IReadonlyTextBuffer, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { FindMatch, IModelDeltaDecoration, IReadonlyTextBuffer, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; import { ITextEditorOptions, ITextResourceEditorInput } from '../../../../platform/editor/common/editor.js'; import { IConstructorSignature } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index b289cdb6142..f63a332458c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -13,7 +13,7 @@ import { DisposableStore, MutableDisposable } from '../../../../base/common/life import { extname, isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { localize } from '../../../../nls.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IEditorOptions } from '../../../../platform/editor/common/editor.js'; @@ -22,7 +22,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IStorageService } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; import { DEFAULT_EDITOR_ASSOCIATION, EditorPaneSelectionChangeReason, EditorPaneSelectionCompareResult, EditorResourceAccessor, IEditorMemento, IEditorOpenContext, IEditorPaneScrollPosition, IEditorPaneSelection, IEditorPaneSelectionChangeEvent, IEditorPaneWithScrolling, createEditorOpenError, createTooLargeFileError, isEditorOpenError } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index c6d652b63a0..e4b44c11e24 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -38,8 +38,8 @@ import { FontMeasurements } from '../../../../editor/browser/config/fontMeasurem import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { BareFontInfo, FontInfo } from '../../../../editor/common/config/fontInfo.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js'; import * as nls from '../../../../nls.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; @@ -91,7 +91,7 @@ import { IEditorGroupsService } from '../../../services/editor/common/editorGrou import { NotebookPerfMarks } from '../common/notebookPerformance.js'; import { BaseCellEditorOptions } from './viewModel/cellEditorOptions.js'; import { FloatingEditorClickMenu } from '../../../browser/codeeditor.js'; -import { IDimension } from '../../../../editor/common/core/dimension.js'; +import { IDimension } from '../../../../editor/common/language/core/dimension.js'; import { CellFindMatchModel } from './contrib/find/findModel.js'; import { INotebookLoggingService } from '../common/notebookLoggingService.js'; import { Schemas } from '../../../../base/common/network.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts index c4612d46048..9c23308f077 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookEditorServiceImpl.ts @@ -215,7 +215,6 @@ export class NotebookEditorWidgetService implements INotebookEditorService { let value = this._borrowableEditors.get(groupId)?.get(input.resource)?.find(widget => widget.editorType === input.typeId); if (!value) { - // NEW widget const editorGroupContextKeyService = accessor.get(IContextKeyService); const editorGroupEditorProgressService = accessor.get(IEditorProgressService); const widgetDisposeStore = new DisposableStore(); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts index 8086431811b..dc25604edf1 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts @@ -12,9 +12,9 @@ import { CellUri, IMainCellDto, INotebookDiffResult, NotebookCellsChangeType, No import { INotebookService } from '../../common/notebookService.js'; import { NotebookWorker } from '../../common/services/notebookWebWorker.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { TextModel } from '../../../../../editor/common/model/textModel.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { FileAccess, Schemas } from '../../../../../base/common/network.js'; import { isEqual } from '../../../../../base/common/resources.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index 276dd3b8bc4..720e6a98803 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -6,7 +6,7 @@ import { coalesce } from '../../../../../../base/common/arrays.js'; import { DisposableMap, DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js'; -import * as languages from '../../../../../../editor/common/languages.js'; +import * as languages from '../../../../../../editor/common/language/languages.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts index b02c85371d4..454d1d32c82 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDragRenderer.ts @@ -9,11 +9,11 @@ import { Color } from '../../../../../../base/common/color.js'; import * as platform from '../../../../../../base/common/platform.js'; import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { ColorId } from '../../../../../../editor/common/encodedTokenAttributes.js'; -import * as languages from '../../../../../../editor/common/languages.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { ColorId } from '../../../../../../editor/common/language/encodedTokenAttributes.js'; +import * as languages from '../../../../../../editor/common/language/languages.js'; import { tokenizeLineToHTML } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { BaseCellRenderTemplate } from '../notebookRenderingCommon.js'; class EditorTextRenderer { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts index 58ae569e6e4..de33342d515 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellEditorOptions.ts @@ -21,7 +21,7 @@ import { CellContentPart } from '../cellPart.js'; import { NotebookCellInternalMetadata, NOTEBOOK_EDITOR_ID } from '../../../common/notebookCommon.js'; import { NotebookOptions } from '../../notebookOptions.js'; import { CellViewModelStateChangeEvent } from '../../notebookViewEvents.js'; -import { ITextModelUpdateOptions } from '../../../../../../editor/common/model.js'; +import { ITextModelUpdateOptions } from '../../../../../../editor/common/language/model.js'; //todo@Yoyokrazy implenets is needed or not? export class CellEditorOptions extends CellContentPart implements ITextModelUpdateOptions { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts index b595e863285..656cae97025 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/codeCell.ts @@ -14,10 +14,10 @@ import { clamp } from '../../../../../../base/common/numbers.js'; import * as strings from '../../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../../base/common/themables.js'; import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; -import { IDimension } from '../../../../../../editor/common/core/dimension.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { IDimension } from '../../../../../../editor/common/language/core/dimension.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { tokenizeToStringSync } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; -import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/model.js'; +import { IReadonlyTextBuffer, ITextModel } from '../../../../../../editor/common/language/model.js'; import { CodeActionController } from '../../../../../../editor/contrib/codeAction/browser/codeActionController.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts index 1797591072f..31156541b30 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markupCell.ts @@ -14,9 +14,9 @@ import { ICodeEditor } from '../../../../../../editor/browser/editorBrowser.js'; import { CodeEditorWidget } from '../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorOptions } from '../../../../../../editor/common/config/editorOptions.js'; import { EditorContextKeys } from '../../../../../../editor/common/editorContextKeys.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { tokenizeToStringSync } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; -import { IReadonlyTextBuffer } from '../../../../../../editor/common/model.js'; +import { IReadonlyTextBuffer } from '../../../../../../editor/common/language/model.js'; import { localize } from '../../../../../../nls.js'; import { IAccessibilityService } from '../../../../../../platform/accessibility/common/accessibility.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts index 973f914aecb..12f95e3a147 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellEditorPool.ts @@ -8,7 +8,7 @@ import { CancelablePromise, createCancelablePromise } from '../../../../../base/ import { Disposable, DisposableStore, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { CodeEditorWidget } from '../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService, IScopedContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index 508e9f18e06..762b00b6477 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -12,10 +12,10 @@ import { Emitter, Event } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { isMacintosh } from '../../../../../base/common/platform.js'; import { ScrollEvent } from '../../../../../base/common/scrollable.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; -import { TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { PrefixSumComputer } from '../../../../../editor/common/model/prefixSumComputer.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; +import { TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { PrefixSumComputer } from '../../../../../editor/common/language/model/prefixSumComputer.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IListService, IWorkbenchListOptions, WorkbenchList } from '../../../../../platform/list/browser/listService.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts index 8d64212d915..28238b445c0 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts @@ -6,7 +6,7 @@ import { IRange } from '../../../../../base/common/range.js'; import { ListView } from '../../../../../base/browser/ui/list/listView.js'; import { IItem, IRangeMap } from '../../../../../base/browser/ui/list/rangeMap.js'; -import { ConstantTimePrefixSumComputer } from '../../../../../editor/common/model/prefixSumComputer.js'; +import { ConstantTimePrefixSumComputer } from '../../../../../editor/common/language/model/prefixSumComputer.js'; export interface IWhitespace { id: string; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts index 0d1adcd6fec..c9178f5dbc4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon.ts @@ -11,8 +11,8 @@ import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ScrollEvent } from '../../../../../base/common/scrollable.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IWorkbenchListOptionsUpdate } from '../../../../../platform/list/browser/listService.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index b6c10143b39..56247294ad8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -20,8 +20,8 @@ import { isMacintosh, isWeb } from '../../../../../../base/common/platform.js'; import { dirname, extname, isEqual, joinPath } from '../../../../../../base/common/resources.js'; import { URI } from '../../../../../../base/common/uri.js'; import * as UUID from '../../../../../../base/common/uuid.js'; -import { TokenizationRegistry } from '../../../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { TokenizationRegistry } from '../../../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { generateTokensCSSForColorMap } from '../../../../../../editor/common/languages/supports/tokenization.js'; import { tokenizeToString } from '../../../../../../editor/common/languages/textToHtmlTokenizer.js'; import * as nls from '../../../../../../nls.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts index 833d9aa5a14..784893302e9 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/OutlineEntry.ts @@ -8,8 +8,8 @@ import { IMarkerService, MarkerSeverity } from '../../../../../platform/markers/ import { ICellViewModel } from '../notebookBrowser.js'; import { executingStateIcon } from '../notebookIcons.js'; import { CellKind } from '../../common/notebookCommon.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; -import { SymbolKind, SymbolKinds } from '../../../../../editor/common/languages.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; +import { SymbolKind, SymbolKinds } from '../../../../../editor/common/language/languages.js'; export interface IOutlineMarkerInfo { readonly count: number; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index cae7e893d81..19a9ae53efe 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -9,13 +9,13 @@ import { Mimes } from '../../../../../base/common/mime.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; import { IEditorCommentsOptions } from '../../../../../editor/common/config/editorOptions.js'; -import { IPosition } from '../../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import * as editorCommon from '../../../../../editor/common/editorCommon.js'; -import * as model from '../../../../../editor/common/model.js'; -import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import * as model from '../../../../../editor/common/language/model.js'; +import { SearchParams } from '../../../../../editor/common/language/model/textModelSearch.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { IWordWrapTransientState, readTransientState, writeTransientState } from '../../../codeEditor/browser/toggleWordWrap.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts index 7878dbc978d..e03e86b4ccf 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellEdit.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../../../editor/common/core/range.js'; -import { Selection } from '../../../../../editor/common/core/selection.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../../editor/common/language/core/selection.js'; import { CellKind, IOutputDto, NotebookCellMetadata, SelectionStateType } from '../../common/notebookCommon.js'; import { IResourceUndoRedoElement, UndoRedoElementType } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { URI } from '../../../../../base/common/uri.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index a1ce3e95da4..a054d26e3f0 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -9,8 +9,8 @@ import { observableValue } from '../../../../../base/common/observable.js'; import * as UUID from '../../../../../base/common/uuid.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; import * as editorCommon from '../../../../../editor/common/editorCommon.js'; -import { PrefixSumComputer } from '../../../../../editor/common/model/prefixSumComputer.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { PrefixSumComputer } from '../../../../../editor/common/language/model/prefixSumComputer.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { CellEditState, CellFindMatch, CellLayoutState, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellOutputViewModel, ICellViewModel } from '../notebookBrowser.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/foldingModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/foldingModel.ts index 2cda442af03..ca5ecb64020 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/foldingModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/foldingModel.ts @@ -7,7 +7,7 @@ import { renderMarkdownAsPlaintext } from '../../../../../base/browser/markdownR import { Emitter, Event } from '../../../../../base/common/event.js'; import { DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; import { marked } from '../../../../../base/common/marked/marked.js'; -import { TrackedRangeStickiness } from '../../../../../editor/common/model.js'; +import { TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; import { FoldingLimitReporter } from '../../../../../editor/contrib/folding/browser/folding.js'; import { FoldingRegion, FoldingRegions } from '../../../../../editor/contrib/folding/browser/foldingRanges.js'; import { IFoldingRangeData, sanitizeRanges } from '../../../../../editor/contrib/folding/browser/syntaxRangeProvider.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts index 875d0f18c49..af29aa4f828 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts @@ -11,7 +11,7 @@ import { CellEditState, CellFindMatch, CellFoldingState, CellLayoutContext, Cell import { BaseCellViewModel } from './baseCellViewModel.js'; import { NotebookCellTextModel } from '../../common/model/notebookCellTextModel.js'; import { CellKind, INotebookFindOptions } from '../../common/notebookCommon.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { ViewContext } from './viewContext.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { NotebookOptionsChangeEvent } from '../notebookOptions.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts index 6c708a2fa67..cb032e4a607 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts @@ -12,10 +12,10 @@ import { getMarkdownHeadersInCell } from './foldingModel.js'; import { OutlineEntry } from './OutlineEntry.js'; import { CellKind } from '../../common/notebookCommon.js'; import { INotebookExecutionStateService } from '../../common/notebookExecutionStateService.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; -import { SymbolKind } from '../../../../../editor/common/languages.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; +import { SymbolKind } from '../../../../../editor/common/language/languages.js'; import { createDecorator } from '../../../../../platform/instantiation/common/instantiation.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; export const enum NotebookOutlineConstants { NonHeaderOutlineLevel = 7, diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 56a7c1409b1..545e6ef01fd 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -11,14 +11,14 @@ import { clamp } from '../../../../../base/common/numbers.js'; import * as strings from '../../../../../base/common/strings.js'; import { URI } from '../../../../../base/common/uri.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import * as editorCommon from '../../../../../editor/common/editorCommon.js'; -import { IWorkspaceTextEdit } from '../../../../../editor/common/languages.js'; -import { FindMatch, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { MultiModelEditStackElement, SingleModelEditStackElement } from '../../../../../editor/common/model/editStack.js'; -import { IntervalNode, IntervalTree } from '../../../../../editor/common/model/intervalTree.js'; -import { ModelDecorationOptions } from '../../../../../editor/common/model/textModel.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IWorkspaceTextEdit } from '../../../../../editor/common/language/languages.js'; +import { FindMatch, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { MultiModelEditStackElement, SingleModelEditStackElement } from '../../../../../editor/common/language/model/editStack.js'; +import { IntervalNode, IntervalTree } from '../../../../../editor/common/language/model/intervalTree.js'; +import { ModelDecorationOptions } from '../../../../../editor/common/language/model/textModel.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { FoldingRegions } from '../../../../../editor/contrib/folding/browser/foldingRanges.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts index 3e08af275e0..756dc71bc9c 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookKernelQuickPickStrategy.ts @@ -12,7 +12,7 @@ import { Event } from '../../../../../base/common/event.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { MarshalledId } from '../../../../../base/common/marshallingIds.js'; import { uppercaseFirstLetter } from '../../../../../base/common/strings.js'; -import { Command } from '../../../../../editor/common/languages.js'; +import { Command } from '../../../../../editor/common/language/languages.js'; import { localize } from '../../../../../nls.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts index 3970dd6be5f..def970fe562 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookCellTextModel.ts @@ -8,18 +8,18 @@ import { hash, StringSHA1 } from '../../../../../base/common/hash.js'; import { Disposable, DisposableStore, dispose } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import * as UUID from '../../../../../base/common/uuid.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import * as model from '../../../../../editor/common/model.js'; -import { PieceTreeTextBuffer } from '../../../../../editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; -import { createTextBuffer, TextModel } from '../../../../../editor/common/model/textModel.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import * as model from '../../../../../editor/common/language/model.js'; +import { PieceTreeTextBuffer } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBuffer.js'; +import { createTextBuffer, TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { PLAINTEXT_LANGUAGE_ID } from '../../../../../editor/common/languages/modesRegistry.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { NotebookCellOutputTextModel } from './notebookCellOutputTextModel.js'; import { CellInternalMetadataChangedEvent, CellKind, ICell, ICellDto2, ICellOutput, IOutputDto, IOutputItemDto, NotebookCellCollapseState, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellOutputsSplice, TransientCellMetadata, TransientOptions } from '../notebookCommon.js'; import { ThrottledDelayer } from '../../../../../base/common/async.js'; import { ILanguageDetectionService } from '../../../../services/languageDetection/common/languageDetectionWorkerService.js'; import { toFormattedString } from '../../../../../base/common/jsonFormatter.js'; -import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { splitLines } from '../../../../../base/common/strings.js'; export class NotebookCellTextModel extends Disposable implements ICell { diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookMetadataTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookMetadataTextModel.ts index 80ea702c5f0..c629836d0c8 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookMetadataTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookMetadataTextModel.ts @@ -8,10 +8,10 @@ import { INotebookDocumentMetadataTextModel, INotebookTextModel, NotebookCellMet import { StringSHA1 } from '../../../../../base/common/hash.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from '../../../../../editor/common/model.js'; +import { DefaultEndOfLine, EndOfLinePreference, ITextBuffer } from '../../../../../editor/common/language/model.js'; import { Emitter } from '../../../../../base/common/event.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { createTextBuffer } from '../../../../../editor/common/model/textModel.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { createTextBuffer } from '../../../../../editor/common/language/model/textModel.js'; export function getFormattedNotebookMetadataJSON(transientMetadata: TransientDocumentMetadata | undefined, metadata: NotebookDocumentMetadata) { let filteredMetadata: { [key: string]: any } = {}; diff --git a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts index 237e0ff8bdf..e8a9032f72d 100644 --- a/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts +++ b/src/vs/workbench/contrib/notebook/common/model/notebookTextModel.ts @@ -12,14 +12,14 @@ import { filter } from '../../../../../base/common/objects.js'; import { isEqual } from '../../../../../base/common/resources.js'; import { isDefined } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { FindMatch, ITextModel } from '../../../../../editor/common/model.js'; -import { TextModel } from '../../../../../editor/common/model/textModel.js'; -import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { FindMatch, ITextModel } from '../../../../../editor/common/language/model.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; +import { SearchParams } from '../../../../../editor/common/language/model/textModelSearch.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { IModelContentChangedEvent } from '../../../../../editor/common/language/textModelEvents.js'; import { IResourceUndoRedoElement, IUndoRedoElement, IUndoRedoService, IWorkspaceUndoRedoElement, UndoRedoElementType, UndoRedoGroup } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { ILanguageDetectionService } from '../../../../services/languageDetection/common/languageDetectionWorkerService.js'; import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index 40c4bd348ab..fa2e5dbe020 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -18,10 +18,10 @@ import { isWindows } from '../../../../base/common/platform.js'; import { ISplice } from '../../../../base/common/sequence.js'; import { ThemeColor } from '../../../../base/common/themables.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import * as editorCommon from '../../../../editor/common/editorCommon.js'; -import { Command, WorkspaceEditMetadata } from '../../../../editor/common/languages.js'; -import { IReadonlyTextBuffer, ITextModel } from '../../../../editor/common/model.js'; +import { Command, WorkspaceEditMetadata } from '../../../../editor/common/language/languages.js'; +import { IReadonlyTextBuffer, ITextModel } from '../../../../editor/common/language/model.js'; import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; diff --git a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts index f6c4b05d618..9471c29a1f0 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookEditorInput.ts @@ -29,7 +29,7 @@ import { IExtensionService } from '../../../services/extensions/common/extension import { localize } from '../../../../nls.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IMarkdownString } from '../../../../base/common/htmlContent.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ICustomEditorLabelService } from '../../../services/editor/common/customEditorLabelService.js'; export interface NotebookEditorInputOptions { diff --git a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts index 8ad13f68f51..5d7dc6f4872 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookExecutionStateService.ts @@ -6,7 +6,7 @@ import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { NotebookCellExecutionState, NotebookExecutionState } from './notebookCommon.js'; import { CellExecutionUpdateType, ICellExecuteOutputEdit, ICellExecuteOutputItemEdit } from './notebookExecutionService.js'; diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts index d95e43c5608..2bcf1b6b356 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookWebWorker.ts @@ -7,13 +7,13 @@ import { doHash, hash, numberHash } from '../../../../../base/common/hash.js'; import { IDisposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { IWebWorkerServerRequestHandler } from '../../../../../base/common/worker/webWorker.js'; -import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; +import { PieceTreeTextBufferBuilder } from '../../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; import { CellKind, IMainCellDto, INotebookDiffResult, IOutputDto, NotebookCellInternalMetadata, NotebookCellMetadata, NotebookCellsChangedEventDto, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookDocumentMetadata, TransientDocumentMetadata } from '../notebookCommon.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { SearchParams } from '../../../../../editor/common/model/textModelSearch.js'; -import { MirrorModel } from '../../../../../editor/common/services/textModelSync/textModelSync.impl.js'; -import { DefaultEndOfLine } from '../../../../../editor/common/model.js'; -import { IModelChangedEvent } from '../../../../../editor/common/model/mirrorTextModel.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { SearchParams } from '../../../../../editor/common/language/model/textModelSearch.js'; +import { MirrorModel } from '../../../../../editor/common/language/services/textModelSync/textModelSync.impl.js'; +import { DefaultEndOfLine } from '../../../../../editor/common/language/model.js'; +import { IModelChangedEvent } from '../../../../../editor/common/language/model/mirrorTextModel.js'; import { filter } from '../../../../../base/common/objects.js'; import { matchCellBasedOnSimilarties } from './notebookCellMatching.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/cellOperations.test.ts b/src/vs/workbench/contrib/notebook/test/browser/cellOperations.test.ts index ba4a760c42f..c869f1599f6 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/cellOperations.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/cellOperations.test.ts @@ -8,11 +8,11 @@ import { FoldingModel, updateFoldingStateAtIndex } from '../../browser/viewModel import { changeCellToKind, computeCellLinesContents, copyCellRange, insertCell, joinNotebookCells, moveCellRange, runDeleteAction } from '../../browser/controller/cellOperations.js'; import { CellEditType, CellKind, SelectionStateType } from '../../common/notebookCommon.js'; import { withTestNotebook } from './testNotebookEditor.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { ResourceTextEdit } from '../../../../../editor/browser/services/bulkEditService.js'; import { ResourceNotebookCellEdit } from '../../../bulkEdit/browser/bulkCellEdits.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextBuffer, ValidAnnotatedEditOperation } from '../../../../../editor/common/model.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextBuffer, ValidAnnotatedEditOperation } from '../../../../../editor/common/language/model.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('CellOperations', () => { diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts index 9bbb172b694..75ceb95020f 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/find.test.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { FindMatch, ITextBuffer, ValidAnnotatedEditOperation } from '../../../../../../editor/common/model.js'; -import { USUAL_WORD_SEPARATORS } from '../../../../../../editor/common/core/wordHelper.js'; -import { ILanguageService } from '../../../../../../editor/common/languages/language.js'; +import { Range } from '../../../../../../editor/common/language/core/range.js'; +import { FindMatch, ITextBuffer, ValidAnnotatedEditOperation } from '../../../../../../editor/common/language/model.js'; +import { USUAL_WORD_SEPARATORS } from '../../../../../../editor/common/language/core/wordHelper.js'; +import { ILanguageService } from '../../../../../../editor/common/language/language.js'; import { FindReplaceState } from '../../../../../../editor/contrib/find/browser/findState.js'; import { IConfigurationService, IConfigurationValue } from '../../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts index 81042265ee8..0260cf05de4 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutline.test.ts @@ -18,8 +18,8 @@ import { DisposableStore } from '../../../../../../base/common/lifecycle.js'; import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { NotebookCellOutline, NotebookOutlineCreator } from '../../../browser/contrib/outline/notebookOutline.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../../../editor/common/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../../editor/common/language/services/languageFeaturesService.js'; import { IEditorPaneSelectionChangeEvent } from '../../../../../common/editor.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { INotebookOutlineEntryFactory, NotebookOutlineEntryFactory } from '../../../browser/viewModel/notebookOutlineEntryFactory.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutlineViewProviders.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutlineViewProviders.test.ts index a7f35bacea3..a399235a0e1 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutlineViewProviders.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookOutlineViewProviders.test.ts @@ -9,7 +9,7 @@ import { CancellationToken } from '../../../../../../base/common/cancellation.js import { IReference } from '../../../../../../base/common/lifecycle.js'; import { mock } from '../../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { IOutlineModelService, OutlineModel } from '../../../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestThemeService } from '../../../../../../platform/theme/test/common/testThemeService.js'; @@ -20,7 +20,7 @@ import { NotebookOutlineEntryFactory } from '../../../browser/viewModel/notebook import { OutlineEntry } from '../../../browser/viewModel/OutlineEntry.js'; import { INotebookExecutionStateService } from '../../../common/notebookExecutionStateService.js'; import { MockDocumentSymbol } from '../testNotebookEditor.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { URI } from '../../../../../../base/common/uri.js'; suite('Notebook Outline View Providers', function () { diff --git a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts index 1eaf85e79f5..7ac3f80439b 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/contrib/notebookSymbols.test.ts @@ -7,13 +7,13 @@ import assert from 'assert'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { mock } from '../../../../../../base/test/common/mock.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/test/common/utils.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { IOutlineModelService, OutlineModel } from '../../../../../../editor/contrib/documentSymbols/browser/outlineModel.js'; import { ICellViewModel } from '../../../browser/notebookBrowser.js'; import { NotebookOutlineEntryFactory } from '../../../browser/viewModel/notebookOutlineEntryFactory.js'; import { INotebookExecutionStateService } from '../../../common/notebookExecutionStateService.js'; import { MockDocumentSymbol } from '../testNotebookEditor.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { URI } from '../../../../../../base/common/uri.js'; import { IReference } from '../../../../../../base/common/lifecycle.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/diff/editorHeightCalculator.test.ts b/src/vs/workbench/contrib/notebook/test/browser/diff/editorHeightCalculator.test.ts index 64dd4ba3073..548ca95c566 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/diff/editorHeightCalculator.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/diff/editorHeightCalculator.test.ts @@ -10,12 +10,12 @@ import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../base/ import { TestConfigurationService } from '../../../../../../platform/configuration/test/common/testConfigurationService.js'; import { DiffEditorHeightCalculatorService } from '../../../browser/diff/editorHeightCalculator.js'; import { FontInfo } from '../../../../../../editor/common/config/fontInfo.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../../editor/common/language/services/resolverService.js'; import { URI } from '../../../../../../base/common/uri.js'; import { createTextModel as createTextModelWithText } from '../../../../../../editor/test/common/testTextModel.js'; -import { ITextModel } from '../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../editor/common/language/model.js'; import { DefaultLinesDiffComputer } from '../../../../../../editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; -import { DiffAlgorithmName, IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; +import { DiffAlgorithmName, IEditorWorkerService } from '../../../../../../editor/common/language/services/editorWorker.js'; import { IDocumentDiffProviderOptions, IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; import { getEditorPadding } from '../../../browser/diff/diffCellEditorOptions.js'; import { HeightOfHiddenLinesRegionInDiffEditor } from '../../../browser/diff/diffElementViewModel.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookCommon.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookCommon.test.ts index a5c1637df4c..4b6abc92dbd 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookCommon.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookCommon.test.ts @@ -8,7 +8,7 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../../base/common/mime.js'; import { URI } from '../../../../../base/common/uri.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { CellKind, CellUri, diff, MimeTypeDisplayOrder, NotebookWorkingCopyTypeIdentifier } from '../../common/notebookCommon.js'; import { cellIndexesToRanges, cellRangesToIndexes, reduceCellRanges } from '../../common/notebookRange.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookSelection.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookSelection.test.ts index 05072b46b2e..efe12532962 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookSelection.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookSelection.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { FoldingModel, updateFoldingStateAtIndex } from '../../browser/viewModel/foldingModel.js'; import { runDeleteAction } from '../../browser/controller/cellOperations.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts index 2b6b687f3a0..f29604dac3d 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookStickyScroll.test.ts @@ -9,8 +9,8 @@ import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { ILanguageFeaturesService } from '../../../../../editor/common/services/languageFeatures.js'; -import { LanguageFeaturesService } from '../../../../../editor/common/services/languageFeaturesService.js'; +import { ILanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeatures.js'; +import { LanguageFeaturesService } from '../../../../../editor/common/language/services/languageFeaturesService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IEditorPaneSelectionChangeEvent } from '../../../../common/editor.js'; import { NotebookCellOutline } from '../../browser/contrib/outline/notebookOutline.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts index 613ee43d9a3..61c9deaa884 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookTextModel.test.ts @@ -8,8 +8,8 @@ import { VSBuffer } from '../../../../../base/common/buffer.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../../base/common/mime.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/notebookViewModel.test.ts b/src/vs/workbench/contrib/notebook/test/browser/notebookViewModel.test.ts index d12be746e53..8df86115948 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/notebookViewModel.test.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/notebookViewModel.test.ts @@ -7,10 +7,10 @@ import assert from 'assert'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; import { IBulkEditService } from '../../../../../editor/browser/services/bulkEditService.js'; -import { TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index bbca74e25cc..1bd126e0703 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -14,12 +14,12 @@ import { Mimes } from '../../../../../base/common/mime.js'; import { URI } from '../../../../../base/common/uri.js'; import { mock } from '../../../../../base/test/common/mock.js'; import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { ILanguageConfigurationService } from '../../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { LanguageService } from '../../../../../editor/common/services/languageService.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ModelService } from '../../../../../editor/common/services/modelService.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { LanguageService } from '../../../../../editor/common/language/services/languageService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../../../editor/common/language/services/modelService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { TestLanguageConfigurationService } from '../../../../../editor/test/common/modes/testLanguageConfigurationService.js'; import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; import { TestClipboardService } from '../../../../../platform/clipboard/test/common/testClipboardService.js'; diff --git a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts index 4fa628f94ac..1aa72b31ee1 100644 --- a/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts +++ b/src/vs/workbench/contrib/output/browser/outputLinkProvider.ts @@ -5,16 +5,16 @@ import { URI } from '../../../../base/common/uri.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILink } from '../../../../editor/common/languages.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILink } from '../../../../editor/common/language/languages.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { OUTPUT_MODE_ID, LOG_MODE_ID } from '../../../services/output/common/output.js'; import { OutputLinkComputer } from '../common/outputLinkComputer.js'; import { IDisposable, dispose, Disposable } from '../../../../base/common/lifecycle.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { createWebWorker } from '../../../../base/browser/webWorkerFactory.js'; import { IWebWorkerClient } from '../../../../base/common/worker/webWorker.js'; -import { WorkerTextModelSyncClient } from '../../../../editor/common/services/textModelSync/textModelSync.impl.js'; +import { WorkerTextModelSyncClient } from '../../../../editor/common/language/services/textModelSync/textModelSync.impl.js'; import { FileAccess } from '../../../../base/common/network.js'; export class OutputLinkProvider extends Disposable { diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index f644a0f2cc1..731dd4d04ed 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -12,14 +12,14 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { Registry } from '../../../../platform/registry/common/platform.js'; import { IOutputChannel, IOutputService, OUTPUT_VIEW_ID, LOG_MIME, OUTPUT_MIME, OutputChannelUpdateMode, IOutputChannelDescriptor, Extensions, IOutputChannelRegistry, ACTIVE_OUTPUT_CHANNEL_CONTEXT, CONTEXT_ACTIVE_FILE_OUTPUT, CONTEXT_ACTIVE_OUTPUT_LEVEL_SETTABLE, CONTEXT_ACTIVE_OUTPUT_LEVEL, CONTEXT_ACTIVE_OUTPUT_LEVEL_IS_DEFAULT, IOutputViewFilters, SHOW_DEBUG_FILTER_CONTEXT, SHOW_ERROR_FILTER_CONTEXT, SHOW_INFO_FILTER_CONTEXT, SHOW_TRACE_FILTER_CONTEXT, SHOW_WARNING_FILTER_CONTEXT, CONTEXT_ACTIVE_LOG_FILE_OUTPUT, IMultiSourceOutputChannelDescriptor, isSingleSourceOutputChannelDescriptor, HIDE_CATEGORY_FILTER_CONTEXT, isMultiSourceOutputChannelDescriptor, ILogEntry } from '../../../services/output/common/output.js'; import { OutputLinkProvider } from './outputLinkProvider.js'; -import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILogService, ILoggerService, LogLevel, LogLevelToString } from '../../../../platform/log/common/log.js'; import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js'; import { DelegatedOutputChannelModel, FileOutputChannelModel, IOutputChannelModel, MultiFileOutputChannelModel } from '../common/outputChannelModel.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { OutputViewPane } from './outputView.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDefaultLogLevelsService } from '../../logs/common/defaultLogLevels.js'; import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index a73d4cc25e5..84a2975ff0f 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -8,7 +8,7 @@ import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IEditorOptions as ICodeEditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IContextKeyService, IContextKey, ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { IEditorOpenContext } from '../../../common/editor.js'; @@ -41,8 +41,8 @@ import { LogLevel } from '../../../../platform/log/common/log.js'; import { IEditorContributionDescription, EditorExtensionsRegistry, EditorContributionInstantiation, EditorContributionCtor } from '../../../../editor/browser/editorExtensions.js'; import { ICodeEditorWidgetOptions } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { IModelDeltaDecoration, ITextModel } from '../../../../editor/common/model.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { IModelDeltaDecoration, ITextModel } from '../../../../editor/common/language/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { FindDecorations } from '../../../../editor/contrib/find/browser/findDecorations.js'; import { Memento, MementoObject } from '../../../common/memento.js'; import { Markers } from '../../markers/common/markers.js'; diff --git a/src/vs/workbench/contrib/output/common/outputChannelModel.ts b/src/vs/workbench/contrib/output/common/outputChannelModel.ts index cf4393de2bf..1f730b01964 100644 --- a/src/vs/workbench/contrib/output/common/outputChannelModel.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModel.ts @@ -5,25 +5,25 @@ import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import * as resources from '../../../../base/common/resources.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { URI } from '../../../../base/common/uri.js'; import { Promises, ThrottledDelayer } from '../../../../base/common/async.js'; import { FileOperationResult, IFileService, toFileOperationResult } from '../../../../platform/files/common/files.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageSelection } from '../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageSelection } from '../../../../editor/common/language/language.js'; import { Disposable, toDisposable, IDisposable, MutableDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { isNumber } from '../../../../base/common/types.js'; -import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { ILogger, ILoggerService, ILogService, LogLevel } from '../../../../platform/log/common/log.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { ILogEntry, IOutputContentSource, LOG_MIME, OutputChannelUpdateMode } from '../../../services/output/common/output.js'; import { isCancellationError } from '../../../../base/common/errors.js'; -import { TextModel } from '../../../../editor/common/model/textModel.js'; +import { TextModel } from '../../../../editor/common/language/model/textModel.js'; import { binarySearch, sortedDiff } from '../../../../base/common/arrays.js'; const LOG_ENTRY_REGEX = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\s(\[(info|trace|debug|error|warning)\])\s(\[(.*?)\])?/; diff --git a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts index 95073543a07..b8d1ad4e89d 100644 --- a/src/vs/workbench/contrib/output/common/outputLinkComputer.ts +++ b/src/vs/workbench/contrib/output/common/outputLinkComputer.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ILink } from '../../../../editor/common/languages.js'; +import { ILink } from '../../../../editor/common/language/languages.js'; import { URI } from '../../../../base/common/uri.js'; import * as extpath from '../../../../base/common/extpath.js'; import * as resources from '../../../../base/common/resources.js'; import * as strings from '../../../../base/common/strings.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { isWindows } from '../../../../base/common/platform.js'; import { Schemas } from '../../../../base/common/network.js'; import { IWebWorkerServerRequestHandler, IWebWorkerServer } from '../../../../base/common/worker/webWorker.js'; -import { WorkerTextModelSyncServer, ICommonModel } from '../../../../editor/common/services/textModelSync/textModelSync.impl.js'; +import { WorkerTextModelSyncServer, ICommonModel } from '../../../../editor/common/language/services/textModelSync/textModelSync.impl.js'; export interface IResourceCreator { toResource: (folderRelativePath: string) => URI | null; diff --git a/src/vs/workbench/contrib/output/test/browser/outputChannelModel.test.ts b/src/vs/workbench/contrib/output/test/browser/outputChannelModel.test.ts index 94e98d07969..3a9993b5a5d 100644 --- a/src/vs/workbench/contrib/output/test/browser/outputChannelModel.test.ts +++ b/src/vs/workbench/contrib/output/test/browser/outputChannelModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { parseLogEntryAt } from '../../common/outputChannelModel.js'; -import { TextModel } from '../../../../../editor/common/model/textModel.js'; +import { TextModel } from '../../../../../editor/common/language/model/textModel.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { LogLevel } from '../../../../../platform/log/common/log.js'; import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js'; diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 20518d6be9b..d7962c22b0a 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -6,12 +6,12 @@ import { localize } from '../../../../nls.js'; import { URI } from '../../../../base/common/uri.js'; import { TextResourceEditorInput } from '../../../common/editor/textResourceEditorInput.js'; -import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { ILifecycleService, LifecyclePhase, StartupKindToString } from '../../../services/lifecycle/common/lifecycle.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ITimerService } from '../../../services/timer/browser/timerService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IDisposable, dispose } from '../../../../base/common/lifecycle.js'; @@ -26,7 +26,7 @@ import { isWeb } from '../../../../base/common/platform.js'; import { IFilesConfigurationService } from '../../../services/filesConfiguration/common/filesConfigurationService.js'; import { ITerminalService } from '../../terminal/browser/terminal.js'; import * as perf from '../../../../base/common/performance.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, getWorkbenchContribution } from '../../../common/contributions.js'; import { ICustomEditorLabelService } from '../../../services/editor/common/customEditorLabelService.js'; diff --git a/src/vs/workbench/contrib/performance/electron-sandbox/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-sandbox/startupProfiler.ts index 448ebcba59f..60ccfef8de6 100644 --- a/src/vs/workbench/contrib/performance/electron-sandbox/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-sandbox/startupProfiler.ts @@ -6,7 +6,7 @@ import { IWorkbenchContribution } from '../../../common/contributions.js'; import { localize } from '../../../../nls.js'; import { dirname, basename } from '../../../../base/common/resources.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { INativeWorkbenchEnvironmentService } from '../../../services/environment/electron-sandbox/environmentService.js'; import { ILifecycleService, LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index f5f2925dd5c..b5cbf97e63a 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -2,7 +2,6 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* eslint-disable local/code-no-dangerous-type-assertions */ import './media/keybindingsEditor.css'; import { localize } from '../../../../nls.js'; @@ -53,7 +52,7 @@ import { IExtensionsWorkbenchService } from '../../extensions/common/extensions. import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { isString } from '../../../../base/common/types.js'; import { SuggestEnabledInput } from '../../codeEditor/browser/suggestEnabledInput/suggestEnabledInput.js'; -import { CompletionItemKind } from '../../../../editor/common/languages.js'; +import { CompletionItemKind } from '../../../../editor/common/language/languages.js'; import { settingsTextInputBorder } from '../common/settingsEditorColorRegistry.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index af01934653d..eb0f61a5fab 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -9,7 +9,7 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { registerEditorContribution, EditorContributionInstantiation } from '../../../../editor/browser/editorExtensions.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/snippetController2.js'; @@ -19,8 +19,8 @@ import { parseTree, Node } from '../../../../base/common/json.js'; import { WindowsNativeResolvedKeybinding } from '../../../services/keybinding/common/windowsKeyboardMapper.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; import { ThemeColor } from '../../../../base/common/themables.js'; -import { overviewRulerInfo, overviewRulerError } from '../../../../editor/common/core/editorColorRegistry.js'; -import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from '../../../../editor/common/model.js'; +import { overviewRulerInfo, overviewRulerError } from '../../../../editor/common/language/core/editorColorRegistry.js'; +import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness, OverviewRulerLane } from '../../../../editor/common/language/model.js'; import { KeybindingParser } from '../../../../base/common/keybindingParser.js'; import { assertIsDefined } from '../../../../base/common/types.js'; import { isEqual } from '../../../../base/common/resources.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts index 46fa025a87e..36afc4c44c5 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesActions.ts @@ -5,9 +5,9 @@ import { Action } from '../../../../base/common/actions.js'; import { URI } from '../../../../base/common/uri.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import * as nls from '../../../../nls.js'; import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts index 1d6c0b83da2..b7af1fc9069 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesRenderers.ts @@ -16,15 +16,15 @@ import { isEqual } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICursorPositionChangedEvent } from '../../../../editor/common/cursorEvents.js'; import * as editorCommon from '../../../../editor/common/editorCommon.js'; -import * as languages from '../../../../editor/common/languages.js'; -import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import * as languages from '../../../../editor/common/language/languages.js'; +import { IModelDeltaDecoration, ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { CodeActionKind } from '../../../../editor/contrib/codeAction/common/types.js'; import * as nls from '../../../../nls.js'; import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 762a4622a6b..f3ee085c96d 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ISettingsEditorModel, ISetting, ISettingsGroup, ISearchResult, IGroupFilter, SettingMatchType, ISettingMatch, SettingKeyMatchTypes, ISettingMatcher } from '../../../services/preferences/common/preferences.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { distinct } from '../../../../base/common/arrays.js'; import * as strings from '../../../../base/common/strings.js'; import { IMatch, matchesContiguousSubString, matchesSubString, matchesWords } from '../../../../base/common/filters.js'; @@ -30,7 +30,6 @@ export interface IEndpointDetails { export class PreferencesSearchService extends Disposable implements IPreferencesSearchService { declare readonly _serviceBrand: undefined; - // @ts-expect-error disable remote search for now, ref https://github.com/microsoft/vscode/issues/172411 private _installedExtensions: Promise; private _remoteSearchProvider: IRemoteSearchProvider | undefined; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index 9bc4c943238..1a51b48068c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -18,7 +18,7 @@ import { Schemas } from '../../../../base/common/network.js'; import { isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; -import { IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../editor/common/model.js'; +import { IModelDeltaDecoration, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; import { localize } from '../../../../nls.js'; import { ContextScopedHistoryInputBox } from '../../../../platform/history/browser/contextScopedHistoryWidget.js'; import { showHistoryKeybindingHint } from '../../../../platform/history/browser/historyWidgetKeybindingHint.js'; @@ -33,7 +33,7 @@ import { ThemeIcon } from '../../../../base/common/themables.js'; import { isWorkspaceFolder, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; import { settingsEditIcon, settingsScopeDropDownIcon } from './preferencesIcons.js'; import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import type { IManagedHover } from '../../../../base/browser/ui/hover/hover.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index e5de67fb278..6b81b626ffb 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -51,11 +51,11 @@ import { IUserDataSyncWorkbenchService } from '../../../services/userDataSync/co import { preferencesClearInputIcon, preferencesFilterIcon } from './preferencesIcons.js'; import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js'; import { APPLICATION_SCOPES, IWorkbenchConfigurationService } from '../../../services/configuration/common/configuration.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { Orientation, Sizing, SplitView } from '../../../../base/browser/ui/splitview/splitview.js'; import { Color } from '../../../../base/common/color.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { SettingsSearchFilterDropdownMenuActionViewItem } from './settingsSearchMenu.js'; import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { ISettingOverrideClickEvent } from './settingsEditorSettingIndicators.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index c669643a71a..29538378853 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -12,7 +12,7 @@ import { Emitter } from '../../../../base/common/event.js'; import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { IDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { localize } from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 2f49546c8e4..906cfcc15b4 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -35,7 +35,7 @@ import { escapeRegExpCharacters } from '../../../../base/common/strings.js'; import { isDefined, isUndefinedOrNull } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { localize } from '../../../../nls.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index 629e04c3744..24d2cd471cf 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -18,7 +18,7 @@ import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { Emitter } from '../../../../base/common/event.js'; import { ConfigurationDefaultValueSource, ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IUserDataProfileService } from '../../../services/userDataProfile/common/userDataProfile.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index c6f874d2938..2850beb7380 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -466,7 +466,6 @@ export class ListSettingWidget extends Abst } protected getEmptyItem(): TListDataItem { - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { value: { type: 'string', @@ -620,7 +619,6 @@ export class ListSettingWidget extends Abst const updatedInputBoxItem = (): TListDataItem => { const inputBox = valueInput as InputBox; - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { value: { type: 'string', @@ -630,7 +628,6 @@ export class ListSettingWidget extends Abst } as TListDataItem; }; const updatedSelectBoxItem = (selectedValue: string): TListDataItem => { - // eslint-disable-next-line local/code-no-dangerous-type-assertions return { value: { type: 'enum', diff --git a/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts b/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts index 9240aaada83..54a00b3a414 100644 --- a/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts +++ b/src/vs/workbench/contrib/preferences/common/smartSnippetInserter.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { JSONScanner, createScanner as createJSONScanner, SyntaxKind as JSONSyntaxKind } from '../../../../base/common/json.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; export interface InsertSnippetResult { position: Position; diff --git a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts index 0c365321b34..3e6ddb02952 100644 --- a/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts +++ b/src/vs/workbench/contrib/preferences/test/common/smartSnippetInserter.test.ts @@ -6,7 +6,7 @@ import assert from 'assert'; import { SmartSnippetInserter } from '../../common/smartSnippetInserter.js'; import { createTextModel } from '../../../../../editor/test/common/testTextModel.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('SmartSnippetInserter', () => { diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 7786bd99c2c..738790fa388 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -762,7 +762,6 @@ export class TunnelPanel extends ViewPane { private protocolChangableContextKey: IContextKey; private isEditing: boolean = false; // TODO: Should this be removed? - //@ts-expect-error private titleActions: IAction[] = []; private lastFocus: number[] = []; diff --git a/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts b/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts index b6ff66566c1..1e14bda3301 100644 --- a/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts +++ b/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts @@ -26,7 +26,7 @@ import { getDefaultNotebookCreationOptions, NotebookEditorWidget } from '../../n import { GroupsOrder, IEditorGroup, IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { ExecutionStateCellStatusBarContrib, TimerCellStatusBarContrib } from '../../notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.js'; import { INotebookKernelService } from '../../notebook/common/notebookKernelService.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ReplEditorSettings, INTERACTIVE_INPUT_CURSOR_BOUNDARY } from '../../interactive/browser/interactiveCommon.js'; @@ -41,7 +41,7 @@ import { ContextMenuController } from '../../../../editor/contrib/contextmenu/br import { SuggestController } from '../../../../editor/contrib/suggest/browser/suggestController.js'; import { MarkerController } from '../../../../editor/contrib/gotoError/browser/gotoError.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ITextEditorOptions, TextEditorSelectionSource } from '../../../../platform/editor/common/editor.js'; import { INotebookExecutionStateService, NotebookExecutionType } from '../../notebook/common/notebookExecutionStateService.js'; import { NOTEBOOK_KERNEL } from '../../notebook/common/notebookContextKeys.js'; diff --git a/src/vs/workbench/contrib/replNotebook/browser/replEditorInput.ts b/src/vs/workbench/contrib/replNotebook/browser/replEditorInput.ts index 271af8eac54..415a3396ea9 100644 --- a/src/vs/workbench/contrib/replNotebook/browser/replEditorInput.ts +++ b/src/vs/workbench/contrib/replNotebook/browser/replEditorInput.ts @@ -5,8 +5,8 @@ import { IReference } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { IFileService } from '../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index e2abe63d3b6..ce3b14b1702 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -22,7 +22,7 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { getRepositoryResourceCount } from './util.js'; import { autorun, autorunWithStore, derived, IObservable, observableFromEvent } from '../../../../base/common/observable.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; const ActiveRepositoryContextKeys = { ActiveRepositoryName: new RawContextKey('scmActiveRepositoryName', ''), diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts index ac52c163d58..c2eb5c891d2 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffDecorator.ts @@ -9,11 +9,11 @@ import './media/dirtydiffDecorator.css'; import { Disposable, DisposableStore, DisposableMap, IReference } from '../../../../base/common/lifecycle.js'; import { Event } from '../../../../base/common/event.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; +import { ModelDecorationOptions } from '../../../../editor/common/language/model/textModel.js'; import { themeColorFromId } from '../../../../platform/theme/common/themeService.js'; import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; -import { OverviewRulerLane, IModelDecorationOptions, MinimapPosition } from '../../../../editor/common/model.js'; +import { OverviewRulerLane, IModelDecorationOptions, MinimapPosition } from '../../../../editor/common/language/model.js'; import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ChangeType, getChangeType, minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../common/quickDiff.js'; diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts index ce0f83a212f..b239d55046a 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffModel.ts @@ -7,12 +7,12 @@ import { ResourceMap } from '../../../../base/common/map.js'; import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { EncodingMode, IResolvedTextFileEditorModel, isTextFileEditorModel, ITextFileEditorModel, ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { Disposable, DisposableMap, DisposableStore, IReference, ReferenceCollection } from '../../../../base/common/lifecycle.js'; -import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/services/editorWorker.js'; +import { DiffAlgorithmName, IEditorWorkerService } from '../../../../editor/common/language/services/editorWorker.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { URI } from '../../../../base/common/uri.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/model.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; +import { ITextModel, shouldSynchronizeModel } from '../../../../editor/common/language/model.js'; import { compareChanges, getModifiedEndLineNumber, IQuickDiffService, QuickDiff, QuickDiffChange, QuickDiffResult } from '../common/quickDiff.js'; import { ThrottledDelayer } from '../../../../base/common/async.js'; import { ISCMRepository, ISCMService } from '../common/scm.js'; diff --git a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts index f22648c7169..dcb59b61a1f 100644 --- a/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts +++ b/src/vs/workbench/contrib/scm/browser/quickDiffWidget.ts @@ -39,8 +39,8 @@ import { IAccessibilityService } from '../../../../platform/accessibility/common import { Iterable } from '../../../../base/common/iterator.js'; import { basename } from '../../../../base/common/resources.js'; import { EditorOption, IDiffEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IActionBarOptions } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 83f8a15401d..9df5478e8bb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -44,7 +44,7 @@ import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor import { CodeEditorWidget, ICodeEditorWidgetOptions } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IEditorConstructionOptions } from '../../../../editor/browser/config/editorConfiguration.js'; import { getSimpleEditorOptions, setupSimpleEditorSelectionStyling } from '../../codeEditor/browser/simpleEditorOptions.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { EditorExtensionsRegistry } from '../../../../editor/browser/editorExtensions.js'; import { MenuPreventer } from '../../codeEditor/browser/menuPreventer.js'; import { SelectionClipboardContributionID } from '../../codeEditor/browser/selectionClipboard.js'; @@ -69,7 +69,7 @@ import { AnchorAlignment } from '../../../../base/browser/ui/contextview/context import { RepositoryActionRunner, RepositoryRenderer } from './scmRepositoryRenderer.js'; import { ColorScheme } from '../../../../platform/theme/common/theme.js'; import { LabelFuzzyScore } from '../../../../base/browser/ui/tree/abstractTree.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from '../../../browser/parts/editor/editorCommands.js'; import { createActionViewItem, getFlatActionBarActions, getFlatContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { MarkdownRenderer, openLinkFromMarkdown } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; @@ -92,7 +92,7 @@ import { FormatOnType } from '../../../../editor/contrib/format/browser/formatAc import { EditorOption, EditorOptions, IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { IAsyncDataTreeViewState, ITreeCompressionDelegate } from '../../../../base/browser/ui/tree/asyncDataTree.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { EditOperation } from '../../../../editor/common/core/editOperation.js'; +import { EditOperation } from '../../../../editor/common/language/core/editOperation.js'; import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { DropdownWithPrimaryActionViewItem } from '../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js'; @@ -101,7 +101,7 @@ import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { OpenScmGroupAction } from '../../multiDiffEditor/browser/scmMultiDiffSourceResolver.js'; import { ContentHoverController } from '../../../../editor/contrib/hover/browser/contentHoverController.js'; import { GlyphHoverController } from '../../../../editor/contrib/hover/browser/glyphHoverController.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { autorun, runOnChange } from '../../../../base/common/observable.js'; import { PlaceholderTextContribution } from '../../../../editor/contrib/placeholderText/browser/placeholderTextContribution.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; diff --git a/src/vs/workbench/contrib/scm/browser/util.ts b/src/vs/workbench/contrib/scm/browser/util.ts index 73a452a4efd..cea897325e2 100644 --- a/src/vs/workbench/contrib/scm/browser/util.ts +++ b/src/vs/workbench/contrib/scm/browser/util.ts @@ -14,7 +14,7 @@ import { equals } from '../../../../base/common/arrays.js'; import { ActionViewItem, IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; import { reset } from '../../../../base/browser/dom.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IResourceNode, ResourceTree } from '../../../../base/common/resourceTree.js'; diff --git a/src/vs/workbench/contrib/scm/common/quickDiff.ts b/src/vs/workbench/contrib/scm/common/quickDiff.ts index 60dd34b399e..f44ac18708e 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiff.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiff.ts @@ -8,7 +8,7 @@ import * as nls from '../../../../nls.js'; import { URI } from '../../../../base/common/uri.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { LanguageSelector } from '../../../../editor/common/languageSelector.js'; +import { LanguageSelector } from '../../../../editor/common/language/languageSelector.js'; import { Event } from '../../../../base/common/event.js'; import { LineRangeMapping } from '../../../../editor/common/diff/rangeMapping.js'; import { IChange } from '../../../../editor/common/diff/legacyLinesDiffComputer.js'; diff --git a/src/vs/workbench/contrib/scm/common/quickDiffService.ts b/src/vs/workbench/contrib/scm/common/quickDiffService.ts index 5b872ae5722..77e1eff7f1a 100644 --- a/src/vs/workbench/contrib/scm/common/quickDiffService.ts +++ b/src/vs/workbench/contrib/scm/common/quickDiffService.ts @@ -7,7 +7,7 @@ import { URI } from '../../../../base/common/uri.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { IQuickDiffService, QuickDiff, QuickDiffProvider } from './quickDiff.js'; import { isEqualOrParent } from '../../../../base/common/resources.js'; -import { score } from '../../../../editor/common/languageSelector.js'; +import { score } from '../../../../editor/common/language/languageSelector.js'; import { Emitter } from '../../../../base/common/event.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index 1987feddc55..91bd1ae67f3 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -7,14 +7,14 @@ import { URI } from '../../../../base/common/uri.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; import { IAction } from '../../../../base/common/actions.js'; import { IMenu } from '../../../../platform/actions/common/actions.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { ResourceTree } from '../../../../base/common/resourceTree.js'; import { ISCMHistoryProvider } from './history.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IObservable } from '../../../../base/common/observable.js'; export const VIEWLET_ID = 'workbench.view.scm'; diff --git a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts index c630a13882c..905e592fa7b 100644 --- a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts +++ b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModel.ts @@ -7,9 +7,9 @@ import { Emitter, Event } from '../../../../../base/common/event.js'; import { Lazy } from '../../../../../base/common/lazy.js'; import { Disposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { IPosition } from '../../../../../editor/common/core/position.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IPosition } from '../../../../../editor/common/language/core/position.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentity.js'; @@ -20,7 +20,7 @@ import { IReplaceService } from '../replace.js'; import { FileMatchImpl } from '../searchTreeModel/fileMatch.js'; import { ISearchResult, TEXT_SEARCH_HEADING_PREFIX, AI_TEXT_SEARCH_RESULT_ID, ISearchTreeFolderMatchWorkspaceRoot, ISearchTreeFolderMatch, ISearchTreeFolderMatchWithResource, ITextSearchHeading, IChangeEvent, ISearchModel, ISearchTreeFileMatch, FOLDER_MATCH_PREFIX, getFileMatches, FILE_MATCH_PREFIX } from '../searchTreeModel/searchTreeCommon.js'; import { TextSearchHeadingImpl } from '../searchTreeModel/textSearchHeading.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { textSearchResultToMatches } from '../searchTreeModel/match.js'; import { ISearchTreeAIFileMatch } from './aiSearchModelBase.js'; import { ResourceSet } from '../../../../../base/common/map.js'; diff --git a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModelBase.ts b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModelBase.ts index 4c8b18ea85c..a7ed91b813f 100644 --- a/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModelBase.ts +++ b/src/vs/workbench/contrib/search/browser/AISearch/aiSearchModelBase.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ISearchTreeFileMatch } from '../searchTreeModel/searchTreeCommon.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; export interface ISearchTreeAIFileMatch extends ISearchTreeFileMatch { getFullRange(): Range | undefined; diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index bcb65cdddfd..9817cc2af59 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -21,16 +21,16 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore, IDisposable, toDisposable, MutableDisposable, Disposable } from '../../../../base/common/lifecycle.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { localize } from '../../../../nls.js'; import { IWorkingCopyService } from '../../../services/workingCopy/common/workingCopyService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IWorkbenchEditorConfiguration, EditorResourceAccessor, isEditorInput } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { Range, IRange } from '../../../../editor/common/core/range.js'; +import { Range, IRange } from '../../../../editor/common/language/core/range.js'; import { ThrottledDelayer } from '../../../../base/common/async.js'; import { top } from '../../../../base/common/arrays.js'; import { FileQueryCacheState } from '../common/cacheState.js'; @@ -43,7 +43,7 @@ import { SymbolsQuickAccessProvider } from './symbolsQuickAccess.js'; import { AnythingQuickAccessProviderRunOptions, DefaultQuickAccessFilterValue, Extensions, IQuickAccessRegistry } from '../../../../platform/quickinput/common/quickAccess.js'; import { PickerEditorState, IWorkbenchQuickAccessConfiguration } from '../../../browser/quickaccess.js'; import { GotoSymbolQuickAccessProvider } from '../../codeEditor/browser/quickaccess/gotoSymbolQuickAccess.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ScrollType, IEditor } from '../../../../editor/common/editorCommon.js'; import { Event } from '../../../../base/common/event.js'; import { Codicon } from '../../../../base/common/codicons.js'; diff --git a/src/vs/workbench/contrib/search/browser/notebookSearch/notebookSearchModel.ts b/src/vs/workbench/contrib/search/browser/notebookSearch/notebookSearchModel.ts index 6d5fe1e346a..eb45f313c0a 100644 --- a/src/vs/workbench/contrib/search/browser/notebookSearch/notebookSearchModel.ts +++ b/src/vs/workbench/contrib/search/browser/notebookSearch/notebookSearchModel.ts @@ -7,8 +7,8 @@ import { coalesce } from '../../../../../base/common/arrays.js'; import { RunOnceScheduler } from '../../../../../base/common/async.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../../base/common/lifecycle.js'; -import { FindMatch } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { FindMatch } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { ISearchRange, ITextSearchMatch, resultIsMatch, ITextSearchContext, IPatternInfo, ITextSearchPreviewOptions, IFileMatch } from '../../../../services/search/common/search.js'; import { getTextSearchMatchWithModelContext } from '../../../../services/search/common/searchHelpers.js'; diff --git a/src/vs/workbench/contrib/search/browser/notebookSearch/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/browser/notebookSearch/searchNotebookHelpers.ts index 4ad4b7e61dd..e62a5a5ec94 100644 --- a/src/vs/workbench/contrib/search/browser/notebookSearch/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/browser/notebookSearch/searchNotebookHelpers.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FindMatch } from '../../../../../editor/common/model.js'; +import { FindMatch } from '../../../../../editor/common/language/model.js'; import { IFileMatch, ITextSearchMatch, TextSearchMatch } from '../../../../services/search/common/search.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { INotebookCellMatchNoModel, INotebookFileMatchNoModel, genericCellMatchesToTextSearchMatches, rawCellPrefix } from '../../common/searchNotebookHelpers.js'; import { CellWebviewFindMatch, ICellViewModel } from '../../../notebook/browser/notebookBrowser.js'; import { URI } from '../../../../../base/common/uri.js'; diff --git a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts index 24193d66651..310f54d932c 100644 --- a/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/quickTextSearch/textSearchQuickAccess.ts @@ -7,7 +7,7 @@ import { DisposableStore, IDisposable } from '../../../../../base/common/lifecyc import { ResourceSet } from '../../../../../base/common/map.js'; import { basenameOrAuthority, dirname } from '../../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; -import { IRange } from '../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { ITextEditorSelection } from '../../../../../platform/editor/common/editor.js'; diff --git a/src/vs/workbench/contrib/search/browser/replaceService.ts b/src/vs/workbench/contrib/search/browser/replaceService.ts index d7c57e1fee3..b0ced0c8be8 100644 --- a/src/vs/workbench/contrib/search/browser/replaceService.ts +++ b/src/vs/workbench/contrib/search/browser/replaceService.ts @@ -9,20 +9,20 @@ import * as network from '../../../../base/common/network.js'; import { Disposable, IReference } from '../../../../base/common/lifecycle.js'; import { IReplaceService } from './replace.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { ISearchViewModelWorkbenchService } from './searchTreeModel/searchViewModelWorkbenchService.js'; import { IProgress, IProgressStep } from '../../../../platform/progress/common/progress.js'; -import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService, ITextModelContentProvider } from '../../../../editor/common/language/services/resolverService.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { createTextBufferFactoryFromSnapshot } from '../../../../editor/common/model/textModel.js'; +import { createTextBufferFactoryFromSnapshot } from '../../../../editor/common/language/model/textModel.js'; import { ITextFileService } from '../../../services/textfile/common/textfiles.js'; import { IBulkEditService, ResourceTextEdit } from '../../../../editor/browser/services/bulkEditService.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { EditOperation, ISingleEditOperation } from '../../../../editor/common/core/editOperation.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { EditOperation, ISingleEditOperation } from '../../../../editor/common/language/core/editOperation.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { dirname } from '../../../../base/common/resources.js'; import { Promises } from '../../../../base/common/async.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchCompare.ts b/src/vs/workbench/contrib/search/browser/searchCompare.ts index ada366dee77..846085a3eb7 100644 --- a/src/vs/workbench/contrib/search/browser/searchCompare.ts +++ b/src/vs/workbench/contrib/search/browser/searchCompare.ts @@ -6,7 +6,7 @@ import { IMatchInNotebook, isIMatchInNotebook } from './notebookSearch/notebookSearchModelBase.js'; import { compareFileExtensions, compareFileNames, comparePaths } from '../../../../base/common/comparers.js'; import { SearchSortOrder } from '../../../services/search/common/search.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { createParentList, isSearchTreeFileMatch, isSearchTreeFolderMatch, isSearchTreeMatch, RenderableMatch } from './searchTreeModel/searchTreeCommon.js'; import { isSearchTreeAIFileMatch } from './AISearch/aiSearchModelBase.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts index 7986616810d..929e7aff2e9 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/fileMatch.ts @@ -8,9 +8,9 @@ import { Lazy } from '../../../../../base/common/lazy.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { themeColorFromId } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; -import { TrackedRangeStickiness, MinimapPosition, ITextModel, FindMatch, IModelDeltaDecoration } from '../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../editor/common/model/textModel.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { TrackedRangeStickiness, MinimapPosition, ITextModel, FindMatch, IModelDeltaDecoration } from '../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../editor/common/language/model/textModel.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { IFileStatWithPartialMetadata, IFileService } from '../../../../../platform/files/common/files.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { overviewRulerFindMatchForeground, minimapFindMatch } from '../../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/folderMatch.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/folderMatch.ts index 22eead9d1c4..abf237c4a48 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/folderMatch.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/folderMatch.ts @@ -9,7 +9,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { TernarySearchTree } from '../../../../../base/common/ternarySearchTree.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentity.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/match.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/match.ts index 9c289104417..5b67e128be3 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/match.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/match.ts @@ -7,7 +7,7 @@ import { memoize } from '../../../../../base/common/decorators.js'; import { lcut } from '../../../../../base/common/strings.js'; import { ISearchRange, ITextSearchMatch, OneLineRange } from '../../../../services/search/common/search.js'; import { ISearchTreeMatch, ISearchTreeFileMatch, MATCH_PREFIX } from './searchTreeCommon.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; export function textSearchResultToMatches(rawMatch: ITextSearchMatch, fileMatch: ISearchTreeFileMatch, isAiContributed: boolean): ISearchTreeMatch[] { const previewLines = rawMatch.previewText.split('\n'); diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/rangeDecorations.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/rangeDecorations.ts index 01e8e557445..0c34e58bc7a 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/rangeDecorations.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/rangeDecorations.ts @@ -5,10 +5,10 @@ import { IDisposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel, TrackedRangeStickiness } from '../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../editor/common/model/textModel.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { ITextModel, TrackedRangeStickiness } from '../../../../../editor/common/language/model.js'; +import { ModelDecorationOptions } from '../../../../../editor/common/language/model/textModel.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; /** * Can add a range highlight decoration to a model. diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts index f2e0fa7ee3b..3d4eb3d17aa 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchResult.ts @@ -6,8 +6,8 @@ import { Event, PauseableEmitter } from '../../../../../base/common/event.js'; import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IProgress, IProgressStep } from '../../../../../platform/progress/common/progress.js'; import { NotebookEditorWidget } from '../../../notebook/browser/notebookEditorWidget.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts index bb05360e4d3..35929d4179c 100644 --- a/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts +++ b/src/vs/workbench/contrib/search/browser/searchTreeModel/searchTreeCommon.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IAITextQuery, IFileMatch, ISearchComplete, ISearchProgressItem, ISearchRange, ITextQuery, ITextSearchQuery, ITextSearchResult } from '../../../../services/search/common/search.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { IFileStatWithPartialMetadata, IFileService } from '../../../../../platform/files/common/files.js'; import { IProgress, IProgressStep } from '../../../../../platform/progress/common/progress.js'; import { ReplacePattern } from '../../../../services/search/common/replace.js'; diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2a7f4d3c326..739e505cbd5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -23,7 +23,7 @@ import { getCodeEditor, isCodeEditor, isDiffEditor } from '../../../../editor/br import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { IEditor } from '../../../../editor/common/editorCommon.js'; import { CommonFindController } from '../../../../editor/contrib/find/browser/findController.js'; import { MultiCursorSelectionController } from '../../../../editor/contrib/multicursor/browser/multicursor.js'; diff --git a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts index e11f2203cda..05e724e1a92 100644 --- a/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/symbolsQuickAccess.ts @@ -9,12 +9,12 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ThrottledDelayer } from '../../../../base/common/async.js'; import { getWorkspaceSymbols, IWorkspaceSymbol, IWorkspaceSymbolProvider } from '../common/search.js'; -import { SymbolKinds, SymbolTag, SymbolKind } from '../../../../editor/common/languages.js'; +import { SymbolKinds, SymbolTag, SymbolKind } from '../../../../editor/common/language/languages.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { Schemas } from '../../../../base/common/network.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IWorkbenchEditorConfiguration } from '../../../common/editor.js'; import { IKeyMods, IQuickPickItemWithResource } from '../../../../platform/quickinput/common/quickInput.js'; diff --git a/src/vs/workbench/contrib/search/common/cellSearchModel.ts b/src/vs/workbench/contrib/search/common/cellSearchModel.ts index 202daaed036..bf6a0a0fb85 100644 --- a/src/vs/workbench/contrib/search/common/cellSearchModel.ts +++ b/src/vs/workbench/contrib/search/common/cellSearchModel.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../base/common/lifecycle.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { DefaultEndOfLine, FindMatch, IReadonlyTextBuffer } from '../../../../editor/common/model.js'; -import { PieceTreeTextBufferBuilder } from '../../../../editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; -import { SearchParams } from '../../../../editor/common/model/textModelSearch.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { DefaultEndOfLine, FindMatch, IReadonlyTextBuffer } from '../../../../editor/common/language/model.js'; +import { PieceTreeTextBufferBuilder } from '../../../../editor/common/language/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder.js'; +import { SearchParams } from '../../../../editor/common/language/model/textModelSearch.js'; interface RawOutputFindMatch { textBuffer: IReadonlyTextBuffer; diff --git a/src/vs/workbench/contrib/search/common/search.ts b/src/vs/workbench/contrib/search/common/search.ts index e7a24b32a0d..aba3fa19066 100644 --- a/src/vs/workbench/contrib/search/common/search.ts +++ b/src/vs/workbench/contrib/search/common/search.ts @@ -6,7 +6,7 @@ import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { ISearchConfiguration, ISearchConfigurationProperties } from '../../../services/search/common/search.js'; -import { SymbolKind, Location, ProviderResult, SymbolTag } from '../../../../editor/common/languages.js'; +import { SymbolKind, Location, ProviderResult, SymbolTag } from '../../../../editor/common/language/languages.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { URI } from '../../../../base/common/uri.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; @@ -14,7 +14,7 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { CancellationToken } from '../../../../base/common/cancellation.js'; import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IFileService } from '../../../../platform/files/common/files.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { isNumber } from '../../../../base/common/types.js'; import { RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { compare } from '../../../../base/common/strings.js'; diff --git a/src/vs/workbench/contrib/search/common/searchNotebookHelpers.ts b/src/vs/workbench/contrib/search/common/searchNotebookHelpers.ts index f85c87838c1..c859fb28159 100644 --- a/src/vs/workbench/contrib/search/common/searchNotebookHelpers.ts +++ b/src/vs/workbench/contrib/search/common/searchNotebookHelpers.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ -import { FindMatch, IReadonlyTextBuffer } from '../../../../editor/common/model.js'; +import { FindMatch, IReadonlyTextBuffer } from '../../../../editor/common/language/model.js'; import { TextSearchMatch, IFileMatch, ITextSearchMatch } from '../../../services/search/common/search.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; export type IRawClosedNotebookFileMatch = INotebookFileMatchNoModel; diff --git a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts index 6116a1004c9..f861d01a3d3 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchActions.test.ts @@ -7,7 +7,7 @@ import assert from 'assert'; import { Keybinding } from '../../../../../base/common/keybindings.js'; import { OS } from '../../../../../base/common/platform.js'; import { URI } from '../../../../../base/common/uri.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { USLayoutResolvedKeybinding } from '../../../../../platform/keybinding/common/usLayoutResolvedKeybinding.js'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts index 6991b3434ae..abe20e5d4fb 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchModel.test.ts @@ -8,9 +8,9 @@ import * as arrays from '../../../../../base/common/arrays.js'; import { DeferredPromise, timeout } from '../../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ModelService } from '../../../../../editor/common/services/modelService.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../../../editor/common/language/services/modelService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; @@ -33,7 +33,7 @@ import { createFileUriFromPathFromRoot, getRootName } from './searchTestCommon.j import { INotebookCellMatchWithModel, INotebookFileMatchWithModel, contentMatchesToTextSearchMatches, webviewMatchesToTextSearchMatches } from '../../browser/notebookSearch/searchNotebookHelpers.js'; import { CellKind } from '../../../notebook/common/notebookCommon.js'; import { ICellViewModel } from '../../../notebook/browser/notebookBrowser.js'; -import { FindMatch, IReadonlyTextBuffer } from '../../../../../editor/common/model.js'; +import { FindMatch, IReadonlyTextBuffer } from '../../../../../editor/common/language/model.js'; import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js'; import { INotebookService } from '../../../notebook/common/notebookService.js'; import { INotebookSearchService } from '../../common/notebookSearch.js'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts index 6081bb08117..2b8f69ed95f 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchNotebookHelpers.test.ts @@ -4,8 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { FindMatch, IReadonlyTextBuffer } from '../../../../../editor/common/model.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { FindMatch, IReadonlyTextBuffer } from '../../../../../editor/common/language/model.js'; import { IFileMatch, ISearchRange, ITextSearchMatch, QueryType } from '../../../../services/search/common/search.js'; import { ICellViewModel } from '../../../notebook/browser/notebookBrowser.js'; import { CellKind } from '../../../notebook/common/notebookCommon.js'; @@ -15,7 +15,7 @@ import { SearchModelImpl } from '../../browser/searchTreeModel/searchModel.js'; import { URI } from '../../../../../base/common/uri.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; import { createFileUriFromPathFromRoot, stubModelService, stubNotebookEditorService } from './searchTestCommon.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { INotebookEditorService } from '../../../notebook/browser/services/notebookEditorService.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { CellMatch, NotebookCompatibleFileMatch, textSearchMatchesToNotebookMatches } from '../../browser/notebookSearch/notebookSearchModel.js'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts index 751c6acd986..beffaed57d3 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchResult.test.ts @@ -10,11 +10,11 @@ import { URI } from '../../../../../base/common/uri.js'; import { IFileMatch, TextSearchMatch, OneLineRange, ITextSearchMatch, QueryType } from '../../../../services/search/common/search.js'; import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; -import { ModelService } from '../../../../../editor/common/services/modelService.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ModelService } from '../../../../../editor/common/language/services/modelService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { IReplaceService } from '../../browser/replace.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { TestThemeService } from '../../../../../platform/theme/test/common/testThemeService.js'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts b/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts index 603fb62c66b..4e28aa2023a 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchTestCommon.ts @@ -6,8 +6,8 @@ import { IDisposable } from '../../../../../base/common/lifecycle.js'; import { isWindows } from '../../../../../base/common/platform.js'; import { URI } from '../../../../../base/common/uri.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ModelService } from '../../../../../editor/common/services/modelService.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ModelService } from '../../../../../editor/common/language/services/modelService.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts index b51e7ced098..f4641afe2f5 100644 --- a/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts +++ b/src/vs/workbench/contrib/search/test/browser/searchViewlet.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { URI } from '../../../../../base/common/uri.js'; import { ILanguageConfigurationService } from '../../../../../editor/common/languages/languageConfigurationRegistry.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; import { TestLanguageConfigurationService } from '../../../../../editor/test/common/modes/testLanguageConfigurationService.js'; import { FileService } from '../../../../../platform/files/common/fileService.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts index b6e2039f19f..e06a1edc0af 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.contribution.ts @@ -7,7 +7,7 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { extname, isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from '../../../../editor/contrib/find/browser/findModel.js'; import { localize, localize2 } from '../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 06c660a0325..b2388726d9e 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -14,12 +14,12 @@ import { assertIsDefined } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import './media/searchEditor.css'; import { ICodeEditorWidgetOptions } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; import { ICodeEditorViewState } from '../../../../editor/common/editorCommon.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextResourceConfigurationService } from '../../../../editor/common/services/textResourceConfiguration.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextResourceConfigurationService } from '../../../../editor/common/language/services/textResourceConfiguration.js'; import { ReferencesController } from '../../../../editor/contrib/gotoSymbol/browser/peek/referencesController.js'; import { localize } from '../../../../nls.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index 6e284998f6a..789a8cfa87c 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -8,9 +8,9 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { basename } from '../../../../base/common/path.js'; import { extname, isEqual, joinPath } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import { ITextModel, TrackedRangeStickiness } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { localize } from '../../../../nls.js'; import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts index f833d6f0123..2b1d33210b6 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorModel.ts @@ -4,15 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../base/common/uri.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { parseSavedSearchEditor, parseSerializedSearchEditor } from './searchEditorSerialization.js'; import { IWorkingCopyBackupService } from '../../../services/workingCopy/common/workingCopyBackup.js'; import { SearchConfiguration, SearchEditorWorkingCopyTypeId } from './constants.js'; import { assertIsDefined } from '../../../../base/common/types.js'; -import { createTextBufferFactoryFromStream } from '../../../../editor/common/model/textModel.js'; +import { createTextBufferFactoryFromStream } from '../../../../editor/common/language/model/textModel.js'; import { Emitter } from '../../../../base/common/event.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { SEARCH_RESULT_LANGUAGE_ID } from '../../../services/search/common/search.js'; diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index 605389b0fcf..19249f5b127 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -7,8 +7,8 @@ import { coalesce } from '../../../../base/common/arrays.js'; import { URI } from '../../../../base/common/uri.js'; import './media/searchEditor.css'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { Range } from '../../../../editor/common/core/range.js'; -import type { ITextModel } from '../../../../editor/common/model.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; +import type { ITextModel } from '../../../../editor/common/language/model.js'; import { localize } from '../../../../nls.js'; import type { SearchConfiguration } from './constants.js'; import { ITextQuery, SearchSortOrder } from '../../../services/search/common/search.js'; diff --git a/src/vs/workbench/contrib/share/browser/shareService.ts b/src/vs/workbench/contrib/share/browser/shareService.ts index 9a94bd370be..882161faaa9 100644 --- a/src/vs/workbench/contrib/share/browser/shareService.ts +++ b/src/vs/workbench/contrib/share/browser/shareService.ts @@ -7,7 +7,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { score } from '../../../../editor/common/languageSelector.js'; +import { score } from '../../../../editor/common/language/languageSelector.js'; import { localize } from '../../../../nls.js'; import { ISubmenuItem, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/share/common/share.ts b/src/vs/workbench/contrib/share/common/share.ts index eb0b3becf7d..68554cdaa1f 100644 --- a/src/vs/workbench/contrib/share/common/share.ts +++ b/src/vs/workbench/contrib/share/common/share.ts @@ -6,8 +6,8 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; -import { LanguageSelector } from '../../../../editor/common/languageSelector.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { LanguageSelector } from '../../../../editor/common/language/languageSelector.js'; import { ISubmenuItem } from '../../../../platform/actions/common/actions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts index 29f82cafa74..ded38608619 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts @@ -7,8 +7,8 @@ import { isValidBasename } from '../../../../../base/common/extpath.js'; import { extname } from '../../../../../base/common/path.js'; import { basename, joinPath } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; -import { getIconClassesForLanguageId } from '../../../../../editor/common/services/getIconClasses.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; +import { getIconClassesForLanguageId } from '../../../../../editor/common/language/services/getIconClasses.js'; import * as nls from '../../../../../nls.js'; import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets.ts index beea5ed367d..a5077786a1b 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/fileTemplateSnippets.ts @@ -6,7 +6,7 @@ import { groupBy, isFalsyOrEmpty } from '../../../../../base/common/arrays.js'; import { compare } from '../../../../../base/common/strings.js'; import { getCodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { SnippetController2 } from '../../../../../editor/contrib/snippet/browser/snippetController2.js'; import { localize, localize2 } from '../../../../../nls.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts b/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts index 5d71cf5e8cf..5185c71b546 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/insertSnippet.ts @@ -6,7 +6,7 @@ import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { SnippetController2 } from '../../../../../editor/contrib/snippet/browser/snippetController2.js'; import * as nls from '../../../../../nls.js'; import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts b/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts index e9f9a63d98a..7b96af145a6 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/surroundWithSnippet.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { SnippetController2 } from '../../../../../editor/contrib/snippet/browser/snippetController2.js'; import { IClipboardService } from '../../../../../platform/clipboard/common/clipboardService.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCodeActionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCodeActionProvider.ts index 53802851eca..34a0f95f31b 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCodeActionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCodeActionProvider.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { Selection } from '../../../../editor/common/core/selection.js'; -import { CodeAction, CodeActionList, CodeActionProvider, WorkspaceEdit } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { Selection } from '../../../../editor/common/language/core/selection.js'; +import { CodeAction, CodeActionList, CodeActionProvider, WorkspaceEdit } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { CodeActionKind } from '../../../../editor/contrib/codeAction/common/types.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts index 4bd9ab73446..915024af8e6 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts @@ -5,11 +5,11 @@ import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { compare, compareSubstring } from '../../../../base/common/strings.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, CompletionItemInsertTextRule, CompletionContext, CompletionTriggerKind, CompletionItemLabel, Command } from '../../../../editor/common/languages.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { CompletionItem, CompletionItemKind, CompletionItemProvider, CompletionList, CompletionItemInsertTextRule, CompletionContext, CompletionTriggerKind, CompletionItemLabel, Command } from '../../../../editor/common/language/languages.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { SnippetParser } from '../../../../editor/contrib/snippet/browser/snippetParser.js'; import { localize } from '../../../../nls.js'; import { ISnippetsService } from './snippets.js'; @@ -19,7 +19,7 @@ import { StopWatch } from '../../../../base/common/stopwatch.js'; import { ILanguageConfigurationService } from '../../../../editor/common/languages/languageConfigurationRegistry.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; -import { IWordAtPosition } from '../../../../editor/common/core/wordHelper.js'; +import { IWordAtPosition } from '../../../../editor/common/language/core/wordHelper.js'; const markSnippetAsUsed = '_snippet.markAsUsed'; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 187615df1de..41be3307726 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -8,8 +8,8 @@ import { combinedDisposable, IDisposable, DisposableStore } from '../../../../ba import * as resources from '../../../../base/common/resources.js'; import { isFalsyOrWhitespace } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { setSnippetSuggestSupport } from '../../../../editor/contrib/suggest/browser/suggest.js'; import { localize } from '../../../../nls.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; diff --git a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts index 43dd710d128..462da6316e3 100644 --- a/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts +++ b/src/vs/workbench/contrib/snippets/browser/tabCompletion.ts @@ -10,7 +10,7 @@ import { ISnippetsService } from './snippets.js'; import { getNonWhitespacePrefix } from './snippetsService.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { registerEditorContribution, EditorCommand, registerEditorCommand, EditorContributionInstantiation } from '../../../../editor/browser/editorExtensions.js'; import { SnippetController2 } from '../../../../editor/contrib/snippet/browser/snippetController2.js'; import { showSimpleSuggestions } from '../../../../editor/contrib/suggest/browser/suggest.js'; @@ -21,8 +21,8 @@ import { SnippetCompletion } from './snippetCompletionProvider.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { EditorState, CodeEditorStateFlag } from '../../../../editor/contrib/editorState/browser/editorState.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { CompletionItemProvider } from '../../../../editor/common/languages.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { CompletionItemProvider } from '../../../../editor/common/language/languages.js'; export class TabCompletionController implements IEditorContribution { diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts index a70814867f7..47fc6183e43 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsRegistry.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { getNonWhitespacePrefix } from '../../browser/snippetsService.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; suite('getNonWhitespacePrefix', () => { diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index bb8b5f8f700..f221e525f7f 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -5,19 +5,19 @@ import assert from 'assert'; import { SnippetCompletion, SnippetCompletionProvider } from '../../browser/snippetCompletionProvider.js'; -import { IPosition, Position } from '../../../../../editor/common/core/position.js'; +import { IPosition, Position } from '../../../../../editor/common/language/core/position.js'; import { createModelServices, instantiateTextModel } from '../../../../../editor/test/common/testTextModel.js'; import { ISnippetsService } from '../../browser/snippets.js'; import { Snippet, SnippetSource } from '../../browser/snippetsFile.js'; -import { CompletionContext, CompletionItemLabel, CompletionItemRanges, CompletionTriggerKind } from '../../../../../editor/common/languages.js'; +import { CompletionContext, CompletionItemLabel, CompletionItemRanges, CompletionTriggerKind } from '../../../../../editor/common/language/languages.js'; import { DisposableStore } from '../../../../../base/common/lifecycle.js'; import { TestLanguageConfigurationService } from '../../../../../editor/test/common/modes/testLanguageConfigurationService.js'; -import { EditOperation } from '../../../../../editor/common/core/editOperation.js'; +import { EditOperation } from '../../../../../editor/common/language/core/editOperation.js'; import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; -import { ILanguageService } from '../../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../../editor/common/language/language.js'; import { generateUuid } from '../../../../../base/common/uuid.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { CompletionModel } from '../../../../../editor/contrib/suggest/browser/completionModel.js'; import { CompletionItem } from '../../../../../editor/contrib/suggest/browser/suggest.js'; import { WordDistance } from '../../../../../editor/contrib/suggest/browser/wordDistance.js'; diff --git a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index e7df0b22037..cb34c89adde 100644 --- a/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -5,7 +5,7 @@ import { localize } from '../../../../nls.js'; import { language } from '../../../../base/common/platform.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from '../../../common/contributions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 25385286aad..dba9cb1caa0 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -34,7 +34,7 @@ import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, WorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; import { Markers } from '../../markers/common/markers.js'; @@ -65,7 +65,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../base/com import { toFormattedString } from '../../../../base/common/jsonFormatter.js'; import { Schemas } from '../../../../base/common/network.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { TextEditorSelectionRevealType } from '../../../../platform/editor/common/editor.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 2fcb98375e2..451f4290198 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -18,7 +18,7 @@ import Severity from '../../../../base/common/severity.js'; import * as Types from '../../../../base/common/types.js'; import * as nls from '../../../../nls.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index 594086eed7c..e833dd2d363 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -8,7 +8,7 @@ import { URI } from '../../../../base/common/uri.js'; import { Event, Emitter } from '../../../../base/common/event.js'; import { IDisposable, DisposableStore, Disposable } from '../../../../base/common/lifecycle.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ILineMatcher, createLineMatcher, ProblemMatcher, IProblemMatch, ApplyToKind, IWatchingPattern, getResource } from './problemMatcher.js'; import { IMarkerService, IMarkerData, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; diff --git a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts index 6a5728c05e8..716ca668462 100644 --- a/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts +++ b/src/vs/workbench/contrib/tasks/electron-sandbox/taskService.ts @@ -15,8 +15,8 @@ import { InstantiationType, registerSingleton } from '../../../../platform/insta import { TerminalTaskSystem } from '../browser/terminalTaskSystem.js'; import { IConfirmationResult, IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { TerminateResponseCode } from '../../../../base/common/processes.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 15ffde581b3..3fc6100efc1 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -22,7 +22,7 @@ import { ITextFileService, ITextFileSaveEvent, ITextFileResolveEvent } from '../ import { extname, basename, isEqual, isEqualOrParent } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { Schemas } from '../../../../base/common/network.js'; -import { getMimeTypes } from '../../../../editor/common/services/languagesAssociations.js'; +import { getMimeTypes } from '../../../../editor/common/language/services/languagesAssociations.js'; import { hash } from '../../../../base/common/hash.js'; import { IPaneCompositePartService } from '../../../services/panecomposite/browser/panecomposite.js'; import { ViewContainerLocation } from '../../../common/views.js'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index ff14b2b4af7..03e9d774c53 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -12,7 +12,7 @@ import { IDisposable } from '../../../../base/common/lifecycle.js'; import { isObject, isString } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { EndOfLinePreference } from '../../../../editor/common/model.js'; +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; import { localize, localize2 } from '../../../../nls.js'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../platform/accessibility/common/accessibility.js'; import { Action2, registerAction2, IAction2Options, MenuId } from '../../../../platform/actions/common/actions.js'; @@ -45,11 +45,11 @@ import { isAbsolute } from '../../../../base/common/path.js'; import { ITerminalQuickPickItem } from './terminalProfileQuickpick.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { getIconId, getColorClass, getUriClasses } from './terminalIcon.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { dirname } from '../../../../base/common/resources.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; +import { getIconClasses } from '../../../../editor/common/language/services/getIconClasses.js'; import { FileKind } from '../../../../platform/files/common/files.js'; import { TerminalCapability } from '../../../../platform/terminal/common/capabilities/capabilities.js'; import { killTerminalIcon, newTerminalIcon } from './terminalIcons.js'; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts b/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts index d37dcbd684d..8eb44244624 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalExtensions.ts @@ -42,7 +42,7 @@ export type ITerminalContributionDescription = { readonly id: string } & ( export function registerTerminalContribution(id: string, ctor: { new(ctx: ITerminalContributionContext, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals?: false): void; export function registerTerminalContribution(id: string, ctor: { new(ctx: IDetachedCompatibleTerminalContributionContext, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals: true): void; export function registerTerminalContribution(id: string, ctor: { new(ctx: any, ...services: Services): ITerminalContribution }, canRunInDetachedTerminals: boolean = false): void { - // eslint-disable-next-line local/code-no-dangerous-type-assertions + TerminalContributionRegistry.INSTANCE.registerTerminalContribution({ id, ctor, canRunInDetachedTerminals } as ITerminalContributionDescription); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index b835c0b5c52..0ecf57ffe61 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -222,7 +222,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach macOptionIsMeta: config.macOptionIsMeta, macOptionClickForcesSelection: config.macOptionClickForcesSelection, rightClickSelectsWord: config.rightClickBehavior === 'selectWord', - fastScrollModifier: 'alt', + //fastScrollModifier: 'alt', fastScrollSensitivity: config.fastScrollSensitivity, scrollSensitivity: config.mouseWheelScrollSensitivity, wordSeparator: config.wordSeparators, diff --git a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts index 267cb5ef998..5b9ace04cef 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { editorOverviewRulerBorder } from '../../../../editor/common/core/editorColorRegistry.js'; +import { editorOverviewRulerBorder } from '../../../../editor/common/language/core/editorColorRegistry.js'; import * as nls from '../../../../nls.js'; import { registerColor, ColorIdentifier, ColorDefaults, editorFindMatch, editorFindMatchHighlight, overviewRulerFindMatchForeground, editorSelectionBackground, transparent, editorHoverHighlight } from '../../../../platform/theme/common/colorRegistry.js'; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 9200de5d255..5d61961482c 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -8,7 +8,7 @@ import { Event } from '../../../../../base/common/event.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { isWindows } from '../../../../../base/common/platform.js'; -import { Position } from '../../../../../editor/common/core/position.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; import { localize2 } from '../../../../../nls.js'; import { AccessibleViewProviderId, IAccessibleViewService, NavigationType } from '../../../../../platform/accessibility/browser/accessibleView.js'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from '../../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts index 1a413fef1bf..1347fedf90b 100644 --- a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts @@ -5,9 +5,9 @@ import { URI } from '../../../../../base/common/uri.js'; import { Event } from '../../../../../base/common/event.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize, localize2 } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EnvironmentVariableMutatorType, EnvironmentVariableScope, IEnvironmentVariableMutator, IMergedEnvironmentVariableCollection } from '../../../../../platform/terminal/common/environmentVariable.js'; diff --git a/src/vs/workbench/contrib/terminalContrib/history/browser/terminalRunRecentQuickPick.ts b/src/vs/workbench/contrib/terminalContrib/history/browser/terminalRunRecentQuickPick.ts index 4c06a09c979..bd96f48212a 100644 --- a/src/vs/workbench/contrib/terminalContrib/history/browser/terminalRunRecentQuickPick.ts +++ b/src/vs/workbench/contrib/terminalContrib/history/browser/terminalRunRecentQuickPick.ts @@ -5,9 +5,9 @@ import { Toggle } from '../../../../../base/browser/ui/toggle/toggle.js'; import { isMacintosh, OperatingSystem } from '../../../../../base/common/platform.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; -import { IModelService } from '../../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts index 29bb3e3df3d..2173756caa6 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkDetectorAdapter.ts @@ -42,6 +42,16 @@ export class TerminalLinkDetectorAdapter extends Disposable implements ILinkProv super(); } + override dispose(): void { + if (this._activeLinks) { + for (const link of this._activeLinks) { + link.dispose(); + } + this._activeLinks = undefined; + } + super.dispose(); + } + private _activeProvideLinkRequests: Map> = new Map(); async provideLinks(bufferLineNumber: number, callback: (links: ILink[] | undefined) => void) { let activeRequest = this._activeProvideLinkRequests.get(bufferLineNumber); diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers.ts index 90fc7d444e3..dc3a5912a03 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminalLinkHelpers.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type { IViewportRange, IBufferRange, IBufferLine, IBuffer, IBufferCellPosition } from '@xterm/xterm'; -import { IRange } from '../../../../../editor/common/core/range.js'; +import { IRange } from '../../../../../editor/common/language/core/range.js'; import { OperatingSystem } from '../../../../../base/common/platform.js'; import { IPath, posix, win32 } from '../../../../../base/common/path.js'; import { ITerminalCapabilityStore, TerminalCapability } from '../../../../../platform/terminal/common/capabilities/capabilities.js'; diff --git a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts index f82948d3106..40a21334563 100644 --- a/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.ts @@ -22,10 +22,10 @@ import { isUriComponents, URI } from '../../../../base/common/uri.js'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, isCodeEditor, MouseTargetType, OverlayWidgetPositionPreference } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { IModelDecorationOptions, InjectedTextCursorStops, InjectedTextOptions, ITextModel } from '../../../../editor/common/model.js'; +import { IModelDecorationOptions, InjectedTextCursorStops, InjectedTextOptions, ITextModel } from '../../../../editor/common/language/model.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testCoverageView.ts b/src/vs/workbench/contrib/testing/browser/testCoverageView.ts index 470a1ff5c10..9cca973a13b 100644 --- a/src/vs/workbench/contrib/testing/browser/testCoverageView.ts +++ b/src/vs/workbench/contrib/testing/browser/testCoverageView.ts @@ -21,8 +21,8 @@ import { IPrefixTreeNode } from '../../../../base/common/prefixTree.js'; import { basenameOrAuthority } from '../../../../base/common/resources.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { getActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts index 6ba098b83f1..ce5b1bbc039 100644 --- a/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts +++ b/src/vs/workbench/contrib/testing/browser/testExplorerActions.ts @@ -15,10 +15,10 @@ import { IActiveCodeEditor, ICodeEditor } from '../../../../editor/browser/edito import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorOption, GoToLocationValues } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { SymbolNavigationAction } from '../../../../editor/contrib/gotoSymbol/browser/goToCommands.js'; import { ReferencesModel } from '../../../../editor/contrib/gotoSymbol/browser/referencesModel.js'; import { MessageController } from '../../../../editor/contrib/message/browser/messageController.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testMessageColorizer.ts b/src/vs/workbench/contrib/testing/browser/testMessageColorizer.ts index f722e7805e1..f66cef814d3 100644 --- a/src/vs/workbench/contrib/testing/browser/testMessageColorizer.ts +++ b/src/vs/workbench/contrib/testing/browser/testMessageColorizer.ts @@ -9,8 +9,8 @@ import { IDisposable, toDisposable } from '../../../../base/common/lifecycle.js' import { GraphemeIterator, forAnsiStringParts, removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; import './media/testMessageColorizer.css'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; const colorAttrRe = /^\x1b\[([0-9]+)m$/; diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts index 4f06c5ee9ca..6914de5dc9f 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -18,7 +18,7 @@ import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEdito import { EmbeddedDiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { IDiffEditorOptions, IEditorOptions } from '../../../../../editor/common/config/editorOptions.js'; -import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IResolvedTextEditorModel, ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { peekViewResultsBackground } from '../../../../../editor/contrib/peekView/browser/peekView.js'; import { localize } from '../../../../../nls.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts index 092eec8fff7..b3d678c8b4f 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts @@ -6,7 +6,7 @@ import { MarshalledId } from '../../../../../base/common/marshallingIds.js'; import { URI } from '../../../../../base/common/uri.js'; -import { Range } from '../../../../../editor/common/core/range.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; import { TestId } from '../../common/testId.js'; import { ITestResult } from '../../common/testResult.js'; import { IRichLocation, ITestItem, ITestMessage, ITestMessageMenuArgs, ITestRunTask, ITestTaskState, InternalTestItem, TestMessageType, TestResultItem } from '../../common/testTypes.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts index 5f2990a413a..e28b8f54865 100644 --- a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts @@ -15,7 +15,7 @@ import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { observableValue } from '../../../../../base/common/observable.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; -import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../../nls.js'; import { FloatingClickMenu } from '../../../../../platform/actions/browser/floatingMenu.js'; import { createActionViewItem } from '../../../../../platform/actions/browser/menuEntryActionViewItem.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index dfe9127e8e4..e292796d9c1 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -28,12 +28,12 @@ import { generateUuid } from '../../../../base/common/uuid.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IContentWidgetRenderedCoordinate, IEditorMouseEvent, MouseTargetType } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { overviewRulerError, overviewRulerInfo } from '../../../../editor/common/core/editorColorRegistry.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IRange } from '../../../../editor/common/core/range.js'; +import { overviewRulerError, overviewRulerInfo } from '../../../../editor/common/language/core/editorColorRegistry.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; -import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationsChangeAccessor, IModelDeltaDecoration, ITextModel, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { localize } from '../../../../nls.js'; import { getFlatContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { IMenuService, MenuId } from '../../../../platform/actions/common/actions.js'; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index bb40133b638..e4467455d6b 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -23,11 +23,11 @@ import { ICodeEditorService } from '../../../../editor/browser/services/codeEdit import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diffEditor/embeddedDiffEditorWidget.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditor, IEditorContribution, ScrollType } from '../../../../editor/common/editorCommon.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from '../../../../editor/contrib/peekView/browser/peekView.js'; import { localize, localize2 } from '../../../../nls.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index 4f9fbbd8e9a..f6d5c9bdfa5 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -13,8 +13,8 @@ import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { IObservable } from '../../../../base/common/observable.js'; import { IPrefixTreeNode, WellDefinedPrefixTree } from '../../../../base/common/prefixTree.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { MutableObservableValue } from './observableValue.js'; diff --git a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts index 00ea40df0f7..0d2162a35c9 100644 --- a/src/vs/workbench/contrib/testing/common/testServiceImpl.ts +++ b/src/vs/workbench/contrib/testing/common/testServiceImpl.ts @@ -11,8 +11,8 @@ import { Disposable, IDisposable, toDisposable } from '../../../../base/common/l import { observableValue } from '../../../../base/common/observable.js'; import { isDefined } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Location } from '../../../../editor/common/languages.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Location } from '../../../../editor/common/language/languages.js'; import { localize } from '../../../../nls.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; diff --git a/src/vs/workbench/contrib/testing/common/testTypes.ts b/src/vs/workbench/contrib/testing/common/testTypes.ts index 6da22ad3f6d..75e1d5cc8a9 100644 --- a/src/vs/workbench/contrib/testing/common/testTypes.ts +++ b/src/vs/workbench/contrib/testing/common/testTypes.ts @@ -6,8 +6,8 @@ import { IMarkdownString } from '../../../../base/common/htmlContent.js'; import { MarshalledId } from '../../../../base/common/marshallingIds.js'; import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { localize } from '../../../../nls.js'; import { TestId } from './testId.js'; diff --git a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts index 3ef6281ac64..a838f2bce70 100644 --- a/src/vs/workbench/contrib/testing/common/testingContentProvider.ts +++ b/src/vs/workbench/contrib/testing/common/testingContentProvider.ts @@ -7,10 +7,10 @@ import { VSBuffer } from '../../../../base/common/buffer.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { removeAnsiEscapeCodes } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; -import { ILanguageSelection, ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { ILanguageSelection, ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { ITestResultService } from './testResultService.js'; diff --git a/src/vs/workbench/contrib/testing/common/testingDecorations.ts b/src/vs/workbench/contrib/testing/common/testingDecorations.ts index 0bfa2aa56ff..cc4cf9f3858 100644 --- a/src/vs/workbench/contrib/testing/common/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/common/testingDecorations.ts @@ -7,8 +7,8 @@ import { IAction } from '../../../../base/common/actions.js'; import { binarySearch } from '../../../../base/common/arrays.js'; import { Event } from '../../../../base/common/event.js'; import { URI } from '../../../../base/common/uri.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IModelDeltaDecoration } from '../../../../editor/common/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { IModelDeltaDecoration } from '../../../../editor/common/language/model.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ITestMessage } from './testTypes.js'; diff --git a/src/vs/workbench/contrib/testing/test/browser/codeCoverageDecorations.test.ts b/src/vs/workbench/contrib/testing/test/browser/codeCoverageDecorations.test.ts index 102ce198d80..52594032fec 100644 --- a/src/vs/workbench/contrib/testing/test/browser/codeCoverageDecorations.test.ts +++ b/src/vs/workbench/contrib/testing/test/browser/codeCoverageDecorations.test.ts @@ -6,9 +6,9 @@ import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; -import { Position } from '../../../../../editor/common/core/position.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { ITextModel } from '../../../../../editor/common/model.js'; +import { Position } from '../../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../../editor/common/language/core/range.js'; +import { ITextModel } from '../../../../../editor/common/language/model.js'; import { CoverageDetailsModel } from '../../browser/codeCoverageDecorations.js'; import { CoverageDetails, DetailType } from '../../common/testTypes.js'; diff --git a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts index 47a41a6d9a4..3669fffe918 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.test.contribution.ts @@ -5,7 +5,7 @@ import { URI } from '../../../../base/common/uri.js'; import type * as Parser from '@vscode/tree-sitter-wasm'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IWorkbenchThemeService, IWorkbenchColorTheme } from '../../../services/themes/common/workbenchThemeService.js'; @@ -13,19 +13,19 @@ import { IEditorService } from '../../../services/editor/common/editorService.js import { EditorResourceAccessor } from '../../../common/editor.js'; import { ITextMateTokenizationService } from '../../../services/textMate/browser/textMateTokenizationFeature.js'; import type { IGrammar, StateStack } from 'vscode-textmate'; -import { TokenizationRegistry, TreeSitterTokenizationRegistry } from '../../../../editor/common/languages.js'; -import { TokenMetadata } from '../../../../editor/common/encodedTokenAttributes.js'; +import { TokenizationRegistry, TreeSitterTokenizationRegistry } from '../../../../editor/common/language/languages.js'; +import { TokenMetadata } from '../../../../editor/common/language/encodedTokenAttributes.js'; import { ThemeRule, findMatchingThemeRule } from '../../../services/textMate/common/TMHelper.js'; import { Color } from '../../../../base/common/color.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { basename } from '../../../../base/common/resources.js'; import { Schemas } from '../../../../base/common/network.js'; import { splitLines } from '../../../../base/common/strings.js'; -import { ITextModelTreeSitter, ITreeSitterParserService } from '../../../../editor/common/services/treeSitterParserService.js'; +import { ITextModelTreeSitter, ITreeSitterParserService } from '../../../../editor/common/language/services/treeSitterParserService.js'; import { ColorThemeData, findMetadata } from '../../../services/themes/common/colorThemeData.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { Event } from '../../../../base/common/event.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; interface IToken { c: string; // token diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index c1a991668cd..6ccda8ce11a 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -7,7 +7,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../base/com import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { Command } from '../../../../editor/common/languages.js'; +import { Command } from '../../../../editor/common/language/languages.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IAccessibilityInformation } from '../../../../platform/accessibility/common/accessibility.js'; diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.ts index 9a47ac6adcd..fc6f71a4efd 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchy.contribution.ts @@ -12,8 +12,8 @@ import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorAction2, EditorContributionInstantiation, registerEditorContribution, ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; import { PeekContext } from '../../../../editor/contrib/peekView/browser/peekView.js'; import { localize, localize2 } from '../../../../nls.js'; diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts index 0af4bb62f51..cc456296c57 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyPeek.ts @@ -16,11 +16,11 @@ import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { IEditorOptions } from '../../../../editor/common/config/editorOptions.js'; -import { IPosition } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; +import { IPosition } from '../../../../editor/common/language/core/position.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; import { ScrollType } from '../../../../editor/common/editorCommon.js'; -import { IModelDecorationOptions, TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from '../../../../editor/common/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IModelDecorationOptions, TrackedRangeStickiness, IModelDeltaDecoration, OverviewRulerLane } from '../../../../editor/common/language/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import * as peekView from '../../../../editor/contrib/peekView/browser/peekView.js'; import { localize } from '../../../../nls.js'; import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; diff --git a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree.ts b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree.ts index 253fb35cc9c..d04d045b1c4 100644 --- a/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree.ts +++ b/src/vs/workbench/contrib/typeHierarchy/browser/typeHierarchyTree.ts @@ -9,9 +9,9 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { FuzzyScore, createMatches } from '../../../../base/common/filters.js'; import { IconLabel } from '../../../../base/browser/ui/iconLabel/iconLabel.js'; -import { SymbolKinds, SymbolTag } from '../../../../editor/common/languages.js'; +import { SymbolKinds, SymbolTag } from '../../../../editor/common/language/languages.js'; import { compare } from '../../../../base/common/strings.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; import { localize } from '../../../../nls.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; diff --git a/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts b/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts index 3144b4c7a99..9e49f5de106 100644 --- a/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts +++ b/src/vs/workbench/contrib/typeHierarchy/common/typeHierarchy.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { SymbolKind, ProviderResult, SymbolTag } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; +import { IRange, Range } from '../../../../editor/common/language/core/range.js'; +import { SymbolKind, ProviderResult, SymbolTag } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { LanguageFeatureRegistry } from '../../../../editor/common/languageFeatureRegistry.js'; import { URI } from '../../../../base/common/uri.js'; -import { IPosition, Position } from '../../../../editor/common/core/position.js'; +import { IPosition, Position } from '../../../../editor/common/language/core/position.js'; import { isNonEmptyArray } from '../../../../base/common/arrays.js'; import { onUnexpectedExternalError } from '../../../../base/common/errors.js'; import { IDisposable, RefCountedDisposable } from '../../../../base/common/lifecycle.js'; import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { assertType } from '../../../../base/common/types.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; export const enum TypeHierarchyDirection { Subtypes = 'subtypes', diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index beedeb01f6e..261957e5d39 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -11,9 +11,9 @@ import { KeybindingParser } from '../../../../base/common/keybindingParser.js'; import { escape } from '../../../../base/common/strings.js'; import { URI } from '../../../../base/common/uri.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { TokenizationRegistry } from '../../../../editor/common/languages.js'; +import { TokenizationRegistry } from '../../../../editor/common/language/languages.js'; import { generateTokensCSSForColorMap } from '../../../../editor/common/languages/supports/tokenization.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; import * as nls from '../../../../nls.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 21d1c9b2065..cc6d2c5404f 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -10,10 +10,10 @@ import { Disposable, DisposableStore, MutableDisposable, toDisposable, IDisposab import { isEqual } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import type { ITextModel } from '../../../../editor/common/model.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/services/resolverService.js'; +import type { ITextModel } from '../../../../editor/common/language/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ILanguageService } from '../../../../editor/common/language/language.js'; +import { ITextModelContentProvider, ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; import { localize, localize2 } from '../../../../nls.js'; import { MenuId, MenuRegistry, registerAction2, Action2 } from '../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; diff --git a/src/vs/workbench/contrib/void/browser/ChatAcpHandler.ts b/src/vs/workbench/contrib/void/browser/ChatAcpHandler.ts new file mode 100644 index 00000000000..edec0106f9e --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatAcpHandler.ts @@ -0,0 +1,1075 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { timeout } from '../../../../base/common/async.js'; +import { encodeBase64 } from '../../../../base/common/buffer.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { IVoidModelService } from '../common/voidModelService.js'; +import { IDirectoryStrService } from '../../../../platform/void/common/directoryStrService.js'; +import { IAcpService, IAcpUserMessage, IAcpChatMessage, IAcpMessageChunk } from '../../../../platform/acp/common/iAcpService.js'; +import { getErrorMessage, RawToolCallObj } from '../../../../platform/void/common/sendLLMMessageTypes.js'; +import { chat_userMessageContent, isAToolName, ToolName } from '../common/prompt/prompts.js'; +import { AnyToolName, ChatAttachment, StagingSelectionItem, ChatMessage } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { IEditCodeService } from './editCodeServiceInterface.js'; +import { ChatHistoryCompressor } from './ChatHistoryCompressor.js'; +import { ChatToolOutputManager } from './ChatToolOutputManager.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; + +const _snakeToCamel = (s: string) => s.replace(/_([a-z])/g, (_, c) => c.toUpperCase()); +const _normalizeFsPath = (p: string) => String(p ?? '').replace(/\\/g, '/').replace(/\/+$/g, ''); +const _stripDotPrefix = (p: string) => { + if (p.startsWith('./')) return p.substring(2); + if (p.startsWith('.\\')) return p.substring(2); + return p; +}; +const _resolvePathWithWorkspace = (pathStr: string, workspaceRoot: URI | undefined): URI => { + try { + const s = String(pathStr ?? '').trim(); + if (!s && workspaceRoot) return workspaceRoot; + + // Real URI with scheme (file://, vscode-remote://, etc) + // Important: don't treat "C:foo" as a scheme URI + const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(s) && !/^[a-zA-Z]:/.test(s); + if (hasScheme) { + return URI.parse(s); + } + + // Windows absolute path: C:\... or C:/... + const isWindowsDriveAbs = /^[a-zA-Z]:[\\/]/.test(s); + // POSIX absolute path: /... + const isPosixAbs = s.startsWith('/'); + + if (workspaceRoot) { + const rootNorm = _normalizeFsPath(workspaceRoot.fsPath); + const inputNorm = _normalizeFsPath(s); + if (rootNorm && (inputNorm === rootNorm || inputNorm.startsWith(rootNorm + '/'))) { + const rel = inputNorm === rootNorm ? '' : inputNorm.slice(rootNorm.length + 1); + return rel ? URI.joinPath(workspaceRoot, rel) : workspaceRoot; + } + + if (!isWindowsDriveAbs && !isPosixAbs) { + const cleanPath = _stripDotPrefix(s); + return URI.joinPath(workspaceRoot, cleanPath); + } + + // In remote/virtual workspaces absolute paths should stay in that scheme. + if (workspaceRoot.scheme !== 'file') { + const remotePath = s.replace(/\\/g, '/'); + return workspaceRoot.with({ path: remotePath.startsWith('/') ? remotePath : `/${remotePath}` }); + } + } + + // Fallback: treat as local file path + return URI.file(s); + } catch { + return URI.file(String(pathStr ?? '')); + } +}; +const _deepCamelize = (v: any): any => { + if (Array.isArray(v)) return v.map(_deepCamelize); + if (v && typeof v === 'object') { + const out: any = {}; + for (const [k, val] of Object.entries(v)) { + out[_snakeToCamel(k)] = _deepCamelize(val); + } + return out; + } + return v; +}; + +export const normalizeAcpToolName = (raw: string): ToolName | string => { + const n = (raw || '').trim(); + const map: Record = { + 'fs/read_text_file': 'read_file', + 'fs/write_text_file': 'rewrite_file', + 'terminal/create': 'run_command', + 'terminal/kill': 'kill_persistent_terminal', + 'terminal/output': 'open_persistent_terminal', + 'terminal/release': 'kill_persistent_terminal', + 'terminal/wait_for_exit': 'run_persistent_command', + }; + return (map[n] ?? n) as ToolName | string; +}; + +export const normalizeAcpArgsForUi = ( + toolName: AnyToolName | string, + rawParams: Record | undefined, + workspaceRoot: URI | undefined +) => { + const src = rawParams && typeof rawParams === 'object' && 'args' in rawParams ? (rawParams as any).args : rawParams; + const p = _deepCamelize(src ?? {}); + + // --- NEW: parse "(from line..., limit ...)" embedded into uri for read_file --- + if (toolName === 'read_file' && p && typeof (p as any).uri === 'string') { + const uriStr = String((p as any).uri); + const range = _parseReadRangeFromText(uriStr); + + // only fill if missing + if (typeof (p as any).startLine !== 'number' && typeof range.startLine === 'number') (p as any).startLine = range.startLine; + if (typeof (p as any).linesCount !== 'number' && typeof range.linesCount === 'number') (p as any).linesCount = range.linesCount; + + // clean uri string before resolving to URI + (p as any).uri = _stripReadRangeSuffixFromUri(uriStr); + } + + const resolvePath = (pathStr: string): URI => _resolvePathWithWorkspace(pathStr, workspaceRoot); + + if (p && typeof (p as any).uri === 'string') (p as any).uri = resolvePath((p as any).uri); + if (p && typeof (p as any).searchInFolder === 'string') (p as any).searchInFolder = resolvePath((p as any).searchInFolder); + if ((p as any)?.isFolder !== null && typeof (p as any).isFolder !== 'boolean') (p as any).isFolder = String((p as any).isFolder).toLowerCase() === 'true'; + return p; +}; + +// ---------- NEW helpers for external ACP normalization ---------- + +const _asObj = (v: any): Record | null => + (v && typeof v === 'object' && !Array.isArray(v)) ? (v as Record) : null; + +const _inferInternalToolNameFromResult = (rawName: string, result: any): AnyToolName | string => { + const rn = String(rawName ?? '').trim(); + const r = _asObj(result); + + // 1) If it already matches internal tool names, keep it + if (isAToolName(rn as any)) return rn as any; + + // 2) Strong structural signals + if (r) { + // read file: path + content + if (typeof r.path === 'string' && typeof r.content === 'string') return 'read_file'; + // edit/patch: diffs array + if (Array.isArray((r as any).diffs) && (r as any).diffs.length > 0) return 'edit_file'; + // terminal: output/exitCode/terminalId + if (typeof (r as any).output === 'string') return 'run_command'; + if (typeof (r as any).terminalId === 'string') return 'run_command'; + if (typeof (r as any).exitCode === 'number' || (r as any).exitCode === null) return 'run_command'; + if (Array.isArray((r as any).terminals) && (r as any).terminals.length > 0) return 'run_command'; + } + + // 3) Weak heuristic based on name strings (last resort) + if (rn.startsWith('read_file')) return 'read_file'; + if (rn.startsWith('edit_file') || rn.toLowerCase().includes('patch')) return 'edit_file'; + + // if it looks like a shell command line, treat as terminal + if (/^(cat|ls|pwd|echo|git|npm|pnpm|yarn|node|python|bash|sh)\b/i.test(rn)) return 'run_command'; + + return rawName; +}; + +const _parseReadRangeFromText = (s: string): { startLine?: number; linesCount?: number } => { + const str = String(s ?? ''); + + // (from line 650, limit 20 lines) + let m = str.match(/from line\s+(\d+)\s*,\s*limit\s+(\d+)\s+lines/i); + if (m) { + const startLine = Number(m[1]); + const linesCount = Number(m[2]); + if (Number.isFinite(startLine) && Number.isFinite(linesCount) && linesCount > 0) { + return { startLine, linesCount }; + } + return {}; + } + + + m = str.match(/limit\s+(\d+)\s+lines/i); + if (m) { + const linesCount = Number(m[1]); + if (Number.isFinite(linesCount) && linesCount > 0) { + return { startLine: 1, linesCount }; + } + } + + return {}; +}; + +const _stripReadRangeSuffixFromUri = (uriStr: string): string => { + const s = String(uriStr ?? ''); + + return s.replace(/\s*\(\s*(?:from line\s+\d+\s*,\s*)?limit\s+\d+\s+lines\s*\)\s*$/i, '').trim(); +}; + +const _buildCommandLine = (command: any, args: any): string | undefined => { + const cmd = typeof command === 'string' ? command : ''; + if (!cmd) return undefined; + const arr = Array.isArray(args) ? args.map(a => String(a ?? '')) : []; + return arr.length ? `${cmd} ${arr.join(' ')}` : cmd; +}; + +const _diffsToPatchUnified = (diffs: Array<{ path: string; oldText?: string; newText: string }>): string => { + const blocks: string[] = []; + for (const d of diffs) { + const path = String(d?.path ?? 'unknown'); + const oldText = String(d?.oldText ?? ''); + const newText = String(d?.newText ?? ''); + blocks.push( + [ + `--- a/${path}`, + `+++ b/${path}`, + `@@`, + ...oldText.split('\n').map(l => `-${l}`), + ...newText.split('\n').map(l => `+${l}`), + `` + ].join('\n') + ); + } + return blocks.join('\n'); +}; + +export interface IThreadStateAccess { + getThreadMessages(threadId: string): ChatMessage[]; + getStreamState(threadId: string): any; + setStreamState(threadId: string, state: any): void; + addMessageToThread(threadId: string, message: ChatMessage): void; + updateLatestTool(threadId: string, tool: any): void; + setThreadState(threadId: string, state: any): void; + accumulateTokenUsage(threadId: string, usage: any): void; + addUserCheckpoint(threadId: string): void; + currentModelSelectionProps(): { modelSelection: any; modelSelectionOptions: any }; +} + +export class ChatAcpHandler extends Disposable { + private readonly _acpStreamByThread = new Map(); + private readonly _acpToolCallInfoByKey = new Map; paramsForUi: any }>(); + + constructor( + @IAcpService private readonly _acpService: IAcpService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, + @IFileService private readonly _fileService: IFileService, + @IDirectoryStrService private readonly _directoryStringService: IDirectoryStrService, + @IVoidModelService private readonly _voidModelService: IVoidModelService, + @IEditCodeService private readonly _editCodeService: IEditCodeService, + @ILogService private readonly _logService: ILogService, + private readonly _historyCompressor: ChatHistoryCompressor, + private readonly _toolOutputManager: ChatToolOutputManager + ) { + super(); + } + + private _getExistingToolMsgById(threadId: string, toolCallId: string, access: IThreadStateAccess): any | null { + const msgs = access.getThreadMessages(threadId) ?? []; + for (let i = msgs.length - 1; i >= 0; i--) { + const m: any = msgs[i] as any; + if (m?.role === 'tool' && String(m.id ?? '') === String(toolCallId)) return m; + } + return null; + } + + private _acpToolKey(threadId: string, toolCallId: string): string { + return `${threadId}:${toolCallId}`; + } + + public enqueueToolRequestFromAcp( + threadId: string, + req: { id: string; name: AnyToolName | string; rawParams: Record; params?: Record }, + access: IThreadStateAccess + ): void { + const normName = normalizeAcpToolName(String(req.name)); + + // Flush assistant text if any + const st = access.getStreamState(threadId); + if (st?.isRunning === 'LLM' && st.llmInfo) { + const { displayContentSoFar, reasoningSoFar } = st.llmInfo; + if ((displayContentSoFar?.length ?? 0) || (reasoningSoFar?.length ?? 0)) { + access.addMessageToThread(threadId, { + role: 'assistant', + displayContent: displayContentSoFar ?? '', + reasoning: reasoningSoFar ?? '', + anthropicReasoning: null + }); + } + } + + const workspace = this._workspaceContextService.getWorkspace(); + const rootUri = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + + const paramsForUi = normalizeAcpArgsForUi(normName, req.params ?? req.rawParams, rootUri); + + // Remember tool call info (used later for tool_result/progress) + this._acpToolCallInfoByKey.set(this._acpToolKey(threadId, String(req.id)), { + name: String(normName), + rawParams: req.rawParams ?? {}, + paramsForUi + }); + + // IMPORTANT: use updateLatestTool (by id) to avoid duplicate headers + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_request', + content: 'Awaiting user permission...', + displayContent: 'Awaiting user permission...', + result: null, + name: (isAToolName(normName) ? normName : (normName as any)), + params: paramsForUi, + id: req.id, + rawParams: req.rawParams + }); + + // Keep stream in awaiting_user + access.setStreamState(threadId, { isRunning: 'awaiting_user' }); + } + + public async runAcp(opts: { + threadId: string; + userMessage: string; + _chatSelections?: StagingSelectionItem[]; + attachments?: ChatAttachment[]; + }, access: IThreadStateAccess): Promise { + + const { threadId, userMessage, _chatSelections, attachments } = opts; + + // --- Helper: Merge Text --- + const MAX_OVERLAP = 512; + function findOverlapLength(prev: string, incoming: string, max: number): number { + const prevSuffix = prev.slice(prev.length - max); + const incomingPrefix = incoming.slice(0, max); + const combined = `${incomingPrefix}\u0000${prevSuffix}`; + + const lps = new Array(combined.length).fill(0); + for (let i = 1; i < combined.length; i += 1) { + let len = lps[i - 1]; + while (len > 0 && combined[i] !== combined[len]) { + len = lps[len - 1]; + } + if (combined[i] === combined[len]) { + len += 1; + } + lps[i] = len; + } + + return Math.min(lps[lps.length - 1], max); + } + + function mergeWithOverlap(prev: string, incoming: string): string { + if (!prev) return incoming; + const max = Math.min(prev.length, incoming.length, MAX_OVERLAP); + if (max <= 0) return prev + incoming; + const overlap = findOverlapLength(prev, incoming, max); + if (overlap > 0) return prev + incoming.slice(overlap); + return prev + incoming; + } + + const mergeAcpText = (prev: string, incoming: string): string => { + if (incoming === '') return prev; + if (!prev) return incoming; + if (incoming === prev) return prev; + if (incoming.startsWith(prev)) return incoming; + if (prev.startsWith(incoming)) return prev; + + const merged = mergeWithOverlap(prev, incoming); + if (merged.length < prev.length + incoming.length) return merged; + + const trimmedPrev = prev.replace(/\s+$/u, ''); + const last = trimmedPrev[trimmedPrev.length - 1] ?? ''; + const first = incoming[0] ?? ''; + + let sep = ''; + if (last && first && !/\s/u.test(last) && !/\s/u.test(first)) { + if (last === ':') sep = ' '; + else if (/[.!?]/.test(last)) sep = ' '; + } + return prev + sep + incoming; + }; + + const mergeAcpReasoning = (prev: string, incoming: string): string => { + if (incoming === '') return prev; + if (!prev) return incoming; + if (incoming === prev) return prev; + if (incoming.startsWith(prev)) return incoming; + if (prev.startsWith(incoming)) return prev; + return mergeWithOverlap(prev, incoming); + }; + + const flushAssistantIfAny = () => { + const info = access.getStreamState(threadId)?.llmInfo; + if (!info) return; + const text = info.displayContentSoFar ?? ''; + const reasoning = info.reasoningSoFar ?? ''; + if (!text && !reasoning) return; + + access.addMessageToThread(threadId, { + role: 'assistant', + displayContent: text, + reasoning, + anthropicReasoning: null + }); + + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: '', + reasoningSoFar: '', + toolCallSoFar: null, + planSoFar: info.planSoFar + }, + interrupt: interruptP + }); + }; + + let done = false; + + this.clearAcpState(threadId); + + const cancelFn = () => { + const entry = this._acpStreamByThread.get(threadId); + try { entry?.stream?.cancel?.(); } catch { /* noop */ } + }; + const interruptP = Promise.resolve(cancelFn); + + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { displayContentSoFar: '', reasoningSoFar: '', toolCallSoFar: null }, + interrupt: interruptP + }); + + let history = this._buildAcpHistory(threadId, access); + + const currSelns = _chatSelections ?? null; + const builtContent = await chat_userMessageContent( + userMessage, + currSelns, + { + directoryStrService: this._directoryStringService, + fileService: this._fileService, + voidModelService: this._voidModelService, + } + ); + + const contentBlocks: any[] = []; + if (builtContent && builtContent.trim()) { + contentBlocks.push({ type: 'text', text: builtContent }); + } + if (attachments && attachments.length) { + for (const att of attachments) { + if (att.kind !== 'image') continue; + try { + const fileData = await this._fileService.readFile(att.uri); + const dataBase64 = encodeBase64(fileData.value); + contentBlocks.push({ + type: 'image', + mimeType: (att as any).mimeType || 'image/png', + data: dataBase64, + }); + } catch { /* ignore */ } + } + } + + let message: IAcpUserMessage = { role: 'user', content: builtContent } as IAcpUserMessage; + if (contentBlocks.length) { + (message as any).contentBlocks = contentBlocks; + } + + // Compression + try { + const { modelSelection, modelSelectionOptions } = access.currentModelSelectionProps(); + if (modelSelection) { + const { summaryText, compressionInfo } = await this._historyCompressor.maybeSummarizeHistoryBeforeLLM({ + threadId, + messages: access.getThreadMessages(threadId), + modelSelection, + modelSelectionOptions, + }); + if (compressionInfo) { + access.setThreadState(threadId, { historyCompression: compressionInfo }); + } + if (summaryText && summaryText.trim()) { + const tail = history.slice(-8); + history = [ + { role: 'assistant', content: summaryText.trim() } as IAcpChatMessage, + ...tail, + ]; + } + } + } catch { /* fail open */ } + + const gs = this._settingsService.state.globalSettings; + let stream: any; + let attempt = 0; + const chatRetries = gs.chatRetries; + const retryDelay = gs.retryDelay; + + while (attempt < chatRetries + 1 && !stream) { + attempt += 1; + try { + stream = await this._acpService.sendChatMessage(threadId, history, message, { + mode: gs.acpMode, + agentUrl: gs.acpAgentUrl || undefined, + command: gs.acpProcessCommand || undefined, + args: gs.acpProcessArgs || undefined, + env: gs.acpProcessEnv || undefined, + model: gs.acpModel || undefined, + system: gs.acpSystemPrompt || undefined, + featureName: 'Chat', + maxToolOutputLength: gs.maxToolOutputLength, + readFileChunkLines: gs.readFileChunkLines, + }); + } catch (e) { + const msg = getErrorMessage(e); + if (attempt > chatRetries) { + access.setStreamState(threadId, { + isRunning: undefined, + error: { message: msg, fullError: e instanceof Error ? e : null } + }); + access.addUserCheckpoint(threadId); + return; + } + if (retryDelay > 0) await timeout(retryDelay); + } + } + + if (!stream) { + access.setStreamState(threadId, { + isRunning: undefined, + error: { message: 'ACP: failed to start session', fullError: null } + }); + access.addUserCheckpoint(threadId); + return; + } + + this._acpStreamByThread.set(threadId, { stream }); + + let sub: IDisposable | undefined; + const finish = () => { + if (done) return; + done = true; + try { sub?.dispose(); } catch { } + this._acpStreamByThread.delete(threadId); + }; + + const onChunk = async (chunk: IAcpMessageChunk) => { + if (done) return; + + if (chunk.type === 'text') { + let incoming = chunk.text ?? ''; + const prevInfo = access.getStreamState(threadId)?.llmInfo; + const prev = prevInfo?.displayContentSoFar ?? ''; + let next = mergeAcpText(prev, incoming); + if (next === prev) return; + + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: next, + reasoningSoFar: prevInfo?.reasoningSoFar ?? '', + toolCallSoFar: prevInfo?.toolCallSoFar ?? null, + planSoFar: prevInfo?.planSoFar + }, + interrupt: interruptP + }); + return; + } + + if (chunk.type === 'reasoning') { + const incoming = chunk.reasoning ?? ''; + const prevInfo = access.getStreamState(threadId)?.llmInfo; + const prev = prevInfo?.reasoningSoFar ?? ''; + const next = mergeAcpReasoning(prev, incoming); + if (next === prev) return; + + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: prevInfo?.displayContentSoFar ?? '', + reasoningSoFar: next, + toolCallSoFar: prevInfo?.toolCallSoFar ?? null, + planSoFar: prevInfo?.planSoFar + }, + interrupt: interruptP + }); + return; + } + + if (chunk.type === 'plan' && chunk.plan) { + const prev = access.getStreamState(threadId)?.llmInfo ?? { displayContentSoFar: '', reasoningSoFar: '', toolCallSoFar: null }; + if (this._settingsService.state.globalSettings.showAcpPlanInChat !== false) { + const normalizedPlan = { + title: chunk.plan.title, + items: (chunk.plan.items ?? []).map(it => ({ + id: it.id, + text: it.text, + state: (it.state ?? 'pending') as 'pending' | 'running' | 'done' | 'error' + })) + }; + const cleanedText = (prev.displayContentSoFar ?? '').replace(/<\/plan\s*>/gi, '').trimEnd(); + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { ...prev, displayContentSoFar: cleanedText, planSoFar: normalizedPlan }, + interrupt: interruptP + }); + access.setThreadState(threadId, { acpPlan: normalizedPlan }); + } + return; + } + + if (chunk.type === 'tool_call' && chunk.toolCall) { + flushAssistantIfAny(); + const { id, name, args } = chunk.toolCall; + + const normName = normalizeAcpToolName(String(name)); + const toolCallSoFar: RawToolCallObj = { + id, + name: (isAToolName(normName) ? normName : (normName as any)), + rawParams: args ?? {}, + isDone: false, + doneParams: [] + }; + + const workspace = this._workspaceContextService.getWorkspace(); + const rootUri = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + + const paramsForUi = normalizeAcpArgsForUi(normName, args, rootUri); + this._acpToolCallInfoByKey.set(this._acpToolKey(threadId, String(id)), { + name: String(normName), + rawParams: args ?? {}, + paramsForUi + }); + + const prev = access.getStreamState(threadId)?.llmInfo; + access.updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + name: (isAToolName(normName) ? normName : (normName as any)), + params: paramsForUi, + content: 'running...', + displayContent: 'running...', + result: null, + id, + rawParams: args ?? {} + }); + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: prev?.displayContentSoFar ?? '', + reasoningSoFar: prev?.reasoningSoFar ?? '', + toolCallSoFar, + planSoFar: prev?.planSoFar + }, + interrupt: interruptP + }); + return; + } + + if (chunk.type === 'tool_progress' && (chunk as any).toolProgress) { + const tp = (chunk as any).toolProgress; + const id = String(tp.id ?? ''); + if (!id) return; + + const existing = this._getExistingToolMsgById(threadId, id, access); + const prevLen = + (typeof existing?.displayContent === 'string' ? existing.displayContent.length : 0) + || (typeof existing?.content === 'string' ? existing.content.length : 0); + + // Normalize output + const output = (typeof tp.output === 'string') ? tp.output : String(tp.output ?? ''); + const exitStatus = tp.exitStatus; + const isDone = + !!exitStatus && ( + typeof exitStatus.exitCode === 'number' || exitStatus.exitCode === null || + typeof exitStatus.signal === 'string' || exitStatus.signal === null + ); + + // CRITICAL: never overwrite existing output with empty progress + if (!output || output.length === 0) { + this._logService.debug('[Void][ChatAcpHandler][tool_progress][SKIP_EMPTY]', JSON.stringify({ + threadId, + toolCallId: id, + prevLen, + terminalId: typeof tp.terminalId === 'string' ? tp.terminalId : null, + hasExitStatus: !!tp.exitStatus + })); + return; + } + + // While tool is still running, never shrink non-empty output. + if (!isDone && prevLen > 0 && output.length < prevLen) { + this._logService.debug('[Void][ChatAcpHandler][tool_progress][SKIP_SHRINK]', JSON.stringify({ + threadId, + toolCallId: id, + prevLen, + nextLen: output.length, + terminalId: typeof tp.terminalId === 'string' ? tp.terminalId : null, + isDone + })); + return; + } + + // Ignore late progress for skipped/rejected + if (existing && (existing.type === 'skipped' || existing.type === 'rejected')) return; + + const rawNameStr = String(tp.name ?? 'run_command'); + const normName = normalizeAcpToolName(rawNameStr); + const uiToolName = (isAToolName(normName) ? normName : (normName as any)); + + const workspace = this._workspaceContextService.getWorkspace(); + const rootUri = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + + const key = this._acpToolKey(threadId, id); + const callInfo = this._acpToolCallInfoByKey.get(key); + + const rawParamsForUi: Record = { ...(callInfo?.rawParams ?? {}) }; + if (typeof tp.terminalId === 'string' && tp.terminalId) rawParamsForUi.terminalId = tp.terminalId; + + const paramsForUi = callInfo?.paramsForUi + ?? normalizeAcpArgsForUi(uiToolName, rawParamsForUi, rootUri) + ?? {}; + + // For streaming we DO NOT run ToolOutputManager: it can dedupe/normalize and accidentally blank output. + // Just show the current output. + const resultForUi: any = { + toolCallId: id, + ...(tp.terminalId ? { terminalId: String(tp.terminalId) } : {}), + output, + ...(typeof tp.truncated === 'boolean' ? { truncated: tp.truncated } : {}), + ...(exitStatus ? { + exitCode: (typeof exitStatus.exitCode === 'number' || exitStatus.exitCode === null) ? exitStatus.exitCode : undefined, + signal: (typeof exitStatus.signal === 'string' || exitStatus.signal === null) ? exitStatus.signal : undefined + } : {}) + }; + + this._logService.debug('[Void][ChatAcpHandler][tool_progress][APPLY]', JSON.stringify({ + threadId, + toolCallId: id, + uiToolName, + prevLen, + nextLen: output.length, + terminalId: resultForUi.terminalId ?? null, + truncated: typeof resultForUi.truncated === 'boolean' ? resultForUi.truncated : null + })); + + access.updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + name: uiToolName, + params: paramsForUi, + result: resultForUi, + content: output, + displayContent: output, + id, + rawParams: rawParamsForUi + }); + + return; + } + + if (chunk.type === 'tool_result' && chunk.toolResult) { + const { id, name, result, error } = chunk.toolResult; + const existing = this._getExistingToolMsgById(threadId, String(id), access); + if (existing && (existing.type === 'skipped' || existing.type === 'rejected')) { + return; + } + const workspace = this._workspaceContextService.getWorkspace(); + const rootUri = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + + const rawNameStr = String(name ?? ''); + + // DEBUG LOG: Show raw ACP tool result for debugging + this._logService.debug('[Void][ChatAcpHandler][tool_result][RAW_ACP_DATA]', JSON.stringify({ + threadId, + toolCallId: id, + rawName: name, + rawResult: result, + rawError: error + }, null, 2)); + + // --- NEW: infer internal tool name from result structure --- + const inferredName = _inferInternalToolNameFromResult(rawNameStr, result); + const normName = normalizeAcpToolName(String(inferredName)); + const uiToolName = (isAToolName(normName) ? normName : (normName as any)); + + // DEBUG LOG: Show inferred tool name + this._logService.debug('[Void][ChatAcpHandler][tool_result][INFERRED_TOOL]', JSON.stringify({ + threadId, + toolCallId: id, + rawName: rawNameStr, + inferredName, + normName, + uiToolName + }, null, 2)); + + // Prefer params captured earlier (tool_request / tool_call), so read_file gets URI and line range + const key = this._acpToolKey(threadId, String(id)); + const callInfo = this._acpToolCallInfoByKey.get(key); + + const msgs = access.getThreadMessages(threadId); + const lastMsg = msgs[msgs.length - 1]; + const prevParams = + (existing && existing.role === 'tool') ? (existing as any).params : + (lastMsg && lastMsg.role === 'tool') ? (lastMsg as any).params : {}; + const prevRawParams = + (existing && existing.role === 'tool') ? (existing as any).rawParams : + (lastMsg && lastMsg.role === 'tool') ? (lastMsg as any).rawParams : {}; + + let rawParamsForUi: Record = + (callInfo?.rawParams ?? prevRawParams ?? {}) as Record; + + // --- NEW: enrich rawParamsForUi from tool_result.result (path/diffs/command) --- + const rObj = _asObj(result); + + if (uiToolName === 'read_file' && rObj) { + // DEBUG LOG: Show raw params before processing + this._logService.debug('[Void][ChatAcpHandler][tool_result][READ_FILE_BEFORE]', JSON.stringify({ + threadId, + toolCallId: id, + rawParamsForUi: rawParamsForUi, + rObjPath: rObj.path, + rObjContentLength: typeof rObj.content === 'string' ? rObj.content.length : undefined + }, null, 2)); + + const uriRaw = (rawParamsForUi as any).uri; + const uriStr = (typeof uriRaw === 'string') ? uriRaw : ''; + + + const rangeFromUri = _parseReadRangeFromText(uriStr); + if (typeof (rawParamsForUi as any).startLine !== 'number' && typeof rangeFromUri.startLine === 'number') { + (rawParamsForUi as any).startLine = rangeFromUri.startLine; + } + if (typeof (rawParamsForUi as any).linesCount !== 'number' && typeof rangeFromUri.linesCount === 'number') { + (rawParamsForUi as any).linesCount = rangeFromUri.linesCount; + } + + + const hasSuffix = /\(\s*(?:from line\s+\d+\s*,\s*)?limit\s+\d+\s+lines\s*\)\s*$/i.test(uriStr); + if (typeof rObj.path === 'string' && rObj.path && (hasSuffix || !uriStr)) { + (rawParamsForUi as any).uri = rObj.path; + } else if (typeof uriStr === 'string' && uriStr) { + (rawParamsForUi as any).uri = _stripReadRangeSuffixFromUri(uriStr); + } + // DEBUG LOG: Show final params after processing + this._logService.debug('[Void][ChatAcpHandler][tool_result][READ_FILE_AFTER]', JSON.stringify({ + threadId, + toolCallId: id, + finalParamsForUi: rawParamsForUi, + }, null, 2)); + } + + if (uiToolName === 'edit_file' && rObj) { + const diffs: Array<{ path: string; oldText?: string; newText: string }> | undefined = Array.isArray((rObj as any).diffs) ? (rObj as any).diffs : undefined; + const firstPath = diffs && diffs.length ? String(diffs[0]?.path ?? '') : ''; + const filePath = typeof (rObj as any).file === 'string' ? String((rObj as any).file) : ''; + + if (typeof (rawParamsForUi as any).uri !== 'string') { + const p = firstPath || filePath; + if (p) (rawParamsForUi as any).uri = p; + } + + // Provide snippets for EditTool preview/apply (best-effort) + if (diffs && diffs.length) { + const d0 = diffs[0]; + if (typeof (rawParamsForUi as any).originalSnippet !== 'string') (rawParamsForUi as any).originalSnippet = String(d0.oldText ?? ''); + if (typeof (rawParamsForUi as any).updatedSnippet !== 'string') (rawParamsForUi as any).updatedSnippet = String(d0.newText ?? ''); + } + } + + if (uiToolName === 'run_command' && rObj) { + // terminalId + if (typeof (rObj as any).terminalId === 'string' && typeof (rawParamsForUi as any).terminalId !== 'string') { + (rawParamsForUi as any).terminalId = (rObj as any).terminalId; + } + + // command line (best-effort, now host returns command on waitForTerminalExit) + const cmdLine = + _buildCommandLine((rawParamsForUi as any).command, (rawParamsForUi as any).args) + ?? (typeof (rObj as any).command === 'string' ? String((rObj as any).command) : undefined) + ?? _buildCommandLine((rObj as any).command, (rObj as any).args) + ?? (typeof (rObj as any).commandLine === 'string' ? String((rObj as any).commandLine) : undefined) + ?? (typeof rawNameStr === 'string' && rawNameStr.trim() ? rawNameStr.trim() : undefined); + + if (cmdLine && typeof (rawParamsForUi as any).command !== 'string') { + (rawParamsForUi as any).command = cmdLine; + } + + // output (host now returns output; but keep fallbacks) + if (typeof (rObj as any).output !== 'string') { + const t = typeof (rObj as any).text === 'string' ? (rObj as any).text : undefined; + if (t) (rObj as any).output = t; + } + if (typeof (rObj as any).output !== 'string' && typeof (rObj as any).rawOutput === 'string') { + (rObj as any).output = (rObj as any).rawOutput; + } + } + + // Compute paramsForUi from augmented raw params + const computedParamsForUi = normalizeAcpArgsForUi(uiToolName, rawParamsForUi, rootUri); + const paramsForUi = { ...(callInfo?.paramsForUi ?? prevParams ?? {}), ...(computedParamsForUi ?? {}) }; + + // --- NEW: for edit_file, generate patch_unified from diffs so UI shows Preview(diff) like internal --- + let normalizedResult: any = result; + if (uiToolName === 'edit_file') { + const ro = _asObj(result); + const diffs: Array<{ path: string; oldText?: string; newText: string }> | undefined = (ro && Array.isArray((ro as any).diffs)) ? (ro as any).diffs : undefined; + if (ro && diffs && diffs.length) { + const patch = _diffsToPatchUnified(diffs); + if (typeof (ro as any).patch_unified !== 'string' || !(ro as any).patch_unified) { + (ro as any).patch_unified = patch; + } + if (!ro.preview || typeof ro.preview !== 'object') { + (ro as any).preview = {}; + } + if (typeof (ro.preview as any).patch_unified !== 'string' || !(ro.preview as any).patch_unified) { + (ro.preview as any).patch_unified = (ro as any).patch_unified; + } + normalizedResult = ro; + } + } + + if (error) { + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_error', + name: uiToolName, + params: paramsForUi, + result: error, + content: String(error), + id, + rawParams: rawParamsForUi + }); + } else { + const terminalIdForKey = + (rObj && typeof (rObj as any).terminalId === 'string' ? String((rObj as any).terminalId) : undefined) + ?? (typeof (rawParamsForUi as any)?.terminalId === 'string' ? String((rawParamsForUi as any).terminalId) : undefined) + ?? (typeof (paramsForUi as any)?.terminalId === 'string' ? String((paramsForUi as any).terminalId) : undefined); + + let resultForToolOutput: any = normalizedResult; + + if (_asObj(resultForToolOutput)) { + // Preserve existing result fields, but guarantee IDs exist. + resultForToolOutput = { + toolCallId: String(id), + ...(terminalIdForKey ? { terminalId: terminalIdForKey } : {}), + ...(resultForToolOutput as any), + }; + } else if (typeof resultForToolOutput === 'string') { + // Wrap string into object so ToolOutputManager can key by ids. + resultForToolOutput = { + toolCallId: String(id), + ...(terminalIdForKey ? { terminalId: terminalIdForKey } : {}), + text: resultForToolOutput + }; + } else { + // Fallback wrap + resultForToolOutput = { + toolCallId: String(id), + ...(terminalIdForKey ? { terminalId: terminalIdForKey } : {}), + value: resultForToolOutput + }; + } + + const { result: processedResult, content, displayContent } = + await this._toolOutputManager.processToolResult(resultForToolOutput, uiToolName); + + access.updateLatestTool(threadId, { + role: 'tool', + type: 'success', + name: uiToolName, + params: paramsForUi, + result: processedResult, + content, + displayContent, + id, + rawParams: rawParamsForUi + }); + } + + + const diffs: Array<{ path: string; oldText?: string; newText: string }> | undefined = (normalizedResult as any)?.diffs; + if (diffs && diffs.length > 0) { + for (const d of diffs) { + try { + const uri = _resolvePathWithWorkspace(String(d.path ?? ''), rootUri); + await this._editCodeService.callBeforeApplyOrEdit(uri); + await (this._editCodeService as any).previewEditFileSimple?.({ + uri, + originalSnippet: d.oldText ?? '', + updatedSnippet: d.newText, + replaceAll: false, + locationHint: undefined, + encoding: null, + newline: null, + applyBoxId: undefined + }); + } catch { /* noop */ } + } + } + + // cleanup per-call cache + try { this._acpToolCallInfoByKey.delete(key); } catch { /* noop */ } + + return; + } + + if (chunk.type === 'error' || chunk.type === 'done') { + const info = access.getStreamState(threadId)?.llmInfo; + if (info?.displayContentSoFar || info?.reasoningSoFar) { + access.addMessageToThread(threadId, { + role: 'assistant', + displayContent: info.displayContentSoFar, + reasoning: info.reasoningSoFar, + anthropicReasoning: null + }); + } + + if (chunk.type === 'done' && chunk.tokenUsageSnapshot) { + access.accumulateTokenUsage(threadId, chunk.tokenUsageSnapshot); + } + + if (chunk.type === 'error') { + access.setStreamState(threadId, { isRunning: undefined, error: { message: chunk.error ?? 'ACP error', fullError: null } }); + } else { + access.setStreamState(threadId, undefined); + } + finish(); + access.addUserCheckpoint(threadId); + return; + } + }; + + sub = stream.onData(onChunk); + const entry = this._acpStreamByThread.get(threadId); + if (entry) entry.sub = sub; + } + + public clearAcpState(threadId: string) { + try { + const entry = this._acpStreamByThread.get(threadId); + try { entry?.stream?.cancel?.(); } catch { /* noop */ } + try { entry?.sub?.dispose?.(); } catch { /* noop */ } + } finally { + this._acpStreamByThread.delete(threadId); + try { + const prefix = `${threadId}:`; + for (const k of Array.from(this._acpToolCallInfoByKey.keys())) { + if (k.startsWith(prefix)) this._acpToolCallInfoByKey.delete(k); + } + } catch { /* noop */ } + } + } + + private _buildAcpHistory(threadId: string, access: IThreadStateAccess): IAcpChatMessage[] { + const msgs = access.getThreadMessages(threadId); + const upto = Math.max(0, msgs.length - 1); + + const history: IAcpChatMessage[] = []; + for (let i = 0; i < upto; i++) { + const m = msgs[i]; + if (m.role === 'user') { + const dc = (m as any).displayContent; + const c = (m as any).content; + const display = typeof dc === 'string' ? dc : ''; + const fallback = typeof c === 'string' ? c : ''; + const content = (display && display.trim().length > 0) ? display : fallback; + if (!content.trim()) continue; + history.push({ role: 'user', content }); + } else if (m.role === 'assistant') { + const content = (m as any).displayContent ?? ''; + if (!String(content).trim()) continue; + history.push({ role: 'assistant', content: String(content) }); + } + } + return history; + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatCheckpointManager.ts b/src/vs/workbench/contrib/void/browser/ChatCheckpointManager.ts new file mode 100644 index 00000000000..c29c5f03c8e --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatCheckpointManager.ts @@ -0,0 +1,268 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { URI } from '../../../../base/common/uri.js'; +import { findLastIdx } from '../../../../base/common/arraysFind.js'; +import { IEditCodeService } from './editCodeServiceInterface.js'; +import { IVoidModelService } from '../common/voidModelService.js'; +import { CheckpointEntry, ChatMessage } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { VoidFileSnapshot } from '../../../../platform/void/common/editCodeServiceTypes.js'; + +export interface ICheckpointThreadAccess { + getThreadMessages(threadId: string): ChatMessage[]; + getThreadState(threadId: string): { currCheckpointIdx: number | null }; + + addMessageToThread(threadId: string, message: ChatMessage): void; + editMessageInThread(threadId: string, messageIdx: number, newMessage: ChatMessage): void; + setThreadState(threadId: string, state: Partial<{ currCheckpointIdx: number | null }>): void; + + isStreaming(threadId: string): boolean; +} + +export class ChatCheckpointManager { + constructor( + @IEditCodeService private readonly _editCodeService: IEditCodeService, + @IVoidModelService private readonly _voidModelService: IVoidModelService + ) { } + + private _uriFromFsPath(fsPath: string): URI { + const { model } = this._voidModelService.getModelFromFsPath(fsPath); + return model?.uri ?? URI.file(fsPath); + } + + public addToolEditCheckpoint(threadId: string, uri: URI, access: ICheckpointThreadAccess) { + const { model } = this._voidModelService.getModel(uri); + if (!model) return; + + const diffAreasSnapshot = this._editCodeService.getVoidFileSnapshot(uri); + this._addCheckpoint(threadId, { + role: 'checkpoint', + type: 'tool_edit', + voidFileSnapshotOfURI: { [uri.fsPath]: diffAreasSnapshot }, + userModifications: { voidFileSnapshotOfURI: {} }, + }, access); + } + + public addUserCheckpoint(threadId: string, access: ICheckpointThreadAccess) { + const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }, access) ?? {}; + this._addCheckpoint(threadId, { + role: 'checkpoint', + type: 'user_edit', + voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {}, + userModifications: { voidFileSnapshotOfURI: {}, }, + }, access); + } + + public jumpToCheckpointBeforeMessageIdx( + opts: { threadId: string, messageIdx: number, jumpToUserModified: boolean }, + access: ICheckpointThreadAccess + ) { + const { threadId, messageIdx, jumpToUserModified } = opts; + + // 1. Ensure we are standing on a checkpoint currently (create temp if needed) + this._makeUsStandOnCheckpoint(threadId, access); + + const msgs = access.getThreadMessages(threadId); + + if (access.isStreaming(threadId)) return; + + // 2. Find target checkpoint + const c = this._getCheckpointBeforeMessage(msgs, messageIdx); + if (c === undefined) return; // should never happen + + const fromIdx = access.getThreadState(threadId).currCheckpointIdx; + if (fromIdx === null) return; // should never happen based on step 1 + + const [_, toIdx] = c; + if (toIdx === fromIdx) return; + + // 3. Update the user's modifications to current checkpoint before jumping away + this._addUserModificationsToCurrCheckpoint({ threadId }, access); + + /* + UNDO Logic (Going Back) + A,B,C are all files. x means a checkpoint where the file changed. + We need to revert anything that happened between to+1 and from. + We do this by finding the last x from 0...`to` for each file and applying those contents. + */ + if (toIdx < fromIdx) { + const { lastIdxOfURI } = this._getCheckpointsBetween(msgs, toIdx + 1, fromIdx); + const pendingFsPaths = new Set(Object.keys(lastIdxOfURI)); + + const idxes = function* () { + for (let k = toIdx; k >= 0; k -= 1) { // first go up + yield k; + } + for (let k = toIdx + 1; k < msgs.length; k += 1) { // then go down + yield k; + } + }; + + for (const k of idxes()) { + if (pendingFsPaths.size === 0) break; + const message = msgs[k]; + if (message.role !== 'checkpoint') continue; + + for (const fsPath in message.voidFileSnapshotOfURI) { + if (!pendingFsPaths.has(fsPath)) continue; + + const res = this._getCheckpointInfo(message as CheckpointEntry, fsPath, { includeUserModifiedChanges: jumpToUserModified }); + if (!res) continue; + + const { voidFileSnapshot } = res; + if (!voidFileSnapshot) continue; + + this._editCodeService.restoreVoidFileSnapshot(this._uriFromFsPath(fsPath), voidFileSnapshot); + pendingFsPaths.delete(fsPath); + } + } + } + + /* + REDO Logic (Going Forward) + We need to apply latest change for anything that happened between from+1 and to. + */ + if (toIdx > fromIdx) { + const { lastIdxOfURI } = this._getCheckpointsBetween(msgs, fromIdx + 1, toIdx); + const pendingFsPaths = new Set(Object.keys(lastIdxOfURI)); + + // apply lowest down content for each uri + for (let k = toIdx; k >= fromIdx + 1; k -= 1) { + if (pendingFsPaths.size === 0) break; + const message = msgs[k]; + if (message.role !== 'checkpoint') continue; + + for (const fsPath in message.voidFileSnapshotOfURI) { + if (!pendingFsPaths.has(fsPath)) continue; + + const res = this._getCheckpointInfo(message as CheckpointEntry, fsPath, { includeUserModifiedChanges: jumpToUserModified }); + if (!res) continue; + + const { voidFileSnapshot } = res; + if (!voidFileSnapshot) continue; + + this._editCodeService.restoreVoidFileSnapshot(this._uriFromFsPath(fsPath), voidFileSnapshot); + pendingFsPaths.delete(fsPath); + } + } + } + + access.setThreadState(threadId, { currCheckpointIdx: toIdx }); + } + + // --- Private Helpers --- + + private _addCheckpoint(threadId: string, checkpoint: CheckpointEntry, access: ICheckpointThreadAccess) { + access.addMessageToThread(threadId, checkpoint); + } + + private _computeNewCheckpointInfo(opts: { threadId: string }, access: ICheckpointThreadAccess) { + const msgs = access.getThreadMessages(opts.threadId); + + const lastCheckpointIdx = findLastIdx(msgs, (m) => m.role === 'checkpoint') ?? -1; + if (lastCheckpointIdx === -1) return; + + const voidFileSnapshotOfURI: { [fsPath: string]: VoidFileSnapshot | undefined } = {}; + + // add a change for all the URIs in the checkpoint history + const { lastIdxOfURI } = this._getCheckpointsBetween(msgs, 0, lastCheckpointIdx) ?? {}; + + for (const fsPath in lastIdxOfURI ?? {}) { + const { model } = this._voidModelService.getModelFromFsPath(fsPath); + if (!model) continue; + + const checkpoint2 = msgs[lastIdxOfURI[fsPath]] || null; + if (!checkpoint2) continue; + if (checkpoint2.role !== 'checkpoint') continue; + + const res = this._getCheckpointInfo(checkpoint2, fsPath, { includeUserModifiedChanges: false }); + if (!res) continue; + const { voidFileSnapshot: oldVoidFileSnapshot } = res; + + // if there was any change to the str or diffAreaSnapshot, update + const voidFileSnapshot = this._editCodeService.getVoidFileSnapshot(this._uriFromFsPath(fsPath)); + if (oldVoidFileSnapshot === voidFileSnapshot) continue; + + voidFileSnapshotOfURI[fsPath] = voidFileSnapshot; + } + + return { voidFileSnapshotOfURI }; + } + + private _getCheckpointsBetween(messages: ChatMessage[], loIdx: number, hiIdx: number) { + const lastIdxOfURI: { [fsPath: string]: number } = {}; + for (let i = loIdx; i <= hiIdx; i += 1) { + const message = messages[i]; + if (message?.role !== 'checkpoint') continue; + for (const fsPath in message.voidFileSnapshotOfURI) { + // do not include userModified.beforeStrOfURI here + lastIdxOfURI[fsPath] = i; + } + } + return { lastIdxOfURI }; + } + + private _getCheckpointInfo(checkpointMessage: CheckpointEntry, fsPath: string, opts: { includeUserModifiedChanges: boolean }) { + const voidFileSnapshot = checkpointMessage.voidFileSnapshotOfURI ? checkpointMessage.voidFileSnapshotOfURI[fsPath] ?? null : null; + if (!opts.includeUserModifiedChanges) { return { voidFileSnapshot }; } + + const userModifiedVoidFileSnapshot = fsPath in checkpointMessage.userModifications.voidFileSnapshotOfURI + ? checkpointMessage.userModifications.voidFileSnapshotOfURI[fsPath] ?? null + : null; + + return { voidFileSnapshot: userModifiedVoidFileSnapshot ?? voidFileSnapshot }; + } + + private _makeUsStandOnCheckpoint(threadId: string, access: ICheckpointThreadAccess) { + const state = access.getThreadState(threadId); + + if (state.currCheckpointIdx === null) { + const msgs = access.getThreadMessages(threadId); + const lastMsg = msgs[msgs.length - 1]; + + if (lastMsg?.role !== 'checkpoint') { + this.addUserCheckpoint(threadId, access); + } + // Update state after adding checkpoint implies messages length changed + const updatedMsgs = access.getThreadMessages(threadId); + access.setThreadState(threadId, { currCheckpointIdx: updatedMsgs.length - 1 }); + } + } + + private _readCurrentCheckpoint(threadId: string, access: ICheckpointThreadAccess): [CheckpointEntry, number] | undefined { + const msgs = access.getThreadMessages(threadId); + const { currCheckpointIdx } = access.getThreadState(threadId); + + if (currCheckpointIdx === null) return; + + const checkpoint = msgs[currCheckpointIdx]; + if (!checkpoint) return; + if (checkpoint.role !== 'checkpoint') return; + + return [checkpoint, currCheckpointIdx]; + } + + private _addUserModificationsToCurrCheckpoint(opts: { threadId: string }, access: ICheckpointThreadAccess) { + const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId: opts.threadId }, access) ?? {}; + const res = this._readCurrentCheckpoint(opts.threadId, access); + if (!res) return; + + const [checkpoint, checkpointIdx] = res; + access.editMessageInThread(opts.threadId, checkpointIdx, { + ...checkpoint, + userModifications: { voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {}, }, + }); + } + + private _getCheckpointBeforeMessage(messages: ChatMessage[], messageIdx: number): [CheckpointEntry, number] | undefined { + for (let i = messageIdx; i >= 0; i--) { + const message = messages[i]; + if (message.role === 'checkpoint') { + return [message, i]; + } + } + return undefined; + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatCodespanManager.ts b/src/vs/workbench/contrib/void/browser/ChatCodespanManager.ts new file mode 100644 index 00000000000..59b91b7d693 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatCodespanManager.ts @@ -0,0 +1,151 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { URI } from '../../../../base/common/uri.js'; +import { shorten } from '../../../../base/common/labels.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { IToolsService } from '../common/toolsService.js'; +import { IVoidModelService } from '../common/voidModelService.js'; +import { CodespanLocationLink, ChatMessage, } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { ToolCallParams, } from '../../../../platform/void/common/toolsServiceTypes.js'; + + +export class ChatCodespanManager { + constructor( + @IToolsService private readonly _toolsService: IToolsService, + @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, + @IVoidModelService private readonly _voidModelService: IVoidModelService + ) { } + + public async generateCodespanLink( + opts: { codespanStr: string, threadId: string }, + getThreadMessages: () => ChatMessage[] + ): Promise { + + const { codespanStr: targetStr } = opts; + const functionOrMethodPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; + const functionParensPattern = /^([^\s(]+)\([^)]*\)$/; + + let target = targetStr; + let codespanType: 'file-or-folder' | 'function-or-class'; + + if (target.includes('.') || target.includes('/')) { + codespanType = 'file-or-folder'; + } else if (functionOrMethodPattern.test(target)) { + codespanType = 'function-or-class'; + } else if (functionParensPattern.test(target)) { + const match = target.match(functionParensPattern); + if (match && match[1]) { + codespanType = 'function-or-class'; + target = match[1]; + } else { return null; } + } else { + return null; + } + + const prevUris = this._getAllSeenFileURIs(getThreadMessages()).reverse(); + + // 1. Search Files + if (codespanType === 'file-or-folder') { + const doesUriMatchTarget = (uri: URI) => uri.path.includes(target); + + // A. Check seen files + for (const [idx, uri] of prevUris.entries()) { + if (doesUriMatchTarget(uri)) { + return this._createLinkResult(uri, prevUris, idx); + } + } + + // B. Search codebase + try { + const { result } = await this._toolsService.callTool['search_pathnames_only']({ query: target, includePattern: null, pageNumber: 0 }); + const { uris } = await result; + for (const uri of uris) { + if (doesUriMatchTarget(uri)) { + // find relative idx in uris is tricky for shorten, using 0 for simplicity or logic from main + return { uri, displayText: target }; // Simplified for extraction + } + } + } catch (e) { return null; } + } + + // 2. Search Symbols + if (codespanType === 'function-or-class') { + for (const uri of prevUris) { + const modelRef = await this._voidModelService.getModelSafe(uri); + const { model } = modelRef; + if (!model) continue; + const definitionProviders = this._languageFeaturesService.definitionProvider.ordered(model); + if (!definitionProviders.length) continue; + + const matches = model.findMatches(target, false, false, true, null, true); + const firstThree = matches.slice(0, 3); + const seenMatchPositions = new Set(); + + for (const match of firstThree) { + const matchKey = `${match.range.startLineNumber}:${match.range.startColumn}`; + if (seenMatchPositions.has(matchKey)) continue; + seenMatchPositions.add(matchKey); + + const position = new Position(match.range.startLineNumber, match.range.startColumn); + + for (const provider of definitionProviders) { + const _definitions = await provider.provideDefinition(model, position, CancellationToken.None); + if (!_definitions) continue; + const definitions = Array.isArray(_definitions) ? _definitions : [_definitions]; + const definition = definitions[0]; + if (!definition) continue; + + return { + uri: definition.uri, + selection: { + startLineNumber: definition.range.startLineNumber, + startColumn: definition.range.startColumn, + endLineNumber: definition.range.endLineNumber, + endColumn: definition.range.endColumn, + }, + displayText: targetStr, + }; + } + } + } + } + return null; + } + + private _createLinkResult(uri: URI, allUris: URI[], idx: number) { + const prevUriStrs = allUris.map(u => u.fsPath); + const shortenedUriStrs = shorten(prevUriStrs); + let displayText = shortenedUriStrs[idx]; + const ellipsisIdx = displayText.lastIndexOf('…/'); + if (ellipsisIdx >= 0) { + displayText = displayText.slice(ellipsisIdx + 2); + } + return { uri, displayText }; + } + + private _getAllSeenFileURIs(messages: ChatMessage[]): URI[] { + const fsPathsSet = new Set(); + const uris: URI[] = []; + const addURI = (uri: URI) => { + if (fsPathsSet.has(uri.fsPath)) return; + fsPathsSet.add(uri.fsPath); + uris.push(uri); + }; + + for (const m of messages) { + if (m.role === 'user') { + for (const sel of m.selections ?? []) addURI(sel.uri); + for (const att of m.attachments ?? []) addURI(att.uri); + } else if (m.role === 'tool' && m.type === 'success' && m.name === 'read_file') { + const params = m.params as ToolCallParams['read_file']; + addURI(params.uri); + } + } + return uris; + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatExecutionEngine.ts b/src/vs/workbench/contrib/void/browser/ChatExecutionEngine.ts new file mode 100644 index 00000000000..3a74c2edb61 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatExecutionEngine.ts @@ -0,0 +1,901 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { timeout } from '../../../../base/common/async.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; +import { IToolsService, isDangerousTerminalCommand } from '../common/toolsService.js'; +import { ILanguageModelToolsService } from '../../chat/common/languageModelToolsService.js'; +import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; +import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'; +import { LLMLoopDetector, LOOP_DETECTED_MESSAGE } from '../../../../platform/void/common/loopGuard.js'; +import { getErrorMessage, RawToolCallObj, RawToolParamsObj, LLMTokenUsage } from '../../../../platform/void/common/sendLLMMessageTypes.js'; +import { isAToolName } from '../common/prompt/prompts.js'; +import { approvalTypeOfToolName, } from '../../../../platform/void/common/toolsServiceTypes.js'; +import { ChatMessage, ToolMessage, ChatAttachment } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { ModelSelection, ModelSelectionOptions } from '../../../../platform/void/common/voidSettingsTypes.js'; +import { getModelCapabilities } from '../../../../platform/void/common/modelInference.js'; +import { type JsonObject, type JsonValue, isJsonObject, stringifyUnknown, toJsonObject } from '../../../../platform/void/common/jsonTypes.js'; + +import { ChatHistoryCompressor } from './ChatHistoryCompressor.js'; +import { ChatToolOutputManager } from './ChatToolOutputManager.js'; +import { IThreadStateAccess } from './ChatAcpHandler.js'; +import { IMCPService } from '../common/mcpService.js'; + +export type IsRunningType = + | 'LLM' // the LLM is currently streaming + | 'tool' // whether a tool is currently running + | 'awaiting_user' // awaiting user call + | 'idle' // nothing is running now, but the chat should still appear like it's going (used in-between calls) + | undefined + + +export class ChatExecutionEngine { + + private readonly toolErrMsgs = { + rejected: 'Tool call was rejected by the user.', + interrupted: 'Tool call was interrupted by the user.', + errWhenStringifying: (error: any) => `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` + }; + + private _getDisabledToolNamesSet(): Set { + const arr = this._settingsService.state.globalSettings.disabledToolNames; + if (!Array.isArray(arr)) return new Set(); + return new Set(arr.map(v => String(v ?? '').trim()).filter(Boolean)); + } + + private _isToolDisabled(name: string): boolean { + return this._getDisabledToolNamesSet().has(String(name ?? '').trim()); + } + + private _disabledToolError(toolName: string): string { + return `Tool "${toolName}" is disabled in Void settings.`; + } + + public readonly skippedToolCallIds = new Set(); + + constructor( + @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, + @IToolsService private readonly _toolsService: IToolsService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, + @ILanguageModelToolsService private readonly _lmToolsService: ILanguageModelToolsService, + @IMetricsService private readonly _metricsService: IMetricsService, + @IConvertToLLMMessageService private readonly _convertToLLMMessagesService: IConvertToLLMMessageService, + @IFileService private readonly _fileService: IFileService, + @IMCPService private readonly _mcpService: IMCPService, + private readonly _historyCompressor: ChatHistoryCompressor, + private readonly _toolOutputManager: ChatToolOutputManager + ) { } + + public async runChatAgent(opts: { + threadId: string, + modelSelection: ModelSelection | null, + modelSelectionOptions: ModelSelectionOptions | undefined, + callThisToolFirst?: ToolMessage & { type: 'tool_request' } + }, access: IThreadStateAccess) { + + const { threadId, modelSelection, modelSelectionOptions, callThisToolFirst } = opts; + + let interruptedWhenIdle = false; + const idleInterruptor = Promise.resolve(() => { interruptedWhenIdle = true }); + + const gs = this._settingsService.state.globalSettings; + const chatMode = gs.chatMode; + const chatRetries = gs.chatRetries; + const retryDelay = gs.retryDelay; + const { overridesOfModel } = this._settingsService.state; + + let nMessagesSent = 0; + let shouldSendAnotherMessage = true; + let isRunningWhenEnd: IsRunningType = undefined + + const loopDetector = new LLMLoopDetector({ + maxTurnsPerPrompt: gs.loopGuardMaxTurnsPerPrompt, + maxSameAssistantPrefix: gs.loopGuardMaxSameAssistantPrefix, + maxSameToolCall: gs.loopGuardMaxSameToolCall, + }); + + + if (callThisToolFirst) { + if (isAToolName(callThisToolFirst.name)) { + const { interrupted } = await this._runToolCall(threadId, callThisToolFirst.name, callThisToolFirst.id, { + preapproved: true, + unvalidatedToolParams: callThisToolFirst.rawParams, + validatedParams: callThisToolFirst.params + }, access); + + if (interrupted) { + if (this.skippedToolCallIds.delete(callThisToolFirst.id)) { + + } else { + access.setStreamState(threadId, undefined); + access.addUserCheckpoint(threadId); + return; + } + } + } else { + // Dynamic tool (MCP) + if (this._isToolDisabled(callThisToolFirst.name)) { + const disabledError = this._disabledToolError(callThisToolFirst.name); + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_error', + params: callThisToolFirst.rawParams as any, + result: disabledError, + name: callThisToolFirst.name as any, + content: disabledError, + displayContent: disabledError, + id: callThisToolFirst.id, + rawParams: callThisToolFirst.rawParams, + }); + } else { + access.updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + params: callThisToolFirst.params as any, + name: callThisToolFirst.name as any, + content: 'running...', + displayContent: 'running...', + result: null, + id: callThisToolFirst.id, + rawParams: callThisToolFirst.rawParams + }); + + const exec = await this._runDynamicToolExec( + callThisToolFirst.name, + toJsonObject(callThisToolFirst.rawParams) + ); + + if (!exec.ok) { + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_error', + params: callThisToolFirst.params as any, + result: exec.error, + name: callThisToolFirst.name as any, + content: exec.error, + displayContent: exec.error, + id: callThisToolFirst.id, + rawParams: callThisToolFirst.rawParams + }); + } else { + const { result: processedResult, content, displayContent } = + await this._toolOutputManager.processToolResult(exec.value, callThisToolFirst.name); + + access.updateLatestTool(threadId, { + role: 'tool', + type: 'success', + params: callThisToolFirst.params as any, + result: processedResult, + name: callThisToolFirst.name as any, + content, + displayContent: displayContent, + id: callThisToolFirst.id, + rawParams: callThisToolFirst.rawParams + }); + } + } + } + + } + + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + + + + while (shouldSendAnotherMessage) { + shouldSendAnotherMessage = false; + isRunningWhenEnd = undefined; + nMessagesSent += 1; + + access.setStreamState(threadId, { isRunning: 'idle', interrupt: idleInterruptor }); + + + const baseChatMessages = access.getThreadMessages(threadId); + let historySummaryForTurn: string | null = null; + + if (nMessagesSent === 1) { + try { + const { summaryText, compressionInfo } = await this._historyCompressor.maybeSummarizeHistoryBeforeLLM({ + threadId, + messages: baseChatMessages, + modelSelection, + modelSelectionOptions, + }); + historySummaryForTurn = summaryText; + if (compressionInfo) { + access.setThreadState(threadId, { historyCompression: compressionInfo }); + } + } catch { /* fail open */ } + } + + const chatMessages: ChatMessage[] = historySummaryForTurn + ? ([{ + role: 'assistant', + displayContent: historySummaryForTurn, + reasoning: '', + anthropicReasoning: null, + } as ChatMessage, ...baseChatMessages]) + : baseChatMessages; + + const { messages, separateSystemMessage } = await this._convertToLLMMessagesService.prepareLLMChatMessages({ + chatMessages, + modelSelection, + chatMode + }); + + await this._patchImagesIntoMessages({ messages, chatMessages, modelSelection }); + + if (interruptedWhenIdle) { + access.setStreamState(threadId, undefined); + return; + } + + let shouldRetryLLM = true; + let nAttempts = 0; + + + while (shouldRetryLLM) { + shouldRetryLLM = false; + nAttempts += 1; + let lastUsageForTurn: LLMTokenUsage | undefined; + + let limitsForThisRequest: any; + try { + if (modelSelection) { + const { providerName, modelName } = modelSelection; + const caps = getModelCapabilities(providerName as any, modelName, overridesOfModel); + limitsForThisRequest = { contextWindow: caps.contextWindow }; + const reserved = caps.reservedOutputTokenSpace ?? 0; + const maxInputTokens = Math.max(0, caps.contextWindow - reserved); + access.setThreadState(threadId, { tokenUsageLastRequestLimits: { maxInputTokens } }); + } + } catch { /* noop */ } + + type ResTypes = + | { type: 'llmDone'; toolCall?: RawToolCallObj; info: { fullText: string; fullReasoning: string; anthropicReasoning: any }; tokenUsage?: LLMTokenUsage } + | { type: 'llmError'; error?: { message: string; fullError: Error | null } } + | { type: 'llmAborted' }; + + let resMessageIsDonePromise: (res: ResTypes) => void; + const messageIsDonePromise = new Promise((res) => { resMessageIsDonePromise = res; }); + + const llmCancelToken = this._llmMessageService.sendLLMMessage({ + messagesType: 'chatMessages', + chatMode, + messages: messages, + modelSelection, + modelSelectionOptions, + overridesOfModel, + logging: { loggingName: `Chat - ${chatMode}`, loggingExtras: { threadId, nMessagesSent, chatMode } }, + separateSystemMessage: separateSystemMessage, + onText: ({ fullText, fullReasoning, toolCall, tokenUsage }) => { + if (tokenUsage) lastUsageForTurn = tokenUsage; + access.setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { displayContentSoFar: fullText, reasoningSoFar: fullReasoning, toolCallSoFar: toolCall ?? null }, + interrupt: Promise.resolve(() => { if (llmCancelToken) this._llmMessageService.abort(llmCancelToken); }) + }); + }, + onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, tokenUsage, }) => { + if (tokenUsage) lastUsageForTurn = tokenUsage; + resMessageIsDonePromise({ type: 'llmDone', toolCall, info: { fullText, fullReasoning, anthropicReasoning }, tokenUsage }); + }, + onError: async (error) => { + resMessageIsDonePromise({ type: 'llmError', error: error }); + }, + onAbort: () => { + if (lastUsageForTurn) access.accumulateTokenUsage(threadId, lastUsageForTurn); + resMessageIsDonePromise({ type: 'llmAborted' }); + this._metricsService.capture('Agent Loop Done (Aborted)', { nMessagesSent, chatMode }); + }, + }); + + if (!llmCancelToken) { + access.setStreamState(threadId, { isRunning: undefined, error: { message: 'Unexpected error sending chat message.', fullError: null } }); + break; + } + + access.setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: '', reasoningSoFar: '', toolCallSoFar: null }, interrupt: Promise.resolve(() => this._llmMessageService.abort(llmCancelToken)) }); + + const llmRes = await messageIsDonePromise; + + const currStream = access.getStreamState(threadId); + if (currStream?.isRunning !== 'LLM') return; // interrupted by new thread + + if (llmRes.type === 'llmAborted') { + access.setStreamState(threadId, undefined); + return; + } + else if (llmRes.type === 'llmError') { + if (lastUsageForTurn) access.accumulateTokenUsage(threadId, lastUsageForTurn); + + if (nAttempts < chatRetries) { + shouldRetryLLM = true; + access.setStreamState(threadId, { isRunning: 'idle', interrupt: idleInterruptor }); + await timeout(retryDelay); + if (interruptedWhenIdle) { + access.setStreamState(threadId, undefined); + return; + } + continue; + } else { + const { error } = llmRes; + const info = access.getStreamState(threadId).llmInfo; + access.addMessageToThread(threadId, { role: 'assistant', displayContent: info.displayContentSoFar, reasoning: info.reasoningSoFar, anthropicReasoning: null }); + if (info.toolCallSoFar) access.addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: info.toolCallSoFar.name }); + + access.setStreamState(threadId, { isRunning: undefined, error }); + access.addUserCheckpoint(threadId); + return; + } + } + + // Success + const { toolCall, info, tokenUsage } = llmRes; + const effectiveUsage = tokenUsage ?? lastUsageForTurn; + if (effectiveUsage) access.accumulateTokenUsage(threadId, effectiveUsage); + + access.addMessageToThread(threadId, { role: 'assistant', displayContent: info.fullText, reasoning: info.fullReasoning, anthropicReasoning: info.anthropicReasoning }); + + // Loop Detection (Assistant) + const loopAfterAssistant = loopDetector.registerAssistantTurn(info.fullText); + if (loopAfterAssistant.isLoop) { + access.setStreamState(threadId, { isRunning: undefined, error: { message: LOOP_DETECTED_MESSAGE, fullError: null } }); + access.addUserCheckpoint(threadId); + return; + } + + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + + + if (toolCall && toolCall.name) { + const loopAfterTool = loopDetector.registerToolCall(toolCall.name, toolCall.rawParams); + if (loopAfterTool.isLoop) { + access.setStreamState(threadId, { isRunning: undefined, error: { message: LOOP_DETECTED_MESSAGE, fullError: null } }); + access.addUserCheckpoint(threadId); + return; + } + + if (isAToolName(toolCall.name)) { + const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, toolCall.name, toolCall.id, { + preapproved: false, + unvalidatedToolParams: toolCall.rawParams + }, access); + + if (interrupted) { + if (this.skippedToolCallIds.delete(toolCall.id)) { + shouldSendAnotherMessage = true; + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + } else { + access.setStreamState(threadId, undefined); + return; + } + } else { + if (awaitingUserApproval) { isRunningWhenEnd = 'awaiting_user'; } + else { shouldSendAnotherMessage = true; } + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + } + } else { + // Dynamic Tool (MCP) + if (this._isToolDisabled(toolCall.name)) { + const disabledError = this._disabledToolError(toolCall.name); + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_error', + params: toolCall.rawParams as any, + result: disabledError, + name: toolCall.name as any, + content: disabledError, + displayContent: disabledError, + id: toolCall.id, + rawParams: toolCall.rawParams + }); + shouldSendAnotherMessage = true; + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + continue; + } + + if (this._settingsService.state.globalSettings.mcpAutoApprove) { + access.updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + name: toolCall.name as any, + params: toolCall.rawParams as any, + content: 'running...', + displayContent: 'running...', + result: null, + id: toolCall.id, + rawParams: toolCall.rawParams + }); + + const exec = await this._runDynamicToolExec( + toolCall.name, + toJsonObject(toolCall.rawParams) + ); + + if (!exec.ok) { + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_error', + params: toolCall.rawParams as any, + result: exec.error, + name: toolCall.name as any, + content: exec.error, + displayContent: exec.error, + id: toolCall.id, + rawParams: toolCall.rawParams + }); + + shouldSendAnotherMessage = true; + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + } else { + const { result: processedResult, content, displayContent } = + await this._toolOutputManager.processToolResult(exec.value, toolCall.name); + + access.updateLatestTool(threadId, { + role: 'tool', + type: 'success', + params: toolCall.rawParams as any, + result: processedResult, + name: toolCall.name as any, + content, + displayContent, + id: toolCall.id, + rawParams: toolCall.rawParams + }); + + shouldSendAnotherMessage = true; + access.setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }); + } + } else { + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_request', + content: '(Awaiting user permission...)', + result: null, + name: toolCall.name as any, + params: toolCall.rawParams as any, + id: toolCall.id, + rawParams: toolCall.rawParams + }); + isRunningWhenEnd = 'awaiting_user'; + } + } + } + } + } + + access.setStreamState(threadId, { isRunning: isRunningWhenEnd }); + if (!isRunningWhenEnd) access.addUserCheckpoint(threadId); + this._metricsService.capture('Agent Loop Done', { nMessagesSent, chatMode }); + } + + private async _runToolCall( + threadId: string, + toolName: string, + toolId: string, + opts: { preapproved: boolean, unvalidatedToolParams: RawToolParamsObj, validatedParams?: any }, + access: IThreadStateAccess + ): Promise<{ awaitingUserApproval?: boolean, interrupted?: boolean }> { + + let toolParams: any; + let toolResult: any; + + const isTerminalTool = toolName === 'run_command' || toolName === 'run_persistent_command'; + + if (this._isToolDisabled(toolName)) { + const disabledError = this._disabledToolError(toolName); + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_error', + params: (opts.validatedParams ?? opts.unvalidatedToolParams) as any, + result: disabledError, + name: toolName as any, + content: disabledError, + displayContent: disabledError, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + return {}; + } + + // 1. Validation & Approval + if (!opts.preapproved) { + try { + if (isAToolName(toolName)) { + toolParams = this._toolsService.validateParams[toolName](opts.unvalidatedToolParams); + } else { + toolParams = opts.unvalidatedToolParams; + } + } catch (error) { + const errorMessage = getErrorMessage(error); + access.addMessageToThread(threadId, { + role: 'tool', + type: 'invalid_params', + rawParams: opts.unvalidatedToolParams, + result: null, + name: toolName as any, + content: errorMessage, + id: toolId + }); + return {}; + } + + if (isAToolName(toolName)) { + const approvalType = approvalTypeOfToolName[toolName]; + if (approvalType) { + let autoApprove = this._settingsService.state.globalSettings.autoApprove[approvalType]; + if (approvalType === 'terminal' && (toolName === 'run_command' || toolName === 'run_persistent_command')) { + try { + const cmd = (toolParams as any)?.command ?? String((opts.unvalidatedToolParams as any)?.command ?? ''); + if (isDangerousTerminalCommand(cmd)) autoApprove = false; + } catch { } + } + if (!autoApprove) { + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_request', + content: '(Awaiting user permission...)', + result: null, + name: toolName as any, + params: toolParams, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + return { awaitingUserApproval: true }; + } + } + } else { + access.addMessageToThread(threadId, { + role: 'tool', + type: 'tool_request', + content: '(Awaiting user permission...)', + result: null, + name: toolName as any, + params: toolParams, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + return { awaitingUserApproval: true }; + } + } else { + toolParams = opts.validatedParams; + } + + // 2. Execution + access.updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + name: toolName as any, + params: toolParams as any, + content: '', + displayContent: '', + result: null, + id: toolId, + rawParams: opts.unvalidatedToolParams + } as const); + + let interrupted = false; + let resolveInterruptor: (r: () => void) => void = () => { }; + const interruptorPromise = new Promise<() => void>(res => { resolveInterruptor = res; }); + + // streamState init + access.setStreamState(threadId, { + isRunning: 'tool', + interrupt: interruptorPromise, + toolInfo: { + toolName: isAToolName(toolName) ? toolName : (toolName as any), + toolParams: toolParams as any, + id: toolId, + content: '', + rawParams: opts.unvalidatedToolParams + } + }); + + // streaming accumulator + let streamed = ''; + let pushTimer: any = null; + let lastPushAt = 0; + const PUSH_INTERVAL_MS = 80; + const MAX_KEEP = 200_000; + + const push = (force: boolean) => { + if (interrupted) return; + const now = Date.now(); + if (!force && now - lastPushAt < PUSH_INTERVAL_MS) { + if (!pushTimer) { + pushTimer = setTimeout(() => { + pushTimer = null; + push(true); + }, PUSH_INTERVAL_MS); + } + return; + } + lastPushAt = now; + + access.setStreamState(threadId, { + isRunning: 'tool', + interrupt: interruptorPromise, + toolInfo: { + toolName: isAToolName(toolName) ? toolName : (toolName as any), + toolParams: toolParams as any, + id: toolId, + content: streamed, + rawParams: opts.unvalidatedToolParams + } + }); + }; + + // For ephemeral commands show "$ cmd" immediately in stream + if (toolName === 'run_command') { + const cmd = String((toolParams as any)?.command ?? ''); + if (cmd) { + streamed = `$ ${cmd}\n`; + push(true); + } + } + + const onOutput = (chunk: string) => { + if (interrupted) return; + if (typeof chunk !== 'string' || !chunk) return; + + streamed += chunk; + if (streamed.length > MAX_KEEP) { + streamed = streamed.slice(streamed.length - MAX_KEEP); + } + push(false); + }; + + try { + let result: Promise; + let interruptTool: (() => void) | undefined; + + if (isAToolName(toolName)) { + // Pass ctx only for terminal tools + const res = isTerminalTool + ? await (this._toolsService.callTool as any)[toolName](toolParams as any, { onOutput }) + : await this._toolsService.callTool[toolName](toolParams as any); + + result = Promise.resolve(res.result as any); + interruptTool = res.interruptTool; + } else { + result = Promise.resolve({}); + } + + const interruptor = () => { interrupted = true; interruptTool?.(); }; + resolveInterruptor(interruptor); + + toolResult = await result; + + if (pushTimer) { + try { clearTimeout(pushTimer); } catch { } + pushTimer = null; + } + push(true); + + if (interrupted) return { interrupted: true }; + } catch (error) { + resolveInterruptor(() => { }); + if (interrupted) return { interrupted: true }; + + const errorMessage = getErrorMessage(error); + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_error', + params: toolParams, + result: errorMessage, + name: toolName, + content: errorMessage, + displayContent: errorMessage, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + return {}; + } + + // 3. Stringify & Process Result + let toolResultStr: string; + try { + if (isAToolName(toolName)) { + toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any); + } else { + toolResultStr = typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult); + } + } catch (error) { + const errorMessage = this.toolErrMsgs.errWhenStringifying(error); + access.updateLatestTool(threadId, { + role: 'tool', + type: 'tool_error', + params: toolParams, + result: errorMessage, + name: toolName as any, + content: errorMessage, + displayContent: errorMessage, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + return {}; + } + + let processedResult = toolResult; + if ((toolName === 'edit_file' || toolName === 'rewrite_file') && toolResult) { + const resultAny = toolResult as any; + if (!resultAny.patch_unified && resultAny.preview?.patch_unified) { + processedResult = { ...toolResult, patch_unified: resultAny.preview.patch_unified }; + } + } + + const { content, displayContent } = await this._toolOutputManager.processToolResult(toolResultStr, toolName); + + access.updateLatestTool(threadId, { + role: 'tool', + type: 'success', + params: toolParams, + result: processedResult, + name: toolName, + content, + displayContent, + id: toolId, + rawParams: opts.unvalidatedToolParams + }); + + return {}; + } + + private async _runDynamicToolExec( + name: string, + args: JsonObject + ): Promise<{ ok: true, value: string | JsonValue } | { ok: false, error: string }> { + if (this._isToolDisabled(name)) { + return { ok: false, error: this._disabledToolError(name) }; + } + + try { + type LmToolShape = { id: string; toolReferenceName?: string; displayName?: string }; + + const isToolShape = (v: unknown): v is LmToolShape => { + if (!isJsonObject(v)) return false; + return typeof v.id === 'string' && v.id.length > 0; + }; + + // ---------------------------- + // 1) Try execute via ILanguageModelToolsService (settings.json MCP path) + // ---------------------------- + const toolFromByNameUnknown = this._lmToolsService.getToolByName?.(name) as unknown; + let tool: LmToolShape | undefined = isToolShape(toolFromByNameUnknown) ? toolFromByNameUnknown : undefined; + + const allToolsUnknown = Array.from(this._lmToolsService.getTools?.() ?? []) as unknown[]; + const allTools: LmToolShape[] = allToolsUnknown.filter(isToolShape); + + if (!tool) { + for (const t of allTools) { + if (t.toolReferenceName === name || t.displayName === name) { tool = t; break; } + } + } + + // Fallback for prefixed names (e.g. "server__tool") + if (!tool && name.includes('__')) { + const baseName = name.split('__').pop(); + if (baseName) { + for (const t of allTools) { + if (t.toolReferenceName === baseName || t.displayName === baseName) { tool = t; break; } + } + } + } + + if (tool) { + const invocation = { + callId: generateUuid(), + toolId: tool.id, + parameters: args ?? {}, + context: undefined, + skipConfirmation: true, + }; + + const resUnknown = await this._lmToolsService.invokeTool(invocation, async () => 0, CancellationToken.None); + + const tryGetTextParts = (content: unknown): string | null => { + if (!Array.isArray(content)) return null; + const texts: string[] = []; + for (const p of content) { + if (!p || typeof p !== 'object') continue; + const kind = (p as { kind?: unknown }).kind; + const value = (p as { value?: unknown }).value; + if (kind === 'text' && typeof value === 'string') { + texts.push(value); + } + } + return texts.length ? texts.join('\n') : null; + }; + + const resObj = isJsonObject(resUnknown) ? (resUnknown as JsonObject) : null; + + const textParts = tryGetTextParts(resObj?.content); + if (textParts) return { ok: true, value: textParts }; + + if (resObj && typeof resObj.toolResultDetails !== 'undefined') return { ok: true, value: resObj.toolResultDetails }; + if (resObj && typeof resObj.toolResultMessage !== 'undefined') return { ok: true, value: resObj.toolResultMessage }; + + return { ok: true, value: {} }; + } + + // ---------------------------- + // 2) If not found: try execute via IMCPService (mcp.json path) + // ---------------------------- + if (name.includes('__')) { + // Best effort: resolve serverName by searching current MCP state tools + let resolvedServerName: string | null = null; + + const state = this._mcpService.state?.mcpServerOfName ?? {}; + for (const [serverName, server] of Object.entries(state)) { + const tools = (server as any)?.tools as Array<{ name: string }> | undefined; + if (tools?.some(t => t.name === name)) { + resolvedServerName = serverName; + break; + } + } + + // Fallback: prefix before '__' (works when prefix equals config serverName) + if (!resolvedServerName) { + resolvedServerName = name.split('__')[0] || null; + } + + if (resolvedServerName) { + const { result } = await this._mcpService.callMCPTool({ + serverName: resolvedServerName, + toolName: name, + params: args ?? {}, + }); + + const text = this._mcpService.stringifyResult(result); + return { ok: true, value: text }; + } + } + + return { ok: false, error: `Unknown dynamic tool: ${name}` }; + } catch (e: unknown) { + return { ok: false, error: stringifyUnknown(e) }; + } + } + + private async _patchImagesIntoMessages(opts: { messages: any[]; chatMessages: ChatMessage[]; modelSelection: ModelSelection | null }) { + const { messages, chatMessages, modelSelection } = opts; + if (!modelSelection) return; + + const lastUserChat = [...chatMessages].reverse().find(m => m.role === 'user') as (ChatMessage & { attachments?: ChatAttachment[] | null }) | undefined; + if (!lastUserChat || !lastUserChat.attachments || !lastUserChat.attachments.length) return; + + let lastUserIdx = -1; + for (let i = messages.length - 1; i >= 0; i -= 1) { + if (messages[i]?.role === 'user') { + lastUserIdx = i; + break; + } + } + if (lastUserIdx === -1) return; + + const lastUser = messages[lastUserIdx]; + const baseContent = typeof lastUser.content === 'string' ? lastUser.content : ''; + const parts: any[] = []; + const trimmed = baseContent.trim(); + if (trimmed) { + parts.push({ type: 'text', text: trimmed }); + } + + for (const att of lastUserChat.attachments) { + try { + const content = await this._fileService.readFile(att.uri); + const dataBase64 = (await import('../../../../base/common/buffer.js')).encodeBase64(content.value); + const mime = (att as any).mimeType || 'image/png'; + const dataUrl = `data:${mime};base64,${dataBase64}`; + parts.push({ type: 'image_url', image_url: { url: dataUrl } }); + } catch { } + } + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatHistoryCompressor.ts b/src/vs/workbench/contrib/void/browser/ChatHistoryCompressor.ts new file mode 100644 index 00000000000..28b417a8dd1 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatHistoryCompressor.ts @@ -0,0 +1,218 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { ChatMessage } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; +import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { ModelSelection, ModelSelectionOptions } from '../../../../platform/void/common/voidSettingsTypes.js'; +import { getModelCapabilities, getReservedOutputTokenSpace, getIsReasoningEnabledState } from '../../../../platform/void/common/modelInference.js'; +import { CHAT_HISTORY_COMPRESSION_SYSTEM_PROMPT, buildChatHistoryCompressionUserMessage } from '../common/prompt/prompts.js'; + + +const CHARS_PER_TOKEN_ESTIMATE = 4; +const HISTORY_COMPRESSION_TAIL_MESSAGE_COUNT = 8; +const HISTORY_COMPRESSION_TOOL_SNIPPET_CHARS = 400; + +export type ThreadHistoryCompressionInfo = { + hasCompressed: boolean; + summarizedMessageCount: number; + approxTokensBefore: number; + approxTokensAfter: number; +}; + +export class ChatHistoryCompressor { + + constructor( + @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, + @IConvertToLLMMessageService private readonly _convertToLLMMessagesService: IConvertToLLMMessageService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService + ) { } + + public estimateTokensForMessages(messages: ChatMessage[]): number { + let totalChars = 0; + for (const m of messages) { + if (m.role === 'checkpoint' || m.role === 'interrupted_streaming_tool') continue; + if (m.role === 'user') { + totalChars += (m.content ?? '').length; + } else if (m.role === 'assistant') { + totalChars += (m.displayContent ?? '').length; + } else if (m.role === 'tool') { + totalChars += (m.content ?? '').length; + } + } + if (totalChars <= 0) return 0; + return Math.ceil(totalChars / CHARS_PER_TOKEN_ESTIMATE); + } + + public async maybeSummarizeHistoryBeforeLLM(opts: { + threadId: string; + messages: ChatMessage[]; + modelSelection: ModelSelection | null; + modelSelectionOptions: ModelSelectionOptions | undefined; + }): Promise<{ summaryText: string | null; compressionInfo?: ThreadHistoryCompressionInfo }> { + const { threadId, messages: chatMessages, modelSelection, modelSelectionOptions } = opts; + + if (!modelSelection || !chatMessages.length) { + return { summaryText: null }; + } + + const { overridesOfModel } = this._settingsService.state; + const { providerName, modelName } = modelSelection; + + let contextWindow: number; + try { + const caps = getModelCapabilities(providerName as any, modelName, overridesOfModel); + contextWindow = caps.contextWindow; + } catch { + return { summaryText: null }; + } + + if (!contextWindow || contextWindow <= 0) return { summaryText: null }; + + const isReasoningEnabled = getIsReasoningEnabledState( + 'Chat', + providerName, + modelName, + modelSelectionOptions, + overridesOfModel + ); + const reservedOutputTokenSpace = getReservedOutputTokenSpace(providerName, modelName, { isReasoningEnabled, overridesOfModel }) ?? 0; + const maxInputTokens = Math.max(0, contextWindow - reservedOutputTokenSpace); + + if (maxInputTokens <= 0) return { summaryText: null }; + + const approxTokensBefore = this.estimateTokensForMessages(chatMessages); + + if (approxTokensBefore <= maxInputTokens) { + return { summaryText: null }; + } + + + const tailCount = HISTORY_COMPRESSION_TAIL_MESSAGE_COUNT; + const splitIdx = Math.max(0, chatMessages.length - tailCount); + const prefixMessages = splitIdx > 0 + ? chatMessages.slice(0, splitIdx) + : chatMessages.slice(0, Math.max(0, chatMessages.length - 1)); + + if (!prefixMessages.length) return { summaryText: null }; + + const tailMessages = chatMessages.slice(prefixMessages.length); + const approxTailTokens = this.estimateTokensForMessages(tailMessages); + + const rawTarget = Math.floor(maxInputTokens * 0.2); + const targetTokensApprox = Math.max(128, Math.min(rawTarget, 1024)); + + const historyText = this._buildHistoryTextForCompression(prefixMessages); + if (!historyText.trim()) return { summaryText: null }; + + const systemMessage = CHAT_HISTORY_COMPRESSION_SYSTEM_PROMPT; + const userMessageContent = buildChatHistoryCompressionUserMessage({ + historyText, + approxTokensBefore, + targetTokensApprox, + }); + + const simpleMessages: any[] = [ + { role: 'user', content: userMessageContent }, + ]; + + const { messages, separateSystemMessage } = this._convertToLLMMessagesService.prepareLLMSimpleMessages({ + simpleMessages, + systemMessage, + modelSelection, + featureName: 'Chat', + }); + + let resolved = false; + let summaryText = ''; + + await new Promise((resolve) => { + const reqId = this._llmMessageService.sendLLMMessage({ + messagesType: 'chatMessages', + messages, + separateSystemMessage, + chatMode: 'normal', + modelSelection, + modelSelectionOptions, + overridesOfModel, + logging: { loggingName: 'Chat - history compression', loggingExtras: { threadId, approxTokensBefore, maxInputTokens } }, + tool_choice: 'none', + onText: () => { /* ignore streaming for compression */ }, + onFinalMessage: ({ fullText }) => { + if (!resolved) { + summaryText = fullText ?? ''; + resolved = true; + resolve(); + } + }, + onError: () => { + if (!resolved) { + summaryText = ''; + resolved = true; + resolve(); + } + }, + onAbort: () => { + if (!resolved) { + summaryText = ''; + resolved = true; + resolve(); + } + }, + }); + + if (!reqId && !resolved) { + resolved = true; + resolve(); + } + }); + + const trimmedSummary = summaryText.trim(); + if (!trimmedSummary) return { summaryText: null }; + + const approxSummaryTokens = Math.ceil(trimmedSummary.length / CHARS_PER_TOKEN_ESTIMATE); + const approxTokensAfter = approxTailTokens + approxSummaryTokens; + + const compressionInfo: ThreadHistoryCompressionInfo = { + hasCompressed: true, + summarizedMessageCount: prefixMessages.length, + approxTokensBefore, + approxTokensAfter, + }; + + return { summaryText: trimmedSummary, compressionInfo }; + } + + private _buildHistoryTextForCompression(messages: ChatMessage[]): string { + const lines: string[] = []; + for (const m of messages) { + if (m.role === 'checkpoint' || m.role === 'interrupted_streaming_tool') continue; + if (m.role === 'user') { + const content = m.displayContent || ''; + if (!content.trim()) continue; + lines.push(`User: ${content}`); + } else if (m.role === 'assistant') { + const content = m.displayContent || ''; + if (!content.trim()) continue; + lines.push(`Assistant: ${content}`); + } else if (m.role === 'tool') { + const header = `Tool ${m.name} (${m.type})`; + const body = (m.content || '').trim(); + if (!body) { + lines.push(header); + continue; + } + let snippet = body; + + if (snippet.length > HISTORY_COMPRESSION_TOOL_SNIPPET_CHARS) { + snippet = `${snippet.slice(0, HISTORY_COMPRESSION_TOOL_SNIPPET_CHARS)}...`; + } + lines.push(`${header}\n${snippet}`); + } + } + return lines.join('\n\n'); + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatNotificationManager.ts b/src/vs/workbench/contrib/void/browser/ChatNotificationManager.ts new file mode 100644 index 00000000000..7b8cc68f63d --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatNotificationManager.ts @@ -0,0 +1,57 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; +import { truncate } from '../../../../base/common/strings.js'; +import { getErrorMessage } from '../../../../platform/void/common/sendLLMMessageTypes.js'; + +export class ChatNotificationManager { + constructor( + private readonly _notificationService: INotificationService + ) { } + + + public wrapRunAgentToNotify( + p: Promise, + threadId: string, + getCurrentThreadId: () => string, + getLastUserMessageContent: () => string | undefined, + onJumpToChat: (threadId: string) => void + ) { + const notify = ({ error }: { error: string | null }) => { + + const userMsgContent = getLastUserMessageContent(); + if (!userMsgContent) return; + + const messageContentTruncated = truncate(userMsgContent, 50, '...'); + + this._notificationService.notify({ + severity: error ? Severity.Warning : Severity.Info, + message: error ? `Error: ${error} ` : `A new Chat result is ready.`, + source: messageContentTruncated, + sticky: true, + actions: { + primary: [{ + id: 'void.goToChat', + enabled: true, + label: `Jump to Chat`, + tooltip: '', + class: undefined, + run: () => { + onJumpToChat(threadId); + } + }] + }, + }); + }; + + p.then(() => { + if (threadId !== getCurrentThreadId()) notify({ error: null }); + }).catch((e) => { + if (threadId !== getCurrentThreadId()) notify({ error: getErrorMessage(e) }); + throw e; + }); + } +} diff --git a/src/vs/workbench/contrib/void/browser/ChatToolOutputManager.ts b/src/vs/workbench/contrib/void/browser/ChatToolOutputManager.ts new file mode 100644 index 00000000000..5c72b4de02a --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/ChatToolOutputManager.ts @@ -0,0 +1,522 @@ +import { URI } from '../../../../base/common/uri.js'; +import { VSBuffer } from '../../../../base/common/buffer.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { defaultGlobalSettings } from '../../../../platform/void/common/voidSettingsTypes.js'; +import { computeTruncatedToolOutput } from '../../../../platform/void/common/toolOutputTruncation.js'; +import { type JsonObject, type JsonValue, type ToolOutputInput, getStringField, isJsonObject } from '../../../../platform/void/common/jsonTypes.js'; + +import { + normalizeMetaLogFilePath, + looksLikeStableToolOutputsRelPath, + stableToolOutputsRelPath, +} from '../../../../platform/void/common/toolOutputFileNames.js'; + + +export class ChatToolOutputManager { + + constructor( + @IFileService private readonly _fileService: IFileService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @IVoidSettingsService private readonly _settingsService: IVoidSettingsService + ) { } + + private async _getToolOutputsDir(): Promise { + const workspace = this._workspaceContextService.getWorkspace(); + const folderUri = workspace.folders.length > 0 ? workspace.folders[0].uri : null; + if (!folderUri) return null; + + const outputDir = URI.joinPath(folderUri, '.void', 'tool_outputs'); + if (!(await this._fileService.exists(outputDir))) { + await this._fileService.createFolder(outputDir); + } + return outputDir; + } + + private async _toolOutputsFileUri(relPathOrAnything: string): Promise { + const outputDir = await this._getToolOutputsDir(); + if (!outputDir) return null; + + const normalized = normalizeMetaLogFilePath(relPathOrAnything); + if (!normalized) return null; + + const base = normalized.split('/').filter(Boolean).slice(-1)[0]; + if (!base) return null; + + return URI.joinPath(outputDir, base); + } + + private async _existsToolOutputsFile(relPathOrAnything: string): Promise { + try { + const uri = await this._toolOutputsFileUri(relPathOrAnything); + if (!uri) return false; + return await this._fileService.exists(uri); + } catch { + return false; + } + } + + private async _writeToolOutputsFileOverwrite(relPathOrAnything: string, content: string): Promise { + try { + const fileUri = await this._toolOutputsFileUri(relPathOrAnything); + if (!fileUri) return false; + await this._fileService.writeFile(fileUri, VSBuffer.fromString(content)); + return true; + } catch { + return false; + } + } + + private async _copyToolOutputsFileBestEffort(fromRelOrAnything: string, toRelOrAnything: string): Promise { + const fromUri = await this._toolOutputsFileUri(fromRelOrAnything); + const toUri = await this._toolOutputsFileUri(toRelOrAnything); + if (!fromUri || !toUri) return false; + + try { + const exists = await this._fileService.exists(fromUri); + if (!exists) return false; + } catch { + return false; + } + + // Prefer native copy if available + try { + const fileService = this._fileService as { copy?: (from: URI, to: URI, overwrite: boolean) => Promise }; + if (typeof fileService.copy === 'function') { + await fileService.copy(fromUri, toUri, true); + return true; + } + } catch { /* ignore */ } + + // Fallback: read+write + try { + const data = await this._fileService.readFile(fromUri); + await this._fileService.writeFile(toUri, data.value); + return true; + } catch { + return false; + } + } + + private _cleanContentForDisplay(content: string): string { + if (!content) return ''; + + let text = String(content).replace(/\r\n/g, '\n'); + let lines = text.split('\n'); + + // Preserve existing behavior: remove leading absolute path line before a code fence + if (lines.length >= 2) { + const firstRaw = (lines[0] ?? '').trim(); + const second = (lines[1] ?? '').trim(); + + const secondIsFence = /^```[a-zA-Z0-9_-]*\s*$/.test(second); + + const firstSansSuffix = firstRaw.replace( + /\s+\(lines?\s+\d+(?:\s*-\s*\d+)?\)\s*$/i, + '' + ); + + const looksLikeAbsPath = + ( + firstSansSuffix.startsWith('/') || + /^[A-Za-z]:[\\/]/.test(firstSansSuffix) + ) && + !firstSansSuffix.includes('```') && + firstSansSuffix.length < 500; + + if (looksLikeAbsPath && secondIsFence) { + lines.shift(); + if ((lines[0] ?? '').trim() === '') { + lines.shift(); + } + } + } + + text = lines.join('\n'); + text = text.replace(/^\s*```[a-zA-Z0-9_-]*\s*\n/, ''); + text = text.replace(/\n\s*```\s*$/, ''); + + return text.trim(); + } + + public async processToolResult(result: ToolOutputInput, toolName?: string): Promise<{ result: ToolOutputInput; content: string; displayContent: string }> { + + const rawMax = (this._settingsService.state.globalSettings as { maxToolOutputLength?: JsonValue }).maxToolOutputLength; + const maxToolOutputLength = + (typeof rawMax === 'number' && Number.isFinite(rawMax) && rawMax > 0) ? rawMax : + (typeof rawMax === 'string' && Number.isFinite(Number(rawMax)) && Number(rawMax) > 0) ? Number(rawMax) : + 16000; + + const safeJson = (v: ToolOutputInput, max = 300): string => { + try { + const s = JSON.stringify(v); + return s.length > max ? s.slice(0, max) + '…' : s; + } catch { + const s = String(v); + return s.length > max ? s.slice(0, max) + '…' : s; + } + }; + + const tool = String(toolName ?? '').trim(); + const isRunCommand = tool === 'run_command' || tool === 'run_persistent_command'; + const isReadFile = tool === 'read_file'; + + const tryExtractReadFileInfo = (): { filePath?: string; startLine?: number; endLine?: number; fileTotalLines?: number } => { + if (!resObj) return {}; + + // read_file payload is often either: + // { uri, startLine, endLine, fileContents } + // or: + // { result: { uri, startLine, endLine, fileContents } } + const container: any = (() => { + const r = (resObj as any).result; + return (r && typeof r === 'object' && !Array.isArray(r)) ? r : resObj; + })(); + + const startLine = (() => { + const n = Number(container?.startLine); + return Number.isFinite(n) && n > 0 ? n : undefined; + })(); + + const endLine = (() => { + const n = Number(container?.endLine); + return Number.isFinite(n) && n > 0 ? n : undefined; + })(); + + const fileTotalLines = (() => { + const n = Number(container?.totalNumLines); + return Number.isFinite(n) && n > 0 ? n : undefined; + })(); + + const uriObj = container?.uri; + if (uriObj && typeof uriObj === 'object' && !Array.isArray(uriObj)) { + const fsPath = (uriObj as any).fsPath; + if (typeof fsPath === 'string' && fsPath.trim()) { + return { + filePath: fsPath, + startLine, + endLine, + fileTotalLines, + }; + } + } + + // Fallback: parse from output text like "/abs/path/file.ts (lines 10-200)" + const candidate = String(fullText || uiText || '').replace(/\r\n/g, '\n'); + const firstNonEmpty = candidate.split('\n').find(l => l.trim().length > 0) ?? ''; + const m = firstNonEmpty.trim().match(/^(.+?)\s+\(lines?\s+(\d+)(?:\s*-\s*(\d+))?\)\s*$/i); + if (m) { + const p = (m[1] ?? '').trim(); + const s = Number(m[2]); + const e = m[3] ? Number(m[3]) : undefined; + return { + filePath: p || undefined, + startLine: Number.isFinite(s) && s > 0 ? s : undefined, + endLine: typeof e === 'number' && Number.isFinite(e) && e > 0 ? e : undefined, + }; + } + + return {}; + }; + + const isStringInput = typeof result === 'string'; + const resObj: JsonObject | null = (!isStringInput && isJsonObject(result)) ? result : null; + + const TRUNC_META_RE = /TRUNCATION_META:\s*(\{[\s\S]*\})\s*$/; + + const extractTruncationMeta = (text: string): JsonObject | null => { + if (!text) return null; + const tail = text.slice(-4000); + const m = tail.match(TRUNC_META_RE); + if (!m) return null; + try { + const parsed = JSON.parse(m[1]) as JsonValue; + return isJsonObject(parsed) ? parsed : null; + } catch { + return null; + } + }; + + const hasTruncationFooter = (text: string): boolean => { + if (!text) return false; + const tail = text.slice(-4000); + return tail.includes('[VOID] TOOL OUTPUT TRUNCATED') && !!extractTruncationMeta(text); + }; + + let uiText: string; + let uiTextSource: string; + + const footerText = + (resObj && typeof getStringField(resObj, 'text') === 'string' && hasTruncationFooter(getStringField(resObj, 'text')!)) ? getStringField(resObj, 'text')! : + (resObj && typeof getStringField(resObj, 'content') === 'string' && hasTruncationFooter(getStringField(resObj, 'content')!)) ? getStringField(resObj, 'content')! : + (resObj && typeof getStringField(resObj, 'output') === 'string' && hasTruncationFooter(getStringField(resObj, 'output')!)) ? getStringField(resObj, 'output')! : + undefined; + + if (typeof footerText === 'string') { + uiText = footerText; + uiTextSource = 'footer_any'; + } else if (resObj && typeof getStringField(resObj, 'output') === 'string') { + uiText = getStringField(resObj, 'output')!; + uiTextSource = 'result.output'; + } else if (resObj && typeof getStringField(resObj, 'content') === 'string') { + uiText = getStringField(resObj, 'content')!; + uiTextSource = 'result.content'; + } else if (isStringInput) { + uiText = result; + uiTextSource = 'string_input'; + } else if (resObj && typeof getStringField(resObj, 'text') === 'string') { + uiText = getStringField(resObj, 'text')!; + uiTextSource = 'result.text'; + } else if (resObj && getStringField(resObj, '_type') === 'text' && typeof getStringField(resObj, 'content') === 'string') { + uiText = getStringField(resObj, 'content')!; + uiTextSource = '_type_text.content'; + } else if (resObj && typeof getStringField(resObj, 'fileContents') === 'string') { + uiText = getStringField(resObj, 'fileContents')!; + uiTextSource = 'fileContents_as_uiText'; + } else { + uiText = safeJson(result, 10_000); + uiTextSource = 'json_fallback'; + } + + const keyText = + (resObj && typeof getStringField(resObj, 'output') === 'string') ? getStringField(resObj, 'output')! : + (resObj && typeof getStringField(resObj, 'content') === 'string') ? getStringField(resObj, 'content')! : + (resObj && typeof getStringField(resObj, 'text') === 'string') ? getStringField(resObj, 'text')! : + uiText; + + const fullText = + (resObj && typeof getStringField(resObj, 'fileContents') === 'string') ? getStringField(resObj, 'fileContents')! : + keyText; + + const hasValidTruncationFooter = hasTruncationFooter(uiText); + + const makeLeanResult = (stripFileContents: boolean): ToolOutputInput => { + if (!resObj) return result; + if (!stripFileContents) return result; + + const lean: JsonObject = { ...resObj }; + if (typeof lean.fileContents === 'string') delete lean.fileContents; + return lean; + }; + + const terminalId = resObj ? getStringField(resObj, 'terminalId') : undefined; + const toolCallId = resObj ? getStringField(resObj, 'toolCallId') : undefined; + + const stablePath = stableToolOutputsRelPath({ + toolName: tool, + terminalId, + toolCallId, + keyText, + fullText, + }); + + // ========================= + // A: footer already present + // ========================= + if (hasValidTruncationFooter) { + + if (isReadFile) { + const uiContent = uiText; + const displayContent = isRunCommand ? uiContent : this._cleanContentForDisplay(uiContent); + return { + result: makeLeanResult(true), + content: uiContent, + displayContent, + }; + } + + + const metaMatch = uiText.match(TRUNC_META_RE); + + if (metaMatch) { + try { + const parsed = JSON.parse(metaMatch[1]) as JsonValue; + if (isJsonObject(parsed)) { + const meta = parsed; + + const metaLogFilePath = typeof meta.logFilePath === 'string' ? meta.logFilePath : undefined; + + const footerNorm = metaLogFilePath ? normalizeMetaLogFilePath(metaLogFilePath) : undefined; + const footerLooksStable = looksLikeStableToolOutputsRelPath(footerNorm); + + const desired = footerLooksStable ? (footerNorm as string | undefined) : stablePath; + + let canRewrite = false; + + if (desired && await this._existsToolOutputsFile(desired)) { + canRewrite = true; + } else { + const fileContents = resObj ? getStringField(resObj, 'fileContents') : undefined; + const hasFullForSave = typeof fileContents === 'string' && fileContents.length > maxToolOutputLength; + + if (hasFullForSave && desired) { + canRewrite = await this._writeToolOutputsFileOverwrite(desired, fileContents); + } else if (footerNorm && desired && footerNorm !== desired) { + canRewrite = await this._copyToolOutputsFileBestEffort(footerNorm, desired); + } + } + + if (canRewrite && desired && meta.logFilePath !== desired) { + meta.logFilePath = desired; + uiText = uiText.replace( + /TRUNCATION_META:\s*\{[\s\S]*\}\s*$/m, + `TRUNCATION_META: ${JSON.stringify(meta)}` + ); + } + } + } catch (e) { + console.error('failed to parse meta', e); + } + } + + let uiContent = uiText; + + if (uiTextSource === 'result.text' && resObj && typeof getStringField(resObj, 'fileContents') === 'string' && getStringField(resObj, 'fileContents')!.length) { + const lines = uiText.split('\n'); + if (lines.length > 0 && /[\\/]/.test(lines[0]) && !lines[0].startsWith('[VOID]')) { + lines.shift(); + if (lines.length > 0 && lines[0].trim() === '') lines.shift(); + uiContent = lines.join('\n'); + } + } + + const displayContent = isRunCommand ? uiContent : this._cleanContentForDisplay(uiContent); + const defaultStrip = ((resObj && typeof getStringField(resObj, 'fileContents') === 'string') ? getStringField(resObj, 'fileContents')!.length : 0) > maxToolOutputLength; + + return { + result: makeLeanResult(defaultStrip), + content: uiContent, + displayContent, + }; + } + + // ========================= + // B: no footer — truncate ourselves + // ========================= + if (!fullText || fullText.length <= maxToolOutputLength) { + const displayContent = isRunCommand ? uiText : this._cleanContentForDisplay(uiText); + return { result: makeLeanResult(false), content: uiText, displayContent }; + } + + const { truncatedBody, originalLength, needsTruncation, lineAfterTruncation } = + computeTruncatedToolOutput(fullText, maxToolOutputLength); + + if (!needsTruncation) { + const displayContent = isRunCommand ? uiText : this._cleanContentForDisplay(uiText); + return { result: makeLeanResult(false), content: uiText, displayContent }; + } + + const startLineExclusive = lineAfterTruncation > 0 ? lineAfterTruncation : 0; + + const headerLines = [ + `[VOID] TOOL OUTPUT TRUNCATED, SEE TRUNCATION_META BELOW.`, + `Only the first ${maxToolOutputLength} characters are included in this message.`, + `Display limit: maxToolOutputLength = ${maxToolOutputLength} characters.`, + ]; + + let instructionsLines: string[]; + let meta: any; + + if (isReadFile) { + const info = tryExtractReadFileInfo(); + const filePath = info.filePath; + + const { truncatedBody, originalLength, needsTruncation, lineAfterTruncation } = + computeTruncatedToolOutput(fullText, maxToolOutputLength); + + if (!needsTruncation) { + const displayContent = isRunCommand ? uiText : this._cleanContentForDisplay(uiText); + return { result: makeLeanResult(false), content: uiText, displayContent }; + } + + const startLineExclusive = lineAfterTruncation > 0 ? lineAfterTruncation : 0; + const requestedStartLine = info.startLine ?? 1; + const nextStartLine = requestedStartLine + startLineExclusive; + + const rawChunk = (this._settingsService.state.globalSettings as { readFileChunkLines?: JsonValue }).readFileChunkLines; + const chunkSize = + (typeof rawChunk === 'number' && Number.isFinite(rawChunk) && rawChunk > 0) ? rawChunk : + (typeof rawChunk === 'string' && Number.isFinite(Number(rawChunk)) && Number(rawChunk) > 0) ? Number(rawChunk) : + defaultGlobalSettings.readFileChunkLines; + const suggestedEndLine = nextStartLine + chunkSize - 1; + const fileTotalLines = info.fileTotalLines; + const suggested = filePath ? { + startLine: nextStartLine, + endLine: suggestedEndLine, + chunkLines: chunkSize, + endLineIsFileEnd: false, + } : undefined; + + const headerLines = [ + `[VOID] TOOL OUTPUT TRUNCATED, SEE TRUNCATION_META BELOW.`, + `Only the first ${maxToolOutputLength} characters are included in this message.`, + `Display limit: maxToolOutputLength = ${maxToolOutputLength} characters.`, + ]; + + const instructionsLines = filePath ? [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. Continue by calling read_file on the ORIGINAL uri (NOT on a tool-output log):`, + ` read_file({ uri: ${JSON.stringify(filePath)}, startLine: ${nextStartLine}, endLine: ${suggestedEndLine} })`, + ` 3. IMPORTANT: endLine above is a chunk boundary, NOT the end of file.`, + ` 4. Recommended next chunk size: readFileChunkLines = ${chunkSize}.`, + ...(typeof fileTotalLines === 'number' + ? [` Known total file lines (from tool): ${fileTotalLines}.`] + : []), + ` 5. If still truncated, increase startLine by about ${chunkSize} and repeat.`, + ] : [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. Re-run read_file with a smaller range (startLine/endLine).`, + ]; + + const meta = { + tool: 'read_file', + uri: filePath, + requestedStartLine, + nextStartLine, + suggested, + ...(typeof fileTotalLines === 'number' ? { fileTotalLines } : {}), + maxChars: maxToolOutputLength, + originalLength + }; + + const finalText = + `${truncatedBody}...\n\n` + + `${headerLines.join('\n')}\n` + + `${instructionsLines.join('\n')}\n` + + `TRUNCATION_META: ${JSON.stringify(meta)}`; + + const displayContent = isRunCommand ? finalText : this._cleanContentForDisplay(finalText); + return { result: makeLeanResult(true), content: finalText, displayContent }; + } else { + await this._writeToolOutputsFileOverwrite(stablePath, fullText); + + instructionsLines = [ + `IMPORTANT FOR THE MODEL:`, + ` 1. Do NOT guess based only on this truncated output.`, + ` 2. To see the rest of this tool output, you MUST call your file-reading tool (for example, read_file)`, + ` on logFilePath, starting from line startLineExclusive + 1.`, + ]; + + meta = { logFilePath: stablePath, startLineExclusive, maxChars: maxToolOutputLength, originalLength }; + } + + const metaLine = `TRUNCATION_META: ${JSON.stringify(meta)}`; + + const finalText = + `${truncatedBody}...\n\n` + + `${headerLines.join('\n')}\n` + + `${instructionsLines.join('\n')}\n` + + `${metaLine}`; + + const displayContent = isRunCommand ? finalText : this._cleanContentForDisplay(finalText); + + return { + result: makeLeanResult(true), + content: finalText, + displayContent, + }; + } +} diff --git a/src/vs/workbench/contrib/void/browser/_markerCheckService.ts b/src/vs/workbench/contrib/void/browser/_markerCheckService.ts index be1ca0074bf..18da7330f2b 100644 --- a/src/vs/workbench/contrib/void/browser/_markerCheckService.ts +++ b/src/vs/workbench/contrib/void/browser/_markerCheckService.ts @@ -7,12 +7,11 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ITextModelService } from '../../../../editor/common/language/services/resolverService.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { CodeActionContext, CodeActionTriggerType } from '../../../../editor/common/languages.js'; -import { URI } from '../../../../base/common/uri.js'; +import { CodeActionContext, CodeActionTriggerType } from '../../../../editor/common/language/languages.js'; import * as dom from '../../../../base/browser/dom.js'; export interface IMarkerCheckService { @@ -34,19 +33,35 @@ class MarkerCheckService extends Disposable implements IMarkerCheckService { const allMarkers = this._markerService.read(); const errors = allMarkers.filter(marker => marker.severity === MarkerSeverity.Error); - if (errors.length > 0) { - for (const error of errors) { + if (errors.length === 0) { + return; + } + + const errorsByResource = new Map(); + for (const error of errors) { + const key = error.resource.toString(); + const group = errorsByResource.get(key); + if (group) { + group.push(error); + } else { + errorsByResource.set(key, [error]); + } + } - console.log(`----------------------------------------------`); + for (const resourceErrors of errorsByResource.values()) { + const resource = resourceErrors[0].resource; + let modelReference: Awaited> | undefined; - console.log(`${error.resource.fsPath}: ${error.startLineNumber} ${error.message} ${error.severity}`); // ! all errors in the file + try { + modelReference = await this._textModelService.createModelReference(resource); + const model = modelReference.object.textEditorModel; + const providers = this._languageFeaturesService.codeActionProvider.ordered(model); + if (providers.length === 0) continue; - try { - // Get the text model for the file - const modelReference = await this._textModelService.createModelReference(error.resource); - const model = modelReference.object.textEditorModel; + for (const error of resourceErrors) { + console.log(`----------------------------------------------`); + console.log(`${error.resource.fsPath}: ${error.startLineNumber} ${error.message} ${error.severity}`); - // Create a range from the marker const range = new Range( error.startLineNumber, error.startColumn, @@ -54,84 +69,40 @@ class MarkerCheckService extends Disposable implements IMarkerCheckService { error.endColumn ); - // Get code action providers for this model - const codeActionProvider = this._languageFeaturesService.codeActionProvider; - const providers = codeActionProvider.ordered(model); - - if (providers.length > 0) { - // Request code actions from each provider - for (const provider of providers) { - const context: CodeActionContext = { - trigger: CodeActionTriggerType.Invoke, // keeping 'trigger' since it works - only: 'quickfix' // adding this to filter for quick fixes - }; - - const actions = await provider.provideCodeActions( - model, - range, - context, - CancellationToken.None - ); - - if (actions?.actions?.length) { - - const quickFixes = actions.actions.filter(action => action.isPreferred); // ! all quickFixes for the error - // const quickFixesForImports = actions.actions.filter(action => action.isPreferred && action.title.includes('import')); // ! all possible imports - // quickFixesForImports - - if (quickFixes.length > 0) { - console.log('Available Quick Fixes:'); - quickFixes.forEach(action => { - console.log(`- ${action.title}`); - }); - } - } + for (const provider of providers) { + const context: CodeActionContext = { + trigger: CodeActionTriggerType.Invoke, + only: 'quickfix' + }; + + const actions = await provider.provideCodeActions( + model, + range, + context, + CancellationToken.None + ); + + if (!actions?.actions?.length) continue; + + const quickFixes = actions.actions.filter(action => action.isPreferred); + if (quickFixes.length > 0) { + console.log('Available Quick Fixes:'); + quickFixes.forEach(action => { + console.log(`- ${action.title}`); + }); } } - - // Dispose the model reference - modelReference.dispose(); - } catch (e) { - console.error('Error getting quick fixes:', e); } + } catch (e) { + console.error('Error getting quick fixes:', e); + } finally { + modelReference?.dispose(); } } } const { window } = dom.getActiveWindow() window.setInterval(check, 5000); } - - - - - fixErrorsInFiles(uris: URI[], contextSoFar: []) { - // const allMarkers = this._markerService.read(); - - - // check errors in files - - - // give LLM errors in files - - - - } - - // private _onMarkersChanged = (changedResources: readonly URI[]): void => { - // for (const resource of changedResources) { - // const markers = this._markerService.read({ resource }); - - // if (markers.length === 0) { - // console.log(`${resource.fsPath}: No diagnostics`); - // continue; - // } - - // console.log(`Diagnostics for ${resource.fsPath}:`); - // markers.forEach(marker => this._logMarker(marker)); - // } - // }; - - } registerSingleton(IMarkerCheckService, MarkerCheckService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/aiRegexService.ts b/src/vs/workbench/contrib/void/browser/aiRegexService.ts deleted file mode 100644 index b0d02024018..00000000000 --- a/src/vs/workbench/contrib/void/browser/aiRegexService.ts +++ /dev/null @@ -1,108 +0,0 @@ -/*-------------------------------------------------------------------------------------- - * Copyright 2025 Glass Devtools, Inc. All rights reserved. - * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. - *--------------------------------------------------------------------------------------*/ - -// 1. search(ai) -// - tool use to find all possible changes -// - if search only: is this file related to the search? -// - if search + replace: should I modify this file? -// 2. replace(ai) -// - what changes to make? -// 3. postprocess errors -// -fastapply changes simultaneously -// -iterate on syntax errors (all files can be changed from a syntax error, not just the one with the error) - - -// private async _searchUsingAI({ searchClause }: { searchClause: string }) { - -// // const relevantURIs: URI[] = [] -// // const gatherPrompt = `\ -// // asdasdas -// // ` -// // const filterPrompt = `\ -// // Is this file relevant? -// // ` - - -// // // optimizations (DO THESE LATER!!!!!!) -// // // if tool includes a uri in uriSet, skip it obviously -// // let uriSet = new Set() -// // // gather -// // let messages = [] -// // while (true) { -// // const result = await new Promise((res, rej) => { -// // sendLLMMessage({ -// // messages, -// // tools: ['search_for_files'], -// // onFinalMessage: ({ result: r, }) => { -// // res(r) -// // }, -// // onError: (error) => { -// // rej(error) -// // } -// // }) -// // }) - -// // messages.push({ role: 'tool', content: turnToString(result) }) - -// // sendLLMMessage({ -// // messages: { 'Output ': result }, -// // onFinalMessage: (r) => { -// // // output is file1\nfile2\nfile3\n... -// // } -// // }) - -// // uriSet.add(...) -// // } - -// // // writes -// // if (!replaceClause) return - -// // for (const uri of uriSet) { -// // // in future, batch these -// // applyWorkflow({ uri, applyStr: replaceClause }) -// // } - - - - - - -// // while (true) { -// // const result = new Promise((res, rej) => { -// // sendLLMMessage({ -// // messages, -// // tools: ['search_for_files'], -// // onResult: (r) => { -// // res(r) -// // } -// // }) -// // }) - -// // messages.push(result) - -// // } - - -// } - - -// private async _replaceUsingAI({ searchClause, replaceClause, relevantURIs }: { searchClause: string, replaceClause: string, relevantURIs: URI[] }) { - -// for (const uri of relevantURIs) { - -// uri - -// } - - - -// // should I change this file? -// // if so what changes to make? - - - -// // fast apply the changes -// } - diff --git a/src/vs/workbench/contrib/void/browser/autocompleteService.ts b/src/vs/workbench/contrib/void/browser/autocompleteService.ts index 22c86eb6afc..f92f264a10d 100644 --- a/src/vs/workbench/contrib/void/browser/autocompleteService.ts +++ b/src/vs/workbench/contrib/void/browser/autocompleteService.ts @@ -4,27 +4,26 @@ *--------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../base/common/lifecycle.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { EndOfLinePreference, ITextModel } from '../../../../editor/common/model.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { InlineCompletion, } from '../../../../editor/common/languages.js'; -import { Range } from '../../../../editor/common/core/range.js'; +import { EndOfLinePreference, ITextModel } from '../../../../editor/common/language/model.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { InlineCompletion, } from '../../../../editor/common/language/languages.js'; +import { Range } from '../../../../editor/common/language/core/range.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { EditorResourceAccessor } from '../../../common/editor.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { extractCodeFromRegular } from '../common/helpers/extractCodeFromResult.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { extractCodeFromRegular } from '../../../../platform/void/common/helpers/extractCodeFromResult.js'; import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { ILLMMessageService } from '../common/sendLLMMessageService.js'; import { isWindows } from '../../../../base/common/platform.js'; -import { IVoidSettingsService } from '../common/voidSettingsService.js'; -import { FeatureName } from '../common/voidSettingsTypes.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { FeatureName } from '../../../../platform/void/common/voidSettingsTypes.js'; import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'; // import { IContextGatheringService } from './contextGatheringService.js'; - const allLinebreakSymbols = ['\r\n', '\n'] const _ln = isWindows ? allLinebreakSymbols[0] : allLinebreakSymbols[1] @@ -811,30 +810,8 @@ export class AutocompleteService extends Disposable implements IAutocompleteServ overridesOfModel, logging: { loggingName: 'Autocomplete' }, onText: () => { }, // unused in FIMMessage - // onText: async ({ fullText, newText }) => { - - // newAutocompletion.insertText = fullText - - // // count newlines in newText - // const numNewlines = newText.match(/\n|\r\n/g)?.length || 0 - // newAutocompletion._newlineCount += numNewlines - - // // if too many newlines, resolve up to last newline - // if (newAutocompletion._newlineCount > 10) { - // const lastNewlinePos = fullText.lastIndexOf('\n') - // newAutocompletion.insertText = fullText.substring(0, lastNewlinePos) - // resolve(newAutocompletion.insertText) - // return - // } - - // // if (!getAutocompletionMatchup({ prefix: this._lastPrefix, autocompletion: newAutocompletion })) { - // // reject('LLM response did not match user\'s text.') - // // } - // }, onFinalMessage: ({ fullText }) => { - // console.log('____res: ', JSON.stringify(newAutocompletion.insertText)) - newAutocompletion.endTime = Date.now() newAutocompletion.status = 'finished' const [text, _] = extractCodeFromRegular({ text: fullText, recentlyAddedTextLen: 0 }) diff --git a/src/vs/workbench/contrib/void/browser/chatThreadService.ts b/src/vs/workbench/contrib/void/browser/chatThreadService.ts index 30f38f10ba8..965b0ea61b0 100644 --- a/src/vs/workbench/contrib/void/browser/chatThreadService.ts +++ b/src/vs/workbench/contrib/void/browser/chatThreadService.ts @@ -7,159 +7,94 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; - import { URI } from '../../../../base/common/uri.js'; import { Emitter, Event } from '../../../../base/common/event.js'; -import { ILLMMessageService } from '../common/sendLLMMessageService.js'; -import { chat_userMessageContent, isABuiltinToolName } from '../common/prompt/prompts.js'; -import { AnthropicReasoning, getErrorMessage, RawToolCallObj, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; import { generateUuid } from '../../../../base/common/uuid.js'; -import { FeatureName, ModelSelection, ModelSelectionOptions } from '../common/voidSettingsTypes.js'; -import { IVoidSettingsService } from '../common/voidSettingsService.js'; -import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, ToolCallParams, ToolName, ToolResult } from '../common/toolsServiceTypes.js'; -import { IToolsService } from './toolsService.js'; -import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { ChatMessage, CheckpointEntry, CodespanLocationLink, StagingSelectionItem, ToolMessage } from '../common/chatThreadServiceTypes.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { IMetricsService } from '../common/metricsService.js'; -import { shorten } from '../../../../base/common/labels.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { ILLMMessageService } from '../common/sendLLMMessageService.js'; +import { IToolsService } from '../common/toolsService.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; +import { ILanguageModelToolsService } from '../../chat/common/languageModelToolsService.js'; +import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; import { IVoidModelService } from '../common/voidModelService.js'; -import { findLast, findLastIdx } from '../../../../base/common/arraysFind.js'; import { IEditCodeService } from './editCodeServiceInterface.js'; -import { VoidFileSnapshot } from '../common/editCodeServiceTypes.js'; -import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; -import { truncate } from '../../../../base/common/strings.js'; -import { THREAD_STORAGE_KEY } from '../common/storageKeys.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'; -import { timeout } from '../../../../base/common/async.js'; -import { deepClone } from '../../../../base/common/objects.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { IDirectoryStrService } from '../common/directoryStrService.js'; +import { IDirectoryStrService } from '../../../../platform/void/common/directoryStrService.js'; import { IFileService } from '../../../../platform/files/common/files.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { IAcpService } from '../../../../platform/acp/common/iAcpService.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { deepClone } from '../../../../base/common/objects.js'; import { IMCPService } from '../common/mcpService.js'; -import { RawMCPToolCall } from '../common/mcpServiceTypes.js'; - - -// related to retrying when LLM message has error -const CHAT_RETRIES = 3 -const RETRY_DELAY = 2500 - - -const findStagingSelectionIndex = (currentSelections: StagingSelectionItem[] | undefined, newSelection: StagingSelectionItem): number | null => { - if (!currentSelections) return null - - for (let i = 0; i < currentSelections.length; i += 1) { - const s = currentSelections[i] - - if (s.uri.fsPath !== newSelection.uri.fsPath) continue - - if (s.type === 'File' && newSelection.type === 'File') { - return i - } - if (s.type === 'CodeSelection' && newSelection.type === 'CodeSelection') { - if (s.uri.fsPath !== newSelection.uri.fsPath) continue - // if there's any collision return true - const [oldStart, oldEnd] = s.range - const [newStart, newEnd] = newSelection.range - if (oldStart !== newStart || oldEnd !== newEnd) continue - return i - } - if (s.type === 'Folder' && newSelection.type === 'Folder') { - return i - } - } - return null -} - - -/* - -Store a checkpoint of all "before" files on each x. -x's show up before user messages and LLM edit tool calls. - -x A (edited A -> A') -(... user modified changes ...) -User message - -x A' B C (edited A'->A'', B->B', C->C') -LLM Edit -x -LLM Edit -x -LLM Edit - - -INVARIANT: -A checkpoint appears before every LLM message, and before every user message (before user really means directly after LLM is done). -*/ - - -type UserMessageType = ChatMessage & { role: 'user' } -type UserMessageState = UserMessageType['state'] -const defaultMessageState: UserMessageState = { - stagingSelections: [], - isBeingEdited: false, -} - -// a 'thread' means a chat message history - -type WhenMounted = { - textAreaRef: { current: HTMLTextAreaElement | null }; // the textarea that this thread has, gets set in SidebarChat - scrollToBottom: () => void; -} - - +import { + ChatMessage, StagingSelectionItem, ChatAttachment, CodespanLocationLink, + AnyToolName +} from '../../../../platform/void/common/chatThreadServiceTypes.js'; + +import { chat_userMessageContent } from '../common/prompt/prompts.js'; +import { LLMTokenUsage, RawToolCallObj, RawToolParamsObj } from '../../../../platform/void/common/sendLLMMessageTypes.js'; +import { THREAD_STORAGE_KEY } from '../../../../platform/void/common/storageKeys.js'; + +import { ChatNotificationManager } from './ChatNotificationManager.js'; +import { ChatHistoryCompressor } from './ChatHistoryCompressor.js'; +import { ChatToolOutputManager } from './ChatToolOutputManager.js'; +import { ChatCheckpointManager, ICheckpointThreadAccess } from './ChatCheckpointManager.js'; +import { ChatCodespanManager } from './ChatCodespanManager.js'; +import { ChatAcpHandler, IThreadStateAccess } from './ChatAcpHandler.js'; +import { ChatExecutionEngine } from './ChatExecutionEngine.js'; +import { getModelCapabilities } from '../../../../platform/void/common/modelInference.js'; + +export type ThreadHistoryCompressionInfo = { + hasCompressed: boolean; + summarizedMessageCount: number; + approxTokensBefore: number; + approxTokensAfter: number; +}; export type ThreadType = { - id: string; // store the id here too - createdAt: string; // ISO string - lastModified: string; // ISO string + id: string; + createdAt: string; + lastModified: string; messages: ChatMessage[]; filesWithUserChanges: Set; - // this doesn't need to go in a state object, but feels right state: { - currCheckpointIdx: number | null; // the latest checkpoint we're at (null if not at a particular checkpoint, like if the chat is streaming, or chat just finished and we haven't clicked on a checkpt) - + currCheckpointIdx: number | null; stagingSelections: StagingSelectionItem[]; - focusedMessageIdx: number | undefined; // index of the user message that is being edited (undefined if none) - - linksOfMessageIdx: { // eg. link = linksOfMessageIdx[4]['RangeFunction'] + focusedMessageIdx: number | undefined; + linksOfMessageIdx: { [messageIdx: number]: { [codespanName: string]: CodespanLocationLink } } - - + acpPlan?: { + title?: string; + items: Array<{ id?: string; text: string; state: 'pending' | 'running' | 'done' | 'error' }>; + }; + tokenUsageSession?: LLMTokenUsage; + tokenUsageLastRequest?: LLMTokenUsage; + tokenUsageLastRequestLimits?: any; + historyCompression?: ThreadHistoryCompressionInfo; mountedInfo?: { - whenMounted: Promise - _whenMountedResolver: (res: WhenMounted) => void + whenMounted: Promise + _whenMountedResolver: (res: any) => void mountedIsResolvedRef: { current: boolean }; } - - }; } -type ChatThreads = { +export type ChatThreads = { [id: string]: undefined | ThreadType; } - export type ThreadsState = { allThreads: ChatThreads; - currentThreadId: string; // intended for internal use only + currentThreadId: string; } -export type IsRunningType = - | 'LLM' // the LLM is currently streaming - | 'tool' // whether a tool is currently running - | 'awaiting_user' // awaiting user call - | 'idle' // nothing is running now, but the chat should still appear like it's going (used in-between calls) - | undefined - export type ThreadStreamState = { [threadId: string]: undefined | { isRunning: undefined; @@ -167,27 +102,27 @@ export type ThreadStreamState = { llmInfo?: undefined; toolInfo?: undefined; interrupt?: undefined; - } | { // an assistant message is being written + } | { isRunning: 'LLM'; error?: undefined; llmInfo: { displayContentSoFar: string; reasoningSoFar: string; toolCallSoFar: RawToolCallObj | null; + planSoFar?: any; }; toolInfo?: undefined; - interrupt: Promise<() => void>; // calling this should have no effect on state - would be too confusing. it just cancels the tool - } | { // a tool is being run + interrupt: Promise<() => void>; + } | { isRunning: 'tool'; error?: undefined; llmInfo?: undefined; toolInfo: { - toolName: ToolName; - toolParams: ToolCallParams; + toolName: AnyToolName; + toolParams: any; id: string; content: string; rawParams: RawToolParamsObj; - mcpServerName: string | undefined; }; interrupt: Promise<() => void>; } | { @@ -201,1684 +136,914 @@ export type ThreadStreamState = { error?: undefined; llmInfo?: undefined; toolInfo?: undefined; - interrupt: 'not_needed' | Promise<() => void>; // calling this should have no effect on state - would be too confusing. it just cancels the tool + interrupt: 'not_needed' | Promise<() => void>; } } -const newThreadObject = () => { - const now = new Date().toISOString() - return { - id: generateUuid(), - createdAt: now, - lastModified: now, - messages: [], - state: { - currCheckpointIdx: null, - stagingSelections: [], - focusedMessageIdx: undefined, - linksOfMessageIdx: {}, - }, - filesWithUserChanges: new Set() - } satisfies ThreadType -} - - - - - +// --- INTERFACES --- export interface IChatThreadService { readonly _serviceBrand: undefined; - readonly state: ThreadsState; - readonly streamState: ThreadStreamState; // not persistent - + readonly streamState: ThreadStreamState; onDidChangeCurrentThread: Event; - onDidChangeStreamState: Event<{ threadId: string }> - + onDidChangeStreamState: Event<{ threadId: string }>; getCurrentThread(): ThreadType; openNewThread(): void; switchToThread(threadId: string): void; - - // thread selector deleteThread(threadId: string): void; duplicateThread(threadId: string): void; - - // exposed getters/setters - // these all apply to current thread - getCurrentMessageState: (messageIdx: number) => UserMessageState - setCurrentMessageState: (messageIdx: number, newState: Partial) => void - getCurrentThreadState: () => ThreadType['state'] - setCurrentThreadState: (newState: Partial) => void - - // you can edit multiple messages - the one you're currently editing is "focused", and we add items to that one when you press cmd+L. + getCurrentMessageState: (messageIdx: number) => any; + setCurrentMessageState: (messageIdx: number, newState: any) => void; + getCurrentThreadState: () => ThreadType['state']; + setCurrentThreadState: (newState: Partial) => void; getCurrentFocusedMessageIdx(): number | undefined; isCurrentlyFocusingMessage(): boolean; setCurrentlyFocusedMessageIdx(messageIdx: number | undefined): void; - popStagingSelections(numPops?: number): void; addNewStagingSelection(newSelection: StagingSelectionItem): void; - dangerousSetState: (newState: ThreadsState) => void; resetState: () => void; - - // // current thread's staging selections - // closeCurrentStagingSelectionsInMessage(opts: { messageIdx: number }): void; - // closeCurrentStagingSelectionsInThread(): void; - - // codespan links (link to symbols in the markdown) getCodespanLink(opts: { codespanStr: string, messageIdx: number, threadId: string }): CodespanLocationLink | undefined; addCodespanLink(opts: { newLinkText: string, newLinkLocation: CodespanLocationLink, messageIdx: number, threadId: string }): void; - generateCodespanLink(opts: { codespanStr: string, threadId: string }): Promise; - getRelativeStr(uri: URI): string | undefined - - // entry pts + generateCodespanLink(opts: { codespanStr: string, threadId: string }): Promise; + getRelativeStr(uri: URI): string | undefined; abortRunning(threadId: string): Promise; dismissStreamError(threadId: string): void; - - // call to edit a message editUserMessageAndStreamResponse({ userMessage, messageIdx, threadId }: { userMessage: string, messageIdx: number, threadId: string }): Promise; - - // call to add a message - addUserMessageAndStreamResponse({ userMessage, threadId }: { userMessage: string, threadId: string }): Promise; - - // approve/reject + addUserMessageAndStreamResponse({ userMessage, threadId, attachments }: { userMessage: string, threadId: string, attachments?: ChatAttachment[] }): Promise; approveLatestToolRequest(threadId: string): void; rejectLatestToolRequest(threadId: string): void; - - // jump to history + skipLatestToolRequest(threadId: string): void; + skipRunningTool(threadId: string): void; jumpToCheckpointBeforeMessageIdx(opts: { threadId: string, messageIdx: number, jumpToUserModified: boolean }): void; + focusCurrentChat: () => Promise; + blurCurrentChat: () => Promise; + enqueueToolRequestFromAcp(threadId: string, req: { id: string; name: AnyToolName | string; rawParams: Record; params?: Record }): void; + onExternalToolDecision: Event<{ threadId: string; toolCallId: string; decision: 'approved' | 'rejected' | 'skipped' }>; +} + +export function normalizeSelectionRelativePath(uri: URI, workspaceFolderUris: readonly URI[]): string | undefined { + if (!workspaceFolderUris.length) return undefined; + const folder = workspaceFolderUris.find(f => uri.fsPath.startsWith(f.fsPath)); + if (!folder) return undefined; + let rel = uri.fsPath.slice(folder.fsPath.length); + rel = rel.replace(/^[\\/]+/, ''); + if (!rel) return './'; + return `./${rel}`; +} - focusCurrentChat: () => Promise - blurCurrentChat: () => Promise +const newThreadObject = () => { + const now = new Date().toISOString() + return { + id: generateUuid(), + createdAt: now, + lastModified: now, + messages: [], + state: { + currCheckpointIdx: null, + stagingSelections: [], + focusedMessageIdx: undefined, + linksOfMessageIdx: {}, + tokenUsageSession: undefined, + historyCompression: undefined, + }, + filesWithUserChanges: new Set() + } satisfies ThreadType } + +// --- MAIN CLASS --- + export const IChatThreadService = createDecorator('voidChatThreadService'); -class ChatThreadService extends Disposable implements IChatThreadService { + +export class ChatThreadService extends Disposable implements IChatThreadService { _serviceBrand: undefined; - // this fires when the current thread changes at all (a switch of currentThread, or a message added to it, etc) + // Events private readonly _onDidChangeCurrentThread = new Emitter(); readonly onDidChangeCurrentThread: Event = this._onDidChangeCurrentThread.event; private readonly _onDidChangeStreamState = new Emitter<{ threadId: string }>(); readonly onDidChangeStreamState: Event<{ threadId: string }> = this._onDidChangeStreamState.event; - readonly streamState: ThreadStreamState = {} - state: ThreadsState // allThreads is persisted, currentThread is not + private readonly _onExternalToolDecision = new Emitter<{ threadId: string; toolCallId: string; decision: 'approved' | 'rejected' | 'skipped' }>(); + readonly onExternalToolDecision = this._onExternalToolDecision.event; - // used in checkpointing - // private readonly _userModifiedFilesToCheckInCheckpoints = new LRUCache(50) + // State + readonly streamState: ThreadStreamState = {}; + state: ThreadsState; + // Sub-Services + private readonly _notificationManager: ChatNotificationManager; + private readonly _historyCompressor: ChatHistoryCompressor; + private readonly _toolOutputManager: ChatToolOutputManager; + private readonly _checkpointManager: ChatCheckpointManager; + private readonly _codespanManager: ChatCodespanManager; + private readonly _acpHandler: ChatAcpHandler; + private readonly _executionEngine: ChatExecutionEngine; + // Access Bridge + private readonly _threadAccess: IThreadStateAccess & ICheckpointThreadAccess; constructor( + @IAcpService _acpService: IAcpService, @IStorageService private readonly _storageService: IStorageService, @IVoidModelService private readonly _voidModelService: IVoidModelService, - @ILLMMessageService private readonly _llmMessageService: ILLMMessageService, - @IToolsService private readonly _toolsService: IToolsService, + @ILLMMessageService _llmMessageService: ILLMMessageService, + @IToolsService _toolsService: IToolsService, @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, - @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, - @IMetricsService private readonly _metricsService: IMetricsService, - @IEditCodeService private readonly _editCodeService: IEditCodeService, - @INotificationService private readonly _notificationService: INotificationService, - @IConvertToLLMMessageService private readonly _convertToLLMMessagesService: IConvertToLLMMessageService, + @ILanguageFeaturesService _languageFeaturesService: ILanguageFeaturesService, + @ILanguageModelToolsService _lmToolsService: ILanguageModelToolsService, + @IMCPService _mcpService: IMCPService, + @IMetricsService _metricsService: IMetricsService, + @IEditCodeService _editCodeService: IEditCodeService, + @INotificationService _notificationService: INotificationService, + @IConvertToLLMMessageService _convertToLLMMessagesService: IConvertToLLMMessageService, @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, @IDirectoryStrService private readonly _directoryStringService: IDirectoryStrService, @IFileService private readonly _fileService: IFileService, - @IMCPService private readonly _mcpService: IMCPService, + @ILabelService private readonly _labelService: ILabelService, + @ILogService private readonly _logService: ILogService, ) { - super() - this.state = { allThreads: {}, currentThreadId: null as unknown as string } // default state - - const readThreads = this._readAllThreads() || {} + super(); - const allThreads = readThreads + // 1. Init State + const readThreads = this._readAllThreads() || {}; this.state = { - allThreads: allThreads, - currentThreadId: null as unknown as string, // gets set in startNewThread() - } + allThreads: readThreads, + currentThreadId: null as unknown as string, + }; + this.openNewThread(); - // always be in a thread - this.openNewThread() - - - // keep track of user-modified files - // const disposablesOfModelId: { [modelId: string]: IDisposable[] } = {} - // this._register( - // this._modelService.onModelAdded(e => { - // if (!(e.id in disposablesOfModelId)) disposablesOfModelId[e.id] = [] - // disposablesOfModelId[e.id].push( - // e.onDidChangeContent(() => { this._userModifiedFilesToCheckInCheckpoints.set(e.uri.fsPath, null) }) - // ) - // }) - // ) - // this._register(this._modelService.onModelRemoved(e => { - // if (!(e.id in disposablesOfModelId)) return - // disposablesOfModelId[e.id].forEach(d => d.dispose()) - // })) + // 2. Init Access Bridge + this._threadAccess = { + getThreadMessages: (tid: string) => this.state.allThreads[tid]?.messages || [], + getThreadState: (tid: string) => this.state.allThreads[tid]?.state || { currCheckpointIdx: null }, + getStreamState: (tid: string) => this.streamState[tid], - } + setStreamState: (tid: string, s: any) => this._setStreamState(tid, s), + setThreadState: (tid: string, s: any) => this._setThreadState(tid, s), - async focusCurrentChat() { - const threadId = this.state.currentThreadId - const thread = this.state.allThreads[threadId] - if (!thread) return - const s = await thread.state.mountedInfo?.whenMounted - if (!this.isCurrentlyFocusingMessage()) { - s?.textAreaRef.current?.focus() - } - } - async blurCurrentChat() { - const threadId = this.state.currentThreadId - const thread = this.state.allThreads[threadId] - if (!thread) return - const s = await thread.state.mountedInfo?.whenMounted - if (!this.isCurrentlyFocusingMessage()) { - s?.textAreaRef.current?.blur() - } - } + addMessageToThread: (tid: string, msg: ChatMessage) => this._addMessageToThread(tid, msg), + editMessageInThread: (tid: string, idx: number, msg: ChatMessage) => this._editMessageInThread(tid, idx, msg), + updateLatestTool: (tid: string, tool: any) => this._updateLatestTool(tid, tool), + + accumulateTokenUsage: (tid: string, usage: any) => this._accumulateTokenUsage(tid, usage), + addUserCheckpoint: (tid: string) => this._checkpointManager.addUserCheckpoint(tid, this._threadAccess), + currentModelSelectionProps: () => this._currentModelSelectionProps(), + isStreaming: (tid: string) => !!this.streamState[tid]?.isRunning + }; + // 3. Init Sub-Services + this._notificationManager = new ChatNotificationManager(_notificationService); + this._historyCompressor = new ChatHistoryCompressor( + _llmMessageService, + _convertToLLMMessagesService, + _settingsService + ); - dangerousSetState = (newState: ThreadsState) => { - this.state = newState - this._onDidChangeCurrentThread.fire() - } - resetState = () => { - this.state = { allThreads: {}, currentThreadId: null as unknown as string } // see constructor - this.openNewThread() - this._onDidChangeCurrentThread.fire() - } + this._toolOutputManager = new ChatToolOutputManager( + _fileService, + _workspaceContextService, + _settingsService + ); - // !!! this is important for properly restoring URIs from storage - // should probably re-use code from void/src/vs/base/common/marshalling.ts instead. but this is simple enough - private _convertThreadDataFromStorage(threadsStr: string): ChatThreads { - return JSON.parse(threadsStr, (key, value) => { - if (value && typeof value === 'object' && value.$mid === 1) { // $mid is the MarshalledId. $mid === 1 means it is a URI - return URI.from(value); // TODO URI.revive instead of this? - } - return value; - }); - } + this._checkpointManager = new ChatCheckpointManager(_editCodeService, _voidModelService); - private _readAllThreads(): ChatThreads | null { - const threadsStr = this._storageService.get(THREAD_STORAGE_KEY, StorageScope.APPLICATION); - if (!threadsStr) { - return null - } - const threads = this._convertThreadDataFromStorage(threadsStr); + this._codespanManager = new ChatCodespanManager(_toolsService, _languageFeaturesService, _voidModelService); - return threads - } + this._acpHandler = new ChatAcpHandler( + _acpService, _workspaceContextService, _settingsService, _fileService, + _directoryStringService, _voidModelService, _editCodeService, this._logService, + this._historyCompressor, this._toolOutputManager, + ); - private _storeAllThreads(threads: ChatThreads) { - const serializedThreads = JSON.stringify(threads); - this._storageService.store( - THREAD_STORAGE_KEY, - serializedThreads, - StorageScope.APPLICATION, - StorageTarget.USER + this._executionEngine = new ChatExecutionEngine( + _llmMessageService, _toolsService, _settingsService, _lmToolsService, + _metricsService, _convertToLLMMessagesService, _fileService, _mcpService, + this._historyCompressor, this._toolOutputManager ); } + private _findLastToolMessageIndexById(threadId: string, toolCallId: string): number | null { + const t = this.state.allThreads[threadId]; + if (!t) return null; - // this should be the only place this.state = ... appears besides constructor - private _setState(state: Partial, doNotRefreshMountInfo?: boolean) { - const newState = { - ...this.state, - ...state + for (let i = t.messages.length - 1; i >= 0; i--) { + const m: any = t.messages[i] as any; + if (m && m.role === 'tool' && String(m.id ?? '') === String(toolCallId)) { + return i; + } } + return null; + } - this.state = newState + private _editToolMessageById(threadId: string, toolCallId: string, patch: Record): void { + const idx = this._findLastToolMessageIndexById(threadId, toolCallId); + if (idx === null) return; - this._onDidChangeCurrentThread.fire() + const t = this.state.allThreads[threadId]; + if (!t) return; + const prev: any = t.messages[idx] as any; + const next: any = { ...prev, ...patch }; + this._editMessageInThread(threadId, idx, next); + } - // if we just switched to a thread, update its current stream state if it's not streaming to possibly streaming - const threadId = newState.currentThreadId - const streamState = this.streamState[threadId] - if (streamState?.isRunning === undefined && !streamState?.error) { + // --- Public API --- - // set streamState - const messages = newState.allThreads[threadId]?.messages - const lastMessage = messages && messages[messages.length - 1] - // if awaiting user but stream state doesn't indicate it (happens if restart Void) - if (lastMessage && lastMessage.role === 'tool' && lastMessage.type === 'tool_request') - this._setStreamState(threadId, { isRunning: 'awaiting_user', }) + async addUserMessageAndStreamResponse({ userMessage, _chatSelections, attachments, threadId }: { userMessage: string, _chatSelections?: StagingSelectionItem[], attachments?: ChatAttachment[], threadId: string }) { + const thread = this.state.allThreads[threadId]; + if (!thread) return; - // if running now but stream state doesn't indicate it (happens if restart Void), cancel that last tool - if (lastMessage && lastMessage.role === 'tool' && lastMessage.type === 'running_now') { + if (thread.state.currCheckpointIdx !== null) { + const checkpointIdx = thread.state.currCheckpointIdx; + const newMessages = thread.messages.slice(0, checkpointIdx + 1); + const newThreads = { + ...this.state.allThreads, + [threadId]: { ...thread, lastModified: new Date().toISOString(), messages: newMessages } + }; + this._storeAllThreads(newThreads); + this._setState({ allThreads: newThreads }); + } - this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', content: lastMessage.content, id: lastMessage.id, rawParams: lastMessage.rawParams, result: null, name: lastMessage.name, params: lastMessage.params, mcpServerName: lastMessage.mcpServerName }) - } + if (this.streamState[threadId]?.isRunning) { + await this.abortRunning(threadId); + } + if (thread.messages.length === 0) { + this._checkpointManager.addUserCheckpoint(threadId, this._threadAccess); } + const currSelns = _chatSelections ?? thread.state.stagingSelections; + const userMessageContent = await chat_userMessageContent(userMessage, currSelns, { + directoryStrService: this._directoryStringService, + fileService: this._fileService, + voidModelService: this._voidModelService, + getRelativePath: (uri: URI) => this._labelService.getUriLabel(uri, { relative: true }) + }); - // if we did not just set the state to true, set mount info - if (doNotRefreshMountInfo) return + this._addMessageToThread(threadId, { + role: 'user', + content: userMessageContent, + displayContent: userMessage, + selections: currSelns, + attachments: attachments && attachments.length ? attachments : null, + state: { stagingSelections: [], isBeingEdited: false }, + }); - let whenMountedResolver: (w: WhenMounted) => void - const whenMountedPromise = new Promise((res) => whenMountedResolver = res) + this._setThreadState(threadId, { currCheckpointIdx: null }); - this._setThreadState(threadId, { - mountedInfo: { - whenMounted: whenMountedPromise, - mountedIsResolvedRef: { current: false }, - _whenMountedResolver: (w: WhenMounted) => { - whenMountedResolver(w) - const mountInfo = this.state.allThreads[threadId]?.state.mountedInfo - if (mountInfo) mountInfo.mountedIsResolvedRef.current = true - }, + try { + const { modelSelection } = this._currentModelSelectionProps(); + if (modelSelection) { + const caps = getModelCapabilities( + modelSelection.providerName as any, + modelSelection.modelName, + this._settingsService.state.overridesOfModel + ); + const reserved = caps.reservedOutputTokenSpace ?? 0; + const maxInputTokens = Math.max(0, caps.contextWindow - reserved); + this._setThreadState(threadId, { tokenUsageLastRequestLimits: { maxInputTokens } }); } - }, true) // do not trigger an update + } catch { } + + if (this._settingsService.state.globalSettings.useAcp === true) { + this._notificationManager.wrapRunAgentToNotify( + this._acpHandler.runAcp( + { + threadId, + userMessage, + _chatSelections: currSelns, + attachments + }, + this._threadAccess + ), + threadId, + () => this.state.currentThreadId, + () => this._getLastUserMessageContent(threadId), + (id: string) => this.switchToThread(id) + ); + } else { + this._notificationManager.wrapRunAgentToNotify( + this._executionEngine.runChatAgent({ threadId, ...this._currentModelSelectionProps() }, this._threadAccess), + threadId, + () => this.state.currentThreadId, + () => this._getLastUserMessageContent(threadId), + (id: string) => this.switchToThread(id) + ); + } + this.state.allThreads[threadId]?.state.mountedInfo?.whenMounted.then((m: any) => m.scrollToBottom()); + } + async abortRunning(threadId: string) { + const st = this.streamState[threadId]; + if (st?.isRunning === 'LLM' && st.llmInfo) { + this._addMessageToThread(threadId, { + role: 'assistant', + displayContent: st.llmInfo.displayContentSoFar, + reasoning: st.llmInfo.reasoningSoFar, + anthropicReasoning: null + }); + if (st.llmInfo.toolCallSoFar) { + this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: st.llmInfo.toolCallSoFar.name as any }); + } + } else if (st?.isRunning === 'tool' && st.toolInfo) { + const { toolName, toolParams, id, content } = st.toolInfo; + this._updateLatestTool(threadId, { + role: 'tool', name: toolName, params: toolParams, id, + content: content || 'Interrupted', displayContent: content || 'Interrupted', + type: 'rejected', result: null, rawParams: st.toolInfo.rawParams + }); + } - } + this._checkpointManager.addUserCheckpoint(threadId, this._threadAccess); + try { + const interrupt = await this.streamState[threadId]?.interrupt; + if (typeof interrupt === 'function') (interrupt as any)(); + } catch { } - private _setStreamState(threadId: string, state: ThreadStreamState[string]) { - this.streamState[threadId] = state - this._onDidChangeStreamState.fire({ threadId }) + this._acpHandler.clearAcpState(threadId); + this._setStreamState(threadId, undefined); } + approveLatestToolRequest(threadId: string) { + const thread = this.state.allThreads[threadId]; + const lastMsg = thread?.messages[thread.messages.length - 1]; + if (!(lastMsg?.role === 'tool' && lastMsg.type === 'tool_request')) return; + + this._onExternalToolDecision.fire({ threadId, toolCallId: lastMsg.id, decision: 'approved' }); + + if (this._settingsService.state.globalSettings.useAcp === true) { + this._updateLatestTool(threadId, { + role: 'tool', + type: 'running_now', + name: lastMsg.name as any, + params: (lastMsg as any).params, + content: 'running...', + displayContent: 'running...', + result: null, + id: lastMsg.id, + rawParams: lastMsg.rawParams + }); + + const prevAny: any = this.streamState[threadId] as any; + const prevInterrupt = prevAny?.interrupt; + const prevLlmInfo = prevAny?.llmInfo; + + this._setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: prevLlmInfo?.displayContentSoFar ?? '', + reasoningSoFar: prevLlmInfo?.reasoningSoFar ?? '', + toolCallSoFar: null, + planSoFar: prevLlmInfo?.planSoFar + }, + interrupt: (prevInterrupt && typeof prevInterrupt !== 'string') ? prevInterrupt : Promise.resolve(() => { }) + }); + return; + } - // ---------- streaming ---------- + // non-ACP unchanged + this._notificationManager.wrapRunAgentToNotify( + this._executionEngine.runChatAgent({ + threadId, + callThisToolFirst: lastMsg as any, + ...this._currentModelSelectionProps() + }, this._threadAccess), + threadId, + () => this.state.currentThreadId, + () => this._getLastUserMessageContent(threadId), + (id: string) => this.switchToThread(id) + ); + } + rejectLatestToolRequest(threadId: string) { + const thread = this.state.allThreads[threadId]; + const lastMsg = thread?.messages[thread.messages.length - 1]; + if (!lastMsg || lastMsg.role !== 'tool') return; + const params = (lastMsg as any).params; - private _currentModelSelectionProps = () => { - // these settings should not change throughout the loop (eg anthropic breaks if you change its thinking mode and it's using tools) - const featureName: FeatureName = 'Chat' - const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] - const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined - return { modelSelection, modelSelectionOptions } + this._updateLatestTool(threadId, { + role: 'tool', type: 'rejected', params: params, name: lastMsg.name, + content: 'Tool call was rejected by the user.', displayContent: 'Tool call was rejected by the user.', + result: null, id: lastMsg.id, rawParams: lastMsg.rawParams + }); + this._setStreamState(threadId, undefined); + this._onExternalToolDecision.fire({ threadId, toolCallId: lastMsg.id, decision: 'rejected' }); } + skipLatestToolRequest(threadId: string) { + const thread = this.state.allThreads[threadId]; + const lastMsg: any = thread?.messages[thread.messages.length - 1]; + if (!lastMsg || lastMsg.role !== 'tool') return; + + // after Approve the tool becomes "running_now". + // In that case, "Skip" should behave like skipping a running tool. + if (lastMsg.type === 'running_now') { + this.skipRunningTool(threadId); + return; + } + + if (lastMsg.type !== 'tool_request') return; + const params = (lastMsg as any).params; - private _swapOutLatestStreamingToolWithResult = (threadId: string, tool: ChatMessage & { role: 'tool' }) => { - const messages = this.state.allThreads[threadId]?.messages - if (!messages) return false - const lastMsg = messages[messages.length - 1] - if (!lastMsg) return false + this._updateLatestTool(threadId, { + role: 'tool', + type: 'skipped', + name: lastMsg.name, + params, + id: lastMsg.id, + content: 'User skipped this tool.', + displayContent: 'User skipped this tool.', + result: null, + rawParams: lastMsg.rawParams + }); - if (lastMsg.role === 'tool' && lastMsg.type !== 'invalid_params') { - this._editMessageInThread(threadId, messages.length - 1, tool) - return true + // IMPORTANT: ACP permission resolution + this._onExternalToolDecision.fire({ threadId, toolCallId: lastMsg.id, decision: 'skipped' }); + + // ACP: do NOT cancel/clear ACP stream. builtin agent continues after permission resolution. + if (this._settingsService.state.globalSettings.useAcp === true) { + // Preserve interrupt if any + const prevAny: any = this.streamState[threadId] as any; + const prevInterrupt = prevAny?.interrupt; + const prevLlmInfo = prevAny?.llmInfo; + + this._setStreamState(threadId, { + isRunning: 'LLM', + llmInfo: { + displayContentSoFar: prevLlmInfo?.displayContentSoFar ?? '', + reasoningSoFar: prevLlmInfo?.reasoningSoFar ?? '', + toolCallSoFar: null, + planSoFar: prevLlmInfo?.planSoFar + }, + interrupt: (prevInterrupt && typeof prevInterrupt !== 'string') ? prevInterrupt : Promise.resolve(() => { }) + }); + return; } - return false - } - private _updateLatestTool = (threadId: string, tool: ChatMessage & { role: 'tool' }) => { - const swapped = this._swapOutLatestStreamingToolWithResult(threadId, tool) - if (swapped) return - this._addMessageToThread(threadId, tool) + + // non-ACP: old behavior + this._addMessageToThread(threadId, { + role: 'user', + content: `Skip ${lastMsg.name}. Continue with next steps.`, + displayContent: '', + selections: [], + state: { stagingSelections: [], isBeingEdited: false }, + hidden: true + }); + + this._notificationManager.wrapRunAgentToNotify( + this._executionEngine.runChatAgent({ threadId, ...this._currentModelSelectionProps() }, this._threadAccess), + threadId, + () => this.state.currentThreadId, + () => this._getLastUserMessageContent(threadId), + (id: string) => this.switchToThread(id) + ); } - approveLatestToolRequest(threadId: string) { - const thread = this.state.allThreads[threadId] - if (!thread) return // should never happen + skipRunningTool(threadId: string): void { + const stAny: any = this.streamState[threadId] as any; + const useAcp = this._settingsService.state.globalSettings.useAcp === true; + + // Determine toolCallId + best-effort metadata + let toolCallId = ''; + let toolName: any = undefined; + let toolParams: any = undefined; + let rawParams: any = undefined; + + // Case A: classic non-ACP "tool" stream state + if (stAny?.isRunning === 'tool' && stAny.toolInfo) { + toolCallId = String(stAny.toolInfo.id ?? ''); + toolName = stAny.toolInfo.toolName; + toolParams = stAny.toolInfo.toolParams; + rawParams = stAny.toolInfo.rawParams; + } + + // Case B: ACP often sits in isRunning === 'LLM' while a tool is running + if (!toolCallId && stAny?.isRunning === 'LLM' && stAny.llmInfo?.toolCallSoFar) { + toolCallId = String(stAny.llmInfo.toolCallSoFar.id ?? ''); + toolName = stAny.llmInfo.toolCallSoFar.name; + rawParams = stAny.llmInfo.toolCallSoFar.rawParams; + toolParams = rawParams; + } - const lastMsg = thread.messages[thread.messages.length - 1] - if (!(lastMsg.role === 'tool' && lastMsg.type === 'tool_request')) return // should never happen + // Case C: fallback by scanning last tool message with type "running_now" + if (!toolCallId) { + const t = this.state.allThreads[threadId]; + const lastTool = t?.messages?.slice()?.reverse()?.find((m: any) => m?.role === 'tool' && m?.type === 'running_now') as any; + if (lastTool) { + toolCallId = String(lastTool.id ?? ''); + toolName = lastTool.name; + toolParams = lastTool.params; + rawParams = lastTool.rawParams; + } + } - const callThisToolFirst: ToolMessage = lastMsg + if (!toolCallId) return; - this._wrapRunAgentToNotify( - this._runChatAgent({ callThisToolFirst, threadId, ...this._currentModelSelectionProps() }) - , threadId - ) - } - rejectLatestToolRequest(threadId: string) { - const thread = this.state.allThreads[threadId] - if (!thread) return // should never happen + // Mark skipped in engine (non-ACP uses this) + this._executionEngine.skippedToolCallIds.add(toolCallId); + + // Update the tool message even if it's not the latest one + this._editToolMessageById(threadId, toolCallId, { + type: 'skipped', + name: toolName, + params: toolParams, + rawParams, + content: 'Skipped', + displayContent: 'Skipped', + result: null + }); - const lastMsg = thread.messages[thread.messages.length - 1] + // ACP: do NOT fire permission decision here (permission already resolved). + // non-ACP: keep existing behavior (hidden user msg helps agent continue). + if (!useAcp) { + this._addMessageToThread(threadId, { + role: 'user', + content: `Skip ${toolName}. Continue with next steps.`, + displayContent: '', + selections: [], + state: { stagingSelections: [], isBeingEdited: false }, + hidden: true + }); + + // existing behavior (harmless if nobody listens) + this._onExternalToolDecision.fire({ threadId, toolCallId, decision: 'skipped' }); + } - let params: ToolCallParams - if (lastMsg.role === 'tool' && lastMsg.type !== 'invalid_params') { - params = lastMsg.params + // Best-effort interrupt/cancel + const interruptPromise = stAny?.interrupt; + if (interruptPromise && typeof interruptPromise !== 'string') { + interruptPromise.then((fn: any) => { + if (typeof fn === 'function') fn(); + }).catch(() => { }); } - else return + } - const { name, id, rawParams, mcpServerName } = lastMsg + // --- State & CRUD --- - const errorMessage = this.toolErrMsgs.rejected - this._updateLatestTool(threadId, { role: 'tool', type: 'rejected', params: params, name: name, content: errorMessage, result: null, id, rawParams, mcpServerName }) - this._setStreamState(threadId, undefined) + getCurrentThread(): ThreadType { + const thread = this.state.allThreads[this.state.currentThreadId]; + if (!thread) throw new Error(`Current thread should never be undefined`); + return thread; } - private _computeMCPServerOfToolName = (toolName: string) => { - return this._mcpService.getMCPTools()?.find(t => t.name === toolName)?.mcpServerName + switchToThread(threadId: string) { + this._setState({ currentThreadId: threadId }); } - async abortRunning(threadId: string) { - const thread = this.state.allThreads[threadId] - if (!thread) return // should never happen - - // add assistant message - if (this.streamState[threadId]?.isRunning === 'LLM') { - const { displayContentSoFar, reasoningSoFar, toolCallSoFar } = this.streamState[threadId].llmInfo - this._addMessageToThread(threadId, { role: 'assistant', displayContent: displayContentSoFar, reasoning: reasoningSoFar, anthropicReasoning: null }) - if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name, mcpServerName: this._computeMCPServerOfToolName(toolCallSoFar.name) }) - } - // add tool that's running - else if (this.streamState[threadId]?.isRunning === 'tool') { - const { toolName, toolParams, id, content: content_, rawParams, mcpServerName } = this.streamState[threadId].toolInfo - const content = content_ || this.toolErrMsgs.interrupted - this._updateLatestTool(threadId, { role: 'tool', name: toolName, params: toolParams, id, content, rawParams, type: 'rejected', result: null, mcpServerName }) - } - // reject the tool for the user if relevant - else if (this.streamState[threadId]?.isRunning === 'awaiting_user') { - this.rejectLatestToolRequest(threadId) - } - else if (this.streamState[threadId]?.isRunning === 'idle') { - // do nothing + openNewThread() { + for (const tid in this.state.allThreads) { + if (this.state.allThreads[tid]!.messages.length === 0) { + this.switchToThread(tid); + return; + } } + const newThread = newThreadObject(); + const newThreads = { ...this.state.allThreads, [newThread.id]: newThread }; + this._storeAllThreads(newThreads); + this._setState({ allThreads: newThreads, currentThreadId: newThread.id }); + } - this._addUserCheckpoint({ threadId }) + deleteThread(threadId: string): void { + const newThreads = { ...this.state.allThreads }; + delete newThreads[threadId]; + this._storeAllThreads(newThreads); + this._setState({ ...this.state, allThreads: newThreads }); + } - // interrupt any effects - const interrupt = await this.streamState[threadId]?.interrupt - if (typeof interrupt === 'function') - interrupt() + duplicateThread(threadId: string) { + const thread = this.state.allThreads[threadId]; + if (!thread) return; + const firstUser = thread.messages.find(m => m.role === 'user'); + if (!firstUser) { + this.openNewThread(); + return; + } - this._setStreamState(threadId, undefined) + const clonedMsg = deepClone(firstUser); + const newThread = { ...newThreadObject(), id: generateUuid(), messages: [clonedMsg] }; + const newThreads = { ...this.state.allThreads, [newThread.id]: newThread }; + this._storeAllThreads(newThreads); + this._setState({ allThreads: newThreads }); } + enqueueToolRequestFromAcp(threadId: string, req: { id: string; name: AnyToolName | string; rawParams: Record; params?: Record }): void { + this._acpHandler.enqueueToolRequestFromAcp(threadId, req, this._threadAccess); + } + // --- Helpers --- - private readonly toolErrMsgs = { - rejected: 'Tool call was rejected by the user.', - interrupted: 'Tool call was interrupted by the user.', - errWhenStringifying: (error: any) => `Tool call succeeded, but there was an error stringifying the output.\n${getErrorMessage(error)}` + jumpToCheckpointBeforeMessageIdx(opts: { threadId: string, messageIdx: number, jumpToUserModified: boolean }) { + this._checkpointManager.jumpToCheckpointBeforeMessageIdx(opts, this._threadAccess); } + generateCodespanLink(opts: { codespanStr: string, threadId: string }): Promise { + return this._codespanManager.generateCodespanLink(opts, () => this.state.allThreads[opts.threadId]?.messages || []) as any; + } - // private readonly _currentlyRunningToolInterruptor: { [threadId: string]: (() => void) | undefined } = {} + getCodespanLink({ codespanStr, messageIdx, threadId }: { codespanStr: string, messageIdx: number, threadId: string }) { + return this.state.allThreads[threadId]?.state.linksOfMessageIdx?.[messageIdx]?.[codespanStr]; + } + addCodespanLink({ newLinkText, newLinkLocation, messageIdx, threadId }: { newLinkText: string, newLinkLocation: CodespanLocationLink, messageIdx: number, threadId: string }) { + const thread = this.state.allThreads[threadId]; + if (!thread) return; + this._setThreadState(threadId, { + linksOfMessageIdx: { + ...thread.state.linksOfMessageIdx, + [messageIdx]: { ...thread.state.linksOfMessageIdx?.[messageIdx], [newLinkText]: newLinkLocation } + } + }); + } - // returns true when the tool call is waiting for user approval - private _runToolCall = async ( - threadId: string, - toolName: ToolName, - toolId: string, - mcpServerName: string | undefined, - opts: { preapproved: true, unvalidatedToolParams: RawToolParamsObj, validatedParams: ToolCallParams } | { preapproved: false, unvalidatedToolParams: RawToolParamsObj }, - ): Promise<{ awaitingUserApproval?: boolean, interrupted?: boolean }> => { + getRelativeStr(uri: URI) { + const folders = this._workspaceContextService.getWorkspace().folders.map(f => f.uri); + return normalizeSelectionRelativePath(uri, folders); + } - // compute these below - let toolParams: ToolCallParams - let toolResult: ToolResult - let toolResultStr: string + async focusCurrentChat() { + const t = this.getCurrentThread(); + const s = await t.state.mountedInfo?.whenMounted; + if (!this.isCurrentlyFocusingMessage()) s?.textAreaRef.current?.focus(); + } + async blurCurrentChat() { + const t = this.getCurrentThread(); + const s = await t.state.mountedInfo?.whenMounted; + if (!this.isCurrentlyFocusingMessage()) s?.textAreaRef.current?.blur(); + } + getCurrentFocusedMessageIdx() { + const t = this.getCurrentThread(); + if (t.state.focusedMessageIdx === undefined) return; + const m = t.messages[t.state.focusedMessageIdx]; + // FIX: safe check for role + if (m.role !== 'user' || !(m as any).state) return; + return t.state.focusedMessageIdx; + } + isCurrentlyFocusingMessage() { return this.getCurrentFocusedMessageIdx() !== undefined; } + setCurrentlyFocusedMessageIdx(idx: number | undefined) { + this._setThreadState(this.state.currentThreadId, { focusedMessageIdx: idx }); + } - // Check if it's a built-in tool - const isBuiltInTool = isABuiltinToolName(toolName) + addNewStagingSelection(newSelection: StagingSelectionItem) { + const focusedIdx = this.getCurrentFocusedMessageIdx(); + let selections: StagingSelectionItem[] = []; + let setSelections = (s: StagingSelectionItem[]) => { }; + if (focusedIdx === undefined) { + selections = this.getCurrentThreadState().stagingSelections; + setSelections = (s) => this.setCurrentThreadState({ stagingSelections: s }); + } else { + selections = this.getCurrentMessageState(focusedIdx).stagingSelections; + setSelections = (s) => this.setCurrentMessageState(focusedIdx, { stagingSelections: s }); + } - if (!opts.preapproved) { // skip this if pre-approved - // 1. validate tool params - try { - if (isBuiltInTool) { - const params = this._toolsService.validateParams[toolName](opts.unvalidatedToolParams) - toolParams = params - } - else { - toolParams = opts.unvalidatedToolParams - } + const findIndex = (arr: any[], item: any) => { + for (let i = 0; i < arr.length; i++) { + if (arr[i].uri.fsPath === item.uri.fsPath && arr[i].type === item.type) return i; } - catch (error) { - const errorMessage = getErrorMessage(error) - this._addMessageToThread(threadId, { role: 'tool', type: 'invalid_params', rawParams: opts.unvalidatedToolParams, result: null, name: toolName, content: errorMessage, id: toolId, mcpServerName }) - return {} - } - // once validated, add checkpoint for edit - if (toolName === 'edit_file') { this._addToolEditCheckpoint({ threadId, uri: (toolParams as BuiltinToolCallParams['edit_file']).uri }) } - if (toolName === 'rewrite_file') { this._addToolEditCheckpoint({ threadId, uri: (toolParams as BuiltinToolCallParams['rewrite_file']).uri }) } - - // 2. if tool requires approval, break from the loop, awaiting approval - - const approvalType = isBuiltInTool ? approvalTypeOfBuiltinToolName[toolName] : 'MCP tools' - if (approvalType) { - const autoApprove = this._settingsService.state.globalSettings.autoApprove[approvalType] - // add a tool_request because we use it for UI if a tool is loading (this should be improved in the future) - this._addMessageToThread(threadId, { role: 'tool', type: 'tool_request', content: '(Awaiting user permission...)', result: null, name: toolName, params: toolParams, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName }) - if (!autoApprove) { - return { awaitingUserApproval: true } - } - } - } - else { - toolParams = opts.validatedParams + return -1; + }; + const idx = findIndex(selections, newSelection); + if (idx !== -1) { + setSelections([...selections.slice(0, idx), newSelection, ...selections.slice(idx + 1)]); + } else { + setSelections([...selections, newSelection]); } + } + popStagingSelections(numPops: number = 1) { + const focusedIdx = this.getCurrentFocusedMessageIdx(); + let selections: StagingSelectionItem[] = []; + let setSelections = (s: StagingSelectionItem[]) => { }; + if (focusedIdx === undefined) { + selections = this.getCurrentThreadState().stagingSelections; + setSelections = (s) => this.setCurrentThreadState({ stagingSelections: s }); + } else { + selections = this.getCurrentMessageState(focusedIdx).stagingSelections; + setSelections = (s) => this.setCurrentMessageState(focusedIdx, { stagingSelections: s }); + } + setSelections(selections.slice(0, Math.max(0, selections.length - numPops))); + } + // FIX: safe access to state + getCurrentMessageState(idx: number) { + const m = this.getCurrentThread()?.messages?.[idx]; + if (m && m.role === 'user') return m.state; + return { stagingSelections: [], isBeingEdited: false } as any; + } + setCurrentMessageState(idx: number, newState: any) { this._setCurrentMessageState(newState, idx); } + getCurrentThreadState() { return this.getCurrentThread().state; } + setCurrentThreadState(newState: any) { this._setThreadState(this.state.currentThreadId, newState); } + editUserMessageAndStreamResponse: IChatThreadService['editUserMessageAndStreamResponse'] = async ({ userMessage, messageIdx, threadId }) => { + const thread = this.state.allThreads[threadId]; + if (!thread || thread.messages[messageIdx].role !== 'user') return; + const currSelns = thread.messages[messageIdx].state.stagingSelections || []; + const prevAttachments = thread.messages[messageIdx].attachments ?? undefined; + this._setState({ allThreads: { ...this.state.allThreads, [threadId]: { ...thread, messages: thread.messages.slice(0, messageIdx) } } }); + this.addUserMessageAndStreamResponse({ userMessage, _chatSelections: currSelns, attachments: prevAttachments, threadId }); + } + dismissStreamError(threadId: string) { this._setStreamState(threadId, undefined); } + dangerousSetState = (newState: ThreadsState) => { this.state = newState; this._onDidChangeCurrentThread.fire(); } + resetState = () => { this.state = { allThreads: {}, currentThreadId: null as unknown as string }; this.openNewThread(); this._onDidChangeCurrentThread.fire(); } - // 3. call the tool - // this._setStreamState(threadId, { isRunning: 'tool' }, 'merge') - const runningTool = { role: 'tool', type: 'running_now', name: toolName, params: toolParams, content: '(value not received yet...)', result: null, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName } as const - this._updateLatestTool(threadId, runningTool) - + // --- Private --- - let interrupted = false - let resolveInterruptor: (r: () => void) => void = () => { } - const interruptorPromise = new Promise<() => void>(res => { resolveInterruptor = res }) - try { + private _getLastUserMessageContent(threadId: string) { + const m = this.state.allThreads[threadId]?.messages; + if (!m) return undefined; + for (let i = m.length - 1; i >= 0; i--) { + if (m[i].role === 'user') return (m[i] as any).displayContent; + } + return undefined; + } - // set stream state - this._setStreamState(threadId, { isRunning: 'tool', interrupt: interruptorPromise, toolInfo: { toolName, toolParams, id: toolId, content: 'interrupted...', rawParams: opts.unvalidatedToolParams, mcpServerName } }) + private _setState(state: Partial, doNotRefreshMountInfo?: boolean) { + const newState = { ...this.state, ...state }; + this.state = newState; + this._onDidChangeCurrentThread.fire(); + + const tid = newState.currentThreadId; + const st = this.streamState[tid]; + if (st?.isRunning === undefined && !st?.error) { + const msgs = newState.allThreads[tid]?.messages; + const last = msgs?.[msgs.length - 1]; + if (last?.role === 'tool' && last.type === 'tool_request') this._setStreamState(tid, { isRunning: 'awaiting_user' }); + } - if (isBuiltInTool) { - const { result, interruptTool } = await this._toolsService.callTool[toolName](toolParams as any) - const interruptor = () => { interrupted = true; interruptTool?.() } - resolveInterruptor(interruptor) + if (doNotRefreshMountInfo) return; - toolResult = await result - } - else { - const mcpTools = this._mcpService.getMCPTools() - const mcpTool = mcpTools?.find(t => t.name === toolName) - if (!mcpTool) { throw new Error(`MCP tool ${toolName} not found`) } - - resolveInterruptor(() => { }) - - toolResult = (await this._mcpService.callMCPTool({ - serverName: mcpTool.mcpServerName ?? 'unknown_mcp_server', - toolName: toolName, - params: toolParams - })).result + let resolver: any; + const p = new Promise(r => resolver = r); + this._setThreadState(tid, { + mountedInfo: { + whenMounted: p, mountedIsResolvedRef: { current: false }, + _whenMountedResolver: (w: any) => { resolver(w); const m = this.state.allThreads[tid]?.state.mountedInfo; if (m) m.mountedIsResolvedRef.current = true; } } + }, true); + } - if (interrupted) { return { interrupted: true } } // the tool result is added where we interrupt, not here - } - catch (error) { - resolveInterruptor(() => { }) // resolve for the sake of it - if (interrupted) { return { interrupted: true } } // the tool result is added where we interrupt, not here - - const errorMessage = getErrorMessage(error) - this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName }) - return {} - } - - // 4. stringify the result to give to the LLM - try { - if (isBuiltInTool) { - toolResultStr = this._toolsService.stringOfResult[toolName](toolParams as any, toolResult as any) - } - // For MCP tools, handle the result based on its type - else { - toolResultStr = this._mcpService.stringifyResult(toolResult as RawMCPToolCall) - } - } catch (error) { - const errorMessage = this.toolErrMsgs.errWhenStringifying(error) - this._updateLatestTool(threadId, { role: 'tool', type: 'tool_error', params: toolParams, result: errorMessage, name: toolName, content: errorMessage, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName }) - return {} - } - - // 5. add to history and keep going - this._updateLatestTool(threadId, { role: 'tool', type: 'success', params: toolParams, result: toolResult, name: toolName, content: toolResultStr, id: toolId, rawParams: opts.unvalidatedToolParams, mcpServerName }) - return {} - }; - - - - - private async _runChatAgent({ - threadId, - modelSelection, - modelSelectionOptions, - callThisToolFirst, - }: { - threadId: string, - modelSelection: ModelSelection | null, - modelSelectionOptions: ModelSelectionOptions | undefined, - - callThisToolFirst?: ToolMessage & { type: 'tool_request' } - }) { - - - let interruptedWhenIdle = false - const idleInterruptor = Promise.resolve(() => { interruptedWhenIdle = true }) - // _runToolCall does not need setStreamState({idle}) before it, but it needs it after it. (handles its own setStreamState) - - // above just defines helpers, below starts the actual function - const { chatMode } = this._settingsService.state.globalSettings // should not change as we loop even if user changes it, so it goes here - const { overridesOfModel } = this._settingsService.state - - let nMessagesSent = 0 - let shouldSendAnotherMessage = true - let isRunningWhenEnd: IsRunningType = undefined - - // before enter loop, call tool - if (callThisToolFirst) { - const { interrupted } = await this._runToolCall(threadId, callThisToolFirst.name, callThisToolFirst.id, callThisToolFirst.mcpServerName, { preapproved: true, unvalidatedToolParams: callThisToolFirst.rawParams, validatedParams: callThisToolFirst.params }) - if (interrupted) { - this._setStreamState(threadId, undefined) - this._addUserCheckpoint({ threadId }) - - } - } - this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative, for clarity - - - // tool use loop - while (shouldSendAnotherMessage) { - // false by default each iteration - shouldSendAnotherMessage = false - isRunningWhenEnd = undefined - nMessagesSent += 1 - - this._setStreamState(threadId, { isRunning: 'idle', interrupt: idleInterruptor }) - - const chatMessages = this.state.allThreads[threadId]?.messages ?? [] - const { messages, separateSystemMessage } = await this._convertToLLMMessagesService.prepareLLMChatMessages({ - chatMessages, - modelSelection, - chatMode - }) - - if (interruptedWhenIdle) { - this._setStreamState(threadId, undefined) - return - } - - let shouldRetryLLM = true - let nAttempts = 0 - while (shouldRetryLLM) { - shouldRetryLLM = false - nAttempts += 1 - - type ResTypes = - | { type: 'llmDone', toolCall?: RawToolCallObj, info: { fullText: string, fullReasoning: string, anthropicReasoning: AnthropicReasoning[] | null } } - | { type: 'llmError', error?: { message: string; fullError: Error | null; } } - | { type: 'llmAborted' } - - let resMessageIsDonePromise: (res: ResTypes) => void // resolves when user approves this tool use (or if tool doesn't require approval) - const messageIsDonePromise = new Promise((res, rej) => { resMessageIsDonePromise = res }) - - const llmCancelToken = this._llmMessageService.sendLLMMessage({ - messagesType: 'chatMessages', - chatMode, - messages: messages, - modelSelection, - modelSelectionOptions, - overridesOfModel, - logging: { loggingName: `Chat - ${chatMode}`, loggingExtras: { threadId, nMessagesSent, chatMode } }, - separateSystemMessage: separateSystemMessage, - onText: ({ fullText, fullReasoning, toolCall }) => { - this._setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: fullText, reasoningSoFar: fullReasoning, toolCallSoFar: toolCall ?? null }, interrupt: Promise.resolve(() => { if (llmCancelToken) this._llmMessageService.abort(llmCancelToken) }) }) - }, - onFinalMessage: async ({ fullText, fullReasoning, toolCall, anthropicReasoning, }) => { - resMessageIsDonePromise({ type: 'llmDone', toolCall, info: { fullText, fullReasoning, anthropicReasoning } }) // resolve with tool calls - }, - onError: async (error) => { - resMessageIsDonePromise({ type: 'llmError', error: error }) - }, - onAbort: () => { - // stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it) - resMessageIsDonePromise({ type: 'llmAborted' }) - this._metricsService.capture('Agent Loop Done (Aborted)', { nMessagesSent, chatMode }) - }, - }) - - // mark as streaming - if (!llmCancelToken) { - this._setStreamState(threadId, { isRunning: undefined, error: { message: 'There was an unexpected error when sending your chat message.', fullError: null } }) - break - } - - this._setStreamState(threadId, { isRunning: 'LLM', llmInfo: { displayContentSoFar: '', reasoningSoFar: '', toolCallSoFar: null }, interrupt: Promise.resolve(() => this._llmMessageService.abort(llmCancelToken)) }) - const llmRes = await messageIsDonePromise // wait for message to complete - - // if something else started running in the meantime - if (this.streamState[threadId]?.isRunning !== 'LLM') { - // console.log('Chat thread interrupted by a newer chat thread', this.streamState[threadId]?.isRunning) - return - } - - // llm res aborted - if (llmRes.type === 'llmAborted') { - this._setStreamState(threadId, undefined) - return - } - // llm res error - else if (llmRes.type === 'llmError') { - // error, should retry - if (nAttempts < CHAT_RETRIES) { - shouldRetryLLM = true - this._setStreamState(threadId, { isRunning: 'idle', interrupt: idleInterruptor }) - await timeout(RETRY_DELAY) - if (interruptedWhenIdle) { - this._setStreamState(threadId, undefined) - return - } - else - continue // retry - } - // error, but too many attempts - else { - const { error } = llmRes - const { displayContentSoFar, reasoningSoFar, toolCallSoFar } = this.streamState[threadId].llmInfo - this._addMessageToThread(threadId, { role: 'assistant', displayContent: displayContentSoFar, reasoning: reasoningSoFar, anthropicReasoning: null }) - if (toolCallSoFar) this._addMessageToThread(threadId, { role: 'interrupted_streaming_tool', name: toolCallSoFar.name, mcpServerName: this._computeMCPServerOfToolName(toolCallSoFar.name) }) - - this._setStreamState(threadId, { isRunning: undefined, error }) - this._addUserCheckpoint({ threadId }) - return - } - } - - // llm res success - const { toolCall, info } = llmRes - - this._addMessageToThread(threadId, { role: 'assistant', displayContent: info.fullText, reasoning: info.fullReasoning, anthropicReasoning: info.anthropicReasoning }) - - this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative for clarity - - // call tool if there is one - if (toolCall) { - const mcpTools = this._mcpService.getMCPTools() - const mcpTool = mcpTools?.find(t => t.name === toolCall.name) - - const { awaitingUserApproval, interrupted } = await this._runToolCall(threadId, toolCall.name, toolCall.id, mcpTool?.mcpServerName, { preapproved: false, unvalidatedToolParams: toolCall.rawParams }) - if (interrupted) { - this._setStreamState(threadId, undefined) - return - } - if (awaitingUserApproval) { isRunningWhenEnd = 'awaiting_user' } - else { shouldSendAnotherMessage = true } - - this._setStreamState(threadId, { isRunning: 'idle', interrupt: 'not_needed' }) // just decorative, for clarity - } - - } // end while (attempts) - } // end while (send message) - - // if awaiting user approval, keep isRunning true, else end isRunning - this._setStreamState(threadId, { isRunning: isRunningWhenEnd }) - - // add checkpoint before the next user message - if (!isRunningWhenEnd) this._addUserCheckpoint({ threadId }) - - // capture number of messages sent - this._metricsService.capture('Agent Loop Done', { nMessagesSent, chatMode }) - } - - - private _addCheckpoint(threadId: string, checkpoint: CheckpointEntry) { - this._addMessageToThread(threadId, checkpoint) - // // update latest checkpoint idx to the one we just added - // const newThread = this.state.allThreads[threadId] - // if (!newThread) return // should never happen - // const currCheckpointIdx = newThread.messages.length - 1 - // this._setThreadState(threadId, { currCheckpointIdx: currCheckpointIdx }) - } - - - - private _editMessageInThread(threadId: string, messageIdx: number, newMessage: ChatMessage,) { - const { allThreads } = this.state - const oldThread = allThreads[threadId] - if (!oldThread) return // should never happen - // update state and store it - const newThreads = { - ...allThreads, - [oldThread.id]: { - ...oldThread, - lastModified: new Date().toISOString(), - messages: [ - ...oldThread.messages.slice(0, messageIdx), - newMessage, - ...oldThread.messages.slice(messageIdx + 1, Infinity), - ], - } - } - this._storeAllThreads(newThreads) - this._setState({ allThreads: newThreads }) // the current thread just changed (it had a message added to it) - } - - - private _getCheckpointInfo = (checkpointMessage: ChatMessage & { role: 'checkpoint' }, fsPath: string, opts: { includeUserModifiedChanges: boolean }) => { - const voidFileSnapshot = checkpointMessage.voidFileSnapshotOfURI ? checkpointMessage.voidFileSnapshotOfURI[fsPath] ?? null : null - if (!opts.includeUserModifiedChanges) { return { voidFileSnapshot, } } - - const userModifiedVoidFileSnapshot = fsPath in checkpointMessage.userModifications.voidFileSnapshotOfURI ? checkpointMessage.userModifications.voidFileSnapshotOfURI[fsPath] ?? null : null - return { voidFileSnapshot: userModifiedVoidFileSnapshot ?? voidFileSnapshot, } - } - - private _computeNewCheckpointInfo({ threadId }: { threadId: string }) { - const thread = this.state.allThreads[threadId] - if (!thread) return - - const lastCheckpointIdx = findLastIdx(thread.messages, (m) => m.role === 'checkpoint') ?? -1 - if (lastCheckpointIdx === -1) return - - const voidFileSnapshotOfURI: { [fsPath: string]: VoidFileSnapshot | undefined } = {} - - // add a change for all the URIs in the checkpoint history - const { lastIdxOfURI } = this._getCheckpointsBetween({ threadId, loIdx: 0, hiIdx: lastCheckpointIdx, }) ?? {} - for (const fsPath in lastIdxOfURI ?? {}) { - const { model } = this._voidModelService.getModelFromFsPath(fsPath) - if (!model) continue - const checkpoint2 = thread.messages[lastIdxOfURI[fsPath]] || null - if (!checkpoint2) continue - if (checkpoint2.role !== 'checkpoint') continue - const res = this._getCheckpointInfo(checkpoint2, fsPath, { includeUserModifiedChanges: false }) - if (!res) continue - const { voidFileSnapshot: oldVoidFileSnapshot } = res - - // if there was any change to the str or diffAreaSnapshot, update. rough approximation of equality, oldDiffAreasSnapshot === diffAreasSnapshot is not perfect - const voidFileSnapshot = this._editCodeService.getVoidFileSnapshot(URI.file(fsPath)) - if (oldVoidFileSnapshot === voidFileSnapshot) continue - voidFileSnapshotOfURI[fsPath] = voidFileSnapshot - } - - // // add a change for all user-edited files (that aren't in the history) - // for (const fsPath of this._userModifiedFilesToCheckInCheckpoints.keys()) { - // if (fsPath in lastIdxOfURI) continue // if already visisted, don't visit again - // const { model } = this._voidModelService.getModelFromFsPath(fsPath) - // if (!model) continue - // currStrOfFsPath[fsPath] = model.getValue(EndOfLinePreference.LF) - // } - - return { voidFileSnapshotOfURI } - } - - - private _addUserCheckpoint({ threadId }: { threadId: string }) { - const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {} - this._addCheckpoint(threadId, { - role: 'checkpoint', - type: 'user_edit', - voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {}, - userModifications: { voidFileSnapshotOfURI: {}, }, - }) - } - // call this right after LLM edits a file - private _addToolEditCheckpoint({ threadId, uri, }: { threadId: string, uri: URI }) { - const thread = this.state.allThreads[threadId] - if (!thread) return - const { model } = this._voidModelService.getModel(uri) - if (!model) return // should never happen - const diffAreasSnapshot = this._editCodeService.getVoidFileSnapshot(uri) - this._addCheckpoint(threadId, { - role: 'checkpoint', - type: 'tool_edit', - voidFileSnapshotOfURI: { [uri.fsPath]: diffAreasSnapshot }, - userModifications: { voidFileSnapshotOfURI: {} }, - }) - } - - - private _getCheckpointBeforeMessage = ({ threadId, messageIdx }: { threadId: string, messageIdx: number }): [CheckpointEntry, number] | undefined => { - const thread = this.state.allThreads[threadId] - if (!thread) return undefined - for (let i = messageIdx; i >= 0; i--) { - const message = thread.messages[i] - if (message.role === 'checkpoint') { - return [message, i] - } - } - return undefined - } - - private _getCheckpointsBetween({ threadId, loIdx, hiIdx }: { threadId: string, loIdx: number, hiIdx: number }) { - const thread = this.state.allThreads[threadId] - if (!thread) return { lastIdxOfURI: {} } // should never happen - const lastIdxOfURI: { [fsPath: string]: number } = {} - for (let i = loIdx; i <= hiIdx; i += 1) { - const message = thread.messages[i] - if (message?.role !== 'checkpoint') continue - for (const fsPath in message.voidFileSnapshotOfURI) { // do not include userModified.beforeStrOfURI here, jumping should not include those changes - lastIdxOfURI[fsPath] = i - } - } - return { lastIdxOfURI } - } - - private _readCurrentCheckpoint(threadId: string): [CheckpointEntry, number] | undefined { - const thread = this.state.allThreads[threadId] - if (!thread) return - - const { currCheckpointIdx } = thread.state - if (currCheckpointIdx === null) return - - const checkpoint = thread.messages[currCheckpointIdx] - if (!checkpoint) return - if (checkpoint.role !== 'checkpoint') return - return [checkpoint, currCheckpointIdx] - } - private _addUserModificationsToCurrCheckpoint({ threadId }: { threadId: string }) { - const { voidFileSnapshotOfURI } = this._computeNewCheckpointInfo({ threadId }) ?? {} - const res = this._readCurrentCheckpoint(threadId) - if (!res) return - const [checkpoint, checkpointIdx] = res - this._editMessageInThread(threadId, checkpointIdx, { - ...checkpoint, - userModifications: { voidFileSnapshotOfURI: voidFileSnapshotOfURI ?? {}, }, - }) - } - - - private _makeUsStandOnCheckpoint({ threadId }: { threadId: string }) { - const thread = this.state.allThreads[threadId] - if (!thread) return - if (thread.state.currCheckpointIdx === null) { - const lastMsg = thread.messages[thread.messages.length - 1] - if (lastMsg?.role !== 'checkpoint') - this._addUserCheckpoint({ threadId }) - this._setThreadState(threadId, { currCheckpointIdx: thread.messages.length - 1 }) - } - } - - jumpToCheckpointBeforeMessageIdx({ threadId, messageIdx, jumpToUserModified }: { threadId: string, messageIdx: number, jumpToUserModified: boolean }) { - - // if null, add a new temp checkpoint so user can jump forward again - this._makeUsStandOnCheckpoint({ threadId }) - - const thread = this.state.allThreads[threadId] - if (!thread) return - if (this.streamState[threadId]?.isRunning) return - - const c = this._getCheckpointBeforeMessage({ threadId, messageIdx }) - if (c === undefined) return // should never happen - - const fromIdx = thread.state.currCheckpointIdx - if (fromIdx === null) return // should never happen - - const [_, toIdx] = c - if (toIdx === fromIdx) return - - // console.log(`going from ${fromIdx} to ${toIdx}`) - - // update the user's checkpoint - this._addUserModificationsToCurrCheckpoint({ threadId }) - - /* -if undoing - -A,B,C are all files. -x means a checkpoint where the file changed. - -A B C D E F G H I - x x x x x x <-- you can't always go up to find the "before" version; sometimes you need to go down - | | | | | | x ---x-|-|-|-x---x-|----- <-- to - | | | | x x - | | x x | - | | | | -----x-|---x-x------- <-- from - x - -We need to revert anything that happened between to+1 and from. -**We do this by finding the last x from 0...`to` for each file and applying those contents.** -We only need to do it for files that were edited since `to`, ie files between to+1...from. -*/ - if (toIdx < fromIdx) { - const { lastIdxOfURI } = this._getCheckpointsBetween({ threadId, loIdx: toIdx + 1, hiIdx: fromIdx }) - - const idxes = function* () { - for (let k = toIdx; k >= 0; k -= 1) { // first go up - yield k - } - for (let k = toIdx + 1; k < thread.messages.length; k += 1) { // then go down - yield k - } - } - - for (const fsPath in lastIdxOfURI) { - // find the first instance of this file starting at toIdx (go up to latest file; if there is none, go down) - for (const k of idxes()) { - const message = thread.messages[k] - if (message.role !== 'checkpoint') continue - const res = this._getCheckpointInfo(message, fsPath, { includeUserModifiedChanges: jumpToUserModified }) - if (!res) continue - const { voidFileSnapshot } = res - if (!voidFileSnapshot) continue - this._editCodeService.restoreVoidFileSnapshot(URI.file(fsPath), voidFileSnapshot) - break - } - } - } - - /* -if redoing - -A B C D E F G H I J - x x x x x x x - | | | | | | x x x ---x-|-|-|-x---x-|-|--- <-- from - | | | | x x - | | x x | - | | | | -----x-|---x-x-----|--- <-- to - x x - - -We need to apply latest change for anything that happened between from+1 and to. -We only need to do it for files that were edited since `from`, ie files between from+1...to. -*/ - if (toIdx > fromIdx) { - const { lastIdxOfURI } = this._getCheckpointsBetween({ threadId, loIdx: fromIdx + 1, hiIdx: toIdx }) - for (const fsPath in lastIdxOfURI) { - // apply lowest down content for each uri - for (let k = toIdx; k >= fromIdx + 1; k -= 1) { - const message = thread.messages[k] - if (message.role !== 'checkpoint') continue - const res = this._getCheckpointInfo(message, fsPath, { includeUserModifiedChanges: jumpToUserModified }) - if (!res) continue - const { voidFileSnapshot } = res - if (!voidFileSnapshot) continue - this._editCodeService.restoreVoidFileSnapshot(URI.file(fsPath), voidFileSnapshot) - break - } - } - } - - this._setThreadState(threadId, { currCheckpointIdx: toIdx }) - } - - - private _wrapRunAgentToNotify(p: Promise, threadId: string) { - const notify = ({ error }: { error: string | null }) => { - const thread = this.state.allThreads[threadId] - if (!thread) return - const userMsg = findLast(thread.messages, m => m.role === 'user') - if (!userMsg) return - if (userMsg.role !== 'user') return - const messageContent = truncate(userMsg.displayContent, 50, '...') - - this._notificationService.notify({ - severity: error ? Severity.Warning : Severity.Info, - message: error ? `Error: ${error} ` : `A new Chat result is ready.`, - source: messageContent, - sticky: true, - actions: { - primary: [{ - id: 'void.goToChat', - enabled: true, - label: `Jump to Chat`, - tooltip: '', - class: undefined, - run: () => { - this.switchToThread(threadId) - // scroll to bottom - this.state.allThreads[threadId]?.state.mountedInfo?.whenMounted.then(m => { - m.scrollToBottom() - }) - } - }] - }, - }) - } - - p.then(() => { - if (threadId !== this.state.currentThreadId) notify({ error: null }) - }).catch((e) => { - if (threadId !== this.state.currentThreadId) notify({ error: getErrorMessage(e) }) - throw e - }) - } - - dismissStreamError(threadId: string): void { - this._setStreamState(threadId, undefined) - } - - - private async _addUserMessageAndStreamResponse({ userMessage, _chatSelections, threadId }: { userMessage: string, _chatSelections?: StagingSelectionItem[], threadId: string }) { - const thread = this.state.allThreads[threadId] - if (!thread) return // should never happen - - // interrupt existing stream - if (this.streamState[threadId]?.isRunning) { - await this.abortRunning(threadId) - } - - // add dummy before this message to keep checkpoint before user message idea consistent - if (thread.messages.length === 0) { - this._addUserCheckpoint({ threadId }) - } - - - // add user's message to chat history - const instructions = userMessage - const currSelns: StagingSelectionItem[] = _chatSelections ?? thread.state.stagingSelections - - const userMessageContent = await chat_userMessageContent(instructions, currSelns, { directoryStrService: this._directoryStringService, fileService: this._fileService }) // user message + names of files (NOT content) - const userHistoryElt: ChatMessage = { role: 'user', content: userMessageContent, displayContent: instructions, selections: currSelns, state: defaultMessageState } - this._addMessageToThread(threadId, userHistoryElt) - - this._setThreadState(threadId, { currCheckpointIdx: null }) // no longer at a checkpoint because started streaming - - this._wrapRunAgentToNotify( - this._runChatAgent({ threadId, ...this._currentModelSelectionProps(), }), - threadId, - ) - - // scroll to bottom - this.state.allThreads[threadId]?.state.mountedInfo?.whenMounted.then(m => { - m.scrollToBottom() - }) - } - - - async addUserMessageAndStreamResponse({ userMessage, _chatSelections, threadId }: { userMessage: string, _chatSelections?: StagingSelectionItem[], threadId: string }) { - const thread = this.state.allThreads[threadId]; - if (!thread) return - - // if there's a current checkpoint, delete all messages after it - if (thread.state.currCheckpointIdx !== null) { - const checkpointIdx = thread.state.currCheckpointIdx; - const newMessages = thread.messages.slice(0, checkpointIdx + 1); - - // Update the thread with truncated messages - const newThreads = { - ...this.state.allThreads, - [threadId]: { - ...thread, - lastModified: new Date().toISOString(), - messages: newMessages, - } - }; - this._storeAllThreads(newThreads); - this._setState({ allThreads: newThreads }); - } - - // Now call the original method to add the user message and stream the response - await this._addUserMessageAndStreamResponse({ userMessage, _chatSelections, threadId }); - - } - - editUserMessageAndStreamResponse: IChatThreadService['editUserMessageAndStreamResponse'] = async ({ userMessage, messageIdx, threadId }) => { - - const thread = this.state.allThreads[threadId] - if (!thread) return // should never happen - - if (thread.messages?.[messageIdx]?.role !== 'user') { - throw new Error(`Error: editing a message with role !=='user'`) - } - - // get prev and curr selections before clearing the message - const currSelns = thread.messages[messageIdx].state.stagingSelections || [] // staging selections for the edited message - - // clear messages up to the index - const slicedMessages = thread.messages.slice(0, messageIdx) - this._setState({ - allThreads: { - ...this.state.allThreads, - [thread.id]: { - ...thread, - messages: slicedMessages - } - } - }) - - // re-add the message and stream it - this._addUserMessageAndStreamResponse({ userMessage, _chatSelections: currSelns, threadId }) - } - - // ---------- the rest ---------- - - private _getAllSeenFileURIs(threadId: string) { - const thread = this.state.allThreads[threadId] - if (!thread) return [] - - const fsPathsSet = new Set() - const uris: URI[] = [] - const addURI = (uri: URI) => { - if (!fsPathsSet.has(uri.fsPath)) uris.push(uri) - fsPathsSet.add(uri.fsPath) - uris.push(uri) - } - - for (const m of thread.messages) { - // URIs of user selections - if (m.role === 'user') { - for (const sel of m.selections ?? []) { - addURI(sel.uri) - } - } - // URIs of files that have been read - else if (m.role === 'tool' && m.type === 'success' && m.name === 'read_file') { - const params = m.params as BuiltinToolCallParams['read_file'] - addURI(params.uri) - } - } - return uris - } - - - - getRelativeStr = (uri: URI) => { - const isInside = this._workspaceContextService.isInsideWorkspace(uri) - if (isInside) { - const f = this._workspaceContextService.getWorkspace().folders.find(f => uri.fsPath.startsWith(f.uri.fsPath)) - if (f) { return uri.fsPath.replace(f.uri.fsPath, '') } - else { return undefined } - } - else { - return undefined - } - } - - - // gets the location of codespan link so the user can click on it - generateCodespanLink: IChatThreadService['generateCodespanLink'] = async ({ codespanStr: _codespanStr, threadId }) => { - - // process codespan to understand what we are searching for - // TODO account for more complicated patterns eg `ITextEditorService.openEditor()` - const functionOrMethodPattern = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; // `fUnCt10n_name` - const functionParensPattern = /^([^\s(]+)\([^)]*\)$/; // `functionName( args )` - - let target = _codespanStr // the string to search for - let codespanType: 'file-or-folder' | 'function-or-class' - if (target.includes('.') || target.includes('/')) { - - codespanType = 'file-or-folder' - target = _codespanStr - - } else if (functionOrMethodPattern.test(target)) { - - codespanType = 'function-or-class' - target = _codespanStr - - } else if (functionParensPattern.test(target)) { - const match = target.match(functionParensPattern) - if (match && match[1]) { - - codespanType = 'function-or-class' - target = match[1] - - } - else { return null } - } - else { - return null - } - - // get history of all AI and user added files in conversation + store in reverse order (MRU) - const prevUris = this._getAllSeenFileURIs(threadId).reverse() - - if (codespanType === 'file-or-folder') { - const doesUriMatchTarget = (uri: URI) => uri.path.includes(target) - - // check if any prevFiles are the `target` - for (const [idx, uri] of prevUris.entries()) { - if (doesUriMatchTarget(uri)) { - - // shorten it - - // TODO make this logic more general - const prevUriStrs = prevUris.map(uri => uri.fsPath) - const shortenedUriStrs = shorten(prevUriStrs) - let displayText = shortenedUriStrs[idx] - const ellipsisIdx = displayText.lastIndexOf('…/'); - if (ellipsisIdx >= 0) { - displayText = displayText.slice(ellipsisIdx + 2) - } - - return { uri, displayText } - } - } - - // else search codebase for `target` - let uris: URI[] = [] - try { - const { result } = await this._toolsService.callTool['search_pathnames_only']({ query: target, includePattern: null, pageNumber: 0 }) - const { uris: uris_ } = await result - uris = uris_ - } catch (e) { - return null - } - - for (const [idx, uri] of uris.entries()) { - if (doesUriMatchTarget(uri)) { - - // TODO make this logic more general - const prevUriStrs = prevUris.map(uri => uri.fsPath) - const shortenedUriStrs = shorten(prevUriStrs) - let displayText = shortenedUriStrs[idx] - const ellipsisIdx = displayText.lastIndexOf('…/'); - if (ellipsisIdx >= 0) { - displayText = displayText.slice(ellipsisIdx + 2) - } - - - return { uri, displayText } - } - } - - } - - - if (codespanType === 'function-or-class') { - - - // check all prevUris for the target - for (const uri of prevUris) { - - const modelRef = await this._voidModelService.getModelSafe(uri) - const { model } = modelRef - if (!model) continue - - const matches = model.findMatches( - target, - false, // searchOnlyEditableRange - false, // isRegex - true, // matchCase - null, //' ', // wordSeparators - true // captureMatches - ); - - const firstThree = matches.slice(0, 3); - - // take first 3 occurences, attempt to goto definition on them - for (const match of firstThree) { - const position = new Position(match.range.startLineNumber, match.range.startColumn); - const definitionProviders = this._languageFeaturesService.definitionProvider.ordered(model); - - for (const provider of definitionProviders) { - - const _definitions = await provider.provideDefinition(model, position, CancellationToken.None); - - if (!_definitions) continue; - - const definitions = Array.isArray(_definitions) ? _definitions : [_definitions]; - - for (const definition of definitions) { - - return { - uri: definition.uri, - selection: { - startLineNumber: definition.range.startLineNumber, - startColumn: definition.range.startColumn, - endLineNumber: definition.range.endLineNumber, - endColumn: definition.range.endColumn, - }, - displayText: _codespanStr, - }; - - // const defModelRef = await this._textModelService.createModelReference(definition.uri); - // const defModel = defModelRef.object.textEditorModel; - - // try { - // const symbolProviders = this._languageFeaturesService.documentSymbolProvider.ordered(defModel); - - // for (const symbolProvider of symbolProviders) { - // const symbols = await symbolProvider.provideDocumentSymbols( - // defModel, - // CancellationToken.None - // ); - - // if (symbols) { - // const symbol = symbols.find(s => { - // const symbolRange = s.range; - // return symbolRange.startLineNumber <= definition.range.startLineNumber && - // symbolRange.endLineNumber >= definition.range.endLineNumber && - // (symbolRange.startLineNumber !== definition.range.startLineNumber || symbolRange.startColumn <= definition.range.startColumn) && - // (symbolRange.endLineNumber !== definition.range.endLineNumber || symbolRange.endColumn >= definition.range.endColumn); - // }); - - // // if we got to a class/function get the full range and return - // if (symbol?.kind === SymbolKind.Function || symbol?.kind === SymbolKind.Method || symbol?.kind === SymbolKind.Class) { - // return { - // uri: definition.uri, - // selection: { - // startLineNumber: definition.range.startLineNumber, - // startColumn: definition.range.startColumn, - // endLineNumber: definition.range.endLineNumber, - // endColumn: definition.range.endColumn, - // } - // }; - // } - // } - // } - // } finally { - // defModelRef.dispose(); - // } - } - } - } - } - - // unlike above do not search codebase (doesnt make sense) - - } - - return null - + private _setStreamState(threadId: string, state: ThreadStreamState[string]) { + this.streamState[threadId] = state; + this._onDidChangeStreamState.fire({ threadId }); } - getCodespanLink({ codespanStr, messageIdx, threadId }: { codespanStr: string, messageIdx: number, threadId: string }): CodespanLocationLink | undefined { - const thread = this.state.allThreads[threadId] - if (!thread) return undefined; - - const links = thread.state.linksOfMessageIdx?.[messageIdx] - if (!links) return undefined; - - const link = links[codespanStr] - - return link + private _setThreadState(threadId: string, state: Partial, noRefresh?: boolean) { + const t = this.state.allThreads[threadId]; + if (!t) return; + this._setState({ allThreads: { ...this.state.allThreads, [t.id]: { ...t, state: { ...t.state, ...state } } } }, noRefresh); } - async addCodespanLink({ newLinkText, newLinkLocation, messageIdx, threadId }: { newLinkText: string, newLinkLocation: CodespanLocationLink, messageIdx: number, threadId: string }) { - const thread = this.state.allThreads[threadId] - if (!thread) return - + private _setCurrentMessageState(state: any, idx: number) { + const tid = this.state.currentThreadId; + const t = this.state.allThreads[tid]; + if (!t) return; this._setState({ - allThreads: { - ...this.state.allThreads, - [threadId]: { - ...thread, - state: { - ...thread.state, - linksOfMessageIdx: { - ...thread.state.linksOfMessageIdx, - [messageIdx]: { - ...thread.state.linksOfMessageIdx?.[messageIdx], - [newLinkText]: newLinkLocation - } - } - } - + ...this.state.allThreads, [tid]: { + ...t, messages: t.messages.map((m, i) => i === idx && m.role === 'user' ? { ...m, state: { ...m.state, ...state } } : m) } } - }) - } - - - getCurrentThread(): ThreadType { - const state = this.state - const thread = state.allThreads[state.currentThreadId] - if (!thread) throw new Error(`Current thread should never be undefined`) - return thread - } - - getCurrentFocusedMessageIdx() { - const thread = this.getCurrentThread() - - // get the focusedMessageIdx - const focusedMessageIdx = thread.state.focusedMessageIdx - if (focusedMessageIdx === undefined) return; - - // check that the message is actually being edited - const focusedMessage = thread.messages[focusedMessageIdx] - if (focusedMessage.role !== 'user') return; - if (!focusedMessage.state) return; - - return focusedMessageIdx - } - - isCurrentlyFocusingMessage() { - return this.getCurrentFocusedMessageIdx() !== undefined - } - - switchToThread(threadId: string) { - this._setState({ currentThreadId: threadId }) - } - - - openNewThread() { - // if a thread with 0 messages already exists, switch to it - const { allThreads: currentThreads } = this.state - for (const threadId in currentThreads) { - if (currentThreads[threadId]!.messages.length === 0) { - // switch to the existing empty thread and exit - this.switchToThread(threadId) - return - } - } - // otherwise, start a new thread - const newThread = newThreadObject() - - // update state - const newThreads: ChatThreads = { - ...currentThreads, - [newThread.id]: newThread - } - this._storeAllThreads(newThreads) - this._setState({ allThreads: newThreads, currentThreadId: newThread.id }) + }); } - - deleteThread(threadId: string): void { - const { allThreads: currentThreads } = this.state - - // delete the thread - const newThreads = { ...currentThreads }; - delete newThreads[threadId]; - - // store the updated threads + private _addMessageToThread(threadId: string, message: ChatMessage) { + const t = this.state.allThreads[threadId]; + if (!t) return; + const newThreads = { ...this.state.allThreads, [t.id]: { ...t, lastModified: new Date().toISOString(), messages: [...t.messages, message] } }; this._storeAllThreads(newThreads); - this._setState({ ...this.state, allThreads: newThreads }) + this._setState({ allThreads: newThreads }); } - duplicateThread(threadId: string) { - const { allThreads: currentThreads } = this.state - const threadToDuplicate = currentThreads[threadId] - if (!threadToDuplicate) return - const newThread = { - ...deepClone(threadToDuplicate), - id: generateUuid(), - } + private _editMessageInThread(threadId: string, idx: number, msg: ChatMessage) { + const t = this.state.allThreads[threadId]; + if (!t) return; const newThreads = { - ...currentThreads, - [newThread.id]: newThread, - } - this._storeAllThreads(newThreads) - this._setState({ allThreads: newThreads }) - } - - - private _addMessageToThread(threadId: string, message: ChatMessage) { - const { allThreads } = this.state - const oldThread = allThreads[threadId] - if (!oldThread) return // should never happen - // update state and store it - const newThreads = { - ...allThreads, - [oldThread.id]: { - ...oldThread, - lastModified: new Date().toISOString(), - messages: [ - ...oldThread.messages, - message - ], + ...this.state.allThreads, [t.id]: { + ...t, lastModified: new Date().toISOString(), messages: [...t.messages.slice(0, idx), msg, ...t.messages.slice(idx + 1)] } - } - this._storeAllThreads(newThreads) - this._setState({ allThreads: newThreads }) // the current thread just changed (it had a message added to it) + }; + this._storeAllThreads(newThreads); + this._setState({ allThreads: newThreads }); } - // sets the currently selected message (must be undefined if no message is selected) - setCurrentlyFocusedMessageIdx(messageIdx: number | undefined) { - - const threadId = this.state.currentThreadId - const thread = this.state.allThreads[threadId] - if (!thread) return + private _updateLatestTool(threadId: string, tool: any) { + const msgs = this.state.allThreads[threadId]?.messages; - this._setState({ - allThreads: { - ...this.state.allThreads, - [threadId]: { - ...thread, - state: { - ...thread.state, - focusedMessageIdx: messageIdx, - } + const safe = (v: any, max = 500) => { + try { + const s = typeof v === 'string' ? v : JSON.stringify(v); + return s.length > max ? s.slice(0, max) + '…' : s; + } catch { + try { + const s = String(v); + return s.length > max ? s.slice(0, max) + '…' : s; + } catch { + return ''; } } - }) - - // // when change focused message idx, jump - do not jump back when click edit, too confusing. - // if (messageIdx !== undefined) - // this.jumpToCheckpointBeforeMessageIdx({ threadId, messageIdx, jumpToUserModified: true }) - } - - - addNewStagingSelection(newSelection: StagingSelectionItem): void { - - const focusedMessageIdx = this.getCurrentFocusedMessageIdx() - - // set the selections to the proper value - let selections: StagingSelectionItem[] = [] - let setSelections = (s: StagingSelectionItem[]) => { } - - if (focusedMessageIdx === undefined) { - selections = this.getCurrentThreadState().stagingSelections - setSelections = (s: StagingSelectionItem[]) => this.setCurrentThreadState({ stagingSelections: s }) - } else { - selections = this.getCurrentMessageState(focusedMessageIdx).stagingSelections - setSelections = (s) => this.setCurrentMessageState(focusedMessageIdx, { stagingSelections: s }) - } - - // if matches with existing selection, overwrite (since text may change) - const idx = findStagingSelectionIndex(selections, newSelection) - if (idx !== null && idx !== -1) { - setSelections([ - ...selections!.slice(0, idx), - newSelection, - ...selections!.slice(idx + 1, Infinity) - ]) - } - // if no match, add it - else { - setSelections([...(selections ?? []), newSelection]) - } - } - - - // Pops the staging selections from the current thread's state - popStagingSelections(numPops: number): void { - - numPops = numPops ?? 1; - - const focusedMessageIdx = this.getCurrentFocusedMessageIdx() + }; - // set the selections to the proper value - let selections: StagingSelectionItem[] = [] - let setSelections = (s: StagingSelectionItem[]) => { } + const payloadBase = (() => { + const id = String((tool as any)?.id ?? ''); + const name = String((tool as any)?.name ?? ''); + const type = String((tool as any)?.type ?? ''); + const contentLen = typeof (tool as any)?.content === 'string' ? (tool as any).content.length : null; + const displayLen = typeof (tool as any)?.displayContent === 'string' ? (tool as any).displayContent.length : null; + const resultKeys = (tool as any)?.result && typeof (tool as any).result === 'object' + ? Object.keys((tool as any).result) + : []; + const resultOutLen = + typeof (tool as any)?.result?.output === 'string' + ? (tool as any).result.output.length + : null; + + return { + threadId, + toolId: id, + toolName: name, + toolType: type, + contentLen, + displayLen, + resultKeys, + resultOutLen, + contentPreview: safe((tool as any)?.content), + resultOutputPreview: safe((tool as any)?.result?.output), + }; + })(); + + //update by id anywhere in the tail, not only "last message is tool" + if (msgs && tool && typeof tool === 'object') { + const wantId = String((tool as any).id ?? ''); + + for (let i = msgs.length - 1; i >= 0; i--) { + const m: any = msgs[i] as any; + if (m && m.role === 'tool' && m.type !== 'invalid_params' && String(m.id ?? '') === wantId) { + this._logService.debug('[Void][ChatThreadService][_updateLatestTool][EDIT]', JSON.stringify({ + ...payloadBase, + foundIdx: i, + prevType: String(m.type ?? ''), + prevContentLen: typeof m.content === 'string' ? m.content.length : null, + prevResultKeys: (m.result && typeof m.result === 'object') ? Object.keys(m.result) : [], + prevResultOutLen: typeof m?.result?.output === 'string' ? m.result.output.length : null, + })); + + this._editMessageInThread(threadId, i, tool); + return; + } + } - if (focusedMessageIdx === undefined) { - selections = this.getCurrentThreadState().stagingSelections - setSelections = (s: StagingSelectionItem[]) => this.setCurrentThreadState({ stagingSelections: s }) - } else { - selections = this.getCurrentMessageState(focusedMessageIdx).stagingSelections - setSelections = (s) => this.setCurrentMessageState(focusedMessageIdx, { stagingSelections: s }) + this._logService.debug('[Void][ChatThreadService][_updateLatestTool][ADD_NO_MATCH]', JSON.stringify({ + ...payloadBase, + reason: 'no message with same toolId found', + msgsLen: msgs.length, + lastMsgRole: msgs.length ? String((msgs[msgs.length - 1] as any)?.role ?? '') : null, + })); } - setSelections([ - ...selections.slice(0, selections.length - numPops) - ]) - + this._logService.debug('[Void][ChatThreadService][_updateLatestTool][ADD_FALLBACK]', JSON.stringify(payloadBase)); + this._addMessageToThread(threadId, tool); } - // set message.state - private _setCurrentMessageState(state: Partial, messageIdx: number): void { - - const threadId = this.state.currentThreadId - const thread = this.state.allThreads[threadId] - if (!thread) return - - this._setState({ - allThreads: { - ...this.state.allThreads, - [threadId]: { - ...thread, - messages: thread.messages.map((m, i) => - i === messageIdx && m.role === 'user' ? { - ...m, - state: { - ...m.state, - ...state - }, - } : m - ) - } - } - }) - + private _accumulateTokenUsage(threadId: string, next: LLMTokenUsage) { + const t = this.state.allThreads[threadId]; + const prev = t?.state?.tokenUsageSession; + const result = prev ? { + input: prev.input + next.input, cacheCreation: prev.cacheCreation + next.cacheCreation, + cacheRead: prev.cacheRead + next.cacheRead, output: prev.output + next.output + } : { ...next }; + this._setThreadState(threadId, { tokenUsageSession: result, tokenUsageLastRequest: next }); } - // set thread.state - private _setThreadState(threadId: string, state: Partial, doNotRefreshMountInfo?: boolean): void { - const thread = this.state.allThreads[threadId] - if (!thread) return - - this._setState({ - allThreads: { - ...this.state.allThreads, - [thread.id]: { - ...thread, - state: { - ...thread.state, - ...state - } - } - } - }, doNotRefreshMountInfo) - + private _currentModelSelectionProps() { + const featureName = 'Chat'; + const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName]; + const modelSelectionOptions = modelSelection + ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] + : undefined; + return { modelSelection, modelSelectionOptions }; } - - // closeCurrentStagingSelectionsInThread = () => { - // const currThread = this.getCurrentThreadState() - - // // close all stagingSelections - // const closedStagingSelections = currThread.stagingSelections.map(s => ({ ...s, state: { ...s.state, isOpened: false } })) - - // const newThread = currThread - // newThread.stagingSelections = closedStagingSelections - - // this.setCurrentThreadState(newThread) - - // } - - // closeCurrentStagingSelectionsInMessage: IChatThreadService['closeCurrentStagingSelectionsInMessage'] = ({ messageIdx }) => { - // const currMessage = this.getCurrentMessageState(messageIdx) - - // // close all stagingSelections - // const closedStagingSelections = currMessage.stagingSelections.map(s => ({ ...s, state: { ...s.state, isOpened: false } })) - - // const newMessage = currMessage - // newMessage.stagingSelections = closedStagingSelections - - // this.setCurrentMessageState(messageIdx, newMessage) - - // } - - - - getCurrentThreadState = () => { - const currentThread = this.getCurrentThread() - return currentThread.state - } - setCurrentThreadState = (newState: Partial) => { - this._setThreadState(this.state.currentThreadId, newState) + private _readAllThreads(): ChatThreads | null { + const s = this._storageService.get(THREAD_STORAGE_KEY, StorageScope.APPLICATION); + if (!s) return null; + return JSON.parse(s, (k, v) => (v && typeof v === 'object' && v.$mid === 1) ? URI.from(v) : v); } - // gets `staging` and `setStaging` of the currently focused element, given the index of the currently selected message (or undefined if no message is selected) - - getCurrentMessageState(messageIdx: number): UserMessageState { - const currMessage = this.getCurrentThread()?.messages?.[messageIdx] - if (!currMessage || currMessage.role !== 'user') return defaultMessageState - return currMessage.state - } - setCurrentMessageState(messageIdx: number, newState: Partial) { - const currMessage = this.getCurrentThread()?.messages?.[messageIdx] - if (!currMessage || currMessage.role !== 'user') return - this._setCurrentMessageState(newState, messageIdx) + private _storeAllThreads(threads: ChatThreads) { + this._storageService.store(THREAD_STORAGE_KEY, JSON.stringify(threads), StorageScope.APPLICATION, StorageTarget.USER); } - - - } registerSingleton(IChatThreadService, ChatThreadService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/contextGatheringService.ts b/src/vs/workbench/contrib/void/browser/contextGatheringService.ts index 6de8205c327..1f4b725a045 100644 --- a/src/vs/workbench/contrib/void/browser/contextGatheringService.ts +++ b/src/vs/workbench/contrib/void/browser/contextGatheringService.ts @@ -1,13 +1,18 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + import { CancellationToken } from '../../../../base/common/cancellation.js'; -import { Position } from '../../../../editor/common/core/position.js'; -import { DocumentSymbol, SymbolKind } from '../../../../editor/common/languages.js'; -import { ITextModel } from '../../../../editor/common/model.js'; -import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; +import { Position } from '../../../../editor/common/language/core/position.js'; +import { DocumentSymbol, SymbolKind } from '../../../../editor/common/language/languages.js'; +import { ITextModel } from '../../../../editor/common/language/model.js'; +import { ILanguageFeaturesService } from '../../../../editor/common/language/services/languageFeatures.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { Range, IRange } from '../../../../editor/common/core/range.js'; +import { Range, IRange } from '../../../../editor/common/language/core/range.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { URI } from '../../../../base/common/uri.js'; @@ -23,6 +28,8 @@ interface IVisitedInterval { endLine: number; } +type DefinitionSymbol = DocumentSymbol & { uri: URI }; + export interface IContextGatheringService { readonly _serviceBrand: undefined; updateCache(model: ITextModel, pos: Position): Promise; @@ -65,10 +72,11 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer public async updateCache(model: ITextModel, pos: Position): Promise { const snippets = new Set(); + const visitedDefinitionKeys = new Set(); this._snippetIntervals = []; // Reset intervals for new cache update - await this._gatherNearbySnippets(model, pos, this._NUM_LINES, 3, snippets, this._snippetIntervals); - await this._gatherParentSnippets(model, pos, this._NUM_LINES, 3, snippets, this._snippetIntervals); + await this._gatherNearbySnippets(model, pos, this._NUM_LINES, 3, snippets, this._snippetIntervals, visitedDefinitionKeys); + await this._gatherParentSnippets(model, pos, this._NUM_LINES, 3, snippets, this._snippetIntervals, visitedDefinitionKeys); // Convert to array and filter overlapping snippets this._cache = Array.from(snippets); @@ -141,7 +149,8 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer numLines: number, depth: number, snippets: Set, - visited: IVisitedInterval[] + visited: IVisitedInterval[], + visitedDefinitionKeys: Set ): Promise { if (depth <= 0) return; @@ -152,14 +161,23 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer this._addSnippetIfNotOverlapping(model, range, snippets, visited); const symbols = await this._getSymbolsNearPosition(model, pos, numLines); + const seenSymbolKeys = new Set(); for (const sym of symbols) { + const symbolKey = this._symbolKey(model.uri, sym); + if (seenSymbolKeys.has(symbolKey)) continue; + seenSymbolKeys.add(symbolKey); + const defs = await this._getDefinitionSymbols(model, sym); for (const def of defs) { + const definitionKey = this._definitionKey(def); + if (visitedDefinitionKeys.has(definitionKey)) continue; + visitedDefinitionKeys.add(definitionKey); + const defModel = this._modelService.getModel(def.uri); if (defModel) { const defPos = new Position(def.range.startLineNumber, def.range.startColumn); this._addSnippetIfNotOverlapping(defModel, def.range, snippets, visited); - await this._gatherNearbySnippets(defModel, defPos, numLines, depth - 1, snippets, visited); + await this._gatherNearbySnippets(defModel, defPos, numLines, depth - 1, snippets, visited, visitedDefinitionKeys); } } } @@ -171,7 +189,8 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer numLines: number, depth: number, snippets: Set, - visited: IVisitedInterval[] + visited: IVisitedInterval[], + visitedDefinitionKeys: Set ): Promise { if (depth <= 0) return; @@ -182,20 +201,29 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer this._addSnippetIfNotOverlapping(model, containerRange, snippets, visited); const symbols = await this._getSymbolsNearRange(model, containerRange, numLines); + const seenSymbolKeys = new Set(); for (const sym of symbols) { + const symbolKey = this._symbolKey(model.uri, sym); + if (seenSymbolKeys.has(symbolKey)) continue; + seenSymbolKeys.add(symbolKey); + const defs = await this._getDefinitionSymbols(model, sym); for (const def of defs) { + const definitionKey = this._definitionKey(def); + if (visitedDefinitionKeys.has(definitionKey)) continue; + visitedDefinitionKeys.add(definitionKey); + const defModel = this._modelService.getModel(def.uri); if (defModel) { const defPos = new Position(def.range.startLineNumber, def.range.startColumn); this._addSnippetIfNotOverlapping(defModel, def.range, snippets, visited); - await this._gatherNearbySnippets(defModel, defPos, numLines, depth - 1, snippets, visited); + await this._gatherNearbySnippets(defModel, defPos, numLines, depth - 1, snippets, visited, visitedDefinitionKeys); } } } const containerPos = new Position(containerRange.startLineNumber, containerRange.startColumn); - await this._gatherParentSnippets(model, containerPos, numLines, depth - 1, snippets, visited); + await this._gatherParentSnippets(model, containerPos, numLines, depth - 1, snippets, visited, visitedDefinitionKeys); } private _isRangeVisited(uri: string, startLine: number, endLine: number, visited: IVisitedInterval[]): boolean { @@ -237,29 +265,36 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer } // Also check reference providers. const refProviders = this._langFeaturesService.referenceProvider.ordered(model); + if (!refProviders.length) return symbols; + + const seenRefSymbols = new Set(); for (let line = range.startLineNumber; line <= range.endLineNumber; line++) { const content = model.getLineContent(line); - const words = content.match(/[a-zA-Z_]\w*/g) || []; - for (const word of words) { - const startColumn = content.indexOf(word) + 1; + const wordsWithColumn = this._getDistinctWordPositions(content); + for (const { word, startColumn } of wordsWithColumn) { const pos = new Position(line, startColumn); if (!this._positionInRange(pos, range)) continue; for (const provider of refProviders) { try { const refs = await provider.provideReferences(model, pos, { includeDeclaration: true }, CancellationToken.None); - if (refs) { - const filtered = refs.filter(ref => this._rangesIntersect(ref.range, range)); - for (const ref of filtered) { - symbols.push({ - name: word, - detail: '', - kind: SymbolKind.Variable, - range: ref.range, - selectionRange: ref.range, - children: [], - tags: [] - }); - } + if (!refs) continue; + + for (const ref of refs) { + if (!this._rangesIntersect(ref.range, range)) continue; + + const refKey = this._referenceKey(model.uri, ref.range, word); + if (seenRefSymbols.has(refKey)) continue; + seenRefSymbols.add(refKey); + + symbols.push({ + name: word, + detail: '', + kind: SymbolKind.Variable, + range: ref.range, + selectionRange: ref.range, + children: [], + tags: [] + }); } } catch (e) { console.warn('Reference provider error:', e); @@ -297,26 +332,68 @@ class ContextGatheringService extends Disposable implements IContextGatheringSer (pos.lineNumber !== range.endLineNumber || pos.column <= range.endColumn); } + private _getDistinctWordPositions(content: string): { word: string; startColumn: number }[] { + const wordPositions: { word: string; startColumn: number }[] = []; + const seenWords = new Set(); + const wordRegex = /[a-zA-Z_]\w*/g; + + let match: RegExpExecArray | null; + while ((match = wordRegex.exec(content)) !== null) { + const word = match[0]; + if (seenWords.has(word)) continue; + seenWords.add(word); + + wordPositions.push({ + word, + startColumn: match.index + 1 + }); + } + return wordPositions; + } + + private _rangeKey(range: IRange): string { + return `${range.startLineNumber}:${range.startColumn}:${range.endLineNumber}:${range.endColumn}`; + } + + private _symbolKey(uri: URI, symbol: DocumentSymbol): string { + return `${uri.toString()}#${symbol.name}#${this._rangeKey(symbol.range)}`; + } + + private _definitionKey(def: DefinitionSymbol): string { + return `${def.uri.toString()}#${this._rangeKey(def.range)}`; + } + + private _referenceKey(uri: URI, range: IRange, word: string): string { + return `${uri.toString()}#${word}#${this._rangeKey(range)}`; + } + // Get definition symbols for a given symbol. - private async _getDefinitionSymbols(model: ITextModel, symbol: DocumentSymbol): Promise<(DocumentSymbol & { uri: URI })[]> { + private async _getDefinitionSymbols(model: ITextModel, symbol: DocumentSymbol): Promise { const pos = new Position(symbol.range.startLineNumber, symbol.range.startColumn); const providers = this._langFeaturesService.definitionProvider.ordered(model); - const defs: (DocumentSymbol & { uri: URI })[] = []; + const defs: DefinitionSymbol[] = []; + const seenDefinitionKeys = new Set(); for (const provider of providers) { try { const res = await provider.provideDefinition(model, pos, CancellationToken.None); if (res) { const links = Array.isArray(res) ? res : [res]; - defs.push(...links.map(link => ({ - name: symbol.name, - detail: symbol.detail, - kind: symbol.kind, - range: link.range, - selectionRange: link.range, - children: [], - tags: symbol.tags || [], - uri: link.uri // Now keeping it as URI instead of converting to string - }))); + for (const link of links) { + const definitionKey = `${link.uri.toString()}#${this._rangeKey(link.range)}`; + if (seenDefinitionKeys.has(definitionKey)) continue; + seenDefinitionKeys.add(definitionKey); + + defs.push({ + name: symbol.name, + detail: symbol.detail, + kind: symbol.kind, + range: link.range, + selectionRange: link.range, + children: [], + tags: symbol.tags || [], + uri: link.uri // Now keeping it as URI instead of converting to string + }); + } } } catch (e) { console.warn('Definition provider error:', e); diff --git a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts index 94545c0d751..fcb0ac6996a 100644 --- a/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts +++ b/src/vs/workbench/contrib/void/browser/convertToLLMMessageService.ts @@ -1,51 +1,77 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + import { Disposable } from '../../../../base/common/lifecycle.js'; import { deepClone } from '../../../../base/common/objects.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { ChatMessage } from '../common/chatThreadServiceTypes.js'; -import { getIsReasoningEnabledState, getReservedOutputTokenSpace, getModelCapabilities } from '../common/modelCapabilities.js'; -import { reParsedToolXMLString, chat_systemMessage } from '../common/prompt/prompts.js'; -import { AnthropicLLMChatMessage, AnthropicReasoning, GeminiLLMChatMessage, LLMChatMessage, LLMFIMMessage, OpenAILLMChatMessage, RawToolParamsObj } from '../common/sendLLMMessageTypes.js'; -import { IVoidSettingsService } from '../common/voidSettingsService.js'; -import { ChatMode, FeatureName, ModelSelection, ProviderName } from '../common/voidSettingsTypes.js'; -import { IDirectoryStrService } from '../common/directoryStrService.js'; -import { ITerminalToolService } from './terminalToolService.js'; +import { AnyToolName, ChatAttachment, ChatMessage } from '../../../../platform/void/common/chatThreadServiceTypes.js'; +import { + getIsReasoningEnabledState, + setDynamicModelService, + getModelCapabilities, + VoidStaticModelInfo, + getReservedOutputTokenSpace, + getModelApiConfiguration +} from '../../../../platform/void/common/modelInference.js'; +import { isAToolName, reParsedToolXMLString, chat_systemMessage, ToolName, SYSTEM_PROMPT_OVERRIDE } from '../common/prompt/prompts.js'; +import { + AnthropicLLMChatMessage, + AnthropicReasoning, + AnthropicUserBlock, + GeminiLLMChatMessage, + LLMChatMessage, + LLMFIMMessage, + OpenAILLMChatMessage, + OpenAITextPart, + OpenAIImageURLPart, + RawToolParamsObj +} from '../../../../platform/void/common/sendLLMMessageTypes.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; +import { + ChatMode, + specialToolFormat, + supportsSystemMessage, + FeatureName, + ModelSelection, + ProviderName +} from '../../../../platform/void/common/voidSettingsTypes.js'; import { IVoidModelService } from '../common/voidModelService.js'; import { URI } from '../../../../base/common/uri.js'; -import { EndOfLinePreference } from '../../../../editor/common/model.js'; -import { ToolName } from '../common/toolsServiceTypes.js'; -import { IMCPService } from '../common/mcpService.js'; - -export const EMPTY_MESSAGE = '(empty message)' +import { EndOfLinePreference } from '../../../../editor/common/language/model.js'; +import { ILocalPtyService } from '../../../../platform/terminal/common/terminal.js' +import { IDynamicProviderRegistryService } from '../../../../platform/void/common/providerReg.js'; +import { IDynamicModelService } from '../../../../platform/void/common/dynamicModelService.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { encodeBase64 } from '../../../../base/common/buffer.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +export const EMPTY_MESSAGE = '' +type ResolvedChatAttachment = ChatAttachment & { dataBase64?: string }; type SimpleLLMMessage = { role: 'tool'; content: string; id: string; - name: ToolName; + name: AnyToolName; rawParams: RawToolParamsObj; } | { role: 'user'; content: string; + attachments?: ResolvedChatAttachment[]; } | { role: 'assistant'; content: string; anthropicReasoning: AnthropicReasoning[] | null; } - - const CHARS_PER_TOKEN = 4 // assume abysmal chars per token const TRIM_TO_LEN = 120 - - - // convert messages as if about to send to openai /* reference - https://platform.openai.com/docs/guides/function-calling#function-calling-steps @@ -69,7 +95,25 @@ openai on developer system message - https://cdn.openai.com/spec/model-spec-2024 */ -const prepareMessages_openai_tools = (messages: SimpleLLMMessage[]): AnthropicOrOpenAILLMMessage[] => { +const buildOpenAIUserContent = (msg: Extract): string | (OpenAITextPart | OpenAIImageURLPart)[] => { + const atts = msg.attachments ?? []; + if (!atts.length) return msg.content; + + const parts: (OpenAITextPart | OpenAIImageURLPart)[] = []; + const trimmed = msg.content.trim(); + if (trimmed) { + parts.push({ type: 'text', text: trimmed }); + } + for (const att of atts) { + if (!att.dataBase64) continue; + const mime = att.mimeType || 'image/png'; + const dataUrl = `data:${mime};base64,${att.dataBase64}`; + parts.push({ type: 'image_url', image_url: { url: dataUrl } }); + } + return parts.length ? parts : msg.content; +}; + +const prepareOpenAIToolsMessages = (messages: SimpleLLMMessage[]): AnthropicOrOpenAILLMMessage[] => { const newMessages: OpenAILLMChatMessage[] = []; @@ -77,7 +121,15 @@ const prepareMessages_openai_tools = (messages: SimpleLLMMessage[]): AnthropicOr const currMsg = messages[i] if (currMsg.role !== 'tool') { - newMessages.push(currMsg) + if (currMsg.role === 'user') { + newMessages.push({ role: 'user', content: buildOpenAIUserContent(currMsg) }); + } else if (currMsg.role === 'assistant') { + newMessages.push({ role: 'assistant', content: currMsg.content }); + } else { + // allow-any-unicode-next-line + // Fallback for unexpected roles – treat as simple user message + newMessages.push({ role: 'user', content: (currMsg as any).content }); + } continue } @@ -138,65 +190,87 @@ user: ...content, result(id, content) type AnthropicOrOpenAILLMMessage = AnthropicLLMChatMessage | OpenAILLMChatMessage -const prepareMessages_anthropic_tools = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): AnthropicOrOpenAILLMMessage[] => { - const newMessages: (AnthropicLLMChatMessage | (SimpleLLMMessage & { role: 'tool' }))[] = messages; +const buildAnthropicUserContent = (msg: Extract): string | AnthropicUserBlock[] => { + const atts = msg.attachments ?? []; + if (!atts.length) return msg.content; + + const parts: AnthropicUserBlock[] = []; + const trimmed = msg.content.trim(); + if (trimmed) { + parts.push({ type: 'text', text: trimmed }); + } + for (const att of atts) { + if (!att.dataBase64) continue; + // Restrict to Anthropic-allowed image media types + let mediaType: 'image/jpeg' | 'image/png' | 'image/gif' | 'image/webp' = 'image/png'; + if (att.mimeType === 'image/jpeg' || att.mimeType === 'image/jpg') mediaType = 'image/jpeg'; + else if (att.mimeType === 'image/gif') mediaType = 'image/gif'; + else if (att.mimeType === 'image/webp') mediaType = 'image/webp'; + parts.push({ + type: 'image', + source: { type: 'base64', media_type: mediaType, data: att.dataBase64 }, + }); + } + return parts.length ? parts : msg.content; +}; + +const prepareAnthropicToolsMessages = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): AnthropicOrOpenAILLMMessage[] => { + const newMessages: AnthropicLLMChatMessage[] = []; for (let i = 0; i < messages.length; i += 1) { - const currMsg = messages[i] + const currMsg = messages[i]; - // add anthropic reasoning if (currMsg.role === 'assistant') { if (currMsg.anthropicReasoning && supportsAnthropicReasoning) { - const content = currMsg.content - newMessages[i] = { - role: 'assistant', - content: content ? [...currMsg.anthropicReasoning, { type: 'text' as const, text: content }] : currMsg.anthropicReasoning - } - } - else { - newMessages[i] = { + const content = currMsg.content; + newMessages.push({ role: 'assistant', - content: currMsg.content, - // strip away anthropicReasoning - } + content: content + ? [...currMsg.anthropicReasoning, { type: 'text' as const, text: content }] + : currMsg.anthropicReasoning + }); + } else { + newMessages.push({ role: 'assistant', content: currMsg.content }); } - continue + continue; } if (currMsg.role === 'user') { - newMessages[i] = { + newMessages.push({ role: 'user', - content: currMsg.content, - } - continue + content: buildAnthropicUserContent(currMsg), + }); + continue; } if (currMsg.role === 'tool') { - // add anthropic tools - const prevMsg = 0 <= i - 1 && i - 1 <= newMessages.length ? newMessages[i - 1] : undefined + const prevMsg = newMessages.length ? newMessages[newMessages.length - 1] : undefined; - // make it so the assistant called the tool if (prevMsg?.role === 'assistant') { - if (typeof prevMsg.content === 'string') prevMsg.content = [{ type: 'text', text: prevMsg.content }] - prevMsg.content.push({ type: 'tool_use', id: currMsg.id, name: currMsg.name, input: currMsg.rawParams }) + if (typeof prevMsg.content === 'string') { + prevMsg.content = [{ type: 'text', text: prevMsg.content }]; + } + (prevMsg.content as any[]).push({ + type: 'tool_use', + id: currMsg.id, + name: currMsg.name as string, + input: currMsg.rawParams, + }); } - // turn each tool into a user message with tool results at the end - newMessages[i] = { + newMessages.push({ role: 'user', - content: [{ type: 'tool_result', tool_use_id: currMsg.id, content: currMsg.content }] - } - continue + content: [{ type: 'tool_result', tool_use_id: currMsg.id, content: currMsg.content }], + }); + continue; } - } - // we just removed the tools - return newMessages as AnthropicLLMChatMessage[] + return newMessages; } -const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): AnthropicOrOpenAILLMMessage[] => { +const prepareXMLToolsMessages = (messages: SimpleLLMMessage[], supportsAnthropicReasoning: boolean): AnthropicOrOpenAILLMMessage[] => { const llmChatMessages: AnthropicOrOpenAILLMMessage[] = []; for (let i = 0; i < messages.length; i += 1) { @@ -208,7 +282,7 @@ const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthrop // if called a tool (message after it), re-add its XML to the message // alternatively, could just hold onto the original output, but this way requires less piping raw strings everywhere let content: AnthropicOrOpenAILLMMessage['content'] = c.content - if (next?.role === 'tool') { + if (next?.role === 'tool' && isAToolName(next.name)) { content = `${content}\n\n${reParsedToolXMLString(next.name, next.rawParams)}` } @@ -226,6 +300,16 @@ const prepareMessages_XML_tools = (messages: SimpleLLMMessage[], supportsAnthrop if (c.role === 'tool') c.content = `<${c.name}_result>\n${c.content}\n` + // NOTE: For XML tool format we cannot send true image parts, so we append + // a lightweight textual placeholder for any attachments. + if (c.role === 'user' && (c as any).attachments && (c as any).attachments.length) { + const atts = (c as any).attachments as ResolvedChatAttachment[]; + const placeholderLines = atts.map(att => `Attached image: ${att.name}`); + c.content = c.content + ? `${c.content}\n\n${placeholderLines.join('\n')}` + : placeholderLines.join('\n'); + } + if (llmChatMessages.length === 0 || llmChatMessages[llmChatMessages.length - 1].role !== 'user') llmChatMessages.push({ role: 'user', @@ -254,8 +338,8 @@ const prepareOpenAIOrAnthropicMessages = ({ messages: SimpleLLMMessage[], systemMessage: string, aiInstructions: string, - supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated', - specialToolFormat: 'openai-style' | 'anthropic-style' | undefined, + supportsSystemMessage: supportsSystemMessage, + specialToolFormat: specialToolFormat, supportsAnthropicReasoning: boolean, contextWindow: number, reservedOutputTokenSpace: number | null | undefined, @@ -370,14 +454,14 @@ const prepareOpenAIOrAnthropicMessages = ({ // SYSTEM MESSAGE HACK: we shifted (removed) the system message role, so now SimpleLLMMessage[] is valid let llmChatMessages: AnthropicOrOpenAILLMMessage[] = [] - if (!specialToolFormat) { // XML tool behavior - llmChatMessages = prepareMessages_XML_tools(messages as SimpleLLMMessage[], supportsAnthropicReasoning) + if (specialToolFormat === 'disabled') { // XML tool behavior + llmChatMessages = prepareXMLToolsMessages(messages as SimpleLLMMessage[], supportsAnthropicReasoning) } else if (specialToolFormat === 'anthropic-style') { - llmChatMessages = prepareMessages_anthropic_tools(messages as SimpleLLMMessage[], supportsAnthropicReasoning) + llmChatMessages = prepareAnthropicToolsMessages(messages as SimpleLLMMessage[], supportsAnthropicReasoning) } else if (specialToolFormat === 'openai-style') { - llmChatMessages = prepareMessages_openai_tools(messages as SimpleLLMMessage[]) + llmChatMessages = prepareOpenAIToolsMessages(messages as SimpleLLMMessage[]) } const llmMessages = llmChatMessages @@ -420,7 +504,6 @@ const prepareOpenAIOrAnthropicMessages = ({ else { // allowed to be empty if has a tool in it or following it if (currMsg.content.find(c => c.type === 'tool_result' || c.type === 'tool_use')) { - currMsg.content = currMsg.content.filter(c => !(c.type === 'text' && !c.text)) as any continue } if (nextMsg?.role === 'tool') continue @@ -440,8 +523,6 @@ const prepareOpenAIOrAnthropicMessages = ({ } - - type GeminiUserPart = (GeminiLLMChatMessage & { role: 'user' })['parts'][0] type GeminiModelPart = (GeminiLLMChatMessage & { role: 'model' })['parts'][0] const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { @@ -457,6 +538,9 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { return { text: c.text } } else if (c.type === 'tool_use') { + if (!isAToolName(c.name)) { + return { text: JSON.stringify({ tool_use: c }) } + } latestToolName = c.name return { functionCall: { id: c.id, name: c.name, args: c.input } } } @@ -475,9 +559,15 @@ const prepareGeminiMessages = (messages: AnthropicLLMChatMessage[]) => { return { text: c.text } } else if (c.type === 'tool_result') { - if (!latestToolName) return null + if (!latestToolName) { + return { text: JSON.stringify({ tool_result: c }) } + } return { functionResponse: { id: c.tool_use_id, name: latestToolName, response: { output: c.content } } } } + else if ((c as any).type === 'image' && (c as any).source?.type === 'base64') { + const src = (c as any).source as { media_type: string; data: string }; + return { inlineData: { mimeType: src.media_type, data: src.data } } as any; + } else return null }).filter(m => !!m) return { role: 'user', parts, } @@ -495,30 +585,25 @@ const prepareMessages = (params: { messages: SimpleLLMMessage[], systemMessage: string, aiInstructions: string, - supportsSystemMessage: false | 'system-role' | 'developer-role' | 'separated', - specialToolFormat: 'openai-style' | 'anthropic-style' | 'gemini-style' | undefined, + supportsSystemMessage: supportsSystemMessage, + specialToolFormat: specialToolFormat, supportsAnthropicReasoning: boolean, contextWindow: number, reservedOutputTokenSpace: number | null | undefined, providerName: ProviderName }): { messages: LLMChatMessage[], separateSystemMessage: string | undefined } => { - - const specialFormat = params.specialToolFormat // this is just for ts stupidness - - // if need to convert to gemini style of messaes, do that (treat as anthropic style, then convert to gemini style) - if (params.providerName === 'gemini' || specialFormat === 'gemini-style') { - const res = prepareOpenAIOrAnthropicMessages({ ...params, specialToolFormat: specialFormat === 'gemini-style' ? 'anthropic-style' : undefined }) + // if need to convert to gemini style of messages, do that (treat as anthropic style, then convert to gemini style) + if (params.providerName === 'gemini' || params.specialToolFormat === 'gemini-style') { + const res = prepareOpenAIOrAnthropicMessages({ ...params, specialToolFormat: 'anthropic-style' }) const messages = res.messages as AnthropicLLMChatMessage[] const messages2 = prepareGeminiMessages(messages) return { messages: messages2, separateSystemMessage: res.separateSystemMessage } } - return prepareOpenAIOrAnthropicMessages({ ...params, specialToolFormat: specialFormat }) + const res = prepareOpenAIOrAnthropicMessages({ ...params }) + return { messages: res.messages, separateSystemMessage: res.separateSystemMessage } } - - - export interface IConvertToLLMMessageService { readonly _serviceBrand: undefined; prepareLLMSimpleMessages: (opts: { simpleMessages: SimpleLLMMessage[], systemMessage: string, modelSelection: ModelSelection | null, featureName: FeatureName }) => { messages: LLMChatMessage[], separateSystemMessage: string | undefined } @@ -529,20 +614,48 @@ export interface IConvertToLLMMessageService { export const IConvertToLLMMessageService = createDecorator('ConvertToLLMMessageService'); + class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMessageService { _serviceBrand: undefined; constructor( - @IModelService private readonly modelService: IModelService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IEditorService private readonly editorService: IEditorService, - @IDirectoryStrService private readonly directoryStrService: IDirectoryStrService, - @ITerminalToolService private readonly terminalToolService: ITerminalToolService, + @ILocalPtyService private readonly ptyHostService: ILocalPtyService, @IVoidSettingsService private readonly voidSettingsService: IVoidSettingsService, @IVoidModelService private readonly voidModelService: IVoidModelService, - @IMCPService private readonly mcpService: IMCPService, + @IDynamicProviderRegistryService private readonly dynamicRegistry: IDynamicProviderRegistryService, + @IDynamicModelService private readonly dynamicModelService: IDynamicModelService, + @IFileService private readonly fileService: IFileService, + @ILogService private readonly logService: ILogService, ) { - super() + super(); + try { + setDynamicModelService(this.dynamicModelService); + void this.dynamicModelService.initialize?.(); + } catch { + // ignore + } + } + + // Resolve explicit user override for supportsSystemMessage, case-insensitive provider and flexible model key + private _getUserSupportsSystemMessageOverride(providerName: ProviderName, modelName: string): supportsSystemMessage | undefined { + try { + const overrides = this.voidSettingsService.state.overridesOfModel; + if (!overrides) return undefined; + const provKey = Object.keys(overrides).find(k => k.toLowerCase() === String(providerName).toLowerCase()); + if (!provKey) return undefined; + const byModel = (overrides as any)[provKey] as Record; + const exact = byModel?.[modelName]?.supportsSystemMessage; + if (exact !== undefined) return exact; + if (modelName.includes('/')) { + const after = modelName.slice(modelName.indexOf('/') + 1); + const alt = byModel?.[after]?.supportsSystemMessage; + if (alt !== undefined) return alt; + } + return undefined; + } catch { + return undefined; + } } // Read .voidrules files from workspace folders @@ -551,59 +664,86 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess const workspaceFolders = this.workspaceContextService.getWorkspace().folders; let voidRules = ''; for (const folder of workspaceFolders) { - const uri = URI.joinPath(folder.uri, '.voidrules') - const { model } = this.voidModelService.getModel(uri) - if (!model) continue + const uri = URI.joinPath(folder.uri, '.voidrules'); + const { model } = this.voidModelService.getModel(uri); + if (!model) continue; voidRules += model.getValue(EndOfLinePreference.LF) + '\n\n'; } return voidRules.trim(); - } - catch (e) { - return '' + } catch { + return ''; } } - // Get combined AI instructions from settings and .voidrules files - private _getCombinedAIInstructions(): string { - const globalAIInstructions = this.voidSettingsService.state.globalSettings.aiInstructions; - const voidRulesFileContent = this._getVoidRulesFileContents(); - - const ans: string[] = [] - if (globalAIInstructions) ans.push(globalAIInstructions) - if (voidRulesFileContent) ans.push(voidRulesFileContent) - return ans.join('\n\n') - } + private _findCustomProviderSlugForModel(fullId: string): string | null { + try { + const cps = this.voidSettingsService.state.customProviders || {}; + if (fullId.includes('/')) { + const prefix = fullId.split('/')[0]; + if (cps[prefix]) return prefix; + } - // system message - private _generateChatMessagesSystemMessage = async (chatMode: ChatMode, specialToolFormat: 'openai-style' | 'anthropic-style' | 'gemini-style' | undefined) => { - const workspaceFolders = this.workspaceContextService.getWorkspace().folders.map(f => f.uri.fsPath) + for (const [slug, entry] of Object.entries(cps)) { + const list: string[] = Array.isArray(entry?.models) ? entry.models : []; + if (list.includes(fullId)) return slug; + } + } catch { + // ignore + } + return null; + } - const openedURIs = this.modelService.getModels().filter(m => m.isAttachedToEditor()).map(m => m.uri.fsPath) || []; - const activeURI = this.editorService.activeEditor?.resource?.fsPath; + private async _getDynamicCapsForSelection(_providerName: ProviderName, modelName: string): Promise | undefined> { + const slug = this._findCustomProviderSlugForModel(modelName); + if (!slug) return undefined; - const directoryStr = await this.directoryStrService.getAllDirectoriesStr({ - cutOffMessage: chatMode === 'agent' || chatMode === 'gather' ? - `...Directories string cut off, use tools to read more...` - : `...Directories string cut off, ask user for more if necessary...` - }) + await this.dynamicRegistry.initialize?.(); - const includeXMLToolDefinitions = !specialToolFormat - const mcpTools = this.mcpService.getMCPTools() + let argModelId = modelName; + if (slug.toLowerCase() !== 'openrouter') { + const i = modelName.indexOf('/'); + argModelId = i >= 0 ? modelName.slice(i + 1) : modelName; + } - const persistentTerminalIDs = this.terminalToolService.listPersistentTerminalIds() - const systemMessage = chat_systemMessage({ workspaceFolders, openedURIs, directoryStr, activeURI, persistentTerminalIDs, chatMode, mcpTools, includeXMLToolDefinitions }) - return systemMessage + try { + return await this.dynamicRegistry.getEffectiveModelCapabilities(slug, argModelId); + } catch { + return undefined; + } } + // Get combined AI instructions from settings and .voidrules files + private _getCombinedAIInstructions(): string { + const globalAIInstructions = this.voidSettingsService.state.globalSettings.aiInstructions; + const voidRulesFileContent = this._getVoidRulesFileContents(); + const ans: string[] = []; + if (globalAIInstructions) ans.push(globalAIInstructions); + if (voidRulesFileContent) ans.push(voidRulesFileContent); + return ans.join('\n\n'); + } + // system message + private _generateChatMessagesSystemMessage = async ( + chatMode: ChatMode, + specialToolFormat: 'openai-style' | 'anthropic-style' | 'gemini-style' | 'disabled' | undefined, + ) => { + const workspaceFolders = this.workspaceContextService.getWorkspace().folders.map(f => f.uri.fsPath); + const systemMessage = await chat_systemMessage({ + workspaceFolders, + chatMode, + toolFormat: (specialToolFormat ?? 'openai-style') as specialToolFormat, + ptyHostService: this.ptyHostService, + }); + return systemMessage; + }; // --- LLM Chat messages --- private _chatMessagesToSimpleMessages(chatMessages: ChatMessage[]): SimpleLLMMessage[] { - const simpleLLMMessages: SimpleLLMMessage[] = [] + const simpleLLMMessages: SimpleLLMMessage[] = []; for (const m of chatMessages) { if (m.role === 'checkpoint') continue @@ -616,6 +756,15 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess }) } else if (m.role === 'tool') { + + this.logService.debug('[DEBUG] _chatMessagesToSimpleMessages tool:', JSON.stringify({ + name: m.name, + type: m.type, + contentLength: m.content?.length, + hasTruncationMeta: m.content?.includes('TRUNCATION_META'), + contentTail: m.content?.slice(-200), + })); + simpleLLMMessages.push({ role: m.role, content: m.content, @@ -625,9 +774,13 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess }) } else if (m.role === 'user') { + const attachments: ResolvedChatAttachment[] | undefined = m.attachments + ? m.attachments.map(att => ({ ...att })) + : undefined simpleLLMMessages.push({ role: m.role, content: m.content, + ...(attachments && attachments.length ? { attachments } : {}), }) } } @@ -640,11 +793,24 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess const { overridesOfModel } = this.voidSettingsService.state const { providerName, modelName } = modelSelection - const { - specialToolFormat, - contextWindow, - supportsSystemMessage, - } = getModelCapabilities(providerName, modelName, overridesOfModel) + const caps = getModelCapabilities(providerName, modelName, overridesOfModel) + let specialToolFormat: specialToolFormat = caps.specialToolFormat ?? 'disabled' + let { contextWindow, supportsSystemMessage } = caps + + // Fallback to provider API config only when tool format is truly missing + // Do NOT override an explicit or inferred 'disabled' value - that means + // "no native tools", and must be respected. + if (!specialToolFormat) { + try { + const modelId = modelName.includes('/') ? modelName : `${providerName}/${modelName}`; + const apiCfg = getModelApiConfiguration(modelId); + specialToolFormat = apiCfg.specialToolFormat; + } catch { /* ignore */ } + } + + // Enforce explicit user override if present (override wins over dynamic caps) + const userSSMOverride = this._getUserSupportsSystemMessageOverride(providerName as ProviderName, modelName); + if (userSSMOverride !== undefined) supportsSystemMessage = userSSMOverride; const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] @@ -654,6 +820,11 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel) const reservedOutputTokenSpace = getReservedOutputTokenSpace(providerName, modelName, { isReasoningEnabled, overridesOfModel }) + // Force global override if provided + if (typeof SYSTEM_PROMPT_OVERRIDE === 'string' && SYSTEM_PROMPT_OVERRIDE.trim() !== '') { + systemMessage = SYSTEM_PROMPT_OVERRIDE + } + const { messages, separateSystemMessage } = prepareMessages({ messages: simpleMessages, systemMessage, @@ -667,29 +838,66 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess }) return { messages, separateSystemMessage }; } + + prepareLLMChatMessages: IConvertToLLMMessageService['prepareLLMChatMessages'] = async ({ chatMessages, chatMode, modelSelection }) => { if (modelSelection === null) return { messages: [], separateSystemMessage: undefined } const { overridesOfModel } = this.voidSettingsService.state const { providerName, modelName } = modelSelection - const { - specialToolFormat, - contextWindow, - supportsSystemMessage, - } = getModelCapabilities(providerName, modelName, overridesOfModel) + const caps = getModelCapabilities(providerName, modelName, overridesOfModel) + let specialToolFormat: specialToolFormat = caps.specialToolFormat ?? 'disabled' + let { contextWindow, supportsSystemMessage } = caps - const { disableSystemMessage } = this.voidSettingsService.state.globalSettings; - const fullSystemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat) - const systemMessage = disableSystemMessage ? '' : fullSystemMessage; + try { + const dynCaps = await this._getDynamicCapsForSelection(providerName, modelName); + if (dynCaps) { + // adopt dynamic value only when present; keeps strict typing and lints happy + specialToolFormat = dynCaps.specialToolFormat ?? specialToolFormat; + // Only adopt dynamic supportsSystemMessage when user didn't explicitly override it + const userSSMOverride = this._getUserSupportsSystemMessageOverride(providerName as ProviderName, modelName); + if (userSSMOverride === undefined) { + const ssm = dynCaps.supportsSystemMessage; + supportsSystemMessage = ssm ?? supportsSystemMessage; + } + if (typeof dynCaps.contextWindow === 'number') contextWindow = dynCaps.contextWindow; + } + } catch { + // ignore + } + // Enforce explicit user override again after all fallbacks + { + const userSSMOverride2 = this._getUserSupportsSystemMessageOverride(providerName as ProviderName, modelName); + if (userSSMOverride2 !== undefined) supportsSystemMessage = userSSMOverride2; + } + + // allow-any-unicode-next-line + // Fallback to provider API config only when tool format is truly missing. + // Never override explicit or inferred 'disabled', since that means + // "no native tools" and must be honored. + if (!specialToolFormat) { + try { + const modelId = modelName.includes('/') ? modelName : `${providerName}/${modelName}`; + const apiCfg = getModelApiConfiguration(modelId); + specialToolFormat = apiCfg.specialToolFormat; + } catch { /* ignore */ } + } + + let systemMessage = await this._generateChatMessagesSystemMessage(chatMode, specialToolFormat) + if (typeof SYSTEM_PROMPT_OVERRIDE === 'string' && SYSTEM_PROMPT_OVERRIDE.trim() !== '') { + systemMessage = SYSTEM_PROMPT_OVERRIDE + } const modelSelectionOptions = this.voidSettingsService.state.optionsOfModelSelection['Chat'][modelSelection.providerName]?.[modelSelection.modelName] // Get combined AI instructions const aiInstructions = this._getCombinedAIInstructions(); + const isReasoningEnabled = getIsReasoningEnabledState('Chat', providerName, modelName, modelSelectionOptions, overridesOfModel) const reservedOutputTokenSpace = getReservedOutputTokenSpace(providerName, modelName, { isReasoningEnabled, overridesOfModel }) const llmMessages = this._chatMessagesToSimpleMessages(chatMessages) + await this._populateAttachmentData(llmMessages) const { messages, separateSystemMessage } = prepareMessages({ messages: llmMessages, @@ -704,8 +912,6 @@ class ConvertToLLMMessageService extends Disposable implements IConvertToLLMMess }) return { messages, separateSystemMessage }; } - - // --- FIM --- prepareFIMMessage: IConvertToLLMMessageService['prepareFIMMessage'] = ({ messages }) => { @@ -725,44 +931,23 @@ ${messages.prefix}` return { prefix, suffix, stopTokens } } - -} - - -registerSingleton(IConvertToLLMMessageService, ConvertToLLMMessageService, InstantiationType.Eager); - - - - - - - - -/* -Gemini has this, but they're openai-compat so we don't need to implement this -gemini request: -{ "role": "assistant", - "content": null, - "function_call": { - "name": "get_weather", - "arguments": { - "latitude": 48.8566, - "longitude": 2.3522 - } - } -} - -gemini response: -{ "role": "assistant", - "function_response": { - "name": "get_weather", - "response": { - "temperature": "15°C", - "condition": "Cloudy" + private async _populateAttachmentData(messages: SimpleLLMMessage[]): Promise { + for (const m of messages) { + if (m.role !== 'user') continue + const atts = m.attachments + if (!atts || !atts.length) continue + for (const att of atts) { + if (att.dataBase64) continue + try { + const content = await this.fileService.readFile(att.uri) + att.dataBase64 = encodeBase64(content.value) + } catch { + // ignore individual attachment failures + } + } } } } -*/ - +registerSingleton(IConvertToLLMMessageService, ConvertToLLMMessageService, InstantiationType.Eager); diff --git a/src/vs/workbench/contrib/void/browser/editCodeService.ts b/src/vs/workbench/contrib/void/browser/editCodeService.ts index 80ee4bc9925..9f6f2d6398b 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeService.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeService.ts @@ -3,53 +3,465 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ +import type * as Parser from '@vscode/tree-sitter-wasm'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor, IOverlayWidget, IViewZone } from '../../../../editor/browser/editorBrowser.js'; - -// import { IUndoRedoService } from '../../../../platform/undoRedo/common/undoRedo.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; -// import { throttle } from '../../../../base/common/decorators.js'; import { findDiffs } from './helpers/findDiffs.js'; -import { EndOfLinePreference, IModelDecorationOptions, ITextModel } from '../../../../editor/common/model.js'; -import { IRange } from '../../../../editor/common/core/range.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; +import { EndOfLinePreference, IModelDecorationOptions, ITextModel } from '../../../../editor/common/language/model.js'; +import { IRange } from '../../../../editor/common/language/core/range.js'; +import { IModelService } from '../../../../editor/common/language/services/model.js'; +import { ITreeSitterParserService } from '../../../../editor/common/language/services/treeSitterParserService.js'; +import { getModuleLocation } from '../../../../editor/common/language/services/treeSitter/treeSitterLanguages.js'; import { IUndoRedoElement, IUndoRedoService, UndoRedoElementType } from '../../../../platform/undoRedo/common/undoRedo.js'; import { RenderOptions } from '../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; -// import { IModelService } from '../../../../editor/common/services/model.js'; - +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import * as dom from '../../../../base/browser/dom.js'; import { Widget } from '../../../../base/browser/ui/widget.js'; import { URI } from '../../../../base/common/uri.js'; +import { FileAccess, type AppResourcePath } from '../../../../base/common/network.js'; +import { importAMDNodeModule } from '../../../../amdX.js'; import { IConsistentEditorItemService, IConsistentItemService } from './helperServices/consistentItemService.js'; -import { voidPrefixAndSuffix, ctrlKStream_userMessage, ctrlKStream_systemMessage, defaultQuickEditFimTags, rewriteCode_systemMessage, rewriteCode_userMessage, searchReplaceGivenDescription_systemMessage, searchReplaceGivenDescription_userMessage, tripleTick, } from '../common/prompt/prompts.js'; +import { buildNativeSysMessageForCtrlK, buildNativeUserMessageForCtrlK, buildXmlSysMessageForCtrlK, buildXmlUserMessageForCtrlK } from '../common/prompt/prompts.js'; +import { getModelCapabilities } from '../../../../platform/void/common/modelInference.js'; import { IVoidCommandBarService } from './voidCommandBarService.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { VOID_ACCEPT_DIFF_ACTION_ID, VOID_REJECT_DIFF_ACTION_ID } from './actionIDs.js'; - +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { mountCtrlK } from './react/out/quick-edit-tsx/index.js' import { QuickEditPropsType } from './quickEditActions.js'; -import { IModelContentChangedEvent } from '../../../../editor/common/textModelEvents.js'; -import { extractCodeFromFIM, extractCodeFromRegular, ExtractedSearchReplaceBlock, extractSearchReplaceBlocks } from '../common/helpers/extractCodeFromResult.js'; +import { IModelContentChangedEvent } from '../../../../editor/common/language/textModelEvents.js'; import { INotificationService, } from '../../../../platform/notification/common/notification.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { Emitter } from '../../../../base/common/event.js'; import { ILLMMessageService } from '../common/sendLLMMessageService.js'; -import { LLMChatMessage } from '../common/sendLLMMessageTypes.js'; -import { IMetricsService } from '../common/metricsService.js'; +import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; import { IEditCodeService, AddCtrlKOpts, StartApplyingOpts, CallBeforeStartApplyingOpts, } from './editCodeServiceInterface.js'; -import { IVoidSettingsService } from '../common/voidSettingsService.js'; -import { FeatureName } from '../common/voidSettingsTypes.js'; +import { IVoidSettingsService } from '../../../../platform/void/common/voidSettingsService.js'; import { IVoidModelService } from '../common/voidModelService.js'; import { deepClone } from '../../../../base/common/objects.js'; -import { acceptBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../common/helpers/colors.js'; -import { DiffArea, Diff, CtrlKZone, VoidFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, TrackingZone, ComputedDiff } from '../common/editCodeServiceTypes.js'; +import { acceptBg, acceptBorder, buttonFontSize, buttonTextColor, rejectBg, rejectBorder } from '../../../../platform/void/common/helpers/colors.js'; +import { DiffArea, Diff, CtrlKZone, VoidFileSnapshot, DiffAreaSnapshotEntry, diffAreaSnapshotKeys, DiffZone, ComputedDiff } from '../../../../platform/void/common/editCodeServiceTypes.js'; import { IConvertToLLMMessageService } from './convertToLLMMessageService.js'; -// import { isMacintosh } from '../../../../base/common/platform.js'; -// import { VOID_OPEN_SETTINGS_ACTION_ID } from './voidSettingsPane.js'; +import { IToolsService } from '../common/toolsService.js'; +import DiffMatchPatch from './lib/diff-match-patch.js' +import { inferSelectionFromCode, inferExactBlockFromCode, InferenceAstContext, InferredBlock } from './react/src/markdown/inferSelection.js' + +type DmpOp = -1 | 0 | 1 + + +// Fixes cases where inference returns only a prefix of a declaration line and we end up +// replacing mid-line (corrupting the file). + +function offsetToLineNumber(text: string, offset: number): number { + // 1-based + return text.slice(0, Math.max(0, offset)).split('\n').length; +} + +function findMatchingCurlyForwardJs(text: string, openIndex: number): number { + type Mode = 'code' | 'sgl' | 'dbl' | 'template' | 'line' | 'block'; + + let i = openIndex + 1; + let depth = 1; + + let mode: Mode = 'code'; + let escaped = false; + + + let templateNesting = 0; + + + + const tplExprDepthStack: number[] = []; + + while (i < text.length) { + const ch = text[i]; + const next = text[i + 1]; + + + if (mode === 'line') { + if (ch === '\n') mode = 'code'; + i++; + continue; + } + if (mode === 'block') { + if (ch === '*' && next === '/') { mode = 'code'; i += 2; continue; } + i++; + continue; + } + + + if (mode === 'sgl' || mode === 'dbl') { + if (escaped) { escaped = false; i++; continue; } + if (ch === '\\') { escaped = true; i++; continue; } + + if ((mode === 'sgl' && ch === '\'') || (mode === 'dbl' && ch === '"')) { + mode = 'code'; + i++; + continue; + } + i++; + continue; + } + + // --- Template raw: ` ... ${ ... } ... ` --- + if (mode === 'template') { + if (escaped) { escaped = false; i++; continue; } + if (ch === '\\') { escaped = true; i++; continue; } + + + if (ch === '$' && next === '{') { + tplExprDepthStack.push(depth); + depth++; + mode = 'code'; + i += 2; + continue; + } + + + if (ch === '`') { + templateNesting--; + mode = 'code'; + i++; + continue; + } + + i++; + continue; + } + + // --- mode === 'code' --- + + if (ch === '/' && next === '/') { mode = 'line'; i += 2; continue; } + if (ch === '/' && next === '*') { mode = 'block'; i += 2; continue; } + + + if (ch === '\'') { mode = 'sgl'; escaped = false; i++; continue; } + if (ch === '"') { mode = 'dbl'; escaped = false; i++; continue; } + + + if (ch === '`') { + templateNesting++; + mode = 'template'; + escaped = false; + i++; + continue; + } + + + if (ch === '{') { depth++; i++; continue; } + if (ch === '}') { + depth--; + + if (tplExprDepthStack.length > 0 && depth === tplExprDepthStack[tplExprDepthStack.length - 1]) { + tplExprDepthStack.pop(); + if (templateNesting > 0) { + mode = 'template'; + i++; + continue; + } + } + + if (depth === 0) return i; + i++; + continue; + } + + i++; + } + + return -1; +} + + +// Finds the "body" '{' for a TS/JS function/method-like declaration starting at startOffset. +// Ignores braces inside (...) (params), and heuristically skips return-type object literals. +function findTopLevelBodyOpenBraceJs(text: string, startOffset: number, searchWindow = 8000): number { + const limit = Math.min(text.length, startOffset + searchWindow); + + let inS = false, inD = false, inT = false, inSL = false, inML = false; + let prev = ''; + + let parenDepth = 0; + let sawParen = false; + + let inReturnType = false; + let typeBraceDepth = 0; + + for (let i = startOffset; i < limit; i++) { + const ch = text[i]; + const next = text[i + 1]; + + // comments (only when not in strings) + if (!inS && !inD && !inT) { + if (!inML && !inSL && ch === '/' && next === '/') { inSL = true; i++; prev = ''; continue; } + if (!inML && !inSL && ch === '/' && next === '*') { inML = true; i++; prev = ''; continue; } + if (inSL && ch === '\n') { inSL = false; prev = ch; continue; } + if (inML && ch === '*' && next === '/') { inML = false; i++; prev = ''; continue; } + if (inSL || inML) { prev = ch; continue; } + } + + // strings + if (!inSL && !inML) { + if (!inD && !inT && ch === '\'' && prev !== '\\') { inS = !inS; prev = ch; continue; } + if (!inS && !inT && ch === '"' && prev !== '\\') { inD = !inD; prev = ch; continue; } + if (!inS && !inD && ch === '`' && prev !== '\\') { inT = !inT; prev = ch; continue; } + } + + if (inS || inD || inT || inSL || inML) { prev = ch; continue; } + + // parentheses (params) + if (ch === '(') { parenDepth++; sawParen = true; prev = ch; continue; } + if (ch === ')') { if (parenDepth > 0) parenDepth--; prev = ch; continue; } + + if (!sawParen) { prev = ch; continue; } + + // Only consider top-level (outside params) + if (parenDepth === 0) { + // detect start of return type: `): Type ... {` + if (ch === ':' && typeBraceDepth === 0) { + inReturnType = true; + prev = ch; + continue; + } + + if (typeBraceDepth > 0) { + if (ch === '{') typeBraceDepth++; + else if (ch === '}') typeBraceDepth--; + prev = ch; + continue; + } + + // Candidate '{' + if (ch === '{') { + if (inReturnType) { + // Heuristic: decide if this is a type-literal `{ a: number; }` vs function body + const close = findMatchingCurlyForwardJs(text, i); + if (close !== -1) { + const inside = text.slice(i + 1, Math.min(close, i + 350)); + const looksTypey = /[:;]/.test(inside) && !/\b(return|const|let|var|if|for|while|switch|try|throw|import|export)\b/.test(inside); + if (looksTypey) { + typeBraceDepth = 1; // enter type-literal braces + prev = ch; + continue; + } + } + } + + // treat as body + return i; + } + + // End of signature without body (abstract/interface etc) + if (ch === ';') return -1; + } + + prev = ch; + } + + return -1; +} + +function looksLikeFullTopLevelBlockSnippet(snippet: string): boolean { + const s = normalizeEol(snippet ?? ''); + const open = findTopLevelBodyOpenBraceJs(s, 0, Math.min(8000, s.length)); + if (open === -1) return false; + const close = findMatchingCurlyForwardJs(s, open); + if (close === -1) return false; + const tail = s.slice(close + 1).trim(); + return tail === '' || tail === ';' || tail === ','; +} + +function expandToEnclosingCurlyBlockJs(text: string, startOffset: number, searchWindow = 8000) { + const open = findTopLevelBodyOpenBraceJs(text, startOffset, searchWindow); + if (open === -1) return null; + + const close = findMatchingCurlyForwardJs(text, open); + if (close === -1) return null; + + const endOffset = close + 1; + + const startLine = offsetToLineNumber(text, startOffset); + const endLine = offsetToLineNumber(text, endOffset); + + return { + startOffset, + endOffset, + text: text.slice(startOffset, endOffset), + range: [startLine, endLine] as [number, number], + }; +} + +// normalize EOLs to LF +// normalize EOLs to LF +function normalizeEol(s: string): string { + return (s ?? '').replace(/\r\n/g, '\n').replace(/\r/g, '\n') +} + +// (Very common LLM failure mode for edit_file snippets.) +function stripMarkdownFence(s: string): string { + const str = String(s ?? ''); + const m = str.match(/^\s*```[a-zA-Z0-9_-]*\s*\n([\s\S]*?)\n```\s*$/); + return m ? m[1] : str; +} -const numLinesOfStr = (str: string) => str.split('\n').length +function escapeForShellDoubleQuotes(s: string): string { + // minimal, practical escaping for bash/zsh inside "...": + return String(s).replace(/(["\\$`])/g, '\\$1'); +} + +function buildInvisibleCharsDebugCmd(filePathForCmd: string, startLine: number, endLine: number) { + const a = Math.max(1, Math.floor(startLine || 1)); + const b = Math.max(a, Math.floor(endLine || a)); + const file = `"${escapeForShellDoubleQuotes(filePathForCmd)}"`; + + return { + gnu: `sed -n '${a},${b}p' ${file} | cat -A`, + // macOS/BSD fallback (often available even when cat -A isn't) + bsd: `sed -n '${a},${b}p' ${file} | cat -vet`, + }; +} + +const EDIT_FILE_FALLBACK_MSG = 'LLM did not correctly provide an ORIGINAL code block.'; + +// Build unified diff in line-mode using DMP's line helpers +function createUnifiedFromLineDiffs(fileLabel: string, original: string, updated: string, context = 3): string { + const dmp = new DiffMatchPatch() + const a: any = (dmp as any).diff_linesToChars_(original, updated) + let diffs = dmp.diff_main(a.chars1, a.chars2, false) + try { dmp.diff_cleanupSemantic(diffs) } catch { } + (dmp as any).diff_charsToLines_(diffs, a.lineArray) + + const out: string[] = [] + out.push(`--- a/${fileLabel}`) + out.push(`+++ b/${fileLabel}`) + + let oldLine = 1 + let newLine = 1 + const ctxBuf: string[] = [] + let inHunk = false + let hunkLines: string[] = [] + let hunkStartOld = 0 + let hunkStartNew = 0 + let oldCount = 0 + let newCount = 0 + let postCtxLeft = 0 + + const flushHunk = () => { + if (!inHunk) return + out.push(`@@ -${hunkStartOld},${Math.max(1, oldCount)} +${hunkStartNew},${Math.max(1, newCount)} @@`) + out.push(...hunkLines) + inHunk = false + hunkLines = [] + oldCount = 0 + newCount = 0 + postCtxLeft = 0 + } + + const startHunkWithCtx = () => { + inHunk = true + hunkStartOld = oldLine - ctxBuf.length + hunkStartNew = newLine - ctxBuf.length + for (const ln of ctxBuf) { + hunkLines.push(' ' + ln) + oldCount++; newCount++ + } + } + + const splitLines = (s: string) => { + const arr = s.split('\n') + if (arr.length && arr[arr.length - 1] === '') arr.pop() + return arr + } + + for (const [op, text] of diffs as [DmpOp, string][]) { + const lines = splitLines(text) + if (op === 0) { + if (inHunk) { + for (const ln of lines) { + if (postCtxLeft > 0) { + hunkLines.push(' ' + ln) + oldCount++; newCount++; oldLine++; newLine++; postCtxLeft-- + } else { + flushHunk() + ctxBuf.push(ln) + if (ctxBuf.length > context) ctxBuf.shift() + oldLine++; newLine++ + } + } + } else { + for (const ln of lines) { + ctxBuf.push(ln) + if (ctxBuf.length > context) ctxBuf.shift() + oldLine++; newLine++ + } + } + } else if (op === -1) { + if (!inHunk) startHunkWithCtx() + for (const ln of lines) { + hunkLines.push('-' + ln) + oldCount++; oldLine++ + } + postCtxLeft = context + } else { + if (!inHunk) startHunkWithCtx() + for (const ln of lines) { + hunkLines.push('+' + ln) + newCount++; newLine++ + } + postCtxLeft = context + } + } + + flushHunk() + return out.join('\n') +} + +// Helper: apply a single diff to an 'original' baseline string, returning the new baseline. +// This is used for per-diff accept in edit_file preview zones so that only the accepted +// change is merged into the baseline while the remaining diffs stay visible. +function applyDiffToBaseline(original: string, diff: Diff): string { + // We mirror the line indexing used in findDiffs: 1-based lines via a leading empty element. + const lines = ('\n' + (original ?? '')).split('\n'); + + const start = diff.originalStartLine; + if (start < 1 || start > lines.length) { + return original; + } + + if (diff.type === 'insertion') { + // Insert the new lines at the insertion point. + const insertPos = Math.min(start, lines.length); + const newLines = (diff.code ?? '').split('\n'); + lines.splice(insertPos, 0, ...newLines); + return lines.slice(1).join('\n'); + } + + if (diff.type === 'deletion' || diff.type === 'edit') { + // For edits and deletions we have an originalEndLine. + const end = diff.originalEndLine; + if (end < start || start >= lines.length) { + return original; + } + + if (diff.type === 'deletion') { + // Drop the lines that were deleted in the new text. + const deleteCount = Math.min(end - start + 1, lines.length - start); + lines.splice(start, deleteCount); + } else { + // Replace the original range with the new content. + const deleteCount = Math.min(end - start + 1, lines.length - start); + const newLines = (diff.code ?? '').split('\n'); + lines.splice(start, deleteCount, ...newLines); + } + } + + return lines.slice(1).join('\n'); +} export const getLengthOfTextPx = ({ tabWidth, spaceWidth, content }: { tabWidth: number, spaceWidth: number, content: string }) => { @@ -61,12 +473,11 @@ export const getLengthOfTextPx = ({ tabWidth, spaceWidth, content }: { tabWidth: lengthOfTextPx += spaceWidth; } } - return lengthOfTextPx } -const getLeadingWhitespacePx = (editor: ICodeEditor, startLine: number): number => { +export const getLeadingWhitespacePx = (editor: ICodeEditor, startLine: number): number => { const model = editor.getModel(); if (!model) { @@ -99,89 +510,388 @@ const getLeadingWhitespacePx = (editor: ICodeEditor, startLine: number): number return leftWhitespacePx; }; +export class EditCodeService extends Disposable implements IEditCodeService { + _serviceBrand: undefined; + + // URI <--> model + diffAreasOfURI: Record | undefined> = {}; // uri -> diffareaId -// Helper function to remove whitespace except newlines -const removeWhitespaceExceptNewlines = (str: string): string => { - return str.replace(/[^\S\n]+/g, ''); -} + diffAreaOfId: Record = {}; // diffareaId -> diffArea + diffOfId: Record = {}; // diffid -> diff (redundant with diffArea._diffOfId) + + // events + + // uri: diffZones // listen on change diffZones + private readonly _onDidAddOrDeleteDiffZones = new Emitter<{ uri: URI }>(); + onDidAddOrDeleteDiffZones = this._onDidAddOrDeleteDiffZones.event; + + // diffZone: [uri], diffs, isStreaming // listen on change diffs, change streaming (uri is const) + private readonly _onDidChangeDiffsInDiffZoneNotStreaming = new Emitter<{ uri: URI, diffareaid: number }>(); + private readonly _onDidChangeStreamingInDiffZone = new Emitter<{ uri: URI, diffareaid: number }>(); + onDidChangeDiffsInDiffZoneNotStreaming = this._onDidChangeDiffsInDiffZoneNotStreaming.event; + onDidChangeStreamingInDiffZone = this._onDidChangeStreamingInDiffZone.event; + // fired when instant apply fell back to locating ORIGINAL snippets and retried + private readonly _onDidUseFallback = new Emitter<{ uri: URI; message?: string }>(); + public readonly onDidUseFallback = this._onDidUseFallback.event; + + // ctrlKZone: [uri], isStreaming // listen on change streaming + private readonly _onDidChangeStreamingInCtrlKZone = new Emitter<{ uri: URI; diffareaid: number }>(); + onDidChangeStreamingInCtrlKZone = this._onDidChangeStreamingInCtrlKZone.event; + // remember last fallback message per file so UI can rehydrate state after status changes + private _lastFallbackMsgByFsPath = new Map(); -// finds block.orig in fileContents and return its range in file -// startingAtLine is 1-indexed and inclusive -// returns 1-indexed lines -const findTextInCode = (text: string, fileContents: string, canFallbackToRemoveWhitespace: boolean, opts: { startingAtLine?: number, returnType: 'lines' }) => { + // optional public binding from applyBoxId -> uri for UI convenience + private _applyBoxIdToUri = new Map() + private _astContextByFsPath = new Map(); + private _astWarmupByFsPath = new Map>(); + private _treeSitterImportPromise: Promise | null = null; + private _treeSitterInitPromise: Promise | null = null; + private _bundledParserByGrammar = new Map(); + private _bundledLanguageByGrammar = new Map(); - const returnAns = (fileContents: string, idx: number) => { - const startLine = numLinesOfStr(fileContents.substring(0, idx + 1)) - const numLines = numLinesOfStr(text) - const endLine = startLine + numLines - 1 + public bindApplyBoxUri(applyBoxId: string, uri: URI) { + this._applyBoxIdToUri.set(applyBoxId, uri) + } - return [startLine, endLine] as const + public getUriByApplyBoxId(applyBoxId: string): URI | undefined { + return this._applyBoxIdToUri.get(applyBoxId) } - const startingAtLineIdx = (fileContents: string) => opts?.startingAtLine !== undefined ? - fileContents.split('\n').slice(0, opts.startingAtLine).join('\n').length // num characters in all lines before startingAtLine - : 0 + public getLastFallbackMessage(uri: URI): string | null { + return this._lastFallbackMsgByFsPath.get(uri.fsPath) ?? null + } - // idx = starting index in fileContents - let idx = fileContents.indexOf(text, startingAtLineIdx(fileContents)) + public recordFallbackMessage(uri: URI, message: string) { + try { + this._lastFallbackMsgByFsPath.set(uri.fsPath, message); + } catch { /* ignore */ } - // if idx was found - if (idx !== -1) { - return returnAns(fileContents, idx) + try { + this._onDidUseFallback.fire({ uri, message }); + } catch { /* ignore */ } } - if (!canFallbackToRemoveWhitespace) - return 'Not found' as const + private _isAstInferenceEnabled(): boolean { + try { + return !!this._settingsService.state.globalSettings.applyAstInference; + } catch { + return false; + } + } + + private async _promiseWithTimeout( + promise: Promise, + timeoutMs: number, + fallback: T, + label: string + ): Promise { + let timer: any; + let didTimeout = false; + const timeoutPromise = new Promise(resolve => { + timer = setTimeout(() => { + didTimeout = true; + resolve(fallback); + }, timeoutMs); + }); - // try to find it ignoring all whitespace this time - text = removeWhitespaceExceptNewlines(text) - fileContents = removeWhitespaceExceptNewlines(fileContents) - idx = fileContents.indexOf(text, startingAtLineIdx(fileContents)); + try { + return await Promise.race([promise, timeoutPromise]); + } catch (e) { + this.logService.debug(`[apply-ast] ${label} failed`, e); + return fallback; + } finally { + if (timer !== undefined) clearTimeout(timer); + if (didTimeout) { + this.logService.debug(`[apply-ast] ${label} timed out after ${timeoutMs}ms`); + } + } + } - if (idx === -1) return 'Not found' as const - const lastIdx = fileContents.lastIndexOf(text) - if (lastIdx !== idx) return 'Not unique' as const + private _grammarNameForLanguageId(languageId: string): string | null { + const id = String(languageId || '').toLowerCase(); + const map: Record = { + typescript: 'tree-sitter-typescript', + typescriptreact: 'tree-sitter-tsx', + tsx: 'tree-sitter-tsx', + javascript: 'tree-sitter-javascript', + javascriptreact: 'tree-sitter-tsx', + css: 'tree-sitter-css', + ini: 'tree-sitter-ini', + regex: 'tree-sitter-regex', + python: 'tree-sitter-python', + go: 'tree-sitter-go', + java: 'tree-sitter-java', + csharp: 'tree-sitter-c-sharp', + 'c#': 'tree-sitter-c-sharp', + cpp: 'tree-sitter-cpp', + 'c++': 'tree-sitter-cpp', + php: 'tree-sitter-php', + ruby: 'tree-sitter-ruby', + rust: 'tree-sitter-rust', + }; + return map[id] ?? null; + } + + private async _getTreeSitterImport() { + if (!this._treeSitterImportPromise) { + this._treeSitterImportPromise = importAMDNodeModule( + '@vscode/tree-sitter-wasm', + 'wasm/tree-sitter.js' + ); + } + return this._treeSitterImportPromise; + } + + private async _ensureBundledTreeSitterInitialized() { + const mod = await this._getTreeSitterImport(); + if (!this._treeSitterInitPromise) { + const parserWasmPath = `${getModuleLocation(this._environmentService)}/tree-sitter.wasm` as AppResourcePath; + this._treeSitterInitPromise = mod.Parser.init({ + locateFile: () => FileAccess.asBrowserUri(parserWasmPath).toString(true) + }).then(() => undefined); + } + await this._treeSitterInitPromise; + return mod; + } + + private async _getBundledLanguage(grammarName: string): Promise { + const cached = this._bundledLanguageByGrammar.get(grammarName); + if (cached) return cached; - return returnAns(fileContents, idx) -} + try { + const mod = await this._ensureBundledTreeSitterInitialized(); + const wasmPath = `${getModuleLocation(this._environmentService)}/${grammarName}.wasm` as AppResourcePath; + const data = await this._fileService.readFile(FileAccess.asFileUri(wasmPath)); + const language = await mod.Language.load(data.value.buffer); + this._bundledLanguageByGrammar.set(grammarName, language); + return language; + } catch (e) { + this.logService.debug('[apply-ast] Failed to load bundled language', grammarName, e); + return null; + } + } + private async _getBundledParser(grammarName: string): Promise { + const language = await this._getBundledLanguage(grammarName); + if (!language) return null; + let parser = this._bundledParserByGrammar.get(grammarName); + if (!parser) { + const mod = await this._ensureBundledTreeSitterInitialized(); + parser = new mod.Parser(); + this._bundledParserByGrammar.set(grammarName, parser); + } + parser.setLanguage(language); + return parser; + } -// line/col is the location, originalCodeStartLine is the start line of the original code being displayed -type StreamLocationMutable = { line: number, col: number, addedSplitYet: boolean, originalCodeStartLine: number } + private _isInterestingAstNodeType(nodeType: string, span: number): boolean { + const t = nodeType.toLowerCase(); + if (span < 20) return false; + if (t === 'program' || t === 'source_file' || t === 'module') return span >= 120; + return /(function|method|class|interface|enum|struct|impl|namespace|module|declaration|definition|statement_block|block|object|trait|record|lambda|arrow_function|closure|if_statement|for_statement|while_statement|switch_statement|try_statement)/.test(t); + } + private _collectAstCandidates(tree: Parser.Tree): InferenceAstContext['candidates'] { + const candidates: InferenceAstContext['candidates'] = []; + const seen = new Set(); + const cursor = tree.rootNode.walk(); + let goDown = true; + let visited = 0; -class EditCodeService extends Disposable implements IEditCodeService { - _serviceBrand: undefined; + try { + while (true) { + if (goDown) { + const startOffset = cursor.startIndex; + const endOffset = cursor.endIndex; + visited += 1; + + if (visited > 45000 || candidates.length >= 1600) break; + + if (endOffset > startOffset && this._isInterestingAstNodeType(cursor.nodeType, endOffset - startOffset)) { + const key = `${startOffset}:${endOffset}`; + if (!seen.has(key)) { + seen.add(key); + candidates.push({ startOffset, endOffset, nodeType: cursor.nodeType }); + } + } - // URI <--> model - diffAreasOfURI: Record | undefined> = {}; // uri -> diffareaId + if (cursor.gotoFirstChild()) continue; + goDown = false; + } - diffAreaOfId: Record = {}; // diffareaId -> diffArea - diffOfId: Record = {}; // diffid -> diff (redundant with diffArea._diffOfId) + if (cursor.gotoNextSibling()) { + goDown = true; + continue; + } + if (!cursor.gotoParent()) break; + } + } finally { + cursor.delete(); + } - // events + candidates.sort((a, b) => a.startOffset - b.startOffset); + return candidates; + } - // uri: diffZones // listen on change diffZones - private readonly _onDidAddOrDeleteDiffZones = new Emitter<{ uri: URI }>(); - onDidAddOrDeleteDiffZones = this._onDidAddOrDeleteDiffZones.event; + private _putAstContextCache(uri: URI, model: ITextModel | null, astContext: InferenceAstContext | null): void { + if (!model) return; + const entry = { + versionId: model.getVersionId(), + languageId: model.getLanguageId(), + astContext + }; + this._astContextByFsPath.delete(uri.fsPath); + this._astContextByFsPath.set(uri.fsPath, entry); - // diffZone: [uri], diffs, isStreaming // listen on change diffs, change streaming (uri is const) - private readonly _onDidChangeDiffsInDiffZoneNotStreaming = new Emitter<{ uri: URI, diffareaid: number }>(); - private readonly _onDidChangeStreamingInDiffZone = new Emitter<{ uri: URI, diffareaid: number }>(); - onDidChangeDiffsInDiffZoneNotStreaming = this._onDidChangeDiffsInDiffZoneNotStreaming.event; - onDidChangeStreamingInDiffZone = this._onDidChangeStreamingInDiffZone.event; + const maxEntries = 80; + while (this._astContextByFsPath.size > maxEntries) { + const oldest = this._astContextByFsPath.keys().next().value as string | undefined; + if (!oldest) break; + this._astContextByFsPath.delete(oldest); + } + } - // ctrlKZone: [uri], isStreaming // listen on change streaming - private readonly _onDidChangeStreamingInCtrlKZone = new Emitter<{ uri: URI; diffareaid: number }>(); - onDidChangeStreamingInCtrlKZone = this._onDidChangeStreamingInCtrlKZone.event; + private _getCachedAstContext(uri: URI, model: ITextModel | null): InferenceAstContext | null { + if (!model) return null; + const cached = this._astContextByFsPath.get(uri.fsPath); + if (!cached) return null; + if (cached.versionId !== model.getVersionId()) return null; + if (cached.languageId !== model.getLanguageId()) return null; + return cached.astContext; + } + + private async _buildAstContextFromService(model: ITextModel): Promise { + try { + let tree = this._treeSitterParserService.getParseResult(model)?.parseResult?.tree; + if (!tree) { + const textModelTree = await this._promiseWithTimeout( + this._treeSitterParserService.getTextModelTreeSitter(model, true), + 500, + null, + 'service.getTextModelTreeSitter' + ); + if (textModelTree && !textModelTree.parseResult?.tree) { + await this._promiseWithTimeout( + textModelTree.parse(model.getLanguageId()), + 700, + undefined, + 'service.parse' + ); + } + tree = textModelTree?.parseResult?.tree; + } + if (!tree) return null; + + const candidates = this._collectAstCandidates(tree); + if (candidates.length === 0) return null; + return { candidates, languageId: model.getLanguageId(), source: 'service' }; + } catch { + return null; + } + } + + private async _buildAstContextFromBundled(fileText: string, languageId: string): Promise { + const grammar = this._grammarNameForLanguageId(languageId); + if (!grammar) return null; + + const parser = await this._getBundledParser(grammar); + if (!parser) return null; + + let tree: Parser.Tree | null = null; + try { + tree = parser.parse(fileText); + if (!tree) return null; + const candidates = this._collectAstCandidates(tree); + if (candidates.length === 0) return null; + return { candidates, languageId, source: 'bundled' }; + } catch (e) { + this.logService.debug('[apply-ast] Bundled parser failed', languageId, e); + return null; + } finally { + tree?.delete?.(); + } + } + + private async _getOrBuildAstContext(uri: URI, fileText: string, model: ITextModel | null): Promise { + if (!this._isAstInferenceEnabled()) return null; + if (!fileText || fileText.length < 24) return null; + if (fileText.length > 2_000_000) return null; + + const cached = this._getCachedAstContext(uri, model); + if (cached) return cached; + + let astContext: InferenceAstContext | null = null; + if (model) { + astContext = await this._promiseWithTimeout( + this._buildAstContextFromService(model), + 900, + null, + 'buildAstContextFromService' + ); + } + if (!astContext) { + astContext = await this._promiseWithTimeout( + this._buildAstContextFromBundled(fileText, model?.getLanguageId() ?? ''), + 900, + null, + 'buildAstContextFromBundled' + ); + } + + this._putAstContextCache(uri, model, astContext); + return astContext; + } + + private async _prewarmAstForUri(uri: URI): Promise { + if (!this._isAstInferenceEnabled()) return; + if (this._astWarmupByFsPath.has(uri.fsPath)) { + await this._astWarmupByFsPath.get(uri.fsPath); + return; + } + + const warmupPromise = (async () => { + const model = this._modelService.getModel(uri); + if (!model) return; + const cached = this._getCachedAstContext(uri, model); + if (cached) return; + const astContext = await this._promiseWithTimeout( + this._buildAstContextFromService(model), + 900, + null, + 'prewarm.buildAstContextFromService' + ); + this._putAstContextCache(uri, model, astContext); + })(); + + this._astWarmupByFsPath.set(uri.fsPath, warmupPromise); + try { + await warmupPromise; + } finally { + this._astWarmupByFsPath.delete(uri.fsPath); + } + } + + public async inferSelectionForApply({ + uri, + codeStr, + fileText + }: { + uri: URI; + codeStr: string; + fileText: string; + }): Promise<{ text: string; range: [number, number] } | null> { + if (!codeStr || !fileText) return null; + const model = this._modelService.getModel(uri); + const astContext = await this._getOrBuildAstContext(uri, fileText, model); + return inferSelectionFromCode({ codeStr, fileText, astContext: astContext ?? undefined }); + } constructor( - // @IHistoryService private readonly _historyService: IHistoryService, // history service is the history of pressing alt left/right @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, @IModelService private readonly _modelService: IModelService, @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, // undoRedo service is the history of pressing ctrl+z @@ -191,11 +901,14 @@ class EditCodeService extends Disposable implements IEditCodeService { @IConsistentEditorItemService private readonly _consistentEditorItemService: IConsistentEditorItemService, @IMetricsService private readonly _metricsService: IMetricsService, @INotificationService private readonly _notificationService: INotificationService, - // @ICommandService private readonly _commandService: ICommandService, @IVoidSettingsService private readonly _settingsService: IVoidSettingsService, - // @IFileService private readonly _fileService: IFileService, @IVoidModelService private readonly _voidModelService: IVoidModelService, @IConvertToLLMMessageService private readonly _convertToLLMMessageService: IConvertToLLMMessageService, + @ILogService private readonly logService: ILogService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, + @ITreeSitterParserService private readonly _treeSitterParserService: ITreeSitterParserService, + @IFileService private readonly _fileService: IFileService, + @IEnvironmentService private readonly _environmentService: IEnvironmentService, ) { super(); @@ -240,10 +953,89 @@ class EditCodeService extends Disposable implements IEditCodeService { // add listeners for all existing editors + listen for editor being added for (let editor of this._codeEditorService.listCodeEditors()) { initializeEditor(editor) } this._register(this._codeEditorService.onCodeEditorAdd(editor => { initializeEditor(editor) })) + } + + private _getWorkspaceRelativePathForCmd(uri: URI): string { + try { + const ws = this._workspaceContextService.getWorkspace(); + const folders = ws?.folders ?? []; + if (folders.length === 0) return uri.fsPath; + + const norm = (p: string) => String(p ?? '').replace(/\\/g, '/').replace(/\/+$/g, ''); + const file = norm(uri.fsPath); + + for (const f of folders) { + const root = norm(f.uri.fsPath); + if (!root) continue; + + if (file === root) return '.'; + if (file.startsWith(root + '/')) { + const rel = file.slice(root.length + 1); + return rel || '.'; + } + } + } catch { /* ignore */ } + + return uri.fsPath; + } + + private async _formatDocumentAtUri(uri: URI): Promise { + try { + const editor = this._codeEditorService + .listCodeEditors() + .find(e => e.getModel()?.uri?.fsPath === uri.fsPath); + + if (!editor) { + this.logService.debug('[format] No editor found for uri:', uri.fsPath); + return; + } + + const action = editor.getAction?.('editor.action.formatDocument'); + if (!action) { + this.logService.debug('[format] No formatDocument action on editor for uri:', uri.fsPath); + return; + } + + this.logService.debug('[format] Running editor.action.formatDocument for:', uri.fsPath); + await action.run(); + + } catch (e: any) { + this.logService.warn('[format] Failed to format document:', uri.fsPath, e); + } + } + + public hasIdleDiffZoneForApplyBox(uri: URI, applyBoxId: string): boolean { + const setIds = this.diffAreasOfURI?.[uri.fsPath]; + if (!setIds || setIds.size === 0) return false; + + for (const id of Array.from(setIds)) { + const da = this.diffAreaOfId?.[id]; + if (da && da.type === 'DiffZone' && !da._streamState?.isStreaming && da.applyBoxId === applyBoxId) { + return true; + } + } + return false; + } + private _getEditFileSimpleDiffZoneForApplyBox(uri: URI, applyBoxId: string): (DiffZone & { _editFileSimple?: any }) | null { + const setIds = this.diffAreasOfURI?.[uri.fsPath]; + if (!setIds || setIds.size === 0) return null; + for (const id of Array.from(setIds)) { + const da = this.diffAreaOfId?.[id]; + if (da && da.type === 'DiffZone' && (da as any)._editFileSimple && da.applyBoxId === applyBoxId) { + return da as any; + } + } + return null; } + public async applyEditFileSimpleForApplyBox({ uri, applyBoxId }: { uri: URI; applyBoxId: string }): Promise { + const dz = this._getEditFileSimpleDiffZoneForApplyBox(uri, applyBoxId); + if (!dz) return false; + await this.applyEditFileSimpleFromDiffZone(dz as any); + return true; + } private _onUserChangeContent(uri: URI, e: IModelContentChangedEvent) { for (const change of e.changes) { @@ -268,34 +1060,14 @@ class EditCodeService extends Disposable implements IEditCodeService { } - public processRawKeybindingText(keybindingStr: string): string { return keybindingStr - .replace(/Enter/g, '↵') // ⏎ + // allow-any-unicode-next-line + .replace(/Enter/g, '↵') + // allow-any-unicode-next-line .replace(/Backspace/g, '⌫'); } - // private _notifyError = (e: Parameters[0]) => { - // const details = errorDetails(e.fullError) - // this._notificationService.notify({ - // severity: Severity.Warning, - // message: `Void Error: ${e.message}`, - // actions: { - // secondary: [{ - // id: 'void.onerror.opensettings', - // enabled: true, - // label: `Open Void's settings`, - // tooltip: '', - // class: undefined, - // run: () => { this._commandService.executeCommand(VOID_OPEN_SETTINGS_ACTION_ID) } - // }] - // }, - // source: details ? `(Hold ${isMacintosh ? 'Option' : 'Alt'} to hover) - ${details}\n\nIf this persists, feel free to [report](https://github.com/voideditor/void/issues/new) it.` : undefined - // }) - // } - - - // highlight the region private _addLineDecoration = (model: ITextModel | null, startLine: number, endLine: number, className: string, options?: Partial) => { if (model === null) return @@ -313,7 +1085,6 @@ class EditCodeService extends Disposable implements IEditCodeService { return disposeHighlight } - private _addDiffAreaStylesToURI = (uri: URI) => { const { model } = this._voidModelService.getModel(uri) @@ -342,7 +1113,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - private _computeDiffsAndAddStylesToURI = (uri: URI) => { const { model } = this._voidModelService.getModel(uri) if (model === null) return @@ -368,8 +1138,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - - mostRecentTextOfCtrlKZoneId: Record = {} private _addCtrlKZoneInput = (ctrlKZone: CtrlKZone) => { @@ -414,13 +1182,13 @@ class EditCodeService extends Disposable implements IEditCodeService { textAreaRef.current = r if (!textAreaRef.current) return - if (!(ctrlKZone.diffareaid in this.mostRecentTextOfCtrlKZoneId)) { // detect first mount this way (a hack) + if (!(ctrlKZone.diffareaid in this.mostRecentTextOfCtrlKZoneId)) { this.mostRecentTextOfCtrlKZoneId[ctrlKZone.diffareaid] = undefined setTimeout(() => textAreaRef.current?.focus(), 100) } }, onChangeHeight(height) { - if (height === 0) return // the viewZone sets this height to the container if it's out of view, ignore it + if (height === 0) return viewZone.heightInPx = height // re-render with this new height editor.changeViewZones(accessor => { @@ -455,15 +1223,12 @@ class EditCodeService extends Disposable implements IEditCodeService { } satisfies CtrlKZone['_mountInfo'] } - - private _refreshCtrlKInputs = async (uri: URI) => { for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) { const diffArea = this.diffAreaOfId[diffareaid] if (diffArea.type !== 'CtrlKZone') continue if (!diffArea._mountInfo) { diffArea._mountInfo = this._addCtrlKZoneInput(diffArea) - console.log('MOUNTED CTRLK', diffArea.diffareaid) } else { diffArea._mountInfo.refresh() @@ -471,7 +1236,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - private _addDiffStylesToURI = (uri: URI, diff: Diff) => { const { type, diffid } = diff @@ -491,9 +1255,9 @@ class EditCodeService extends Disposable implements IEditCodeService { // red in a view zone if (type !== 'insertion') { - const consistentZoneId = this._consistentItemService.addConsistentItemToURI({ + const consistentZoneId = (this._consistentItemService as any)?.addConsistentItemToURI?.({ uri, - fn: (editor) => { + fn: (editor: ICodeEditor) => { const domNode = document.createElement('div'); domNode.className = 'void-redBG' @@ -553,9 +1317,11 @@ class EditCodeService extends Disposable implements IEditCodeService { editor.changeViewZones(accessor => { zoneId = accessor.addZone(viewZone) }) return () => editor.changeViewZones(accessor => { if (zoneId) accessor.removeZone(zoneId) }) }, - }) + }) ?? null - disposeInThisEditorFns.push(() => { this._consistentItemService.removeConsistentItemFromURI(consistentZoneId) }) + if (consistentZoneId !== null) { + disposeInThisEditorFns.push(() => { (this._consistentItemService as any)?.removeConsistentItemFromURI?.(consistentZoneId) }) + } } @@ -564,9 +1330,9 @@ class EditCodeService extends Disposable implements IEditCodeService { const diffZone = this.diffAreaOfId[diff.diffareaid] if (diffZone.type === 'DiffZone' && !diffZone._streamState.isStreaming) { // Accept | Reject widget - const consistentWidgetId = this._consistentItemService.addConsistentItemToURI({ + const consistentWidgetId = (this._consistentItemService as any)?.addConsistentItemToURI?.({ uri, - fn: (editor) => { + fn: (editor: ICodeEditor) => { let startLine: number let offsetLines: number if (diff.type === 'insertion' || diff.type === 'edit') { @@ -590,8 +1356,18 @@ class EditCodeService extends Disposable implements IEditCodeService { const buttonsWidget = this._instantiationService.createInstance(AcceptRejectInlineWidget, { editor, onAccept: () => { - this.acceptDiff({ diffid }) - this._metricsService.capture('Accept Diff', { diffid }) + try { + const currentDiffZone = this.diffAreaOfId[diff.diffareaid] + const isEditFile = currentDiffZone && (currentDiffZone as any)._editFileSimple + void this.acceptDiff({ diffid }).then(() => { + this._metricsService.capture(isEditFile ? 'Accept Diff (edit_file)' : 'Accept Diff', { diffid }) + }).catch((e) => { + this._notificationService?.warn?.(`Accept failed: ${e?.message ?? String(e)}`) + this.logService.error('acceptDiff error:', e) + }) + } catch (e) { + this.logService.error('Error in onAccept handler:', e) + } }, onReject: () => { this.rejectDiff({ diffid }) @@ -603,8 +1379,10 @@ class EditCodeService extends Disposable implements IEditCodeService { }) return () => { buttonsWidget.dispose() } } - }) - disposeInThisEditorFns.push(() => { this._consistentItemService.removeConsistentItemFromURI(consistentWidgetId) }) + }) ?? null + if (consistentWidgetId !== null) { + disposeInThisEditorFns.push(() => { (this._consistentItemService as any)?.removeConsistentItemFromURI?.(consistentWidgetId) }) + } } const disposeInEditor = () => { disposeInThisEditorFns.forEach(f => f()) } @@ -612,9 +1390,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } - - - private _getActiveEditorURI(): URI | null { const editor = this._codeEditorService.getActiveCodeEditor() if (!editor) return null @@ -624,6 +1399,7 @@ class EditCodeService extends Disposable implements IEditCodeService { } weAreWriting = false + private readonly _activeBulkAcceptRejectUris = new Set() private _writeURIText(uri: URI, text: string, range_: IRange | 'wholeFileRange', { shouldRealignDiffAreas, }: { shouldRealignDiffAreas: boolean, }) { const { model } = this._voidModelService.getModel(uri) if (!model) { @@ -641,7 +1417,6 @@ class EditCodeService extends Disposable implements IEditCodeService { const oldRange = range this._realignAllDiffAreasLines(uri, newText, oldRange) } - const uriStr = model.getValue(EndOfLinePreference.LF) // heuristic check @@ -659,10 +1434,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } - - - - private _getCurrentVoidFileSnapshot = (uri: URI): VoidFileSnapshot => { const { model } = this._voidModelService.getModel(uri) const snapshottedDiffAreaOfId: Record = {} @@ -733,10 +1504,9 @@ class EditCodeService extends Disposable implements IEditCodeService { 'wholeFileRange', { shouldRealignDiffAreas: false } ) - // this._noLongerNeedModelReference(uri) } - private _addToHistory(uri: URI, opts?: { onWillUndo?: () => void }) { + private _addToHistory(uri: URI, opts?: { onWillUndo?: () => void; save?: boolean }) { const beforeSnapshot: VoidFileSnapshot = this._getCurrentVoidFileSnapshot(uri) let afterSnapshot: VoidFileSnapshot | null = null @@ -752,7 +1522,9 @@ class EditCodeService extends Disposable implements IEditCodeService { const onFinishEdit = async () => { afterSnapshot = this._getCurrentVoidFileSnapshot(uri) - await this._voidModelService.saveModel(uri) + if (opts?.save !== false) { + await this._voidModelService.saveModel(uri) + } } return { onFinishEdit } } @@ -801,7 +1573,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - // delete all diffs, update diffAreaOfId, update diffAreasOfModelId private _deleteDiffZone(diffZone: DiffZone) { this._clearAllDiffAreaEffects(diffZone) @@ -810,11 +1581,6 @@ class EditCodeService extends Disposable implements IEditCodeService { this._onDidAddOrDeleteDiffZones.fire({ uri: diffZone._URI }) } - private _deleteTrackingZone(trackingZone: TrackingZone) { - delete this.diffAreaOfId[trackingZone.diffareaid] - this.diffAreasOfURI[trackingZone._URI.fsPath]?.delete(trackingZone.diffareaid.toString()) - } - private _deleteCtrlKZone(ctrlKZone: CtrlKZone) { this._clearAllEffects(ctrlKZone._URI) ctrlKZone._mountInfo?.dispose() @@ -870,14 +1636,9 @@ class EditCodeService extends Disposable implements IEditCodeService { return newDiff } - - - // changes the start/line locations of all DiffAreas on the page (adjust their start/end based on the change) based on the change that was recently made private _realignAllDiffAreasLines(uri: URI, text: string, recentChange: { startLineNumber: number; endLineNumber: number }) { - // console.log('recent change', recentChange) - // compute net number of newlines lines that were added/removed const startLine = recentChange.startLineNumber const endLine = recentChange.endLineNumber @@ -890,12 +1651,10 @@ class EditCodeService extends Disposable implements IEditCodeService { // if the diffArea is entirely above the range, it is not affected if (diffArea.endLine < startLine) { - // console.log('CHANGE FULLY BELOW DA (doing nothing)') continue } // if a diffArea is entirely below the range, shift the diffArea up/down by the delta amount of newlines else if (endLine < diffArea.startLine) { - // console.log('CHANGE FULLY ABOVE DA') const changedRangeHeight = endLine - startLine + 1 const deltaNewlines = newTextHeight - changedRangeHeight diffArea.startLine += deltaNewlines @@ -903,20 +1662,17 @@ class EditCodeService extends Disposable implements IEditCodeService { } // if the diffArea fully contains the change, elongate it by the delta amount of newlines else if (startLine >= diffArea.startLine && endLine <= diffArea.endLine) { - // console.log('DA FULLY CONTAINS CHANGE') const changedRangeHeight = endLine - startLine + 1 const deltaNewlines = newTextHeight - changedRangeHeight diffArea.endLine += deltaNewlines } // if the change fully contains the diffArea, make the diffArea have the same range as the change else if (diffArea.startLine > startLine && diffArea.endLine < endLine) { - // console.log('CHANGE FULLY CONTAINS DA') diffArea.startLine = startLine diffArea.endLine = startLine + newTextHeight } // if the change contains only the diffArea's top else if (startLine < diffArea.startLine && diffArea.startLine <= endLine) { - // console.log('CHANGE CONTAINS TOP OF DA ONLY') const numOverlappingLines = endLine - diffArea.startLine + 1 const numRemainingLinesInDA = diffArea.endLine - diffArea.startLine + 1 - numOverlappingLines const newHeight = (numRemainingLinesInDA - 1) + (newTextHeight - 1) + 1 @@ -925,7 +1681,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } // if the change contains only the diffArea's bottom else if (startLine <= diffArea.endLine && diffArea.endLine < endLine) { - // console.log('CHANGE CONTAINS BOTTOM OF DA ONLY') const numOverlappingLines = diffArea.endLine - startLine + 1 diffArea.endLine += newTextHeight - numOverlappingLines } @@ -933,8 +1688,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } - - private _fireChangeDiffsIfNotStreaming(uri: URI) { for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) { const diffArea = this.diffAreaOfId[diffareaid] @@ -946,7 +1699,6 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - private _refreshStylesAndDiffsInURI(uri: URI) { // 1. clear DiffArea styles and Diffs @@ -965,88 +1717,6 @@ class EditCodeService extends Disposable implements IEditCodeService { this._fireChangeDiffsIfNotStreaming(uri) } - - - - // @throttle(100) - private _writeStreamedDiffZoneLLMText(uri: URI, originalCode: string, llmTextSoFar: string, deltaText: string, latestMutable: StreamLocationMutable) { - - let numNewLines = 0 - - // ----------- 1. Write the new code to the document ----------- - // figure out where to highlight based on where the AI is in the stream right now, use the last diff to figure that out - const computedDiffs = findDiffs(originalCode, llmTextSoFar) - - // if streaming, use diffs to figure out where to write new code - // these are two different coordinate systems - new and old line number - let endLineInLlmTextSoFar: number // get file[diffArea.startLine...newFileEndLine] with line=newFileEndLine highlighted - let startLineInOriginalCode: number // get original[oldStartingPoint...] (line in the original code, so starts at 1) - - const lastDiff = computedDiffs.pop() - - if (!lastDiff) { - // console.log('!lastDiff') - // if the writing is identical so far, display no changes - startLineInOriginalCode = 1 - endLineInLlmTextSoFar = 1 - } - else { - startLineInOriginalCode = lastDiff.originalStartLine - if (lastDiff.type === 'insertion' || lastDiff.type === 'edit') - endLineInLlmTextSoFar = lastDiff.endLine - else if (lastDiff.type === 'deletion') - endLineInLlmTextSoFar = lastDiff.startLine - else - throw new Error(`Void: diff.type not recognized on: ${lastDiff}`) - } - - // at the start, add a newline between the stream and originalCode to make reasoning easier - if (!latestMutable.addedSplitYet) { - this._writeURIText(uri, '\n', - { startLineNumber: latestMutable.line, startColumn: latestMutable.col, endLineNumber: latestMutable.line, endColumn: latestMutable.col, }, - { shouldRealignDiffAreas: true } - ) - latestMutable.addedSplitYet = true - numNewLines += 1 - } - - // insert deltaText at latest line and col - this._writeURIText(uri, deltaText, - { startLineNumber: latestMutable.line, startColumn: latestMutable.col, endLineNumber: latestMutable.line, endColumn: latestMutable.col }, - { shouldRealignDiffAreas: true } - ) - const deltaNumNewLines = deltaText.split('\n').length - 1 - latestMutable.line += deltaNumNewLines - const lastNewlineIdx = deltaText.lastIndexOf('\n') - latestMutable.col = lastNewlineIdx === -1 ? latestMutable.col + deltaText.length : deltaText.length - lastNewlineIdx - numNewLines += deltaNumNewLines - - // delete or insert to get original up to speed - if (latestMutable.originalCodeStartLine < startLineInOriginalCode) { - // moved up, delete - const numLinesDeleted = startLineInOriginalCode - latestMutable.originalCodeStartLine - this._writeURIText(uri, '', - { startLineNumber: latestMutable.line, startColumn: latestMutable.col, endLineNumber: latestMutable.line + numLinesDeleted, endColumn: Number.MAX_SAFE_INTEGER, }, - { shouldRealignDiffAreas: true } - ) - numNewLines -= numLinesDeleted - } - else if (latestMutable.originalCodeStartLine > startLineInOriginalCode) { - const newText = '\n' + originalCode.split('\n').slice((startLineInOriginalCode - 1), (latestMutable.originalCodeStartLine - 1) - 1 + 1).join('\n') - this._writeURIText(uri, newText, - { startLineNumber: latestMutable.line, startColumn: latestMutable.col, endLineNumber: latestMutable.line, endColumn: latestMutable.col }, - { shouldRealignDiffAreas: true } - ) - numNewLines += newText.split('\n').length - 1 - } - latestMutable.originalCodeStartLine = startLineInOriginalCode - - return { endLineInLlmTextSoFar, numNewLines } // numNewLines here might not be correct.... - } - - - - // called first, then call startApplying public addCtrlKZone({ startLine, endLine, editor }: AddCtrlKOpts) { @@ -1104,10 +1774,9 @@ class EditCodeService extends Disposable implements IEditCodeService { onFinishEdit() } - - - private _getURIBeforeStartApplying(opts: CallBeforeStartApplyingOpts) { + this.logService.debug('[DEBUG] _getURIBeforeStartApplying opts:', JSON.stringify(opts, null, 2)); + // SR if (opts.from === 'ClickApply') { const uri = this._uriOfGivenURI(opts.uri) @@ -1115,95 +1784,265 @@ class EditCodeService extends Disposable implements IEditCodeService { return uri } else if (opts.from === 'QuickEdit') { - const { diffareaid } = opts + const { diffareaid } = opts as any + this.logService.debug('[DEBUG] QuickEdit branch, diffareaid:', diffareaid); const ctrlKZone = this.diffAreaOfId[diffareaid] - if (ctrlKZone?.type !== 'CtrlKZone') return + this.logService.debug('[DEBUG] ctrlKZone:', ctrlKZone); + if (ctrlKZone?.type !== 'CtrlKZone') { + this.logService.debug('[DEBUG] Invalid ctrlKZone or wrong type'); + return + } const { _URI: uri } = ctrlKZone + this.logService.debug('[DEBUG] URI from ctrlKZone:', uri.toString()); return uri } return } - public async callBeforeApplyOrEdit(givenURI: URI | 'current') { - const uri = this._uriOfGivenURI(givenURI) - if (!uri) return + public async callBeforeApplyOrEdit(givenURI: URI | 'current' | CallBeforeStartApplyingOpts) { + this.logService.debug('[DEBUG] callBeforeApplyOrEdit givenURI:', JSON.stringify(givenURI)); + + let uri: URI | undefined; + if (givenURI === 'current' || URI.isUri(givenURI)) { + uri = this._uriOfGivenURI(givenURI as URI | 'current'); + } else { + uri = this._getURIBeforeStartApplying(givenURI); + } + + if (!uri) { + this.logService.debug('[DEBUG] No URI found in callBeforeApplyOrEdit'); + return + } + this.logService.debug('[DEBUG] Initializing model with URI:', JSON.stringify(uri)); await this._voidModelService.initializeModel(uri) await this._voidModelService.saveModel(uri) // save the URI + this._prewarmAstForUri(uri).catch(e => { + this.logService.debug('[apply-ast] prewarm failed', uri.fsPath, e); + }); } - - // the applyDonePromise this returns can reject, and should be caught with .catch public startApplying(opts: StartApplyingOpts): [URI, Promise] | null { + this.logService.debug('[startApplying] Called with opts:', JSON.stringify({ + from: opts.from, + uri: (opts as any).uri?.toString?.(), + applyStr: (opts as any).applyStr?.substring?.(0, 100) + '...', + applyBoxId: (opts as any).applyBoxId + })) + let res: [DiffZone, Promise] | undefined = undefined if (opts.from === 'QuickEdit') { - res = this._initializeWriteoverStream(opts) // rewrite + this.logService.debug('[startApplying] QuickEdit branch') + res = this._initializeWriteoverStream(opts) } else if (opts.from === 'ClickApply') { - if (this._settingsService.state.globalSettings.enableFastApply) { - const numCharsInFile = this._fileLengthOfGivenURI(opts.uri) - if (numCharsInFile === null) return null - if (numCharsInFile < 1000) { // slow apply for short files (especially important for empty files) - res = this._initializeWriteoverStream(opts) - } - else { - res = this._initializeSearchAndReplaceStream(opts) // fast apply - } - } - else { - res = this._initializeWriteoverStream(opts) // rewrite - } + this.logService.debug('[startApplying] ClickApply branch') + res = this._handleClickApply(opts) + } + + if (!res) { + this.logService.debug('[startApplying] No result, returning null') + return null } - if (!res) return null const [diffZone, applyDonePromise] = res + this.logService.debug('[startApplying] Success, returning URI:', diffZone._URI.toString()) return [diffZone._URI, applyDonePromise] } + private _handleClickApply(opts: StartApplyingOpts): [DiffZone, Promise] | undefined { + const startOpts = opts as any + this.logService.debug('[_handleClickApply] Start with opts:', JSON.stringify({ + uri: startOpts.uri?.toString?.(), + hasApplyStr: !!startOpts.applyStr, + applyStrLength: startOpts.applyStr?.length, + applyBoxId: startOpts.applyBoxId + })) - public instantlyApplySearchReplaceBlocks({ uri, searchReplaceBlocks }: { uri: URI, searchReplaceBlocks: string }) { - // start diffzone - const res = this._startStreamingDiffZone({ - uri, - streamRequestIdRef: { current: null }, - startBehavior: 'keep-conflicts', - linkedCtrlKZone: null, - onWillUndo: () => { }, - }) - if (!res) return - const { diffZone, onFinishEdit } = res - - - const onDone = () => { - diffZone._streamState = { isStreaming: false, } - this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }) - this._refreshStylesAndDiffsInURI(uri) - onFinishEdit() - - // auto accept - if (this._settingsService.state.globalSettings.autoAcceptLLMChanges) { - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: false, behavior: 'accept' }) - } + // Try to infer ORIGINAL and create preview + const inferredResult = this._tryInferAndPreview(startOpts) + if (inferredResult) { + this.logService.debug('[_handleClickApply] Inferred result found, using it') + return inferredResult } + // Fallback: reuse existing preview DiffZone + this.logService.debug('[_handleClickApply] No inferred result, falling back to existing preview') + const fallbackResult = this._previewAndPrepareEditFileSimple(startOpts) + this.logService.debug('[_handleClickApply] Fallback result:', fallbackResult ? 'found' : 'not found') + return fallbackResult + } - const onError = (e: { message: string; fullError: Error | null; }) => { - // this._notifyError(e) - onDone() - this._undoHistory(uri) - throw e.fullError || new Error(e.message) - } + private _getFileTextWithEol(uri: URI): { text: string | null, eol: '\n' | '\r\n' } { + try { + const { model } = this._voidModelService.getModel(uri) + if (!model) return { text: null, eol: '\n' } + const eol = (model.getEOL?.() === '\r\n') ? '\r\n' : '\n' + const text = model.getValue() + return { text, eol } + } catch (e) { + this.logService.error('[_getFileTextWithEol] Failed:', e) + return { text: null, eol: '\n' } + } + } + + private _inferOriginalSnippet(applyStr: string, fileText: string, uri?: URI): InferredBlock | null { + this.logService.debug('[_inferOriginalSnippet] Inferring with:', JSON.stringify({ + applyStrLength: applyStr.length, + fileTextLength: fileText.length, + applyStrPreview: applyStr.substring(0, 50) + '...' + })); try { - this._instantlyApplySRBlocks(uri, searchReplaceBlocks) - } - catch (e) { - onError({ message: e + '', fullError: null }) - } + const model = uri ? this._modelService.getModel(uri) : null; + const astContext = uri ? this._getCachedAstContext(uri, model) : null; + const inferred = inferExactBlockFromCode({ + codeStr: applyStr, + fileText, + astContext: astContext ?? undefined + }); + + this.logService.debug('[_inferOriginalSnippet] Inference result:', JSON.stringify({ + hasResult: !!inferred, + hasText: !!(inferred as any)?.text, + textLength: (inferred as any)?.text?.length, + range: (inferred as any)?.range, + offsets: (inferred as any)?.offsets, + occurrence: (inferred as any)?.occurrence, + astSource: astContext?.source ?? null, + preview: (inferred as any)?.text?.substring(0, 50) + '...' + })); + + if (!inferred || !(inferred as any).text) return inferred; + + const inferredText = String((inferred as any).text ?? ''); + const inferredNorm = normalizeEol(inferredText); + const applyNorm = normalizeEol(applyStr); + + const inferredLines = inferredNorm.split('\n').length; + const applyLines = applyNorm.split('\n').length; + + const inferredStart = + (Array.isArray((inferred as any).offsets) ? (inferred as any).offsets[0] : null) ?? + fileText.indexOf(inferredText); + + // Suspicious when: + // - inferred is a single line but applyStr is multi-line + // - inferred is very short compared to applyStr + // - inferred seems to be a prefix (common: "public foo({ a }") + const suspiciouslyShort = + (inferredLines === 1 && applyLines > 1) || + inferredText.length < 80 || + (inferredText.length < Math.max(40, Math.floor(applyStr.length * 0.25))); + + const canSafelyExpand = + suspiciouslyShort && + inferredStart !== -1 && + looksLikeFullTopLevelBlockSnippet(applyStr); + + if (!canSafelyExpand) { + return inferred; + } - onDone() - } + // Expand to full enclosing { ... } block, starting from the line start + const lineStart = Math.max(0, fileText.lastIndexOf('\n', Math.max(0, inferredStart - 1)) + 1); + const expanded = expandToEnclosingCurlyBlockJs(fileText, lineStart); + + if (!expanded || !expanded.text || expanded.text.length <= inferredText.length) { + this.logService.debug('[_inferOriginalSnippet] Expansion skipped (no better block found).', JSON.stringify({ + inferredStart, + inferredLen: inferredText.length, + expandedLen: expanded?.text?.length ?? null + })); + return inferred; + } + // Compute occurrence for expanded text based on start offset (so replace picks the right one if repeated) + const occurrences: number[] = []; + for (let from = 0; ;) { + const i = fileText.indexOf(expanded.text, from); + if (i === -1) break; + occurrences.push(i); + from = i + Math.max(1, expanded.text.length); + } + const occIdx = occurrences.indexOf(expanded.startOffset); + const occurrence = occIdx >= 0 ? (occIdx + 1) : 1; + + (inferred as any).text = expanded.text; + (inferred as any).range = expanded.range; + (inferred as any).offsets = [expanded.startOffset, expanded.endOffset]; + (inferred as any).occurrence = occurrence; + + this.logService.debug('[_inferOriginalSnippet] Expanded inferred ORIGINAL to enclosing block.', JSON.stringify({ + oldLen: inferredText.length, + newLen: expanded.text.length, + oldLines: inferredLines, + newLines: expanded.text.split('\n').length, + newRange: expanded.range, + occurrence + })); + + return inferred; + } catch (e) { + this.logService.error('[_inferOriginalSnippet] inferExactBlockFromCode failed:', JSON.stringify({ + error: (e as any)?.message || String(e), + applyStrLength: applyStr.length + })); + return null; + } + } + + private _tryInferAndPreview(startOpts: any): [DiffZone, Promise] | undefined { + this.logService.debug('[_tryInferAndPreview] Starting inference') + + const maybeUri = this._uriOfGivenURI(startOpts.uri) + this.logService.debug('[_tryInferAndPreview] Resolved URI:', maybeUri?.toString() || 'null') + if (!maybeUri) return undefined + + const { text: fileText, eol } = this._getFileTextWithEol(maybeUri) + this.logService.debug('[_tryInferAndPreview] File text retrieved:', JSON.stringify({ hasText: !!fileText, textLength: fileText?.length, eol })) + if (!fileText) return undefined + + const inferred = this._inferOriginalSnippet(startOpts.applyStr, fileText, maybeUri) + this.logService.debug('[_tryInferAndPreview] Inferred block:', JSON.stringify({ + ok: !!inferred, textLength: inferred?.text?.length, range: inferred?.range, offsets: inferred?.offsets, occurrence: inferred?.occurrence + })) + if (!inferred) return undefined + + + const foundAt = + (Array.isArray((inferred as any).offsets) ? (inferred as any).offsets[0] : null) ?? + fileText.indexOf(inferred.text); + this.logService.debug('[_tryInferAndPreview] Exact text recheck in model:', JSON.stringify({ foundAt })) + + const updatedWithModelEol = startOpts.applyStr.replace(/\r\n|\n/g, eol) + + const previewParams = { + uri: maybeUri, + originalSnippet: inferred.text, + updatedSnippet: updatedWithModelEol, + occurrence: inferred.occurrence, + replaceAll: false, + locationHint: { startLineNumber: inferred.range[0], endLineNumber: inferred.range[1] }, + encoding: null, + newline: eol, + applyBoxId: startOpts.applyBoxId, + } + + this.logService.debug('[_tryInferAndPreview] Calling _previewAndPrepareEditFileSimple with params:', JSON.stringify({ + uri: previewParams.uri.toString(), + originalLength: previewParams.originalSnippet.length, + updatedLength: previewParams.updatedSnippet.length, + occurrence: previewParams.occurrence, + locationHint: previewParams.locationHint, + newline: previewParams.newline, + applyBoxId: previewParams.applyBoxId + })) + + const result = this._previewAndPrepareEditFileSimple(previewParams) + this.logService.debug('[_tryInferAndPreview] Preview result:', result ? 'success' : 'failed') + return result + } public instantlyRewriteFile({ uri, newContent }: { uri: URI, newContent: string }) { // start diffzone @@ -1213,6 +2052,7 @@ class EditCodeService extends Disposable implements IEditCodeService { startBehavior: 'keep-conflicts', linkedCtrlKZone: null, onWillUndo: () => { }, + applyBoxId: undefined, }) if (!res) return const { diffZone, onFinishEdit } = res @@ -1223,51 +2063,44 @@ class EditCodeService extends Disposable implements IEditCodeService { this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }) this._refreshStylesAndDiffsInURI(uri) onFinishEdit() - - // auto accept - if (this._settingsService.state.globalSettings.autoAcceptLLMChanges) { - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: false, behavior: 'accept' }) - } } this._writeURIText(uri, newContent, 'wholeFileRange', { shouldRealignDiffAreas: true }) onDone() } - private _findOverlappingDiffArea({ startLine, endLine, uri, filter }: { startLine: number, endLine: number, uri: URI, filter?: (diffArea: DiffArea) => boolean }): DiffArea | null { // check if there's overlap with any other diffAreas and return early if there is for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) { const diffArea = this.diffAreaOfId[diffareaid] - if (!diffArea) continue - if (!filter?.(diffArea)) continue - const noOverlap = diffArea.startLine > endLine || diffArea.endLine < startLine + if (!diffArea) { + continue; + } + if (!filter?.(diffArea)) { + continue; + } + const noOverlap = diffArea.startLine > endLine || diffArea.endLine < startLine; if (!noOverlap) { - return diffArea + return diffArea; } } - return null + return null; } - - - - - - - private _startStreamingDiffZone({ uri, startBehavior, streamRequestIdRef, linkedCtrlKZone, onWillUndo, + applyBoxId, }: { uri: URI, startBehavior: 'accept-conflicts' | 'reject-conflicts' | 'keep-conflicts', streamRequestIdRef: { current: string | null }, linkedCtrlKZone: CtrlKZone | null, onWillUndo: () => void, + applyBoxId?: string, }) { const { model } = this._voidModelService.getModel(uri) if (!model) return @@ -1292,7 +2125,7 @@ class EditCodeService extends Disposable implements IEditCodeService { } else { // keep conflict on whole file - to keep conflict, revert the change and use those contents as original, then un-revert the file - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: 'reject', _addToHistory: false }) + // this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: 'reject', _addToHistory: false }) const oldFileStr = model.getValue(EndOfLinePreference.LF) // use this as original code this._writeURIText(uri, originalFileStr, 'wholeFileRange', { shouldRealignDiffAreas: true }) // un-revert originalCode = oldFileStr @@ -1300,8 +2133,9 @@ class EditCodeService extends Disposable implements IEditCodeService { } else if (startBehavior === 'accept-conflicts' || startBehavior === 'reject-conflicts') { - const behavior = startBehavior === 'accept-conflicts' ? 'accept' : 'reject' - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior, _addToHistory: false }) + + // const behavior: 'accept' | 'reject' = startBehavior === 'accept-conflicts' ? 'accept' : 'reject' + // this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior, _addToHistory: false }) } const adding: Omit = { @@ -1317,9 +2151,11 @@ class EditCodeService extends Disposable implements IEditCodeService { }, _diffOfId: {}, // added later _removeStylesFns: new Set(), + applyBoxId: applyBoxId, } const diffZone = this._addDiffArea(adding) + this.logService.debug(`[_startStreamingDiffZone] Created DiffZone with applyBoxId: ${applyBoxId}, diffareaid: ${diffZone.diffareaid}`) this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }) this._onDidAddOrDeleteDiffZones.fire({ uri }) @@ -1334,239 +2170,22 @@ class EditCodeService extends Disposable implements IEditCodeService { return { diffZone, onFinishEdit } } - - - private _uriIsStreaming(uri: URI) { const diffAreas = this.diffAreasOfURI[uri.fsPath] if (!diffAreas) return false for (const diffareaid of diffAreas) { - const diffArea = this.diffAreaOfId[diffareaid] - if (diffArea?.type !== 'DiffZone') continue - if (diffArea._streamState.isStreaming) return true - } - return false - } - - - private _initializeWriteoverStream(opts: StartApplyingOpts): [DiffZone, Promise] | undefined { - - const { from, } = opts - const featureName: FeatureName = opts.from === 'ClickApply' ? 'Apply' : 'Ctrl+K' - const overridesOfModel = this._settingsService.state.overridesOfModel - const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] - const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined - - - const uri = this._getURIBeforeStartApplying(opts) - if (!uri) return - - let startRange: 'fullFile' | [number, number] - let ctrlKZoneIfQuickEdit: CtrlKZone | null = null - - if (from === 'ClickApply') { - startRange = 'fullFile' - } - else if (from === 'QuickEdit') { - const { diffareaid } = opts - const ctrlKZone = this.diffAreaOfId[diffareaid] - if (ctrlKZone?.type !== 'CtrlKZone') return - ctrlKZoneIfQuickEdit = ctrlKZone - const { startLine: startLine_, endLine: endLine_ } = ctrlKZone - startRange = [startLine_, endLine_] - } - else { - throw new Error(`Void: diff.type not recognized on: ${from}`) - } - - const { model } = this._voidModelService.getModel(uri) - if (!model) return - - let streamRequestIdRef: { current: string | null } = { current: null } // can use this as a proxy to set the diffArea's stream state requestId - - // build messages - const quickEditFIMTags = defaultQuickEditFimTags // TODO can eventually let users customize modelFimTags - const originalFileCode = model.getValue(EndOfLinePreference.LF) - const originalCode = startRange === 'fullFile' ? originalFileCode : originalFileCode.split('\n').slice((startRange[0] - 1), (startRange[1] - 1) + 1).join('\n') - const language = model.getLanguageId() - let messages: LLMChatMessage[] - let separateSystemMessage: string | undefined - if (from === 'ClickApply') { - const { messages: a, separateSystemMessage: b } = this._convertToLLMMessageService.prepareLLMSimpleMessages({ - systemMessage: rewriteCode_systemMessage, - simpleMessages: [{ role: 'user', content: rewriteCode_userMessage({ originalCode, applyStr: opts.applyStr, language }), }], - featureName, - modelSelection, - }) - messages = a - separateSystemMessage = b - } - else if (from === 'QuickEdit') { - if (!ctrlKZoneIfQuickEdit) return - const { _mountInfo } = ctrlKZoneIfQuickEdit - const instructions = _mountInfo?.textAreaRef.current?.value ?? '' - - const startLine = startRange === 'fullFile' ? 1 : startRange[0] - const endLine = startRange === 'fullFile' ? model.getLineCount() : startRange[1] - const { prefix, suffix } = voidPrefixAndSuffix({ fullFileStr: originalFileCode, startLine, endLine }) - const userContent = ctrlKStream_userMessage({ selection: originalCode, instructions: instructions, prefix, suffix, fimTags: quickEditFIMTags, language }) - - const { messages: a, separateSystemMessage: b } = this._convertToLLMMessageService.prepareLLMSimpleMessages({ - systemMessage: ctrlKStream_systemMessage({ quickEditFIMTags: quickEditFIMTags }), - simpleMessages: [{ role: 'user', content: userContent, }], - featureName, - modelSelection, - }) - messages = a - separateSystemMessage = b - - } - else { throw new Error(`featureName ${from} is invalid`) } - - // if URI is already streaming, return (should never happen, caller is responsible for checking) - if (this._uriIsStreaming(uri)) return - - // start diffzone - const res = this._startStreamingDiffZone({ - uri, - streamRequestIdRef, - startBehavior: opts.startBehavior, - linkedCtrlKZone: ctrlKZoneIfQuickEdit, - onWillUndo: () => { - if (streamRequestIdRef.current) { - this._llmMessageService.abort(streamRequestIdRef.current) - } - }, - - }) - if (!res) return - const { diffZone, onFinishEdit, } = res - - - // helpers - const onDone = () => { - console.log('called onDone') - diffZone._streamState = { isStreaming: false, } - this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }) - - if (ctrlKZoneIfQuickEdit) { - const ctrlKZone = ctrlKZoneIfQuickEdit - - ctrlKZone._linkedStreamingDiffZone = null - this._onDidChangeStreamingInCtrlKZone.fire({ uri, diffareaid: ctrlKZone.diffareaid }) - this._deleteCtrlKZone(ctrlKZone) - } - this._refreshStylesAndDiffsInURI(uri) - onFinishEdit() - - // auto accept - if (this._settingsService.state.globalSettings.autoAcceptLLMChanges) { - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: false, behavior: 'accept' }) - } - } - - // throws - const onError = (e: { message: string; fullError: Error | null; }) => { - // this._notifyError(e) - onDone() - this._undoHistory(uri) - throw e.fullError || new Error(e.message) - } - - const extractText = (fullText: string, recentlyAddedTextLen: number) => { - if (from === 'QuickEdit') { - return extractCodeFromFIM({ text: fullText, recentlyAddedTextLen, midTag: quickEditFIMTags.midTag }) + const diffArea = this.diffAreaOfId[diffareaid]; + if (diffArea?.type !== 'DiffZone') { + continue; } - else if (from === 'ClickApply') { - return extractCodeFromRegular({ text: fullText, recentlyAddedTextLen }) + if (diffArea._streamState.isStreaming) { + return true; } - throw new Error('Void 1') } - - // refresh now in case onText takes a while to get 1st message - this._refreshStylesAndDiffsInURI(uri) - - const latestStreamLocationMutable: StreamLocationMutable = { line: diffZone.startLine, addedSplitYet: false, col: 1, originalCodeStartLine: 1 } - - // allowed to throw errors - this is called inside a promise that handles everything - const runWriteover = async () => { - let shouldSendAnotherMessage = true - while (shouldSendAnotherMessage) { - shouldSendAnotherMessage = false - - let resMessageDonePromise: () => void = () => { } - const messageDonePromise = new Promise((res_) => { resMessageDonePromise = res_ }) - - // state used in onText: - let fullTextSoFar = '' // so far (INCLUDING ignored suffix) - let prevIgnoredSuffix = '' - let aborted = false - let weAreAborting = false - - - streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ - messagesType: 'chatMessages', - logging: { loggingName: `Edit (Writeover) - ${from}` }, - messages, - modelSelection, - modelSelectionOptions, - overridesOfModel, - separateSystemMessage, - chatMode: null, // not chat - onText: (params) => { - const { fullText: fullText_ } = params - const newText_ = fullText_.substring(fullTextSoFar.length, Infinity) - - const newText = prevIgnoredSuffix + newText_ // add the previously ignored suffix because it's no longer the suffix! - fullTextSoFar += newText // full text, including ```, etc - - const [croppedText, deltaCroppedText, croppedSuffix] = extractText(fullTextSoFar, newText.length) - const { endLineInLlmTextSoFar } = this._writeStreamedDiffZoneLLMText(uri, originalCode, croppedText, deltaCroppedText, latestStreamLocationMutable) - diffZone._streamState.line = (diffZone.startLine - 1) + endLineInLlmTextSoFar // change coordinate systems from originalCode to full file - - this._refreshStylesAndDiffsInURI(uri) - - prevIgnoredSuffix = croppedSuffix - }, - onFinalMessage: (params) => { - const { fullText } = params - // console.log('DONE! FULL TEXT\n', extractText(fullText), diffZone.startLine, diffZone.endLine) - // at the end, re-write whole thing to make sure no sync errors - const [croppedText, _1, _2] = extractText(fullText, 0) - this._writeURIText(uri, croppedText, - { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER }, // 1-indexed - { shouldRealignDiffAreas: true } - ) - - onDone() - resMessageDonePromise() - }, - onError: (e) => { - onError(e) - }, - onAbort: () => { - if (weAreAborting) return - // stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it) - aborted = true - resMessageDonePromise() - }, - }) - // should never happen, just for safety - if (streamRequestIdRef.current === null) { return } - - await messageDonePromise - if (aborted) { - throw new Error(`Edit was interrupted by the user.`) - } - } // end while - } // end writeover - - const applyDonePromise = new Promise((res, rej) => { runWriteover().then(res).catch(rej) }) - return [diffZone, applyDonePromise] + return false; } - _uriOfGivenURI(givenURI: URI | 'current') { if (givenURI === 'current') { const uri_ = this._getActiveEditorURI() @@ -1575,6 +2194,7 @@ class EditCodeService extends Disposable implements IEditCodeService { } return givenURI } + _fileLengthOfGivenURI(givenURI: URI | 'current') { const uri = this._uriOfGivenURI(givenURI) if (!uri) return null @@ -1584,437 +2204,373 @@ class EditCodeService extends Disposable implements IEditCodeService { return numCharsInFile } + public async acceptOrRejectDiffAreasByApplyBox( + { uri, applyBoxId, behavior }: { uri: URI; applyBoxId: string; behavior: 'accept' | 'reject' } + ): Promise { + const diffareaids = this.diffAreasOfURI[uri.fsPath]; + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] start uri=${uri.fsPath} applyBoxId=${applyBoxId} behavior=${behavior} totalAreas=${diffareaids?.size ?? 0}`); + if (!diffareaids || diffareaids.size === 0) return; + + const { onFinishEdit } = this._addToHistory(uri); + const serializeZones = (zones: DiffZone[]) => { + try { + return JSON.stringify(zones.map(z => ({ + diffareaid: z.diffareaid, + startLine: z.startLine, + endLine: z.endLine, + applyBoxId: z.applyBoxId ?? null, + isEditFileSimple: !!(z as any)._editFileSimple + }))); + } catch { + return String(zones.map(z => z.diffareaid).join(',')); + } + }; + + if (behavior === 'reject') { + const diffZones: DiffZone[] = []; + for (const id of diffareaids) { + const da = this.diffAreaOfId[id]; + if (da && da.type === 'DiffZone' && da.applyBoxId === applyBoxId) { + diffZones.push(da); + } + } + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] reject targetZones(beforeSort)=${serializeZones(diffZones)}`); + // Revert bottom-to-top so earlier rewrites do not shift later ranges. + diffZones.sort((a, b) => { + if (a.startLine !== b.startLine) return b.startLine - a.startLine; + return b.diffareaid - a.diffareaid; + }); + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] reject targetZones(sorted)=${serializeZones(diffZones)}`); + for (const dz of diffZones) { + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] reject revert diffareaid=${dz.diffareaid} start=${dz.startLine} end=${dz.endLine}`); + this._revertDiffZone(dz); + this._deleteDiffZone(dz); + } - /** - * Generates a human-readable error message for an invalid ORIGINAL search block. - */ - private _errContentOfInvalidStr = ( - str: 'Not found' | 'Not unique' | 'Has overlap', - blockOrig: string, - ): string => { - const problematicCode = `${tripleTick[0]}\n${JSON.stringify(blockOrig)}\n${tripleTick[1]}` - - // use a switch for better readability / exhaustiveness check - let descStr: string - switch (str) { - case 'Not found': - descStr = `The edit was not applied. The text in ORIGINAL must EXACTLY match lines of code in the file, but there was no match for:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code matches a code excerpt exactly.` - break - case 'Not unique': - descStr = `The edit was not applied. The text in ORIGINAL must be unique in the file being edited, but the following ORIGINAL code appears multiple times in the file:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code is unique.` - break - case 'Has overlap': - descStr = `The edit was not applied. The text in the ORIGINAL blocks must not overlap, but the following ORIGINAL code had overlap with another ORIGINAL string:\n${problematicCode}. Ensure you have the latest version of the file, and ensure the ORIGINAL code blocks do not overlap.` - break - default: - descStr = '' + this._refreshStylesAndDiffsInURI(uri); + await onFinishEdit(); + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] done uri=${uri.fsPath} applyBoxId=${applyBoxId} behavior=reject remainingAreas=${this.diffAreasOfURI[uri.fsPath]?.size ?? 0}`); + return; } - return descStr - } - - private _instantlyApplySRBlocks(uri: URI, blocksStr: string) { - const blocks = extractSearchReplaceBlocks(blocksStr) - if (blocks.length === 0) throw new Error(`No Search/Replace blocks were received!`) - - const { model } = this._voidModelService.getModel(uri) - if (!model) throw new Error(`Error applying Search/Replace blocks: File does not exist.`) - const modelStr = model.getValue(EndOfLinePreference.LF) - // .split('\n').map(l => '\t' + l).join('\n') // for testing purposes only, remember to remove this - const modelStrLines = modelStr.split('\n') + // === accept === + const acceptedZones: DiffZone[] = []; + for (const id of diffareaids) { + const da = this.diffAreaOfId[id]; + if (da && da.type === 'DiffZone' && da.applyBoxId === applyBoxId) { + acceptedZones.push(da); + this._deleteDiffZone(da); + } + } + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] accept deletedZones=${serializeZones(acceptedZones)}`); + this._refreshStylesAndDiffsInURI(uri); + // Auto format after accept (Format Document) + await this._formatDocumentAtUri(uri); + // Formatting may change lines → refresh decorations again + this._refreshStylesAndDiffsInURI(uri); - const replacements: { origStart: number; origEnd: number; block: ExtractedSearchReplaceBlock }[] = [] - for (const b of blocks) { - const res = findTextInCode(b.orig, modelStr, true, { returnType: 'lines' }) - if (typeof res === 'string') - throw new Error(this._errContentOfInvalidStr(res, b.orig)) - let [startLine, endLine] = res - startLine -= 1 // 0-index - endLine -= 1 + await onFinishEdit(); + this.logService.debug(`[acceptOrRejectDiffAreasByApplyBox] done uri=${uri.fsPath} applyBoxId=${applyBoxId} behavior=accept remainingAreas=${this.diffAreasOfURI[uri.fsPath]?.size ?? 0}`); + } - // including newline before start - const origStart = (startLine !== 0 ? - modelStrLines.slice(0, startLine).join('\n') + '\n' - : '').length + private _previewAndPrepareEditFileSimple( + paramsOrOpts: StartApplyingOpts | { + uri: URI; originalSnippet: string; updatedSnippet: string; + occurrence?: number | null; replaceAll?: boolean; + locationHint?: any; encoding?: string | null; newline?: string | null; applyBoxId?: string + } + ): [DiffZone, Promise] | undefined { + const safeStringify = (o: any) => { try { return JSON.stringify(o, null, 2) } catch { return String(o) } } - // including endline at end - const origEnd = modelStrLines.slice(0, endLine + 1).join('\n').length - 1 - replacements.push({ origStart, origEnd, block: b }); - } - // sort in increasing order - replacements.sort((a, b) => a.origStart - b.origStart) + if ((paramsOrOpts as StartApplyingOpts).from) { + const opts = paramsOrOpts as StartApplyingOpts + const uri = this._getURIBeforeStartApplying(opts) + this.logService.debug(`[_previewAndPrepareEditFileSimple] called with StartApplyingOpts: ${safeStringify({ opts, resolvedUri: uri?.fsPath ?? null })}`) + if (!uri) return undefined - // ensure no overlap - for (let i = 1; i < replacements.length; i++) { - if (replacements[i].origStart <= replacements[i - 1].origEnd) { - throw new Error(this._errContentOfInvalidStr('Has overlap', replacements[i]?.block?.orig)) + for (const diffareaid of this.diffAreasOfURI[uri.fsPath] || []) { + const da = this.diffAreaOfId[diffareaid] + if (da?.type === 'DiffZone' && (da as any)._editFileSimple) { + const optsApplyBoxId = opts.from === 'ClickApply' ? opts.applyBoxId : undefined + if (optsApplyBoxId && da.applyBoxId === optsApplyBoxId) { + return [da as DiffZone, Promise.resolve()] + } + } } + this.logService.debug(`[_previewAndPrepareEditFileSimple] No preview available via UI state: ${safeStringify({ uri: uri.fsPath })}`) + return undefined } - // apply each replacement from right to left (so indexes don't shift) - let newCode: string = modelStr - for (let i = replacements.length - 1; i >= 0; i--) { - const { origStart, origEnd, block } = replacements[i] - newCode = newCode.slice(0, origStart) + block.final + newCode.slice(origEnd + 1, Infinity) - } - this._writeURIText(uri, newCode, - 'wholeFileRange', - { shouldRealignDiffAreas: true } - ) - } + this.logService.debug(`[_previewAndPrepareEditFileSimple] called with explicit params: ${safeStringify(paramsOrOpts)}`) - private _initializeSearchAndReplaceStream(opts: StartApplyingOpts & { from: 'ClickApply' }): [DiffZone, Promise] | undefined { - const { from, applyStr, } = opts - const featureName: FeatureName = 'Apply' - const overridesOfModel = this._settingsService.state.overridesOfModel - const modelSelection = this._settingsService.state.modelSelectionOfFeature[featureName] - const modelSelectionOptions = modelSelection ? this._settingsService.state.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName] : undefined - const uri = this._getURIBeforeStartApplying(opts) - if (!uri) return + const opts = paramsOrOpts as StartApplyingOpts + const applyBoxId = opts.from === 'ClickApply' ? opts.applyBoxId : + (paramsOrOpts as any).applyBoxId - const { model } = this._voidModelService.getModel(uri) - if (!model) return + const { uri, originalSnippet, updatedSnippet, occurrence, replaceAll, locationHint, encoding, newline } = + paramsOrOpts as { uri: URI; originalSnippet: string; updatedSnippet: string; occurrence?: number | null; replaceAll?: boolean; locationHint?: any; encoding?: string | null; newline?: string | null } - let streamRequestIdRef: { current: string | null } = { current: null } // can use this as a proxy to set the diffArea's stream state requestId + const beforeIds = new Set(this.diffAreasOfURI[uri.fsPath] || []) - // build messages - ask LLM to generate search/replace block text - const originalFileCode = model.getValue(EndOfLinePreference.LF) - const userMessageContent = searchReplaceGivenDescription_userMessage({ originalCode: originalFileCode, applyStr: applyStr }) - const { messages, separateSystemMessage: separateSystemMessage } = this._convertToLLMMessageService.prepareLLMSimpleMessages({ - systemMessage: searchReplaceGivenDescription_systemMessage, - simpleMessages: [{ role: 'user', content: userMessageContent, }], - featureName, - modelSelection, - }) - // if URI is already streaming, return (should never happen, caller is responsible for checking) - if (this._uriIsStreaming(uri)) return + this.logService.debug(`[_previewAndPrepareEditFileSimple] Calling previewEditFileSimple with applyBoxId: ${applyBoxId}`) + const donePromise = this.previewEditFileSimple({ + uri, originalSnippet, updatedSnippet, occurrence, replaceAll, locationHint, encoding, newline, applyBoxId + }).then(() => { /* no-op */ }) - // start diffzone - const res = this._startStreamingDiffZone({ - uri, - streamRequestIdRef, - startBehavior: opts.startBehavior, - linkedCtrlKZone: null, - onWillUndo: () => { - if (streamRequestIdRef.current) { - this._llmMessageService.abort(streamRequestIdRef.current) // triggers onAbort() - } - }, - }) - if (!res) return - const { diffZone, onFinishEdit } = res + let newId: string | undefined + for (const id of this.diffAreasOfURI[uri.fsPath] || []) { + if (!beforeIds.has(id)) { newId = id; break } + } - // helpers - type SearchReplaceDiffAreaMetadata = { - originalBounds: [number, number], // 1-indexed - originalCode: string, + let diffZone: DiffZone | undefined + if (newId) { + const da = this.diffAreaOfId[newId] + if (da?.type === 'DiffZone') diffZone = da as DiffZone } - const convertOriginalRangeToFinalRange = (originalRange: readonly [number, number]): [number, number] => { - // adjust based on the changes by computing line offset - const [originalStart, originalEnd] = originalRange - let lineOffset = 0 - for (const blockDiffArea of addedTrackingZoneOfBlockNum) { - const { - startLine, endLine, - metadata: { originalBounds: [originalStart2, originalEnd2], }, - } = blockDiffArea - if (originalStart2 >= originalEnd) continue - const numNewLines = endLine - startLine + 1 - const numOldLines = originalEnd2 - originalStart2 + 1 - lineOffset += numNewLines - numOldLines - } - return [originalStart + lineOffset, originalEnd + lineOffset] + + if (!diffZone) { + const list = [...(this.diffAreasOfURI[uri.fsPath] || [])] + .map(id => this.diffAreaOfId[id]) + .filter(da => da?.type === 'DiffZone') as DiffZone[] + diffZone = list.sort((a, b) => a.diffareaid - b.diffareaid).pop() } + if (!diffZone) { + this.logService.warn('[_previewAndPrepareEditFileSimple] Could not locate created DiffZone after previewEditFileSimple') + return undefined + } - const onDone = () => { - diffZone._streamState = { isStreaming: false, } - this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }) - this._refreshStylesAndDiffsInURI(uri) + return [diffZone, donePromise] + } - // delete the tracking zones - for (const trackingZone of addedTrackingZoneOfBlockNum) - this._deleteTrackingZone(trackingZone) + private _initializeWriteoverStream(opts: StartApplyingOpts): [DiffZone, Promise] | undefined { + const { from } = opts; + if (from !== 'QuickEdit') return undefined; - onFinishEdit() + this.logService.debug('[DEBUG] _initializeWriteoverStreamSimple2 opts:', JSON.stringify(opts, null, 2)); - // auto accept - if (this._settingsService.state.globalSettings.autoAcceptLLMChanges) { - this.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: false, behavior: 'accept' }) - } + const uri = this._getURIBeforeStartApplying(opts); + if (!uri) { + this.logService.debug('[DEBUG] No URI found, returning undefined'); + return undefined; } - const onError = (e: { message: string; fullError: Error | null; }) => { - // this._notifyError(e) - onDone() - this._undoHistory(uri) - throw e.fullError || new Error(e.message) - } + this.logService.debug('[DEBUG] URI found:', uri); + this.logService.debug('[DEBUG] URI type:', typeof uri); + this.logService.debug('[DEBUG] URI keys:', Object.keys(uri)); - // refresh now in case onText takes a while to get 1st message - this._refreshStylesAndDiffsInURI(uri) + const { model } = this._voidModelService.getModel(uri); + if (!model) return undefined; - // stream style related - TODO replace these with whatever block we're on initially if already started (if add caching of apply S/R blocks) - let latestStreamLocationMutable: StreamLocationMutable | null = null - let shouldUpdateOrigStreamStyle = true - let oldBlocks: ExtractedSearchReplaceBlock[] = [] - const addedTrackingZoneOfBlockNum: TrackingZone[] = [] - diffZone._streamState.line = 1 - - const N_RETRIES = 4 - - // allowed to throw errors - this is called inside a promise that handles everything - const runSearchReplace = async () => { - // this generates >>>>>>> ORIGINAL <<<<<<< REPLACE blocks and and simultaneously applies it - let shouldSendAnotherMessage = true - let nMessagesSent = 0 - let currStreamingBlockNum = 0 - let aborted = false - let weAreAborting = false - while (shouldSendAnotherMessage) { - shouldSendAnotherMessage = false - nMessagesSent += 1 - if (nMessagesSent >= N_RETRIES) { - const e = { - message: `Tried to Fast Apply ${N_RETRIES} times but failed. This may be related to model intelligence, or it may an edit that's too complex. Please retry or disable Fast Apply.`, - fullError: null - } - onError(e) - break - } + const { diffareaid } = opts as any; + const ctrlKZone = this.diffAreaOfId[diffareaid]; + if (!ctrlKZone || ctrlKZone.type !== 'CtrlKZone') return undefined; - let resMessageDonePromise: () => void = () => { } - const messageDonePromise = new Promise((res, rej) => { resMessageDonePromise = res }) + const selectionRange = { + startLineNumber: ctrlKZone.startLine, + startColumn: 1, + endLineNumber: ctrlKZone.endLine, + endColumn: Number.MAX_SAFE_INTEGER + }; + const selectionCode = model.getValueInRange(selectionRange, EndOfLinePreference.LF); - const onText = (params: { fullText: string; fullReasoning: string }) => { - const { fullText } = params - // blocks are [done done done ... {writingFinal|writingOriginal}] - // ^ - // currStreamingBlockNum + const language = model.getLanguageId(); - const blocks = extractSearchReplaceBlocks(fullText) - for (let blockNum = currStreamingBlockNum; blockNum < blocks.length; blockNum += 1) { - const block = blocks[blockNum] + const streamRequestIdRef: { current: string | null } = { current: null }; + const started = this._startStreamingDiffZone({ + uri, + startBehavior: 'keep-conflicts', + streamRequestIdRef, + linkedCtrlKZone: ctrlKZone, + onWillUndo: () => { }, + applyBoxId: undefined, + }); + if (!started) return undefined; + + const { diffZone, onFinishEdit } = started; + + const instructions = ctrlKZone._mountInfo?.textAreaRef.current?.value ?? ''; + + + const modelSelection = this._settingsService.state.modelSelectionOfFeature['Ctrl+K']; + const overridesOfModel = this._settingsService.state.overridesOfModel; + const { specialToolFormat } = getModelCapabilities( + modelSelection?.providerName ?? 'openAI', + modelSelection?.modelName ?? '', + overridesOfModel + ); + + let systemMessage: string; + let userMessageContent: string; + + if (!specialToolFormat || specialToolFormat === 'disabled') { + systemMessage = buildXmlSysMessageForCtrlK(); + userMessageContent = buildXmlUserMessageForCtrlK({ + selectionRange, + selectionCode, + instructions, + language + }); + } else { + systemMessage = buildNativeSysMessageForCtrlK; + userMessageContent = buildNativeUserMessageForCtrlK({ + selectionRange, + selectionCode, + instructions, + language + }); + } + + // Logging for debugging + this.logService.debug('[Ctrl+K] User message content:', userMessageContent); + this.logService.debug('[Ctrl+K] System message:', systemMessage); + + const prepared = this._convertToLLMMessageService.prepareLLMSimpleMessages({ + systemMessage, + simpleMessages: [{ + role: 'user', + content: userMessageContent + }], + featureName: 'Ctrl+K', + modelSelection: this._settingsService.state.modelSelectionOfFeature['Ctrl+K'] + }); + + const messages = prepared.messages; + const separateSystemMessage = prepared.separateSystemMessage; + + // Logging for debugging + this.logService.debug('[Ctrl+K] Prepared messages:', JSON.stringify(messages, null, 2)); + this.logService.debug('[Ctrl+K] Separate system message:', separateSystemMessage); + + const modelSelectionOptions = modelSelection + ? this._settingsService.state.optionsOfModelSelection['Ctrl+K'][modelSelection.providerName]?.[modelSelection.modelName] + : undefined; + + let resolveDone: () => void = () => { }; + const donePromise = new Promise((res) => { resolveDone = res; }); + + + let toolChoice: any = undefined; + if (specialToolFormat === 'openai-style') { + toolChoice = { type: 'function', function: { name: 'edit_file' } }; + } else if (specialToolFormat === 'anthropic-style') { + toolChoice = { type: 'tool', name: 'edit_file' }; + } else if (specialToolFormat === 'gemini-style') { + toolChoice = 'auto'; + } + + const requestId = this._llmMessageService.sendLLMMessage({ + messagesType: 'chatMessages', + logging: { loggingName: `Edit (Ctrl+K)` }, + messages, + modelSelection, + modelSelectionOptions, + overridesOfModel, + separateSystemMessage, + ...(toolChoice !== undefined ? { tool_choice: toolChoice } : {}), + chatMode: 'agent', - if (block.state === 'writingOriginal') { - // update stream state to the first line of original if some portion of original has been written - if (shouldUpdateOrigStreamStyle && block.orig.trim().length >= 20) { - const startingAtLine = diffZone._streamState.line ?? 1 // dont go backwards if already have a stream line - const originalRange = findTextInCode(block.orig, originalFileCode, false, { startingAtLine, returnType: 'lines' }) - if (typeof originalRange !== 'string') { - const [startLine, _] = convertOriginalRangeToFinalRange(originalRange) - diffZone._streamState.line = startLine - shouldUpdateOrigStreamStyle = false - } - } + onText: (_chunk) => { }, - // // starting line is at least the number of lines in the generated code minus 1 - // const numLinesInOrig = numLinesOfStr(block.orig) - // const newLine = Math.max(numLinesInOrig - 1, 1, diffZone._streamState.line ?? 1) - // if (newLine !== diffZone._streamState.line) { - // diffZone._streamState.line = newLine - // this._refreshStylesAndDiffsInURI(uri) - // } + onFinalMessage: async (params) => { + try { + let toolApplied = false; - // must be done writing original to move on to writing streamed content - continue - } - shouldUpdateOrigStreamStyle = true - - - // if this is the first time we're seeing this block, add it as a diffarea so we can start streaming in it - if (!(blockNum in addedTrackingZoneOfBlockNum)) { - - const originalBounds = findTextInCode(block.orig, originalFileCode, true, { returnType: 'lines' }) - // if error - // Check for overlap with existing modified ranges - const hasOverlap = addedTrackingZoneOfBlockNum.some(trackingZone => { - const [existingStart, existingEnd] = trackingZone.metadata.originalBounds; - const hasNoOverlap = endLine < existingStart || startLine > existingEnd - return !hasNoOverlap - }); - - if (typeof originalBounds === 'string' || hasOverlap) { - const errorMessage = typeof originalBounds === 'string' ? originalBounds : 'Has overlap' as const - - console.log('--------------Error finding text in code:') - console.log('originalFileCode', { originalFileCode }) - console.log('fullText', { fullText }) - console.log('error:', errorMessage) - console.log('block.orig:', block.orig) - console.log('---------') - const content = this._errContentOfInvalidStr(errorMessage, block.orig) - const retryMsg = 'All of your previous outputs have been ignored. Please re-output ALL SEARCH/REPLACE blocks starting from the first one, and avoid the error this time.' - messages.push( - { role: 'assistant', content: fullText }, // latest output - { role: 'user', content: content + '\n' + retryMsg } // user explanation of what's wrong - ) - - // REVERT ALL BLOCKS - currStreamingBlockNum = 0 - latestStreamLocationMutable = null - shouldUpdateOrigStreamStyle = true - oldBlocks = [] - for (const trackingZone of addedTrackingZoneOfBlockNum) - this._deleteTrackingZone(trackingZone) - addedTrackingZoneOfBlockNum.splice(0, Infinity) - - this._writeURIText(uri, originalFileCode, 'wholeFileRange', { shouldRealignDiffAreas: true }) - - // abort and resolve - shouldSendAnotherMessage = true - if (streamRequestIdRef.current) { - weAreAborting = true - this._llmMessageService.abort(streamRequestIdRef.current) - weAreAborting = false - } - diffZone._streamState.line = 1 - resMessageDonePromise() - this._refreshStylesAndDiffsInURI(uri) - return - } + if (params.toolCall && params.toolCall.name === 'edit_file') { + const toolsService = this._instantiationService.invokeFunction((a: any) => a.get(IToolsService)) as any; + if (toolsService?.validateParams?.['edit_file'] && toolsService?.callTool?.['edit_file']) { + const rawParams = params.toolCall.rawParams || {}; + const paramsWithUri = { + ...rawParams, + uri: uri.fsPath + }; + this.logService.debug('[DEBUG] Params with injected URI:', JSON.stringify(paramsWithUri, null, 2)); - const [startLine, endLine] = convertOriginalRangeToFinalRange(originalBounds) + await this.interruptStreamingIfActive(uri); - // console.log('---------adding-------') - // console.log('CURRENT TEXT!!!', { current: model?.getValue(EndOfLinePreference.LF) }) - // console.log('block', deepClone(block)) - // console.log('origBounds', originalBounds) - // console.log('start end', startLine, endLine) - - // otherwise if no error, add the position as a diffarea - const adding: Omit, 'diffareaid'> = { - type: 'TrackingZone', - startLine: startLine, - endLine: endLine, - _URI: uri, - metadata: { - originalBounds: [...originalBounds], - originalCode: block.orig, - }, - } - const trackingZone = this._addDiffArea(adding) - addedTrackingZoneOfBlockNum.push(trackingZone) - latestStreamLocationMutable = { line: startLine, addedSplitYet: false, col: 1, originalCodeStartLine: 1 } - } // end adding diffarea - - - // should always be in streaming state here - if (!diffZone._streamState.isStreaming) { - console.error('DiffZone was not in streaming state in _initializeSearchAndReplaceStream') - continue + const validated = toolsService.validateParams['edit_file'](paramsWithUri); + const { result } = await toolsService.callTool['edit_file'](validated); + await result; + toolApplied = true; } + } - // if a block is done, finish it by writing all - if (block.state === 'done') { - const { startLine: finalStartLine, endLine: finalEndLine } = addedTrackingZoneOfBlockNum[blockNum] - this._writeURIText(uri, block.final, - { startLineNumber: finalStartLine, startColumn: 1, endLineNumber: finalEndLine, endColumn: Number.MAX_SAFE_INTEGER }, // 1-indexed - { shouldRealignDiffAreas: true } - ) - diffZone._streamState.line = finalEndLine + 1 - currStreamingBlockNum = blockNum + 1 - continue - } - - // write the added text to the file - if (!latestStreamLocationMutable) continue - const oldBlock = oldBlocks[blockNum] - const oldFinalLen = (oldBlock?.final ?? '').length - const deltaFinalText = block.final.substring(oldFinalLen, Infinity) - - this._writeStreamedDiffZoneLLMText(uri, block.orig, block.final, deltaFinalText, latestStreamLocationMutable) - oldBlocks = blocks // oldblocks is only used if writingFinal - - // const { endLine: currentEndLine } = addedTrackingZoneOfBlockNum[blockNum] // would be bad to do this because a lot of the bottom lines might be the same. more accurate to go with latestStreamLocationMutable - // diffZone._streamState.line = currentEndLine - diffZone._streamState.line = latestStreamLocationMutable.line - - } // end for - - this._refreshStylesAndDiffsInURI(uri) + if (!toolApplied) { + this.logService.warn('[Ctrl+K] No tool applied.'); + this._notificationService.warn('No changes applied: Model did not return a valid edit_file tool call.'); + } + } catch (toolErr) { + this.logService.error('Ctrl+K tool/codex apply error:', toolErr); + this._notificationService.error(`Edit failed: ${toolErr.message}`); + } finally { + + diffZone._streamState = { isStreaming: false }; + this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }); + resolveDone(); + onFinishEdit(); } + }, - streamRequestIdRef.current = this._llmMessageService.sendLLMMessage({ - messagesType: 'chatMessages', - logging: { loggingName: `Edit (Search/Replace) - ${from}` }, - messages, - modelSelection, - modelSelectionOptions, - overridesOfModel, - separateSystemMessage, - chatMode: null, // not chat - onText: (params) => { - onText(params) - }, - onFinalMessage: async (params) => { - const { fullText } = params - onText(params) + onError: (e) => { + this.logService.error('LLM error in Ctrl+K:', e); + diffZone._streamState = { isStreaming: false }; + this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }); + resolveDone(); + onFinishEdit(); + }, - const blocks = extractSearchReplaceBlocks(fullText) - if (blocks.length === 0) { - this._notificationService.info(`Void: We ran Fast Apply, but the LLM didn't output any changes.`) - } - this._writeURIText(uri, originalFileCode, 'wholeFileRange', { shouldRealignDiffAreas: true }) + onAbort: () => { + diffZone._streamState = { isStreaming: false }; + this._onDidChangeStreamingInDiffZone.fire({ uri, diffareaid: diffZone.diffareaid }); + resolveDone(); + onFinishEdit(); + } + }); - try { - this._instantlyApplySRBlocks(uri, fullText) - onDone() - resMessageDonePromise() - } - catch (e) { - onError(e) - } - }, - onError: (e) => { - onError(e) - }, - onAbort: () => { - if (weAreAborting) return - // stop the loop to free up the promise, but don't modify state (already handled by whatever stopped it) - aborted = true - resMessageDonePromise() - }, - }) + streamRequestIdRef.current = requestId; - // should never happen, just for safety - if (streamRequestIdRef.current === null) { break } + return [diffZone, donePromise]; + } - await messageDonePromise - if (aborted) { - throw new Error(`Edit was interrupted by the user.`) + private async interruptStreamingIfActive(uri: URI): Promise { + try { + const cmdBar = this._instantiationService.invokeFunction((a: any) => a.get(IVoidCommandBarService)) as any; + if (cmdBar && typeof cmdBar.getStreamState === 'function') { + const state = cmdBar.getStreamState(uri); + if (state === 'streaming') { + try { + await this.interruptURIStreaming({ uri }); + this.logService.debug('[DEBUG] Successfully interrupted streaming for', uri.fsPath); + } catch (ie) { + this.logService.warn('Interrupt failed for URI:', uri.fsPath, ie); + } } - } // end while - - } // end retryLoop - - const applyDonePromise = new Promise((res, rej) => { runSearchReplace().then(res).catch(rej) }) - return [diffZone, applyDonePromise] + } + } catch (e) { + this.logService.warn('Error checking stream state before tool call:', e); + } } - _undoHistory(uri: URI) { this._undoRedoService.undo(uri) } - - isCtrlKZoneStreaming({ diffareaid }: { diffareaid: number }) { const ctrlKZone = this.diffAreaOfId[diffareaid] if (!ctrlKZone) return false @@ -2022,7 +2578,6 @@ class EditCodeService extends Disposable implements IEditCodeService { return !!ctrlKZone._linkedStreamingDiffZone } - private _stopIfStreaming(diffZone: DiffZone) { const uri = diffZone._URI @@ -2063,23 +2618,18 @@ class EditCodeService extends Disposable implements IEditCodeService { } } - - // public removeDiffZone(diffZone: DiffZone, behavior: 'reject' | 'accept') { - // const uri = diffZone._URI - // const { onFinishEdit } = this._addToHistory(uri) - - // if (behavior === 'reject') this._revertAndDeleteDiffZone(diffZone) - // else if (behavior === 'accept') this._deleteDiffZone(diffZone) - - // this._refreshStylesAndDiffsInURI(uri) - // onFinishEdit() - // } - private _revertDiffZone(diffZone: DiffZone) { const uri = diffZone._URI + const { model } = this._voidModelService.getModel(uri) + if (!model) return const writeText = diffZone.originalCode - const toRange: IRange = { startLineNumber: diffZone.startLine, startColumn: 1, endLineNumber: diffZone.endLine, endColumn: Number.MAX_SAFE_INTEGER } + const lineCount = model.getLineCount() + const startLineNumber = Math.max(1, Math.min(diffZone.startLine, lineCount)) + const endLineNumber = Math.max(startLineNumber, Math.min(diffZone.endLine, lineCount)) + + const toRange: IRange = { startLineNumber, startColumn: 1, endLineNumber, endColumn: Number.MAX_SAFE_INTEGER } + this.logService.debug(`[_revertDiffZone] uri=${uri.fsPath} diffareaid=${diffZone.diffareaid} applyBoxId=${diffZone.applyBoxId ?? 'none'} from=${diffZone.startLine}-${diffZone.endLine} to=${startLineNumber}-${endLineNumber} originalLen=${writeText.length}`) this._writeURIText(uri, writeText, toRange, { shouldRealignDiffAreas: true }) } @@ -2087,117 +2637,954 @@ class EditCodeService extends Disposable implements IEditCodeService { // remove a batch of diffareas all at once (and handle accept/reject of their diffs) public acceptOrRejectAllDiffAreas: IEditCodeService['acceptOrRejectAllDiffAreas'] = async ({ uri, behavior, removeCtrlKs, _addToHistory }) => { - const diffareaids = this.diffAreasOfURI[uri.fsPath] - if ((diffareaids?.size ?? 0) === 0) return // do nothing + const uriKey = uri.fsPath + if (this._activeBulkAcceptRejectUris.has(uriKey)) { + this.logService.warn(`[acceptOrRejectAllDiffAreas] reentrant start uri=${uriKey} behavior=${behavior}`) + } + this._activeBulkAcceptRejectUris.add(uriKey) - const { onFinishEdit } = _addToHistory === false ? { onFinishEdit: () => { } } : this._addToHistory(uri) + try { + const diffareaids = this.diffAreasOfURI[uri.fsPath] + this.logService.debug(`[acceptOrRejectAllDiffAreas] start uri=${uri.fsPath} behavior=${behavior} removeCtrlKs=${removeCtrlKs} addToHistory=${_addToHistory !== false} totalAreas=${diffareaids?.size ?? 0}`) + if ((diffareaids?.size ?? 0) === 0) return // do nothing + + const { onFinishEdit } = _addToHistory === false ? { onFinishEdit: () => { } } : this._addToHistory(uri) + const serializeZones = (zones: DiffZone[]) => { + try { + return JSON.stringify(zones.map(z => ({ + diffareaid: z.diffareaid, + startLine: z.startLine, + endLine: z.endLine, + applyBoxId: z.applyBoxId ?? null, + isEditFileSimple: !!(z as any)._editFileSimple + }))); + } catch { + return String(zones.map(z => z.diffareaid).join(',')); + } + }; - for (const diffareaid of diffareaids ?? []) { - const diffArea = this.diffAreaOfId[diffareaid] - if (!diffArea) continue + if (behavior === 'reject') { - if (diffArea.type === 'DiffZone') { - if (behavior === 'reject') { - this._revertDiffZone(diffArea) - this._deleteDiffZone(diffArea) + const diffZones: DiffZone[] = []; + for (const diffareaid of diffareaids ?? []) { + const diffArea = this.diffAreaOfId[diffareaid]; + if (diffArea && diffArea.type === 'DiffZone') { + diffZones.push(diffArea); + } + } + this.logService.debug(`[acceptOrRejectAllDiffAreas] reject targetZones(beforeSort)=${serializeZones(diffZones)}`) + + // Revert bottom-to-top so earlier rewrites do not shift later ranges. + diffZones.sort((a, b) => { + if (a.startLine !== b.startLine) return b.startLine - a.startLine; + return b.diffareaid - a.diffareaid; + }); + this.logService.debug(`[acceptOrRejectAllDiffAreas] reject targetZones(sorted)=${serializeZones(diffZones)}`) + for (const diffZone of diffZones) { + this.logService.debug(`[acceptOrRejectAllDiffAreas] reject revert diffareaid=${diffZone.diffareaid} start=${diffZone.startLine} end=${diffZone.endLine}`) + this._revertDiffZone(diffZone); + this._deleteDiffZone(diffZone); + } + + + if (removeCtrlKs) { + for (const diffareaid of diffareaids ?? []) { + const diffArea = this.diffAreaOfId[diffareaid]; + if (diffArea && diffArea.type === 'CtrlKZone') { + this.logService.debug(`[acceptOrRejectAllDiffAreas] reject removeCtrlK diffareaid=${diffArea.diffareaid} start=${diffArea.startLine} end=${diffArea.endLine}`) + this._deleteCtrlKZone(diffArea); + } + } + } + } else { + const acceptedZones: DiffZone[] = []; + const removedCtrlKs: CtrlKZone[] = []; + + for (const diffareaid of diffareaids ?? []) { + const diffArea = this.diffAreaOfId[diffareaid]; + if (!diffArea) { + continue; + } + + if (diffArea.type === 'DiffZone') { + if (behavior === 'accept') { + acceptedZones.push(diffArea); + this._deleteDiffZone(diffArea); + } + } + else if (diffArea.type === 'CtrlKZone' && removeCtrlKs) { + removedCtrlKs.push(diffArea); + this._deleteCtrlKZone(diffArea); + } + } + if (behavior === 'accept') { + this.logService.debug(`[acceptOrRejectAllDiffAreas] accept deletedZones=${serializeZones(acceptedZones)}`) + } + if (removedCtrlKs.length > 0) { + this.logService.debug(`[acceptOrRejectAllDiffAreas] accept/removeCtrlKs removedCtrlKs=${JSON.stringify(removedCtrlKs.map(z => ({ diffareaid: z.diffareaid, startLine: z.startLine, endLine: z.endLine })))}`) } - else if (behavior === 'accept') this._deleteDiffZone(diffArea) } - else if (diffArea.type === 'CtrlKZone' && removeCtrlKs) { - this._deleteCtrlKZone(diffArea) + + this._refreshStylesAndDiffsInURI(uri) + this.logService.debug(`[acceptOrRejectAllDiffAreas] finishEdit(start) uri=${uri.fsPath} behavior=${behavior}`) + await onFinishEdit() + this.logService.debug(`[acceptOrRejectAllDiffAreas] finishEdit(done) uri=${uri.fsPath} behavior=${behavior}`) + this.logService.debug(`[acceptOrRejectAllDiffAreas] done uri=${uri.fsPath} behavior=${behavior} remainingAreas=${this.diffAreasOfURI[uri.fsPath]?.size ?? 0}`) + } finally { + this._activeBulkAcceptRejectUris.delete(uriKey) + } + } + + private decodeHtmlEntities(text: string): string { + if (!text) return text; + const entities: Record = { + '<': '<', + '>': '>', + '&': '&', + '"': '"', + ''': '\'', + ''': '\'', + ' ': ' ', + ''': '\'', + '/': '/', + '<': '<', + '>': '>', + '&': '&', + '"': '"', + }; + + const pattern = Object.keys(entities) + .sort((a, b) => b.length - a.length) + .map(entity => entity.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')) + .join('|'); + + const regex = new RegExp(pattern, 'g'); + const result = text.replace(regex, match => entities[match] || match); + return result; + } + + + private hasHtmlEntities(text: string): boolean { + if (!text) return false; + const hasEntities = /&(?:lt|gt|amp|quot|#39|apos|nbsp|#x27|#x2F|#60|#62|#38|#34);/i.test(text); + return hasEntities; + } + + + private shouldDecodeEntities(originalSnippet: string, updatedSnippet: string, fileExtension?: string): boolean { + + if (!this.hasHtmlEntities(originalSnippet) && !this.hasHtmlEntities(updatedSnippet)) { + return false; + } + + const originalHasEntities = this.hasHtmlEntities(originalSnippet); + const updatedHasEntities = this.hasHtmlEntities(updatedSnippet); + + const looksLikeJSXTag = (text: string): boolean => { + const patterns = [ + /<(\w+)[\s>]/, // <div> or <Component + /<\/(\w+)>/, // </div> + /<(\w+)\s+\w+=/, // <div className= + /<(\w+)\s*\/>/, // <Component /> + ]; + const isJSX = patterns.some(p => p.test(text)); + return isJSX; + }; + + const entitiesInString = (text: string): boolean => { + const lines = text.split('\n'); + for (const line of lines) { + const entityMatch = /&(?:lt|gt|amp|quot|#39);/.exec(line); + if (entityMatch) { + const beforeEntity = line.substring(0, entityMatch.index); + const afterEntity = line.substring(entityMatch.index + entityMatch[0].length); + + const quotesBefore = (beforeEntity.match(/['"]/g) || []).length; + const quotesAfter = (afterEntity.match(/['"]/g) || []).length; + + if (quotesBefore % 2 === 1 && quotesAfter % 2 === 1) { + return true; + } + } + } + return false; + }; + + const codeExtensions = ['js', 'jsx', 'ts', 'tsx', 'vue', 'svelte', 'html', 'htm']; + const dataExtensions = ['json', 'xml', 'yaml', 'yml']; + + const isCodeFile = fileExtension ? codeExtensions.includes(fileExtension.toLowerCase()) : false; + const isDataFile = fileExtension ? dataExtensions.includes(fileExtension.toLowerCase()) : false; + + if (!originalHasEntities && updatedHasEntities) { + const looksLikeJSX = looksLikeJSXTag(updatedSnippet); + const inString = entitiesInString(updatedSnippet); + + if (looksLikeJSX && !inString) { + return true; } + return false; } - this._refreshStylesAndDiffsInURI(uri) - onFinishEdit() + if (originalHasEntities && updatedHasEntities) { + const origDecoded = this.decodeHtmlEntities(originalSnippet); + const hasDecodedTags = origDecoded.includes('<') && origDecoded.includes('>'); + const hasOriginalTags = originalSnippet.includes('<') && originalSnippet.includes('>'); + + if (hasDecodedTags && !hasOriginalTags) { + const decision = isCodeFile && !isDataFile; + return decision; + } + } + + if (updatedHasEntities && !originalHasEntities) { + const decodedUpdated = this.decodeHtmlEntities(updatedSnippet); + const areIdentical = decodedUpdated === originalSnippet; + + if (areIdentical) { + return true; + } + } + return false; } + public async previewEditFileSimple({ + uri, originalSnippet, updatedSnippet, occurrence, replaceAll, locationHint, encoding, newline, applyBoxId + }: { + uri: URI; originalSnippet: string; updatedSnippet: string; + occurrence?: number | null; replaceAll?: boolean; locationHint?: any; encoding?: string | null; newline?: string | null; applyBoxId?: string + }) { + this.logService.debug(`[previewEditFileSimple] Called with applyBoxId: ${applyBoxId}`) + const fileExtension = uri.path ? uri.path.split('.').pop()?.toLowerCase() : undefined; - // called on void.acceptDiff - public async acceptDiff({ diffid }: { diffid: number }) { + let cleanOriginalSnippet = originalSnippet; + let cleanUpdatedSnippet = updatedSnippet; + let entitiesDetected = false; + let entitiesAutoFixed = false; - // TODO could use an ITextModelto do this instead, would be much simpler + if (this.shouldDecodeEntities(originalSnippet, updatedSnippet, fileExtension)) { + entitiesDetected = true; - const diff = this.diffOfId[diffid] - if (!diff) return + const decodedOriginal = this.decodeHtmlEntities(originalSnippet); + const decodedUpdated = this.decodeHtmlEntities(updatedSnippet); - const { diffareaid } = diff - const diffArea = this.diffAreaOfId[diffareaid] - if (!diffArea) return + const looksValidAfterDecode = (original: string, decoded: string): boolean => { + const hasValidTags = /<\w+[\s>]/.test(decoded) || /<\/\w+>/.test(decoded); + const hasValidJSX = /\/>/.test(decoded) || /\{.*\}/.test(decoded); - if (diffArea.type !== 'DiffZone') return + if (hasValidTags || hasValidJSX) return true; - const uri = diffArea._URI + const lengthRatio = decoded.length / original.length; + if (lengthRatio < 0.7 || lengthRatio > 1.3) return false; + return true; + }; - // add to history - const { onFinishEdit } = this._addToHistory(uri) + if (looksValidAfterDecode(originalSnippet, decodedOriginal) && + looksValidAfterDecode(updatedSnippet, decodedUpdated)) { + cleanOriginalSnippet = decodedOriginal; + cleanUpdatedSnippet = decodedUpdated; + entitiesAutoFixed = true; + } + } - const originalLines = diffArea.originalCode.split('\n') - let newOriginalCode: string + cleanOriginalSnippet = stripMarkdownFence(cleanOriginalSnippet); + cleanUpdatedSnippet = stripMarkdownFence(cleanUpdatedSnippet); - if (diff.type === 'deletion') { - newOriginalCode = [ - ...originalLines.slice(0, (diff.originalStartLine - 1)), // everything before startLine - // <-- deletion has nothing here - ...originalLines.slice((diff.originalEndLine - 1) + 1, Infinity) // everything after endLine - ].join('\n') + const { model } = this._voidModelService.getModel(uri) + if (!model) return { + applied: false, + occurrences_found: 0, + error: 'File not found', + preview: { before: '', after: '' }, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed, + match_kind: 'none', + match_range: { startLine: 0, endLine: 0, startColumn: 0, endColumn: 0 }, + debug_cmd: null, + debug_cmd_alt: null + } + + const fullText = model.getValue(EndOfLinePreference.LF) + + let matchKind: 'exact' | 'whitespace' | 'inferred' | 'location_hint' | 'none' = 'none'; + let fallbackReason: string | null = null; + let debugCmd: { gnu: string; bsd: string } | null = null; + + const recordFallbackLater = (reason: string) => { + fallbackReason = reason; + }; + + const origNorm = normalizeEol(cleanOriginalSnippet) + const updNorm = normalizeEol(cleanUpdatedSnippet) + + const collapseWsKeepNL = (s: string) => { + const out: string[] = []; + const map: number[] = []; + let i = 0; + while (i < s.length) { + const ch = s[i]; + // For the whitespace-agnostic search we ignore spaces/tabs completely + // but keep newlines so multi-line structure is preserved. + if (ch === ' ' || ch === '\t') { + i++; + continue; + } + out.push(ch); + map.push(i); + i++; + } + return { text: out.join(''), map }; + }; + + const startsWithWsAgnostic = (a: string, b: string) => { + const A = collapseWsKeepNL(a).text; + const B = collapseWsKeepNL(b).text; + return A.startsWith(B); + }; + + const findAllWsAgnostic = (haystack: string, needle: string): Array<{ start: number; end: number }> => { + const H = collapseWsKeepNL(haystack); + const N = collapseWsKeepNL(needle); + const found: Array<{ start: number; end: number }> = []; + let from = 0; + while (true) { + const pos = H.text.indexOf(N.text, from); + if (pos === -1) break; + const rawStart = H.map[pos]; + const rawEnd = H.map[Math.min(pos + N.text.length - 1, H.map.length - 1)] + 1; // exclusive + found.push({ start: rawStart, end: rawEnd }); + from = pos + Math.max(1, N.text.length); + } + return found; + }; + + const findMatchingCurlyForward = (text: string, openIndex: number): number => { + let i = openIndex + 1; + let depth = 1; + + let inSgl = false; // ' + let inDbl = false; // " + let inBT = false; // ` + let inLine = false; // // + let inBlock = false; // /* */ + let prev = ''; + + while (i < text.length) { + const ch = text[i]; + const next = text[i + 1]; + + if (!inSgl && !inDbl && !inBT) { + if (!inBlock && !inLine && ch === '/' && next === '/') { inLine = true; i += 2; prev = ''; continue; } + if (!inBlock && !inLine && ch === '/' && next === '*') { inBlock = true; i += 2; prev = ''; continue; } + if (inLine && ch === '\n') { inLine = false; i++; prev = ch; continue; } + if (inBlock && ch === '*' && next === '/') { inBlock = false; i += 2; prev = ''; continue; } + if (inLine || inBlock) { i++; prev = ch; continue; } + } + + if (!inBlock && !inLine) { + if (!inDbl && !inBT && ch === '\'' && prev !== '\\') { inSgl = !inSgl; i++; prev = ch; continue; } + if (!inSgl && !inBT && ch === '"' && prev !== '\\') { inDbl = !inDbl; i++; prev = ch; continue; } + if (!inSgl && !inDbl && ch === '`' && prev !== '\\') { inBT = !inBT; i++; prev = ch; continue; } + } + + if (!inSgl && !inDbl && !inBT && !inBlock && !inLine) { + if (ch === '{') { depth++; i++; prev = ch; continue; } + if (ch === '}') { depth--; if (depth === 0) return i; i++; prev = ch; continue; } + } + + prev = ch; + i++; + } + return -1; + }; + + const updatedLooksLikeFullCurlyBlock = (updated: string): boolean => { + const open = updated.indexOf('{'); + if (open === -1) return false; + + const close = findMatchingCurlyForward(updated, open); + if (close === -1) return false; + + const tail = updated.slice(close + 1).trim(); + return tail === '' || tail === ';' || tail === ','; + }; + + const tryExpandHeaderRange = (text: string, guessStart: number, guessEnd: number, searchWindow = 200) => { + let startOffset = guessStart; + let endOffset = guessEnd; + const origTrim = (origNorm ?? '').trimEnd(); + const looksLikeHeaderOnly = /{\s*$/.test(origTrim) && !origTrim.includes('}'); + + const looksLikeExpansion = + (updNorm ?? '').length > (origNorm ?? '').length && + startsWithWsAgnostic(updNorm ?? '', origNorm ?? ''); + + const allowBlockExpansion = + looksLikeHeaderOnly && + looksLikeExpansion && + updatedLooksLikeFullCurlyBlock(updNorm ?? ''); + + if (!allowBlockExpansion) { + return { startOffset, endOffset }; + } + + let inS = false, inD = false, inT = false, inSL = false, inML = false; + let openPos = -1; + + const limit = Math.min(text.length, guessStart + Math.max((origNorm ?? '').length + 20, searchWindow)); + for (let pos = guessStart; pos < limit; pos++) { + const c = text[pos]; + const next = pos + 1 < text.length ? text[pos + 1] : ''; + + + if (!inS && !inD && !inT) { + if (!inML && !inSL && c === '/' && next === '/') { inSL = true; pos++; continue; } + if (!inML && !inSL && c === '/' && next === '*') { inML = true; pos++; continue; } + if (inSL && c === '\n') { inSL = false; continue; } + if (inML && c === '*' && next === '/') { inML = false; pos++; continue; } + if (inSL || inML) continue; + } + + + if (!inML && !inSL) { + if (!inD && !inT && c === '\'') { inS = !inS; continue; } + if (!inS && !inT && c === '"') { inD = !inD; continue; } + if (!inS && !inD && c === '`') { inT = !inT; continue; } + } + if (inS || inD || inT) continue; + + if (c === '{') { openPos = pos; break; } + } + + if (openPos !== -1) { + const closePos = findMatchingCurlyForward(text, openPos); + if (closePos !== -1) { + endOffset = Math.max(endOffset, closePos + 1); + } + } + + return { startOffset, endOffset }; + }; + + const escapeRx = (s: string) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + const endsWithToken = (s: string, token: string) => new RegExp(`${escapeRx(token)}\\s*$`).test(s); + const nextNonWsIndex = (text: string, from: number) => { + let i = from; + while (i < text.length && /\s/.test(text[i])) i++; + return i; + }; + + const swallowTrailingTokenLen = (full: string, endOff: number, updated: string, original: string) => { + const i = nextNonWsIndex(full, endOff); + const ch = full[i]; + const tokens = [';', ',']; + for (const tok of tokens) { + if (ch === tok && endsWithToken(updated, tok) && !endsWithToken(original, tok)) { + + return (i - endOff) + 1; + } + } + return 0; + }; + + + const indices: number[] = [] + for (let from = 0; ;) { + const i = fullText.indexOf(origNorm, from) + if (i === -1) break + indices.push(i) + from = i + Math.max(origNorm.length, 1) } - else if (diff.type === 'insertion') { - newOriginalCode = [ - ...originalLines.slice(0, (diff.originalStartLine - 1)), // everything before startLine - diff.code, // code - ...originalLines.slice((diff.originalStartLine - 1), Infinity) // startLine (inclusive) and on (no +1) - ].join('\n') + + let wsAgnosticMatches: Array<{ start: number; end: number }> = [] + if (indices.length === 0) { + wsAgnosticMatches = findAllWsAgnostic(fullText, origNorm) + if (wsAgnosticMatches.length > 0) { + matchKind = 'whitespace'; + recordFallbackLater('LLM did not correctly provide an ORIGINAL code block (whitespace-insensitive match used).'); + } } - else if (diff.type === 'edit') { - newOriginalCode = [ - ...originalLines.slice(0, (diff.originalStartLine - 1)), // everything before startLine - diff.code, // code - ...originalLines.slice((diff.originalEndLine - 1) + 1, Infinity) // everything after endLine - ].join('\n') + + const linesCount = fullText.split('\n').length + let updatedText = fullText + let startLine = 1 + let endLine = linesCount + let startColumn = 1 + let endColumn = Number.MAX_SAFE_INTEGER + let occurrenceApplied = 0 + let originalCodeForZone = '' + + const offsetToLine = (text: string, offset: number) => text.slice(0, offset).split('\n').length + const offsetToLineCol = (text: string, offset: number) => { + const line = offsetToLine(text, offset) + const prevNL = text.lastIndexOf('\n', Math.max(0, offset - 1)) + const col = (prevNL === -1 ? offset : (offset - (prevNL + 1))) + 1 + return { line, column: col } } - else { - throw new Error(`Void error: ${diff}.type not recognized`) + + // replaceAll + if (replaceAll) { + if (indices.length > 0) { + + let textAcc = fullText + for (let k = indices.length - 1; k >= 0; k--) { + const start = indices[k] + let end = start + origNorm.length + + { + const r = tryExpandHeaderRange(textAcc, start, end) + end = r.endOffset + } + + const swallow = swallowTrailingTokenLen(textAcc, end, updNorm, origNorm) + textAcc = textAcc.slice(0, start) + updNorm + textAcc.slice(end + swallow) + } + updatedText = textAcc + startLine = 1 + endLine = linesCount + startColumn = 1 + endColumn = Number.MAX_SAFE_INTEGER + originalCodeForZone = fullText + } else if (wsAgnosticMatches.length > 0) { + + let textAcc = fullText + const matches = [...wsAgnosticMatches].sort((a, b) => b.start - a.start) + for (const m of matches) { + const start = m.start + let end = m.end + + { + const r = tryExpandHeaderRange(textAcc, start, end) + end = r.endOffset + } + + const swallow = swallowTrailingTokenLen(textAcc, end, updNorm, origNorm) + textAcc = textAcc.slice(0, start) + updNorm + textAcc.slice(end + swallow) + } + updatedText = textAcc + startLine = 1 + endLine = linesCount + startColumn = 1 + endColumn = Number.MAX_SAFE_INTEGER + originalCodeForZone = fullText + } else { + originalCodeForZone = fullText + } + } else { + + if (indices.length === 0 && wsAgnosticMatches.length === 0) { + + let inferred: any = null + try { + const model = this._modelService.getModel(uri) + const astContext = this._getCachedAstContext(uri, model) + inferred = inferSelectionFromCode({ + codeStr: cleanOriginalSnippet, + fileText: fullText, + astContext: astContext ?? undefined + }) + } catch { } + + if (!inferred) { + const sample = fullText.split('\n').slice(0, 20).join('\n') + return { + applied: false, + occurrences_found: 0, + error: 'original_snippet not found', + preview: { before: sample, after: '' }, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed + } + } + + matchKind = 'inferred'; + recordFallbackLater('LLM did not correctly provide an ORIGINAL code block (inferred selection used).'); + + originalCodeForZone = inferred.text + if (inferred.range) { + startLine = inferred.range[0] + endLine = inferred.range[1] + } else { + const idx2 = fullText.indexOf(originalCodeForZone) + if (idx2 !== -1) { + const s = offsetToLineCol(fullText, idx2) + startLine = s.line + startColumn = s.column + endLine = startLine + originalCodeForZone.split('\n').length - 1 + const endOffset = idx2 + originalCodeForZone.length + const e = offsetToLineCol(fullText, endOffset) + endColumn = e.column + } + } + return { + applied: false, + occurrences_found: 0, + error: 'original_snippet not found (inferred)', + preview: { before: originalCodeForZone.slice(0, 1000), after: '' }, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed + } + } + + if (indices.length > 0) { + + let pickIndexInText = indices[0] + let which = 1 + if (typeof occurrence === 'number') { + const idxNum = occurrence < 0 ? indices.length + occurrence : occurrence - 1 + if (idxNum < 0 || idxNum >= indices.length) { + return { + applied: false, + occurrences_found: indices.length, + error: `occurrence ${occurrence} out of range`, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed + } + } + pickIndexInText = indices[idxNum] + which = idxNum + 1 + } + occurrenceApplied = which + + let startReplaceOffset = pickIndexInText + let endReplaceOffset = pickIndexInText + origNorm.length + + { + const r = tryExpandHeaderRange(fullText, startReplaceOffset, endReplaceOffset) + startReplaceOffset = r.startOffset + endReplaceOffset = r.endOffset + } + + endReplaceOffset += swallowTrailingTokenLen(fullText, endReplaceOffset, updNorm, origNorm) + + const s = offsetToLineCol(fullText, startReplaceOffset) + const e = offsetToLineCol(fullText, endReplaceOffset) + startLine = s.line + startColumn = s.column + endLine = e.line + endColumn = e.column + + const originalCodeForZoneFull = model.getValueInRange( + { + startLineNumber: startLine, + startColumn: 1, + endLineNumber: endLine, + endColumn: Number.MAX_SAFE_INTEGER + }, + EndOfLinePreference.LF + ) + originalCodeForZone = originalCodeForZoneFull + + updatedText = fullText.slice(0, startReplaceOffset) + updNorm + fullText.slice(endReplaceOffset) + } else { + + let pickIdx = 0 + if (typeof occurrence === 'number') { + const idxNum = occurrence < 0 ? wsAgnosticMatches.length + occurrence : occurrence - 1 + if (idxNum < 0 || idxNum >= wsAgnosticMatches.length) { + return { + applied: false, + occurrences_found: wsAgnosticMatches.length, + error: `occurrence ${occurrence} out of range`, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed + } + } + pickIdx = idxNum + } + const picked = wsAgnosticMatches[pickIdx] + occurrenceApplied = pickIdx + 1 + + let startReplaceOffset = picked.start + let endReplaceOffset = picked.end + + { + const r = tryExpandHeaderRange(fullText, startReplaceOffset, endReplaceOffset) + startReplaceOffset = r.startOffset + endReplaceOffset = r.endOffset + } + + + endReplaceOffset += swallowTrailingTokenLen(fullText, endReplaceOffset, updNorm, origNorm) + + const s = offsetToLineCol(fullText, startReplaceOffset) + const e = offsetToLineCol(fullText, endReplaceOffset) + startLine = s.line; startColumn = s.column + endLine = e.line; endColumn = e.column + + const originalCodeForZoneFull = model.getValueInRange( + { + startLineNumber: startLine, + startColumn: 1, + endLineNumber: endLine, + endColumn: Number.MAX_SAFE_INTEGER + }, + EndOfLinePreference.LF + ) + originalCodeForZone = originalCodeForZoneFull + + updatedText = fullText.slice(0, startReplaceOffset) + updNorm + fullText.slice(endReplaceOffset) + } } - // console.log('DIFF', diff) - // console.log('DIFFAREA', diffArea) - // console.log('ORIGINAL', diffArea.originalCode) - // console.log('new original Code', newOriginalCode) - // update code now accepted as original - diffArea.originalCode = newOriginalCode + const dmp = new DiffMatchPatch() + const diffs = dmp.diff_main(fullText, updatedText) + try { dmp.diff_cleanupSemantic(diffs) } catch { } + const fileLabel = (uri.path ?? uri.fsPath ?? 'file').toString().replace(/^[\\/]+/, '') + const patch_unified = createUnifiedFromLineDiffs(fileLabel, fullText, updatedText, 3) - // delete the diff - this._deleteDiff(diff) + if (fallbackReason) { + const linesTotal = linesCount || fullText.split('\n').length || 1; - // diffArea should be removed if it has no more diffs in it - if (Object.keys(diffArea._diffOfId).length === 0) { - this._deleteDiffZone(diffArea) + // clamp to a reasonable window + let from = Math.max(1, (startLine || 1) - 3); + let to = Math.min(linesTotal, (endLine || startLine || 1) + 3); + if (to - from > 200) to = Math.min(linesTotal, from + 200); + + const relForCmd = this._getWorkspaceRelativePathForCmd(uri); + debugCmd = buildInvisibleCharsDebugCmd(relForCmd, from, to); + + this.recordFallbackMessage(uri, EDIT_FILE_FALLBACK_MSG); } + const adding: Omit = { + type: 'DiffZone', + originalCode: originalCodeForZone, + startLine, + endLine, + _URI: uri, + _streamState: { isStreaming: false }, + _diffOfId: {}, + _removeStylesFns: new Set(), + applyBoxId: applyBoxId, + } + const diffZone = this._addDiffArea(adding) + this.logService.debug(`[previewEditFileSimple] Created DiffZone with applyBoxId: ${applyBoxId}, diffareaid: ${diffZone.diffareaid}`); + (diffZone as any)._editFileSimple = { + original_snippet: cleanOriginalSnippet, + updated_snippet: cleanUpdatedSnippet, + occurrence: occurrence ?? null, + replace_all: !!replaceAll, + location_hint: locationHint, + encoding, + newline, + updated_text: updatedText, + patch_unified, + entities_auto_fixed: entitiesAutoFixed + } + + this._onDidAddOrDeleteDiffZones.fire({ uri }) this._refreshStylesAndDiffsInURI(uri) - onFinishEdit() + const { onFinishEdit } = this._addToHistory(uri) + if (replaceAll) { + this._writeURIText(uri, updatedText, 'wholeFileRange', { shouldRealignDiffAreas: true }) + } else { + const toRange: IRange = { + startLineNumber: startLine, + startColumn, + endLineNumber: endLine, + endColumn + } + this._writeURIText(uri, updNorm, toRange, { shouldRealignDiffAreas: true }) + } + await onFinishEdit?.() + + const occurrencesFound = indices.length > 0 ? indices.length : wsAgnosticMatches.length + + // Check if the original and updated snippets are identical + if (origNorm === updNorm) { + return { + applied: false, + occurrences_found: occurrencesFound, + error: 'original_snippet and updated_snippet are identical', + preview: { before: originalCodeForZone.slice(0, 1000), after: updNorm.slice(0, 1000) }, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed, + match_kind: matchKind, + match_range: { startLine, endLine, startColumn, endColumn }, + fallback_available: !!fallbackReason, + debug_cmd: debugCmd?.gnu ?? null, + debug_cmd_alt: debugCmd?.bsd ?? null + } + } + return { + applied: true, + occurrences_found: occurrencesFound, + occurrence_applied: occurrenceApplied || undefined, + updated_text: updatedText, + patch_unified, + preview: { before: originalCodeForZone.slice(0, 1000), after: updNorm.slice(0, 1000) }, + entities_detected: entitiesDetected, + entities_auto_fixed: entitiesAutoFixed, + fallback_available: !!fallbackReason, + debug_cmd: debugCmd?.gnu ?? null, + debug_cmd_alt: debugCmd?.bsd ?? null + } } + public async acceptDiff({ diffid }: { diffid: number }) { + const diff: Diff | undefined = this.diffOfId[diffid]; + if (!diff) { + this.logService.debug(`[acceptDiff] skipped missing diffid=${diffid}`); + return; + } + + const { diffareaid } = diff; + const diffArea = this.diffAreaOfId[diffareaid]; + if (!diffArea || diffArea.type !== 'DiffZone') { + this.logService.debug(`[acceptDiff] skipped diffid=${diffid} invalid diffareaid=${diffareaid}`); + return; + } + + const uri = diffArea._URI; + const loggedEndLine = diff.type === 'deletion' ? diff.startLine : diff.endLine; + this.logService.debug(`[acceptDiff] start uri=${uri.fsPath} diffid=${diffid} diffareaid=${diffareaid} type=${diff.type} range=${diff.startLine}-${loggedEndLine} applyBoxId=${diffArea.applyBoxId ?? 'none'} editFileSimple=${!!(diffArea as any)._editFileSimple}`); + // For edit_file preview zones, accepting a single diff should only merge that + // change into the baseline, keeping other diffs in the same zone intact. + if ((diffArea as any)._editFileSimple) { + try { + const before = diffArea.originalCode; + diffArea.originalCode = applyDiffToBaseline(before, diff); + } catch (e) { + console.error('[acceptDiff] Failed to update baseline for edit_file zone:', e); + } + + // Remove this diff from the current zone bookkeeping + this._deleteDiff(diff); + if (Object.keys(diffArea._diffOfId).length === 0) { + this._deleteDiffZone(diffArea); + } + + this._refreshStylesAndDiffsInURI(uri); + this.logService.debug(`[acceptDiff] done(edit_file) uri=${uri.fsPath} diffid=${diffid} remainingDiffs=${Object.keys(diffArea._diffOfId).length}`); + return; + } + + // Default behavior for non-edit_file zones: apply the change to the model and + // then update the baseline for the whole zone. + const model = this._modelService.getModel(uri); + if (!model) { + console.warn('[acceptDiff] Model not found for URI:', uri); + return; + } + + const { onFinishEdit } = this._addToHistory(uri); + + let range: IRange; + let text: string; + + if (diff.type === 'deletion') { + range = { + startLineNumber: diff.originalStartLine, + startColumn: 1, + endLineNumber: diff.originalEndLine + 1, + endColumn: 1 + }; + text = ''; + } else if (diff.type === 'insertion') { + range = { + startLineNumber: diff.originalStartLine, + startColumn: 1, + endLineNumber: diff.originalStartLine, + endColumn: 1 + }; + text = diff.code; + } else if (diff.type === 'edit') { + range = { + startLineNumber: diff.originalStartLine, + startColumn: 1, + endLineNumber: diff.originalEndLine + 1, + endColumn: 1 + }; + text = diff.code; + } else { + throw new Error(`Void error: unknown diff type for diffid ${diffid}`); + } + + model.pushEditOperations([], [{ range, text }], () => null); + + diffArea.originalCode = model.getValueInRange({ + startLineNumber: diffArea.startLine, + startColumn: 1, + endLineNumber: diffArea.endLine, + endColumn: Number.MAX_SAFE_INTEGER + }, EndOfLinePreference.LF); + + this._deleteDiff(diff); + if (Object.keys(diffArea._diffOfId).length === 0) { + this._deleteDiffZone(diffArea); + } + + this._refreshStylesAndDiffsInURI(uri); + await onFinishEdit(); + this.logService.debug(`[acceptDiff] done uri=${uri.fsPath} diffid=${diffid} remainingDiffs=${Object.keys(diffArea._diffOfId).length}`); + } + + public async applyEditFileSimpleFromDiffZone(diffZone: DiffZone & { _editFileSimple?: any }) { + if (!diffZone || !diffZone._editFileSimple) throw new Error('No edit_file metadata'); + const meta = diffZone._editFileSimple; + const uri = diffZone._URI; + + const modelEntry = this._voidModelService.getModel(uri) + const model = modelEntry?.model + if (!model) throw new Error('File not found') + + + let text = String(meta.updated_text ?? '') + if (!text) throw new Error('No updated_text to apply') + if (meta.newline === 'lf') { + text = text.replace(/\r\n/g, '\n') + } else if (meta.newline === 'crlf') { + text = text.replace(/\r?\n/g, '\r\n') + } else { + const eol = model.getEOL() + text = (eol === '\r\n') ? text.replace(/\r?\n/g, '\r\n') : text.replace(/\r\n/g, '\n') + } + + + const currentLF = model.getValue(EndOfLinePreference.LF) + const targetLF = text.replace(/\r\n/g, '\n') + if (currentLF === targetLF) { + try { this.acceptOrRejectAllDiffAreas?.({ uri, behavior: 'accept', removeCtrlKs: false }) } catch { } + this._refreshStylesAndDiffsInURI(uri) + return + } + + this._writeURIText(uri, text, 'wholeFileRange', { shouldRealignDiffAreas: true }) + try { await (this as any)._saveModelIfNeeded?.(uri, meta.encoding) } catch { } + try { this.acceptOrRejectAllDiffAreas?.({ uri, behavior: 'accept', removeCtrlKs: false }) } catch { } + this._refreshStylesAndDiffsInURI(uri) + } // called on void.rejectDiff public async rejectDiff({ diffid }: { diffid: number }) { const diff = this.diffOfId[diffid] - if (!diff) return + if (!diff) { + this.logService.debug(`[rejectDiff] skipped missing diffid=${diffid}`) + return + } const { diffareaid } = diff const diffArea = this.diffAreaOfId[diffareaid] - if (!diffArea) return + if (!diffArea) { + this.logService.debug(`[rejectDiff] skipped diffid=${diffid} missing diffareaid=${diffareaid}`) + return + } - if (diffArea.type !== 'DiffZone') return + if (diffArea.type !== 'DiffZone') { + this.logService.debug(`[rejectDiff] skipped diffid=${diffid} diffareaid=${diffareaid} non-diffZone`) + return + } const uri = diffArea._URI + const loggedEndLine = diff.type === 'deletion' ? diff.startLine : diff.endLine + this.logService.debug(`[rejectDiff] start uri=${uri.fsPath} diffid=${diffid} diffareaid=${diffareaid} type=${diff.type} range=${diff.startLine}-${loggedEndLine} applyBoxId=${diffArea.applyBoxId ?? 'none'}`) // add to history const { onFinishEdit } = this._addToHistory(uri) @@ -2227,7 +3614,6 @@ class EditCodeService extends Disposable implements IEditCodeService { // B| <-- endLine (we want to delete this whole line) // C else if (diff.type === 'insertion') { - // console.log('REJECTING:', diff) // handle the case where the insertion was a newline at end of diffarea (applying to the next line doesnt work because it doesnt exist, vscode just doesnt delete the correct # of newlines) if (diff.endLine === diffArea.endLine) { // delete the line before instead of after @@ -2252,6 +3638,7 @@ class EditCodeService extends Disposable implements IEditCodeService { else { throw new Error(`Void error: ${diff}.type not recognized`) } + this.logService.debug(`[rejectDiff] computedWrite uri=${uri.fsPath} diffid=${diffid} writeLen=${writeText.length} toRange=${JSON.stringify(toRange)}`) // update the file this._writeURIText(uri, writeText, toRange, { shouldRealignDiffAreas: true }) @@ -2269,15 +3656,22 @@ class EditCodeService extends Disposable implements IEditCodeService { this._refreshStylesAndDiffsInURI(uri) onFinishEdit() - + this.logService.debug(`[rejectDiff] done uri=${uri.fsPath} diffid=${diffid} remainingDiffs=${Object.keys(diffArea._diffOfId).length}`) } - } registerSingleton(IEditCodeService, EditCodeService, InstantiationType.Eager); - - +// Internal helpers exported for targeted unit testing +export const __test_only = { + normalizeEol, + createUnifiedFromLineDiffs, + getLengthOfTextPx, + getLeadingWhitespacePx, + processRawKeybindingText: (keybindingStr: string) => + EditCodeService.prototype.processRawKeybindingText.call({}, keybindingStr), + applyDiffToBaseline, +}; class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { @@ -2386,7 +3780,6 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { acceptButton.style.boxShadow = '0 2px 3px rgba(0,0,0,0.2)'; acceptButton.style.pointerEvents = 'auto'; - // Style reject button rejectButton.onclick = onReject; rejectButton.textContent = rejectText; @@ -2405,8 +3798,6 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { rejectButton.style.boxShadow = '0 2px 3px rgba(0,0,0,0.2)'; rejectButton.style.pointerEvents = 'auto'; - - this._domNode = buttons; const updateTop = () => { @@ -2429,9 +3820,9 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { updateLeft() }, 0) - this._register(editor.onDidScrollChange(e => { updateTop() })) - this._register(editor.onDidChangeModelContent(e => { updateTop() })) - this._register(editor.onDidLayoutChange(e => { updateTop(); updateLeft() })) + this._register(editor.onDidScrollChange(() => { updateTop() })) + this._register(editor.onDidChangeModelContent(() => { updateTop() })) + this._register(editor.onDidLayoutChange(() => { updateTop(); updateLeft() })) // Listen for state changes in the command bar service @@ -2449,7 +3840,6 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { // mount this widget editor.addOverlayWidget(this); - // console.log('created elt', this._domNode) } public override dispose(): void { @@ -2458,8 +3848,3 @@ class AcceptRejectInlineWidget extends Widget implements IOverlayWidget { } } - - - - - diff --git a/src/vs/workbench/contrib/void/browser/editCodeServiceInterface.ts b/src/vs/workbench/contrib/void/browser/editCodeServiceInterface.ts index 9e33fbd21d9..8a11cb66dc1 100644 --- a/src/vs/workbench/contrib/void/browser/editCodeServiceInterface.ts +++ b/src/vs/workbench/contrib/void/browser/editCodeServiceInterface.ts @@ -7,7 +7,7 @@ import { Event } from '../../../../base/common/event.js'; import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { Diff, DiffArea, VoidFileSnapshot } from '../common/editCodeServiceTypes.js'; +import { Diff, DiffArea, VoidFileSnapshot } from '../../../../platform/void/common/editCodeServiceTypes.js'; export type StartBehavior = 'accept-conflicts' | 'reject-conflicts' | 'keep-conflicts' @@ -27,8 +27,10 @@ export type StartApplyingOpts = { } | { from: 'ClickApply'; applyStr: string; + selections?: string[]; uri: 'current' | URI; startBehavior: StartBehavior; + applyBoxId?: string; // Optional applyBoxId to associate with the diff zone } export type AddCtrlKOpts = { @@ -44,10 +46,14 @@ export interface IEditCodeService { processRawKeybindingText(keybindingStr: string): string; - callBeforeApplyOrEdit(uri: URI | 'current'): Promise; + callBeforeApplyOrEdit(uri: URI | 'current' | CallBeforeStartApplyingOpts): Promise; startApplying(opts: StartApplyingOpts): [URI, Promise] | null; - instantlyApplySearchReplaceBlocks(opts: { uri: URI; searchReplaceBlocks: string }): void; + //instantlyApplySearchReplaceBlocks(opts: { uri: URI; searchReplaceBlocks: string }): void; instantlyRewriteFile(opts: { uri: URI; newContent: string }): void; + getLastFallbackMessage(uri: URI): string | null; + + recordFallbackMessage(uri: URI, message: string): void; + addCtrlKZone(opts: AddCtrlKOpts): number | undefined; removeCtrlKZone(opts: { diffareaid: number }): void; @@ -59,11 +65,25 @@ export interface IEditCodeService { acceptDiff({ diffid }: { diffid: number }): void; rejectDiff({ diffid }: { diffid: number }): void; + previewEditFileSimple(params: { + uri: URI; + originalSnippet: string; + updatedSnippet: string; + occurrence?: number | null; + replaceAll?: boolean; + locationHint?: any; + encoding?: string | null; + newline?: string | null; + applyBoxId?: string; + }): Promise; + // events onDidAddOrDeleteDiffZones: Event<{ uri: URI }>; onDidChangeDiffsInDiffZoneNotStreaming: Event<{ uri: URI; diffareaid: number }>; // only fires when not streaming!!! streaming would be too much onDidChangeStreamingInDiffZone: Event<{ uri: URI; diffareaid: number }>; onDidChangeStreamingInCtrlKZone: Event<{ uri: URI; diffareaid: number }>; + // fired when instant apply fell back to locating ORIGINAL snippets and retried + onDidUseFallback?: Event<{ uri: URI; message?: string }>; // CtrlKZone streaming state isCtrlKZoneStreaming(opts: { diffareaid: number }): boolean; @@ -75,4 +95,20 @@ export interface IEditCodeService { // testDiffs(): void; getVoidFileSnapshot(uri: URI): VoidFileSnapshot; restoreVoidFileSnapshot(uri: URI, snapshot: VoidFileSnapshot): void; + + + bindApplyBoxUri(applyBoxId: string, uri: URI): void; + getUriByApplyBoxId(applyBoxId: string): URI | undefined; + + // UI helper: tells if there are non-streaming diff zones for a given applyBoxId on this file + hasIdleDiffZoneForApplyBox(uri: URI, applyBoxId: string): boolean; + + // UI helper: if a preview DiffZone was created by edit_file flow, apply it without UI poking internals + applyEditFileSimpleForApplyBox(args: { uri: URI; applyBoxId: string }): Promise; + + // UI helper: infer best snippet selection for Apply (AST-first with heuristic fallback) + inferSelectionForApply(args: { uri: URI; codeStr: string; fileText: string }): Promise<{ text: string; range: [number, number] } | null>; + + // Make accept/reject by applyBox awaitable (needed now that we format-on-accept) + acceptOrRejectDiffAreasByApplyBox(args: { uri: URI; applyBoxId: string; behavior: 'accept' | 'reject' }): Promise; } diff --git a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts index b8843e98b96..89be86fcac8 100644 --- a/src/vs/workbench/contrib/void/browser/extensionTransferService.ts +++ b/src/vs/workbench/contrib/void/browser/extensionTransferService.ts @@ -11,7 +11,7 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { TransferEditorType, TransferFilesInfo } from './extensionTransferTypes.js'; - +import { ILogService } from '../../../../platform/log/common/log.js'; export interface IExtensionTransferService { readonly _serviceBrand: undefined; // services need this, just leave it undefined @@ -22,10 +22,6 @@ export interface IExtensionTransferService { export const IExtensionTransferService = createDecorator('ExtensionTransferService'); - - - - // Define extensions to skip when transferring const extensionBlacklist = [ // ignore extensions @@ -51,6 +47,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS constructor( @IFileService private readonly _fileService: IFileService, + @ILogService private readonly logService: ILogService, ) { super() } @@ -65,7 +62,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS // Check if the source file exists before attempting to copy try { if (!isExtensions) { - console.log('transferring item', from, to) + this.logService.debug('transferring item', from, to) const exists = await fileService.exists(from) if (exists) { @@ -77,12 +74,12 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } await fileService.copy(from, to, true) } else { - console.log(`Skipping file that doesn't exist: ${from.toString()}`) + this.logService.debug(`Skipping file that doesn't exist: ${from.toString()}`) } } // extensions folder else { - console.log('transferring extensions...', from, to) + this.logService.debug('transferring extensions...', from, to) const exists = await fileService.exists(from) if (exists) { const stat = await fileService.resolve(from) @@ -111,7 +108,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS await fileService.writeFile(to, VSBuffer.fromString(jsonStr)) } catch { - console.log('Error copying extensions.json, skipping') + this.logService.debug('Error copying extensions.json, skipping') } } } @@ -120,11 +117,11 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS } else { console.log(`Skipping file that doesn't exist: ${from.toString()}`) } - console.log('done transferring extensions.') + this.logService.debug('done transferring extensions.') } } catch (e) { - console.error('Error copying file:', e) + this.logService.error('Error copying file:', e) errAcc += `Error copying ${from.toString()}: ${e}\n` } } @@ -145,7 +142,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS if (child.isDirectory) { // if is blacklisted if (isBlacklisted(child.resource.fsPath)) { - console.log('Deleting extension', child.resource.fsPath) + this.logService.debug('Deleting extension', child.resource.fsPath) await fileService.del(child.resource, { recursive: true, useTrash: true }) } } @@ -153,7 +150,7 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS // if is extensions.json if (child.name === 'extensions.json') { - console.log('Updating extensions.json', child.resource.fsPath) + this.logService.debug('Updating extensions.json', child.resource.fsPath) try { const contentsStr = await fileService.readFile(child.resource) const json: any = JSON.parse(contentsStr.value.toString()) @@ -162,13 +159,13 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS await fileService.writeFile(child.resource, VSBuffer.fromString(jsonStr)) } catch { - console.log('Error copying extensions.json, skipping') + this.logService.debug('Error copying extensions.json, skipping') } } } } catch (e) { - console.error('Could not delete extension', child.resource.fsPath, e) + this.logService.error('Could not delete extension', child.resource.fsPath, e) } } } @@ -177,14 +174,6 @@ class ExtensionTransferService extends Disposable implements IExtensionTransferS registerSingleton(IExtensionTransferService, ExtensionTransferService, InstantiationType.Eager); // lazily loaded, even if Eager - - - - - - - - const transferTheseFilesOfOS = (os: 'mac' | 'windows' | 'linux' | null, fromEditor: TransferEditorType = 'VS Code'): TransferFilesInfo => { if (os === null) throw new Error(`One-click switch is not possible in this environment.`) diff --git a/src/vs/workbench/contrib/void/browser/fileService.ts b/src/vs/workbench/contrib/void/browser/fileService.ts index 93da1b1e2cf..50a1fec5260 100644 --- a/src/vs/workbench/contrib/void/browser/fileService.ts +++ b/src/vs/workbench/contrib/void/browser/fileService.ts @@ -5,12 +5,11 @@ import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; -import { IDirectoryStrService } from '../common/directoryStrService.js'; +import { IDirectoryStrService } from '../../../../platform/void/common/directoryStrService.js'; import { messageOfSelection } from '../common/prompt/prompts.js'; import { IVoidModelService } from '../common/voidModelService.js'; - class FilePromptActionService extends Action2 { private static readonly VOID_COPY_FILE_PROMPT_ID = 'void.copyfileprompt' diff --git a/src/vs/workbench/contrib/void/browser/helperServices/consistentItemService.ts b/src/vs/workbench/contrib/void/browser/helperServices/consistentItemService.ts index ba906ff5978..a8cca0a3d96 100644 --- a/src/vs/workbench/contrib/void/browser/helperServices/consistentItemService.ts +++ b/src/vs/workbench/contrib/void/browser/helperServices/consistentItemService.ts @@ -94,8 +94,6 @@ export class ConsistentItemService extends Disposable implements IConsistentItem this._register(this._editorService.onCodeEditorRemove(editor => { removeItemsFromEditor(editor) })) } - - _putItemOnEditor(editor: ICodeEditor, consistentItemId: string) { const { fn } = this.infoOfConsistentItemId[consistentItemId] @@ -113,7 +111,6 @@ export class ConsistentItemService extends Disposable implements IConsistentItem this.consistentItemIdOfItemId[itemId] = consistentItemId this.disposeFnOfItemId[itemId] = () => { - // console.log('calling remove for', itemId) dispose?.() } diff --git a/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts b/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts index 703b2775be6..4aac7bf9d65 100644 --- a/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts +++ b/src/vs/workbench/contrib/void/browser/helpers/findDiffs.ts @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { ComputedDiff } from '../../common/editCodeServiceTypes.js'; +import { ComputedDiff } from '../../../../../platform/void/common/editCodeServiceTypes.js'; import { diffLines } from '../react/out/diff/index.js' export function findDiffs(oldStr: string, newStr: string) { @@ -100,152 +100,5 @@ export function findDiffs(oldStr: string, newStr: string) { } } // end for - // console.log('DIFF', { oldStr, newStr, replacements }) return replacements } - - - - - - - - - - - - - - - - - - - - -// // uncomment this to test -// let name_ = '' -// let testsFailed = 0 -// const assertEqual = (a: { [s: string]: any }, b: { [s: string]: any }) => { -// let keys = new Set([...Object.keys(a), ...Object.keys(b)]) -// for (let k of keys) { -// if (a[k] !== b[k]) { -// console.error('Void Test Error:', name_, '\n', `${k}=`, `${JSON.stringify(a[k])}, ${JSON.stringify(b[k])}`) -// // console.error(JSON.stringify(a, null, 4)) -// // console.error(JSON.stringify(b, null, 4)) -// testsFailed += 1 -// } -// } -// } -// const test = (name: string, fn: () => void) => { -// name_ = name -// fn() -// } - -// const originalCode = `\ -// A -// B -// C -// D -// E` - -// const insertedCode = `\ -// A -// B -// C -// F -// D -// E` - -// const modifiedCode = `\ -// A -// B -// C -// F -// E` - -// const modifiedCode2 = `\ -// A -// B -// C -// D -// E -// ` - - -// test('Diffs Insertion', () => { -// const diffs = findDiffs(originalCode, insertedCode) - -// const expected: BaseDiff = { -// type: 'insertion', -// originalCode: '', -// originalStartLine: 4, // empty range where the insertion happened -// originalEndLine: 4, - -// startLine: 4, -// startCol: 1, -// endLine: 4, -// endCol: Number.MAX_SAFE_INTEGER, -// } -// assertEqual(diffs[0], expected) -// }) - -// test('Diffs Deletion', () => { -// const diffs = findDiffs(insertedCode, originalCode) -// assertEqual({ length: diffs.length }, { length: 1 }) -// const expected: BaseDiff = { -// type: 'deletion', -// originalCode: 'F', -// originalStartLine: 4, -// originalEndLine: 4, - -// startLine: 4, -// startCol: 1, // empty range where the deletion happened -// endLine: 4, -// endCol: 1, -// } -// assertEqual(diffs[0], expected) -// }) - -// test('Diffs Modification', () => { -// const diffs = findDiffs(originalCode, modifiedCode) -// assertEqual({ length: diffs.length }, { length: 1 }) -// const expected: BaseDiff = { -// type: 'edit', -// originalCode: 'D', -// originalStartLine: 4, -// originalEndLine: 4, - -// startLine: 4, -// startCol: 1, -// endLine: 4, -// endCol: Number.MAX_SAFE_INTEGER, -// } -// assertEqual(diffs[0], expected) -// }) - -// test('Diffs Modification 2', () => { -// const diffs = findDiffs(originalCode, modifiedCode2) -// assertEqual({ length: diffs.length }, { length: 1 }) -// const expected: BaseDiff = { -// type: 'insertion', -// originalCode: '', -// originalStartLine: 6, -// originalEndLine: 6, - -// startLine: 6, -// startCol: 1, -// endLine: 6, -// endCol: Number.MAX_SAFE_INTEGER, -// } -// assertEqual(diffs[0], expected) -// }) - - - -// if (testsFailed === 0) { -// console.log('✅ Void - All tests passed') -// } -// else { -// console.log('❌ Void - At least one test failed') -// } diff --git a/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.d.ts b/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.d.ts new file mode 100644 index 00000000000..89dc8751f33 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.d.ts @@ -0,0 +1,7 @@ +declare const diff_match_patch: any; +declare const DIFF_DELETE: -1; +declare const DIFF_INSERT: 1; +declare const DIFF_EQUAL: 0; + +export default diff_match_patch; +export { diff_match_patch, DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL }; diff --git a/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.js b/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.js new file mode 100644 index 00000000000..74139e1235a --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/lib/diff-match-patch.js @@ -0,0 +1,2220 @@ +/** + * Diff Match and Patch + * Copyright 2018 The diff-match-patch Authors. + * https://github.com/google/diff-match-patch + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * @author fraser@google.com (Neil Fraser) + */ + +/** + * Class containing the diff, match and patch methods. + * @constructor + */ +let diff_match_patch = function () { + + // Defaults. + // Redefine these in your program to override the defaults. + + // Number of seconds to map a diff before giving up (0 for infinity). + this.Diff_Timeout = 1.0; + // Cost of an empty edit operation in terms of edit characters. + this.Diff_EditCost = 4; + // At what point is no match declared (0.0 = perfection, 1.0 = very loose). + this.Match_Threshold = 0.5; + // How far to search for a match (0 = exact location, 1000+ = broad match). + // A match this many characters away from the expected location will add + // 1.0 to the score (0.0 is a perfect match). + this.Match_Distance = 1000; + // When deleting a large block of text (over ~64 characters), how close do + // the contents have to be to match the expected contents. (0.0 = perfection, + // 1.0 = very loose). Note that Match_Threshold controls how closely the + // end points of a delete need to match. + this.Patch_DeleteThreshold = 0.5; + // Chunk size for context length. + this.Patch_Margin = 4; + + // The number of bits in an int. + this.Match_MaxBits = 32; +}; + + +// DIFF FUNCTIONS + + +/** + * The data structure representing a diff is an array of tuples: + * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']] + * which means: delete 'Hello', add 'Goodbye' and keep ' world.' + */ +let DIFF_DELETE = -1; +let DIFF_INSERT = 1; +let DIFF_EQUAL = 0; + +/** + * Class representing one diff tuple. + * ~Attempts to look like a two-element array (which is what this used to be).~ + * Constructor returns an actual two-element array, to allow destructing @JackuB + * See https://github.com/JackuB/diff-match-patch/issues/14 for details + * @param {number} op Operation, one of: DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL. + * @param {string} text Text to be deleted, inserted, or retained. + * @constructor + */ +diff_match_patch.Diff = function (op, text) { + return [op, text]; +}; + +/** + * Find the differences between two texts. Simplifies the problem by stripping + * any common prefix or suffix off the texts before diffing. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean=} opt_checklines Optional speedup flag. If present and false, + * then don't run a line-level diff first to identify the changed areas. + * Defaults to true, which does a faster, slightly less optimal diff. + * @param {number=} opt_deadline Optional time when the diff should be complete + * by. Used internally for recursive calls. Users should set DiffTimeout + * instead. + * @return {!Array.} Array of diff tuples. + */ +diff_match_patch.prototype.diff_main = function (text1, text2, opt_checklines, + opt_deadline) { + // Set a deadline by which time the diff must be complete. + if (typeof opt_deadline === 'undefined') { + if (this.Diff_Timeout <= 0) { + opt_deadline = Number.MAX_VALUE; + } else { + opt_deadline = (new Date).getTime() + this.Diff_Timeout * 1000; + } + } + let deadline = opt_deadline; + + // Check for null inputs. + if (text1 === null || text2 === null) { + throw new Error('Null input. (diff_main)'); + } + + // Check for equality (speedup). + if (text1 === text2) { + if (text1) { + return [new diff_match_patch.Diff(DIFF_EQUAL, text1)]; + } + return []; + } + + if (typeof opt_checklines === 'undefined') { + opt_checklines = true; + } + let checklines = opt_checklines; + + // Trim off common prefix (speedup). + let commonlength = this.diff_commonPrefix(text1, text2); + let commonprefix = text1.substring(0, commonlength); + text1 = text1.substring(commonlength); + text2 = text2.substring(commonlength); + + // Trim off common suffix (speedup). + commonlength = this.diff_commonSuffix(text1, text2); + let commonsuffix = text1.substring(text1.length - commonlength); + text1 = text1.substring(0, text1.length - commonlength); + text2 = text2.substring(0, text2.length - commonlength); + + // Compute the diff on the middle block. + let diffs = this.diff_compute_(text1, text2, checklines, deadline); + + // Restore the prefix and suffix. + if (commonprefix) { + diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, commonprefix)); + } + if (commonsuffix) { + diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, commonsuffix)); + } + this.diff_cleanupMerge(diffs); + return diffs; +}; + + +/** + * Find the differences between two texts. Assumes that the texts do not + * have any common prefix or suffix. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {boolean} checklines Speedup flag. If false, then don't run a + * line-level diff first to identify the changed areas. + * If true, then run a faster, slightly less optimal diff. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_compute_ = function (text1, text2, checklines, + deadline) { + let diffs; + + if (!text1) { + // Just add some text (speedup). + return [new diff_match_patch.Diff(DIFF_INSERT, text2)]; + } + + if (!text2) { + // Just delete some text (speedup). + return [new diff_match_patch.Diff(DIFF_DELETE, text1)]; + } + + let longtext = text1.length > text2.length ? text1 : text2; + let shorttext = text1.length > text2.length ? text2 : text1; + let i = longtext.indexOf(shorttext); + if (i !== -1) { + // Shorter text is inside the longer text (speedup). + diffs = [new diff_match_patch.Diff(DIFF_INSERT, longtext.substring(0, i)), + new diff_match_patch.Diff(DIFF_EQUAL, shorttext), + new diff_match_patch.Diff(DIFF_INSERT, + longtext.substring(i + shorttext.length))]; + // Swap insertions for deletions if diff is reversed. + if (text1.length > text2.length) { + diffs[0][0] = diffs[2][0] = DIFF_DELETE; + } + return diffs; + } + + if (shorttext.length === 1) { + // Single character string. + // After the previous speedup, the character can't be an equality. + return [new diff_match_patch.Diff(DIFF_DELETE, text1), + new diff_match_patch.Diff(DIFF_INSERT, text2)]; + } + + // Check to see if the problem can be split in two. + let hm = this.diff_halfMatch_(text1, text2); + if (hm) { + // A half-match was found, sort out the return data. + let text1_a = hm[0]; + let text1_b = hm[1]; + let text2_a = hm[2]; + let text2_b = hm[3]; + let mid_common = hm[4]; + // Send both pairs off for separate processing. + let diffs_a = this.diff_main(text1_a, text2_a, checklines, deadline); + let diffs_b = this.diff_main(text1_b, text2_b, checklines, deadline); + // Merge the results. + return diffs_a.concat([new diff_match_patch.Diff(DIFF_EQUAL, mid_common)], + diffs_b); + } + + if (checklines && text1.length > 100 && text2.length > 100) { + return this.diff_lineMode_(text1, text2, deadline); + } + + return this.diff_bisect_(text1, text2, deadline); +}; + + +/** + * Do a quick line-level diff on both strings, then rediff the parts for + * greater accuracy. + * This speedup can produce non-minimal diffs. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time when the diff should be complete by. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_lineMode_ = function (text1, text2, deadline) { + // Scan the text on a line-by-line basis first. + let a = this.diff_linesToChars_(text1, text2); + text1 = a.chars1; + text2 = a.chars2; + let linearray = a.lineArray; + + let diffs = this.diff_main(text1, text2, false, deadline); + + // Convert the diff back to original text. + this.diff_charsToLines_(diffs, linearray); + // Eliminate freak matches (e.g. blank lines) + this.diff_cleanupSemantic(diffs); + + // Rediff any replacement blocks, this time character-by-character. + // Add a dummy entry at the end. + diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, '')); + let pointer = 0; + let count_delete = 0; + let count_insert = 0; + let text_delete = ''; + let text_insert = ''; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete >= 1 && count_insert >= 1) { + // Delete the offending records and add the merged ones. + diffs.splice(pointer - count_delete - count_insert, + count_delete + count_insert); + pointer = pointer - count_delete - count_insert; + let subDiff = + this.diff_main(text_delete, text_insert, false, deadline); + for (let j = subDiff.length - 1; j >= 0; j--) { + diffs.splice(pointer, 0, subDiff[j]); + } + pointer = pointer + subDiff.length; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + pointer++; + } + diffs.pop(); // Remove the dummy entry at the end. + + return diffs; +}; + + +/** + * Find the 'middle snake' of a diff, split the problem in two + * and return the recursively constructed diff. + * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisect_ = function (text1, text2, deadline) { + + // Cache the text lengths to prevent multiple calls. + let text1_length = text1.length; + let text2_length = text2.length; + let max_d = Math.ceil((text1_length + text2_length) / 2); + let v_offset = max_d; + let v_length = 2 * max_d; + let v1 = new Array(v_length); + let v2 = new Array(v_length); + // Setting all elements to -1 is faster in Chrome & Firefox than mixing + // integers and undefined. + for (let x = 0; x < v_length; x++) { + v1[x] = -1; + v2[x] = -1; + } + v1[v_offset + 1] = 0; + v2[v_offset + 1] = 0; + let delta = text1_length - text2_length; + // If the total number of characters is odd, then the front path will collide + // with the reverse path. + let front = (delta % 2 !== 0); + // Offsets for start and end of k loop. + // Prevents mapping of space beyond the grid. + let k1start = 0; + let k1end = 0; + let k2start = 0; + let k2end = 0; + for (let d = 0; d < max_d; d++) { + // Bail out if deadline is reached. + if ((new Date()).getTime() > deadline) { + break; + } + + // Walk the front path one step. + for (let k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + let k1_offset = v_offset + k1; + let x1; + if (k1 === -d || (k1 !== d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + x1 = v1[k1_offset + 1]; + } else { + x1 = v1[k1_offset - 1] + 1; + } + let y1 = x1 - k1; + while (x1 < text1_length && y1 < text2_length && + text1.charAt(x1) === text2.charAt(y1)) { + x1++; + y1++; + } + v1[k1_offset] = x1; + if (x1 > text1_length) { + // Ran off the right of the graph. + k1end += 2; + } else if (y1 > text2_length) { + // Ran off the bottom of the graph. + k1start += 2; + } else if (front) { + let k2_offset = v_offset + delta - k1; + if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] !== -1) { + // Mirror x2 onto top-left coordinate system. + let x2 = text1_length - v2[k2_offset]; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + + // Walk the reverse path one step. + for (let k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + let k2_offset = v_offset + k2; + let x2; + if (k2 === -d || (k2 !== d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + x2 = v2[k2_offset + 1]; + } else { + x2 = v2[k2_offset - 1] + 1; + } + let y2 = x2 - k2; + while (x2 < text1_length && y2 < text2_length && + text1.charAt(text1_length - x2 - 1) === + text2.charAt(text2_length - y2 - 1)) { + x2++; + y2++; + } + v2[k2_offset] = x2; + if (x2 > text1_length) { + // Ran off the left of the graph. + k2end += 2; + } else if (y2 > text2_length) { + // Ran off the top of the graph. + k2start += 2; + } else if (!front) { + let k1_offset = v_offset + delta - k2; + if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] !== -1) { + let x1 = v1[k1_offset]; + let y1 = v_offset + x1 - k1_offset; + // Mirror x2 onto top-left coordinate system. + x2 = text1_length - x2; + if (x1 >= x2) { + // Overlap detected. + return this.diff_bisectSplit_(text1, text2, x1, y1, deadline); + } + } + } + } + } + // Diff took too long and hit the deadline or + // number of diffs equals number of characters, no commonality at all. + return [new diff_match_patch.Diff(DIFF_DELETE, text1), + new diff_match_patch.Diff(DIFF_INSERT, text2)]; +}; + + +/** + * Given the location of the 'middle snake', split the diff in two parts + * and recurse. + * @param {string} text1 Old string to be diffed. + * @param {string} text2 New string to be diffed. + * @param {number} x Index of split point in text1. + * @param {number} y Index of split point in text2. + * @param {number} deadline Time at which to bail if not yet complete. + * @return {!Array.} Array of diff tuples. + * @private + */ +diff_match_patch.prototype.diff_bisectSplit_ = function (text1, text2, x, y, + deadline) { + let text1a = text1.substring(0, x); + let text2a = text2.substring(0, y); + let text1b = text1.substring(x); + let text2b = text2.substring(y); + + // Compute both diffs serially. + let diffs = this.diff_main(text1a, text2a, false, deadline); + let diffsb = this.diff_main(text1b, text2b, false, deadline); + + return diffs.concat(diffsb); +}; + + +/** + * Split two texts into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {{chars1: string, chars2: string, lineArray: !Array.}} + * An object containing the encoded text1, the encoded text2 and + * the array of unique strings. + * The zeroth element of the array of unique strings is intentionally blank. + * @private + */ +diff_match_patch.prototype.diff_linesToChars_ = function (text1, text2) { + let lineArray = []; // e.g. lineArray[4] == 'Hello\n' + let lineHash = {}; // e.g. lineHash['Hello\n'] == 4 + + // '\x00' is a valid character, but various debuggers don't like it. + // So we'll insert a junk entry to avoid generating a null character. + lineArray[0] = ''; + + /** + * Split a text into an array of strings. Reduce the texts to a string of + * hashes where each Unicode character represents one line. + * Modifies linearray and linehash through being a closure. + * @param {string} text String to encode. + * @return {string} Encoded string. + * @private + */ + function diff_linesToCharsMunge_(text) { + let chars = ''; + // Walk the text, pulling out a substring for each line. + // text.split('\n') would would temporarily double our memory footprint. + // Modifying text would create many large strings to garbage collect. + let lineStart = 0; + let lineEnd = -1; + // Keeping our own length letiable is faster than looking it up. + let lineArrayLength = lineArray.length; + while (lineEnd < text.length - 1) { + lineEnd = text.indexOf('\n', lineStart); + if (lineEnd === -1) { + lineEnd = text.length - 1; + } + let line = text.substring(lineStart, lineEnd + 1); + + if (lineHash.hasOwnProperty ? lineHash.hasOwnProperty(line) : + (lineHash[line] !== undefined)) { + chars += String.fromCharCode(lineHash[line]); + } else { + if (lineArrayLength === maxLines) { + // Bail out at 65535 because + // String.fromCharCode(65536) == String.fromCharCode(0) + line = text.substring(lineStart); + lineEnd = text.length; + } + chars += String.fromCharCode(lineArrayLength); + lineHash[line] = lineArrayLength; + lineArray[lineArrayLength++] = line; + } + lineStart = lineEnd + 1; + } + return chars; + } + // Allocate 2/3rds of the space for text1, the rest for text2. + let maxLines = 40000; + let chars1 = diff_linesToCharsMunge_(text1); + maxLines = 65535; + let chars2 = diff_linesToCharsMunge_(text2); + return { chars1: chars1, chars2: chars2, lineArray: lineArray }; +}; + + +/** + * Rehydrate the text in a diff from a string of line hashes to real lines of + * text. + * @param {!Array.} diffs Array of diff tuples. + * @param {!Array.} lineArray Array of unique strings. + * @private + */ +diff_match_patch.prototype.diff_charsToLines_ = function (diffs, lineArray) { + for (let i = 0; i < diffs.length; i++) { + let chars = diffs[i][1]; + let text = []; + for (let j = 0; j < chars.length; j++) { + text[j] = lineArray[chars.charCodeAt(j)]; + } + diffs[i][1] = text.join(''); + } +}; + + +/** + * Determine the common prefix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the start of each + * string. + */ +diff_match_patch.prototype.diff_commonPrefix = function (text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) { + return 0; + } + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + let pointermin = 0; + let pointermax = Math.min(text1.length, text2.length); + let pointermid = pointermax; + let pointerstart = 0; + while (pointermin < pointermid) { + if (text1.substring(pointerstart, pointermid) === + text2.substring(pointerstart, pointermid)) { + pointermin = pointermid; + pointerstart = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine the common suffix of two strings. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of each string. + */ +diff_match_patch.prototype.diff_commonSuffix = function (text1, text2) { + // Quick check for common null cases. + if (!text1 || !text2 || + text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) { + return 0; + } + // Binary search. + // Performance analysis: https://neil.fraser.name/news/2007/10/09/ + let pointermin = 0; + let pointermax = Math.min(text1.length, text2.length); + let pointermid = pointermax; + let pointerend = 0; + while (pointermin < pointermid) { + if (text1.substring(text1.length - pointermid, text1.length - pointerend) === + text2.substring(text2.length - pointermid, text2.length - pointerend)) { + pointermin = pointermid; + pointerend = pointermin; + } else { + pointermax = pointermid; + } + pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin); + } + return pointermid; +}; + + +/** + * Determine if the suffix of one string is the prefix of another. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {number} The number of characters common to the end of the first + * string and the start of the second string. + * @private + */ +diff_match_patch.prototype.diff_commonOverlap_ = function (text1, text2) { + // Cache the text lengths to prevent multiple calls. + let text1_length = text1.length; + let text2_length = text2.length; + // Eliminate the null case. + if (text1_length === 0 || text2_length === 0) { + return 0; + } + // Truncate the longer string. + if (text1_length > text2_length) { + text1 = text1.substring(text1_length - text2_length); + } else if (text1_length < text2_length) { + text2 = text2.substring(0, text1_length); + } + let text_length = Math.min(text1_length, text2_length); + // Quick check for the worst case. + if (text1 === text2) { + return text_length; + } + + // Start by looking for a single character match + // and increase length until no match is found. + // Performance analysis: https://neil.fraser.name/news/2010/11/04/ + let best = 0; + let length = 1; + while (true) { + let pattern = text1.substring(text_length - length); + let found = text2.indexOf(pattern); + if (found === -1) { + return best; + } + length += found; + if (found === 0 || text1.substring(text_length - length) === + text2.substring(0, length)) { + best = length; + length++; + } + } +}; + + +/** + * Do the two texts share a substring which is at least half the length of the + * longer text? + * This speedup can produce non-minimal diffs. + * @param {string} text1 First string. + * @param {string} text2 Second string. + * @return {Array.} Five element Array, containing the prefix of + * text1, the suffix of text1, the prefix of text2, the suffix of + * text2 and the common middle. Or null if there was no match. + * @private + */ +diff_match_patch.prototype.diff_halfMatch_ = function (text1, text2) { + if (this.Diff_Timeout <= 0) { + // Don't risk returning a non-optimal diff if we have unlimited time. + return null; + } + let longtext = text1.length > text2.length ? text1 : text2; + let shorttext = text1.length > text2.length ? text2 : text1; + if (longtext.length < 4 || shorttext.length * 2 < longtext.length) { + return null; // Pointless. + } + let dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Does a substring of shorttext exist within longtext such that the substring + * is at least half the length of longtext? + * Closure, but does not reference any external variables. + * @param {string} longtext Longer string. + * @param {string} shorttext Shorter string. + * @param {number} i Start index of quarter length substring within longtext. + * @return {Array.} Five element Array, containing the prefix of + * longtext, the suffix of longtext, the prefix of shorttext, the suffix + * of shorttext and the common middle. Or null if there was no match. + * @private + */ + function diff_halfMatchI_(longtext, shorttext, i) { + // Start with a 1/4 length substring at position i as a seed. + let seed = longtext.substring(i, i + Math.floor(longtext.length / 4)); + let j = -1; + let best_common = ''; + let best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b; + while ((j = shorttext.indexOf(seed, j + 1)) !== -1) { + let prefixLength = dmp.diff_commonPrefix(longtext.substring(i), + shorttext.substring(j)); + let suffixLength = dmp.diff_commonSuffix(longtext.substring(0, i), + shorttext.substring(0, j)); + if (best_common.length < suffixLength + prefixLength) { + best_common = shorttext.substring(j - suffixLength, j) + + shorttext.substring(j, j + prefixLength); + best_longtext_a = longtext.substring(0, i - suffixLength); + best_longtext_b = longtext.substring(i + prefixLength); + best_shorttext_a = shorttext.substring(0, j - suffixLength); + best_shorttext_b = shorttext.substring(j + prefixLength); + } + } + if (best_common.length * 2 >= longtext.length) { + return [best_longtext_a, best_longtext_b, + best_shorttext_a, best_shorttext_b, best_common]; + } else { + return null; + } + } + + // First check if the second quarter is the seed for a half-match. + let hm1 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 4)); + // Check again based on the third quarter. + let hm2 = diff_halfMatchI_(longtext, shorttext, + Math.ceil(longtext.length / 2)); + let hm; + if (!hm1 && !hm2) { + return null; + } else if (!hm2) { + hm = hm1; + } else if (!hm1) { + hm = hm2; + } else { + // Both matched. Select the longest. + hm = hm1[4].length > hm2[4].length ? hm1 : hm2; + } + + // A half-match was found, sort out the return data. + let text1_a, text1_b, text2_a, text2_b; + if (text1.length > text2.length) { + text1_a = hm[0]; + text1_b = hm[1]; + text2_a = hm[2]; + text2_b = hm[3]; + } else { + text2_a = hm[0]; + text2_b = hm[1]; + text1_a = hm[2]; + text1_b = hm[3]; + } + let mid_common = hm[4]; + return [text1_a, text1_b, text2_a, text2_b, mid_common]; +}; + + +/** + * Reduce the number of edits by eliminating semantically trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemantic = function (diffs) { + let changes = false; + let equalities = []; // Stack of indices where equalities are found. + let equalitiesLength = 0; // Keeping our own length let is faster in JS. + /** @type {?string} */ + let lastEquality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + let pointer = 0; // Index of current position. + // Number of characters that changed prior to the equality. + let length_insertions1 = 0; + let length_deletions1 = 0; + // Number of characters that changed after the equality. + let length_insertions2 = 0; + let length_deletions2 = 0; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. + equalities[equalitiesLength++] = pointer; + length_insertions1 = length_insertions2; + length_deletions1 = length_deletions2; + length_insertions2 = 0; + length_deletions2 = 0; + lastEquality = diffs[pointer][1]; + } else { // An insertion or deletion. + if (diffs[pointer][0] === DIFF_INSERT) { + length_insertions2 += diffs[pointer][1].length; + } else { + length_deletions2 += diffs[pointer][1].length; + } + // Eliminate an equality that is smaller or equal to the edits on both + // sides of it. + if (lastEquality && (lastEquality.length <= + Math.max(length_insertions1, length_deletions1)) && + (lastEquality.length <= Math.max(length_insertions2, + length_deletions2))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + new diff_match_patch.Diff(DIFF_DELETE, lastEquality)); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + // Throw away the equality we just deleted. + equalitiesLength--; + // Throw away the previous equality (it needs to be reevaluated). + equalitiesLength--; + pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1; + length_insertions1 = 0; // Reset the counters. + length_deletions1 = 0; + length_insertions2 = 0; + length_deletions2 = 0; + lastEquality = null; + changes = true; + } + } + pointer++; + } + + // Normalize the diff. + if (changes) { + this.diff_cleanupMerge(diffs); + } + this.diff_cleanupSemanticLossless(diffs); + + // Find any overlaps between deletions and insertions. + // e.g: abcxxxxxxdef + // -> abcxxxdef + // e.g: xxxabcdefxxx + // -> defxxxabc + // Only extract an overlap if it is as big as the edit ahead or behind it. + pointer = 1; + while (pointer < diffs.length) { + if (diffs[pointer - 1][0] === DIFF_DELETE && + diffs[pointer][0] === DIFF_INSERT) { + let deletion = diffs[pointer - 1][1]; + let insertion = diffs[pointer][1]; + let overlap_length1 = this.diff_commonOverlap_(deletion, insertion); + let overlap_length2 = this.diff_commonOverlap_(insertion, deletion); + if (overlap_length1 >= overlap_length2) { + if (overlap_length1 >= deletion.length / 2 || + overlap_length1 >= insertion.length / 2) { + // Overlap found. Insert an equality and trim the surrounding edits. + diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL, + insertion.substring(0, overlap_length1))); + diffs[pointer - 1][1] = + deletion.substring(0, deletion.length - overlap_length1); + diffs[pointer + 1][1] = insertion.substring(overlap_length1); + pointer++; + } + } else { + if (overlap_length2 >= deletion.length / 2 || + overlap_length2 >= insertion.length / 2) { + // Reverse overlap found. + // Insert an equality and swap and trim the surrounding edits. + diffs.splice(pointer, 0, new diff_match_patch.Diff(DIFF_EQUAL, + deletion.substring(0, overlap_length2))); + diffs[pointer - 1][0] = DIFF_INSERT; + diffs[pointer - 1][1] = + insertion.substring(0, insertion.length - overlap_length2); + diffs[pointer + 1][0] = DIFF_DELETE; + diffs[pointer + 1][1] = + deletion.substring(overlap_length2); + pointer++; + } + } + pointer++; + } + pointer++; + } +}; + + +/** + * Look for single edits surrounded on both sides by equalities + * which can be shifted sideways to align the edit to a word boundary. + * e.g: The cat came. -> The cat came. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupSemanticLossless = function (diffs) { + /** + * Given two strings, compute a score representing whether the internal + * boundary falls on logical boundaries. + * Scores range from 6 (best) to 0 (worst). + * Closure, but does not reference any external variables. + * @param {string} one First string. + * @param {string} two Second string. + * @return {number} The score. + * @private + */ + function diff_cleanupSemanticScore_(one, two) { + if (!one || !two) { + // Edges are the best. + return 6; + } + + // Each port of this function behaves slightly differently due to + // subtle differences in each language's definition of things like + // 'whitespace'. Since this function's purpose is largely cosmetic, + // the choice has been made to use each language's native features + // rather than force total conformity. + let char1 = one.charAt(one.length - 1); + let char2 = two.charAt(0); + let nonAlphaNumeric1 = char1.match(diff_match_patch.nonAlphaNumericRegex_); + let nonAlphaNumeric2 = char2.match(diff_match_patch.nonAlphaNumericRegex_); + let whitespace1 = nonAlphaNumeric1 && + char1.match(diff_match_patch.whitespaceRegex_); + let whitespace2 = nonAlphaNumeric2 && + char2.match(diff_match_patch.whitespaceRegex_); + let lineBreak1 = whitespace1 && + char1.match(diff_match_patch.linebreakRegex_); + let lineBreak2 = whitespace2 && + char2.match(diff_match_patch.linebreakRegex_); + let blankLine1 = lineBreak1 && + one.match(diff_match_patch.blanklineEndRegex_); + let blankLine2 = lineBreak2 && + two.match(diff_match_patch.blanklineStartRegex_); + + if (blankLine1 || blankLine2) { + // Five points for blank lines. + return 5; + } else if (lineBreak1 || lineBreak2) { + // Four points for line breaks. + return 4; + } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { + // Three points for end of sentences. + return 3; + } else if (whitespace1 || whitespace2) { + // Two points for whitespace. + return 2; + } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { + // One point for non-alphanumeric. + return 1; + } + return 0; + } + + let pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + let equality1 = diffs[pointer - 1][1]; + let edit = diffs[pointer][1]; + let equality2 = diffs[pointer + 1][1]; + + // First, shift the edit as far left as possible. + let commonOffset = this.diff_commonSuffix(equality1, edit); + if (commonOffset) { + let commonString = edit.substring(edit.length - commonOffset); + equality1 = equality1.substring(0, equality1.length - commonOffset); + edit = commonString + edit.substring(0, edit.length - commonOffset); + equality2 = commonString + equality2; + } + + // Second, step character by character right, looking for the best fit. + let bestEquality1 = equality1; + let bestEdit = edit; + let bestEquality2 = equality2; + let bestScore = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + while (edit.charAt(0) === equality2.charAt(0)) { + equality1 += edit.charAt(0); + edit = edit.substring(1) + equality2.charAt(0); + equality2 = equality2.substring(1); + let score = diff_cleanupSemanticScore_(equality1, edit) + + diff_cleanupSemanticScore_(edit, equality2); + // The >= encourages trailing rather than leading whitespace on edits. + if (score >= bestScore) { + bestScore = score; + bestEquality1 = equality1; + bestEdit = edit; + bestEquality2 = equality2; + } + } + + if (diffs[pointer - 1][1] !== bestEquality1) { + // We have an improvement, save it back to the diff. + if (bestEquality1) { + diffs[pointer - 1][1] = bestEquality1; + } else { + diffs.splice(pointer - 1, 1); + pointer--; + } + diffs[pointer][1] = bestEdit; + if (bestEquality2) { + diffs[pointer + 1][1] = bestEquality2; + } else { + diffs.splice(pointer + 1, 1); + pointer--; + } + } + } + pointer++; + } +}; + +// Define some regex patterns for matching boundaries. +diff_match_patch.nonAlphaNumericRegex_ = /[^a-zA-Z0-9]/; +diff_match_patch.whitespaceRegex_ = /\s/; +diff_match_patch.linebreakRegex_ = /[\r\n]/; +diff_match_patch.blanklineEndRegex_ = /\n\r?\n$/; +diff_match_patch.blanklineStartRegex_ = /^\r?\n\r?\n/; + +/** + * Reduce the number of edits by eliminating operationally trivial equalities. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupEfficiency = function (diffs) { + let changes = false; + let equalities = []; // Stack of indices where equalities are found. + let equalitiesLength = 0; // Keeping our own length let is faster in JS. + /** @type {?string} */ + let lastEquality = null; + // Always equal to diffs[equalities[equalitiesLength - 1]][1] + let pointer = 0; // Index of current position. + // Is there an insertion operation before the last equality. + let pre_ins = false; + // Is there a deletion operation before the last equality. + let pre_del = false; + // Is there an insertion operation after the last equality. + let post_ins = false; + // Is there a deletion operation after the last equality. + let post_del = false; + while (pointer < diffs.length) { + if (diffs[pointer][0] === DIFF_EQUAL) { // Equality found. + if (diffs[pointer][1].length < this.Diff_EditCost && + (post_ins || post_del)) { + // Candidate found. + equalities[equalitiesLength++] = pointer; + pre_ins = post_ins; + pre_del = post_del; + lastEquality = diffs[pointer][1]; + } else { + // Not a candidate, and can never become one. + equalitiesLength = 0; + lastEquality = null; + } + post_ins = post_del = false; + } else { // An insertion or deletion. + if (diffs[pointer][0] === DIFF_DELETE) { + post_del = true; + } else { + post_ins = true; + } + /* + * Five types to be split: + * ABXYCD + * AXCD + * ABXC + * AXCD + * ABXC + */ + if (lastEquality && ((pre_ins && pre_del && post_ins && post_del) || + ((lastEquality.length < this.Diff_EditCost / 2) && + (pre_ins + pre_del + post_ins + post_del) === 3))) { + // Duplicate record. + diffs.splice(equalities[equalitiesLength - 1], 0, + new diff_match_patch.Diff(DIFF_DELETE, lastEquality)); + // Change second copy to insert. + diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT; + equalitiesLength--; // Throw away the equality we just deleted; + lastEquality = null; + if (pre_ins && pre_del) { + // No changes made which could affect previous entry, keep going. + post_ins = post_del = true; + equalitiesLength = 0; + } else { + equalitiesLength--; // Throw away the previous equality. + pointer = equalitiesLength > 0 ? + equalities[equalitiesLength - 1] : -1; + post_ins = post_del = false; + } + changes = true; + } + } + pointer++; + } + + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * Reorder and merge like edit sections. Merge equalities. + * Any edit section can move as long as it doesn't cross an equality. + * @param {!Array.} diffs Array of diff tuples. + */ +diff_match_patch.prototype.diff_cleanupMerge = function (diffs) { + // Add a dummy entry at the end. + diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, '')); + let pointer = 0; + let count_delete = 0; + let count_insert = 0; + let text_delete = ''; + let text_insert = ''; + let commonlength; + while (pointer < diffs.length) { + switch (diffs[pointer][0]) { + case DIFF_INSERT: + count_insert++; + text_insert += diffs[pointer][1]; + pointer++; + break; + case DIFF_DELETE: + count_delete++; + text_delete += diffs[pointer][1]; + pointer++; + break; + case DIFF_EQUAL: + // Upon reaching an equality, check for prior redundancies. + if (count_delete + count_insert > 1) { + if (count_delete !== 0 && count_insert !== 0) { + // Factor out any common prefixies. + commonlength = this.diff_commonPrefix(text_insert, text_delete); + if (commonlength !== 0) { + if ((pointer - count_delete - count_insert) > 0 && + diffs[pointer - count_delete - count_insert - 1][0] === + DIFF_EQUAL) { + diffs[pointer - count_delete - count_insert - 1][1] += + text_insert.substring(0, commonlength); + } else { + diffs.splice(0, 0, new diff_match_patch.Diff(DIFF_EQUAL, + text_insert.substring(0, commonlength))); + pointer++; + } + text_insert = text_insert.substring(commonlength); + text_delete = text_delete.substring(commonlength); + } + // Factor out any common suffixies. + commonlength = this.diff_commonSuffix(text_insert, text_delete); + if (commonlength !== 0) { + diffs[pointer][1] = text_insert.substring(text_insert.length - + commonlength) + diffs[pointer][1]; + text_insert = text_insert.substring(0, text_insert.length - + commonlength); + text_delete = text_delete.substring(0, text_delete.length - + commonlength); + } + } + // Delete the offending records and add the merged ones. + pointer -= count_delete + count_insert; + diffs.splice(pointer, count_delete + count_insert); + if (text_delete.length) { + diffs.splice(pointer, 0, + new diff_match_patch.Diff(DIFF_DELETE, text_delete)); + pointer++; + } + if (text_insert.length) { + diffs.splice(pointer, 0, + new diff_match_patch.Diff(DIFF_INSERT, text_insert)); + pointer++; + } + pointer++; + } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) { + // Merge this equality with the previous one. + diffs[pointer - 1][1] += diffs[pointer][1]; + diffs.splice(pointer, 1); + } else { + pointer++; + } + count_insert = 0; + count_delete = 0; + text_delete = ''; + text_insert = ''; + break; + } + } + if (diffs[diffs.length - 1][1] === '') { + diffs.pop(); // Remove the dummy entry at the end. + } + + // Second pass: look for single edits surrounded on both sides by equalities + // which can be shifted sideways to eliminate an equality. + // e.g: ABAC -> ABAC + let changes = false; + pointer = 1; + // Intentionally ignore the first and last element (don't need checking). + while (pointer < diffs.length - 1) { + if (diffs[pointer - 1][0] === DIFF_EQUAL && + diffs[pointer + 1][0] === DIFF_EQUAL) { + // This is a single edit surrounded by equalities. + if (diffs[pointer][1].substring(diffs[pointer][1].length - + diffs[pointer - 1][1].length) === diffs[pointer - 1][1]) { + // Shift the edit over the previous equality. + diffs[pointer][1] = diffs[pointer - 1][1] + + diffs[pointer][1].substring(0, diffs[pointer][1].length - + diffs[pointer - 1][1].length); + diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1]; + diffs.splice(pointer - 1, 1); + changes = true; + } else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) === + diffs[pointer + 1][1]) { + // Shift the edit over the next equality. + diffs[pointer - 1][1] += diffs[pointer + 1][1]; + diffs[pointer][1] = + diffs[pointer][1].substring(diffs[pointer + 1][1].length) + + diffs[pointer + 1][1]; + diffs.splice(pointer + 1, 1); + changes = true; + } + } + pointer++; + } + // If shifts were made, the diff needs reordering and another shift sweep. + if (changes) { + this.diff_cleanupMerge(diffs); + } +}; + + +/** + * loc is a location in text1, compute and return the equivalent location in + * text2. + * e.g. 'The cat' vs 'The big cat', 1->1, 5->8 + * @param {!Array.} diffs Array of diff tuples. + * @param {number} loc Location within text1. + * @return {number} Location within text2. + */ +diff_match_patch.prototype.diff_xIndex = function (diffs, loc) { + let chars1 = 0; + let chars2 = 0; + let last_chars1 = 0; + let last_chars2 = 0; + let x; + for (x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { // Equality or deletion. + chars1 += diffs[x][1].length; + } + if (diffs[x][0] !== DIFF_DELETE) { // Equality or insertion. + chars2 += diffs[x][1].length; + } + if (chars1 > loc) { // Overshot the location. + break; + } + last_chars1 = chars1; + last_chars2 = chars2; + } + // Was the location was deleted? + if (diffs.length !== x && diffs[x][0] === DIFF_DELETE) { + return last_chars2; + } + // Add the remaining character length. + return last_chars2 + (loc - last_chars1); +}; + + +/** + * Convert a diff array into a pretty HTML report. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} HTML representation. + */ +diff_match_patch.prototype.diff_prettyHtml = function (diffs) { + let html = []; + let pattern_amp = /&/g; + let pattern_lt = //g; + let pattern_para = /\n/g; + for (let x = 0; x < diffs.length; x++) { + let op = diffs[x][0]; // Operation (insert, delete, equal) + let data = diffs[x][1]; // Text of change. + let text = data.replace(pattern_amp, '&').replace(pattern_lt, '<') + .replace(pattern_gt, '>').replace(pattern_para, '¶
'); + switch (op) { + case DIFF_INSERT: + html[x] = '' + text + ''; + break; + case DIFF_DELETE: + html[x] = '' + text + ''; + break; + case DIFF_EQUAL: + html[x] = '' + text + ''; + break; + } + } + return html.join(''); +}; + + +/** + * Compute and return the source text (all equalities and deletions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Source text. + */ +diff_match_patch.prototype.diff_text1 = function (diffs) { + let text = []; + for (let x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_INSERT) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute and return the destination text (all equalities and insertions). + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Destination text. + */ +diff_match_patch.prototype.diff_text2 = function (diffs) { + let text = []; + for (let x = 0; x < diffs.length; x++) { + if (diffs[x][0] !== DIFF_DELETE) { + text[x] = diffs[x][1]; + } + } + return text.join(''); +}; + + +/** + * Compute the Levenshtein distance; the number of inserted, deleted or + * substituted characters. + * @param {!Array.} diffs Array of diff tuples. + * @return {number} Number of changes. + */ +diff_match_patch.prototype.diff_levenshtein = function (diffs) { + let levenshtein = 0; + let insertions = 0; + let deletions = 0; + for (let x = 0; x < diffs.length; x++) { + let op = diffs[x][0]; + let data = diffs[x][1]; + switch (op) { + case DIFF_INSERT: + insertions += data.length; + break; + case DIFF_DELETE: + deletions += data.length; + break; + case DIFF_EQUAL: + // A deletion and an insertion is one substitution. + levenshtein += Math.max(insertions, deletions); + insertions = 0; + deletions = 0; + break; + } + } + levenshtein += Math.max(insertions, deletions); + return levenshtein; +}; + + +/** + * Crush the diff into an encoded string which describes the operations + * required to transform text1 into text2. + * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. + * Operations are tab-separated. Inserted text is escaped using %xx notation. + * @param {!Array.} diffs Array of diff tuples. + * @return {string} Delta text. + */ +diff_match_patch.prototype.diff_toDelta = function (diffs) { + let text = []; + for (let x = 0; x < diffs.length; x++) { + switch (diffs[x][0]) { + case DIFF_INSERT: + text[x] = '+' + encodeURI(diffs[x][1]); + break; + case DIFF_DELETE: + text[x] = '-' + diffs[x][1].length; + break; + case DIFF_EQUAL: + text[x] = '=' + diffs[x][1].length; + break; + } + } + return text.join('\t').replace(/%20/g, ' '); +}; + + +/** + * Given the original text1, and an encoded string which describes the + * operations required to transform text1 into text2, compute the full diff. + * @param {string} text1 Source string for the diff. + * @param {string} delta Delta text. + * @return {!Array.} Array of diff tuples. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.diff_fromDelta = function (text1, delta) { + let diffs = []; + let diffsLength = 0; // Keeping our own length let is faster in JS. + let pointer = 0; // Cursor in text1 + let tokens = delta.split(/\t/g); + for (let x = 0; x < tokens.length; x++) { + // Each token begins with a one character parameter which specifies the + // operation of this token (delete, insert, equality). + let param = tokens[x].substring(1); + switch (tokens[x].charAt(0)) { + case '+': + try { + diffs[diffsLength++] = + new diff_match_patch.Diff(DIFF_INSERT, decodeURI(param)); + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in diff_fromDelta: ' + param); + } + break; + case '-': + // Fall through. + case '=': { + let n = parseInt(param, 10); + if (isNaN(n) || n < 0) { + throw new Error('Invalid number in diff_fromDelta: ' + param); + } + let text = text1.substring(pointer, pointer += n); + if (tokens[x].charAt(0) === '=') { + diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_EQUAL, text); + } else { + diffs[diffsLength++] = new diff_match_patch.Diff(DIFF_DELETE, text); + } + break; + } + + } + } + if (pointer !== text1.length) { + throw new Error('Delta length (' + pointer + + ') does not equal source text length (' + text1.length + ').'); + } + return diffs; +}; + + +// MATCH FUNCTIONS + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc'. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + */ +diff_match_patch.prototype.match_main = function (text, pattern, loc) { + // Check for null inputs. + if (text === null || pattern === null || loc === null) { + throw new Error('Null input. (match_main)'); + } + + loc = Math.max(0, Math.min(loc, text.length)); + if (text === pattern) { + // Shortcut (potentially not guaranteed by the algorithm) + return 0; + } else if (!text.length) { + // Nothing to match. + return -1; + } else if (text.substring(loc, loc + pattern.length) === pattern) { + // Perfect match at the perfect spot! (Includes case of null pattern) + return loc; + } else { + // Do a fuzzy compare. + return this.match_bitap_(text, pattern, loc); + } +}; + + +/** + * Locate the best instance of 'pattern' in 'text' near 'loc' using the + * Bitap algorithm. + * @param {string} text The text to search. + * @param {string} pattern The pattern to search for. + * @param {number} loc The location to search around. + * @return {number} Best match index or -1. + * @private + */ +diff_match_patch.prototype.match_bitap_ = function (text, pattern, loc) { + if (pattern.length > this.Match_MaxBits) { + throw new Error('Pattern too long for this browser.'); + } + + // Initialise the alphabet. + let s = this.match_alphabet_(pattern); + + let dmp = this; // 'this' becomes 'window' in a closure. + + /** + * Compute and return the score for a match with e errors and x location. + * Accesses loc and pattern through being a closure. + * @param {number} e Number of errors in match. + * @param {number} x Location of match. + * @return {number} Overall score for match (0.0 = good, 1.0 = bad). + * @private + */ + function match_bitapScore_(e, x) { + let accuracy = e / pattern.length; + let proximity = Math.abs(loc - x); + if (!dmp.Match_Distance) { + // Dodge divide by zero error. + return proximity ? 1.0 : accuracy; + } + return accuracy + (proximity / dmp.Match_Distance); + } + + // Highest score beyond which we give up. + let score_threshold = this.Match_Threshold; + // Is there a nearby exact match? (speedup) + let best_loc = text.indexOf(pattern, loc); + if (best_loc !== -1) { + score_threshold = Math.min(match_bitapScore_(0, best_loc), score_threshold); + // What about in the other direction? (speedup) + best_loc = text.lastIndexOf(pattern, loc + pattern.length); + if (best_loc !== -1) { + score_threshold = + Math.min(match_bitapScore_(0, best_loc), score_threshold); + } + } + + // Initialise the bit arrays. + let matchmask = 1 << (pattern.length - 1); + best_loc = -1; + + let bin_min, bin_mid; + let bin_max = pattern.length + text.length; + let last_rd; + for (let d = 0; d < pattern.length; d++) { + // Scan for the best match; each iteration allows for one more error. + // Run a binary search to determine how far from 'loc' we can stray at this + // error level. + bin_min = 0; + bin_mid = bin_max; + while (bin_min < bin_mid) { + if (match_bitapScore_(d, loc + bin_mid) <= score_threshold) { + bin_min = bin_mid; + } else { + bin_max = bin_mid; + } + bin_mid = Math.floor((bin_max - bin_min) / 2 + bin_min); + } + // Use the result from this iteration as the maximum for the next. + bin_max = bin_mid; + let start = Math.max(1, loc - bin_mid + 1); + let finish = Math.min(loc + bin_mid, text.length) + pattern.length; + + let rd = Array(finish + 2); + rd[finish + 1] = (1 << d) - 1; + for (let j = finish; j >= start; j--) { + // The alphabet (s) is a sparse hash, so the following line generates + // warnings. + let charMatch = s[text.charAt(j - 1)]; + if (d === 0) { // First pass: exact match. + rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; + } else { // Subsequent passes: fuzzy match. + rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) | + (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | + last_rd[j + 1]; + } + if (rd[j] & matchmask) { + let score = match_bitapScore_(d, j - 1); + // This match will almost certainly be better than any existing match. + // But check anyway. + if (score <= score_threshold) { + // Told you so. + score_threshold = score; + best_loc = j - 1; + if (best_loc > loc) { + // When passing loc, don't exceed our current distance from loc. + start = Math.max(1, 2 * loc - best_loc); + } else { + // Already passed loc, downhill from here on in. + break; + } + } + } + } + // No hope for a (better) match at greater error levels. + if (match_bitapScore_(d + 1, loc) > score_threshold) { + break; + } + last_rd = rd; + } + return best_loc; +}; + + +/** + * Initialise the alphabet for the Bitap algorithm. + * @param {string} pattern The text to encode. + * @return {!Object} Hash of character locations. + * @private + */ +diff_match_patch.prototype.match_alphabet_ = function (pattern) { + let s = {}; + for (let i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] = 0; + } + for (let i = 0; i < pattern.length; i++) { + s[pattern.charAt(i)] |= 1 << (pattern.length - i - 1); + } + return s; +}; + + +// PATCH FUNCTIONS + + +/** + * Increase the context until it is unique, + * but don't let the pattern expand beyond Match_MaxBits. + * @param {!diff_match_patch.patch_obj} patch The patch to grow. + * @param {string} text Source text. + * @private + */ +diff_match_patch.prototype.patch_addContext_ = function (patch, text) { + if (text.length === 0) { + return; + } + if (patch.start2 === null) { + throw Error('patch not initialized'); + } + let pattern = text.substring(patch.start2, patch.start2 + patch.length1); + let padding = 0; + + // Look for the first and last matches of pattern in text. If two different + // matches are found, increase the pattern length. + while (text.indexOf(pattern) !== text.lastIndexOf(pattern) && + pattern.length < this.Match_MaxBits - this.Patch_Margin - + this.Patch_Margin) { + padding += this.Patch_Margin; + pattern = text.substring(patch.start2 - padding, + patch.start2 + patch.length1 + padding); + } + // Add one chunk for good luck. + padding += this.Patch_Margin; + + // Add the prefix. + let prefix = text.substring(patch.start2 - padding, patch.start2); + if (prefix) { + patch.diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, prefix)); + } + // Add the suffix. + let suffix = text.substring(patch.start2 + patch.length1, + patch.start2 + patch.length1 + padding); + if (suffix) { + patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, suffix)); + } + + // Roll back the start points. + patch.start1 -= prefix.length; + patch.start2 -= prefix.length; + // Extend the lengths. + patch.length1 += prefix.length + suffix.length; + patch.length2 += prefix.length + suffix.length; +}; + + +/** + * Compute a list of patches to turn text1 into text2. + * Use diffs if provided, otherwise compute it ourselves. + * There are four ways to call this function, depending on what data is + * available to the caller: + * Method 1: + * a = text1, b = text2 + * Method 2: + * a = diffs + * Method 3 (optimal): + * a = text1, b = diffs + * Method 4 (deprecated, use method 3): + * a = text1, b = text2, c = diffs + * + * @param {string|!Array.} a text1 (methods 1,3,4) or + * Array of diff tuples for text1 to text2 (method 2). + * @param {string|!Array.=} opt_b text2 (methods 1,4) or + * Array of diff tuples for text1 to text2 (method 3) or undefined (method 2). + * @param {string|!Array.=} opt_c Array of diff tuples + * for text1 to text2 (method 4) or undefined (methods 1,2,3). + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_make = function (a, opt_b, opt_c) { + let text1, diffs; + if (typeof a === 'string' && typeof opt_b === 'string' && + typeof opt_c === 'undefined') { + // Method 1: text1, text2 + // Compute diffs from text1 and text2. + text1 = /** @type {string} */(a); + diffs = this.diff_main(text1, /** @type {string} */(opt_b), true); + if (diffs.length > 2) { + this.diff_cleanupSemantic(diffs); + this.diff_cleanupEfficiency(diffs); + } + } else if (a && typeof a === 'object' && typeof opt_b === 'undefined' && + typeof opt_c === 'undefined') { + // Method 2: diffs + // Compute text1 from diffs. + diffs = /** @type {!Array.} */(a); + text1 = this.diff_text1(diffs); + } else if (typeof a === 'string' && opt_b && typeof opt_b === 'object' && + typeof opt_c === 'undefined') { + // Method 3: text1, diffs + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_b); + } else if (typeof a === 'string' && typeof opt_b === 'string' && + opt_c && typeof opt_c === 'object') { + // Method 4: text1, text2, diffs + // text2 is not used. + text1 = /** @type {string} */(a); + diffs = /** @type {!Array.} */(opt_c); + } else { + throw new Error('Unknown call format to patch_make.'); + } + + if (diffs.length === 0) { + return []; // Get rid of the null case. + } + let patches = []; + let patch = new diff_match_patch.patch_obj(); + let patchDiffLength = 0; // Keeping our own length let is faster in JS. + let char_count1 = 0; // Number of characters into the text1 string. + let char_count2 = 0; // Number of characters into the text2 string. + // Start with text1 (prepatch_text) and apply the diffs until we arrive at + // text2 (postpatch_text). We recreate the patches one by one to determine + // context info. + let prepatch_text = text1; + let postpatch_text = text1; + for (let x = 0; x < diffs.length; x++) { + let diff_type = diffs[x][0]; + let diff_text = diffs[x][1]; + + if (!patchDiffLength && diff_type !== DIFF_EQUAL) { + // A new patch starts here. + patch.start1 = char_count1; + patch.start2 = char_count2; + } + + switch (diff_type) { + case DIFF_INSERT: + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length2 += diff_text.length; + postpatch_text = postpatch_text.substring(0, char_count2) + diff_text + + postpatch_text.substring(char_count2); + break; + case DIFF_DELETE: + patch.length1 += diff_text.length; + patch.diffs[patchDiffLength++] = diffs[x]; + postpatch_text = postpatch_text.substring(0, char_count2) + + postpatch_text.substring(char_count2 + + diff_text.length); + break; + case DIFF_EQUAL: + if (diff_text.length <= 2 * this.Patch_Margin && + patchDiffLength && diffs.length !== x + 1) { + // Small equality inside a patch. + patch.diffs[patchDiffLength++] = diffs[x]; + patch.length1 += diff_text.length; + patch.length2 += diff_text.length; + } else if (diff_text.length >= 2 * this.Patch_Margin) { + // Time for a new patch. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + patch = new diff_match_patch.patch_obj(); + patchDiffLength = 0; + // Unlike Unidiff, our patch lists have a rolling context. + // https://github.com/google/diff-match-patch/wiki/Unidiff + // Update prepatch text & pos to reflect the application of the + // just completed patch. + prepatch_text = postpatch_text; + char_count1 = char_count2; + } + } + break; + } + + // Update the current character count. + if (diff_type !== DIFF_INSERT) { + char_count1 += diff_text.length; + } + if (diff_type !== DIFF_DELETE) { + char_count2 += diff_text.length; + } + } + // Pick up the leftover patch if not empty. + if (patchDiffLength) { + this.patch_addContext_(patch, prepatch_text); + patches.push(patch); + } + + return patches; +}; + + +/** + * Given an array of patches, return another array that is identical. + * @param {!Array.} patches Array of Patch objects. + * @return {!Array.} Array of Patch objects. + */ +diff_match_patch.prototype.patch_deepCopy = function (patches) { + // Making deep copies is hard in JavaScript. + let patchesCopy = []; + for (let x = 0; x < patches.length; x++) { + let patch = patches[x]; + let patchCopy = new diff_match_patch.patch_obj(); + patchCopy.diffs = []; + for (let y = 0; y < patch.diffs.length; y++) { + patchCopy.diffs[y] = + new diff_match_patch.Diff(patch.diffs[y][0], patch.diffs[y][1]); + } + patchCopy.start1 = patch.start1; + patchCopy.start2 = patch.start2; + patchCopy.length1 = patch.length1; + patchCopy.length2 = patch.length2; + patchesCopy[x] = patchCopy; + } + return patchesCopy; +}; + + +/** + * Merge a set of patches onto the text. Return a patched text, as well + * as a list of true/false values indicating which patches were applied. + * @param {!Array.} patches Array of Patch objects. + * @param {string} text Old text. + * @return {!Array.>} Two element Array, containing the + * new text and an array of boolean values. + */ +diff_match_patch.prototype.patch_apply = function (patches, text) { + if (patches.length === 0) { + return [text, []]; + } + + // Deep copy the patches so that no changes are made to originals. + patches = this.patch_deepCopy(patches); + + let nullPadding = this.patch_addPadding(patches); + text = nullPadding + text + nullPadding; + + this.patch_splitMax(patches); + // delta keeps track of the offset between the expected and actual location + // of the previous patch. If there are patches expected at positions 10 and + // 20, but the first patch was found at 12, delta is 2 and the second patch + // has an effective expected position of 22. + let delta = 0; + let results = []; + for (let x = 0; x < patches.length; x++) { + let expected_loc = patches[x].start2 + delta; + let text1 = this.diff_text1(patches[x].diffs); + let start_loc; + let end_loc = -1; + if (text1.length > this.Match_MaxBits) { + // patch_splitMax will only provide an oversized pattern in the case of + // a monster delete. + start_loc = this.match_main(text, text1.substring(0, this.Match_MaxBits), + expected_loc); + if (start_loc !== -1) { + end_loc = this.match_main(text, + text1.substring(text1.length - this.Match_MaxBits), + expected_loc + text1.length - this.Match_MaxBits); + if (end_loc === -1 || start_loc >= end_loc) { + // Can't find valid trailing context. Drop this patch. + start_loc = -1; + } + } + } else { + start_loc = this.match_main(text, text1, expected_loc); + } + if (start_loc === -1) { + // No match found. :( + results[x] = false; + // Subtract the delta for this failed patch from subsequent patches. + delta -= patches[x].length2 - patches[x].length1; + } else { + // Found a match. :) + results[x] = true; + delta = start_loc - expected_loc; + let text2; + if (end_loc === -1) { + text2 = text.substring(start_loc, start_loc + text1.length); + } else { + text2 = text.substring(start_loc, end_loc + this.Match_MaxBits); + } + if (text1 === text2) { + // Perfect match, just shove the replacement text in. + text = text.substring(0, start_loc) + + this.diff_text2(patches[x].diffs) + + text.substring(start_loc + text1.length); + } else { + // Imperfect match. Run a diff to get a framework of equivalent + // indices. + let diffs = this.diff_main(text1, text2, false); + if (text1.length > this.Match_MaxBits && + this.diff_levenshtein(diffs) / text1.length > + this.Patch_DeleteThreshold) { + // The end points match, but the content is unacceptably bad. + results[x] = false; + } else { + this.diff_cleanupSemanticLossless(diffs); + let index1 = 0; + let index2; + for (let y = 0; y < patches[x].diffs.length; y++) { + let mod = patches[x].diffs[y]; + if (mod[0] !== DIFF_EQUAL) { + index2 = this.diff_xIndex(diffs, index1); + } + if (mod[0] === DIFF_INSERT) { // Insertion + text = text.substring(0, start_loc + index2) + mod[1] + + text.substring(start_loc + index2); + } else if (mod[0] === DIFF_DELETE) { // Deletion + text = text.substring(0, start_loc + index2) + + text.substring(start_loc + this.diff_xIndex(diffs, + index1 + mod[1].length)); + } + if (mod[0] !== DIFF_DELETE) { + index1 += mod[1].length; + } + } + } + } + } + } + // Strip the padding off. + text = text.substring(nullPadding.length, text.length - nullPadding.length); + return [text, results]; +}; + + +/** + * Add some padding on text start and end so that edges can match something. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + * @return {string} The padding string added to each side. + */ +diff_match_patch.prototype.patch_addPadding = function (patches) { + let paddingLength = this.Patch_Margin; + let nullPadding = ''; + for (let x = 1; x <= paddingLength; x++) { + nullPadding += String.fromCharCode(x); + } + + // Bump all the patches forward. + for (let x = 0; x < patches.length; x++) { + patches[x].start1 += paddingLength; + patches[x].start2 += paddingLength; + } + + // Add some padding on start of first diff. + let patch = patches[0]; + let diffs = patch.diffs; + if (diffs.length === 0 || diffs[0][0] !== DIFF_EQUAL) { + // Add nullPadding equality. + diffs.unshift(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding)); + patch.start1 -= paddingLength; // Should be 0. + patch.start2 -= paddingLength; // Should be 0. + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[0][1].length) { + // Grow first equality. + let extraLength = paddingLength - diffs[0][1].length; + diffs[0][1] = nullPadding.substring(diffs[0][1].length) + diffs[0][1]; + patch.start1 -= extraLength; + patch.start2 -= extraLength; + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + // Add some padding on end of last diff. + patch = patches[patches.length - 1]; + diffs = patch.diffs; + if (diffs.length === 0 || diffs[diffs.length - 1][0] !== DIFF_EQUAL) { + // Add nullPadding equality. + diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, nullPadding)); + patch.length1 += paddingLength; + patch.length2 += paddingLength; + } else if (paddingLength > diffs[diffs.length - 1][1].length) { + // Grow last equality. + let extraLength = paddingLength - diffs[diffs.length - 1][1].length; + diffs[diffs.length - 1][1] += nullPadding.substring(0, extraLength); + patch.length1 += extraLength; + patch.length2 += extraLength; + } + + return nullPadding; +}; + + +/** + * Look through the patches and break up any which are longer than the maximum + * limit of the match algorithm. + * Intended to be called only from within patch_apply. + * @param {!Array.} patches Array of Patch objects. + */ +diff_match_patch.prototype.patch_splitMax = function (patches) { + let patch_size = this.Match_MaxBits; + for (let x = 0; x < patches.length; x++) { + if (patches[x].length1 <= patch_size) { + continue; + } + let bigpatch = patches[x]; + // Remove the big old patch. + patches.splice(x--, 1); + let start1 = bigpatch.start1; + let start2 = bigpatch.start2; + let precontext = ''; + while (bigpatch.diffs.length !== 0) { + // Create one of several smaller patches. + let patch = new diff_match_patch.patch_obj(); + let empty = true; + patch.start1 = start1 - precontext.length; + patch.start2 = start2 - precontext.length; + if (precontext !== '') { + patch.length1 = patch.length2 = precontext.length; + patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, precontext)); + } + while (bigpatch.diffs.length !== 0 && + patch.length1 < patch_size - this.Patch_Margin) { + let diff_type = bigpatch.diffs[0][0]; + let diff_text = bigpatch.diffs[0][1]; + if (diff_type === DIFF_INSERT) { + // Insertions are harmless. + patch.length2 += diff_text.length; + start2 += diff_text.length; + patch.diffs.push(bigpatch.diffs.shift()); + empty = false; + } else if (diff_type === DIFF_DELETE && patch.diffs.length === 1 && + patch.diffs[0][0] === DIFF_EQUAL && + diff_text.length > 2 * patch_size) { + // This is a large deletion. Let it pass in one chunk. + patch.length1 += diff_text.length; + start1 += diff_text.length; + empty = false; + patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text)); + bigpatch.diffs.shift(); + } else { + // Deletion or equality. Only take as much as we can stomach. + diff_text = diff_text.substring(0, + patch_size - patch.length1 - this.Patch_Margin); + patch.length1 += diff_text.length; + start1 += diff_text.length; + if (diff_type === DIFF_EQUAL) { + patch.length2 += diff_text.length; + start2 += diff_text.length; + } else { + empty = false; + } + patch.diffs.push(new diff_match_patch.Diff(diff_type, diff_text)); + if (diff_text === bigpatch.diffs[0][1]) { + bigpatch.diffs.shift(); + } else { + bigpatch.diffs[0][1] = + bigpatch.diffs[0][1].substring(diff_text.length); + } + } + } + // Compute the head context for the next patch. + precontext = this.diff_text2(patch.diffs); + precontext = + precontext.substring(precontext.length - this.Patch_Margin); + // Append the end context for this patch. + let postcontext = this.diff_text1(bigpatch.diffs) + .substring(0, this.Patch_Margin); + if (postcontext !== '') { + patch.length1 += postcontext.length; + patch.length2 += postcontext.length; + if (patch.diffs.length !== 0 && + patch.diffs[patch.diffs.length - 1][0] === DIFF_EQUAL) { + patch.diffs[patch.diffs.length - 1][1] += postcontext; + } else { + patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, postcontext)); + } + } + if (!empty) { + patches.splice(++x, 0, patch); + } + } + } +}; + + +/** + * Take a list of patches and return a textual representation. + * @param {!Array.} patches Array of Patch objects. + * @return {string} Text representation of patches. + */ +diff_match_patch.prototype.patch_toText = function (patches) { + let text = []; + for (let x = 0; x < patches.length; x++) { + text[x] = patches[x]; + } + return text.join(''); +}; + + +/** + * Parse a textual representation of patches and return a list of Patch objects. + * @param {string} textline Text representation of patches. + * @return {!Array.} Array of Patch objects. + * @throws {!Error} If invalid input. + */ +diff_match_patch.prototype.patch_fromText = function (textline) { + let patches = []; + if (!textline) { + return patches; + } + let text = textline.split('\n'); + let textPointer = 0; + let patchHeader = /^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/; + while (textPointer < text.length) { + let m = text[textPointer].match(patchHeader); + if (!m) { + throw new Error('Invalid patch string: ' + text[textPointer]); + } + let patch = new diff_match_patch.patch_obj(); + patches.push(patch); + patch.start1 = parseInt(m[1], 10); + if (m[2] === '') { + patch.start1--; + patch.length1 = 1; + } else if (m[2] === '0') { + patch.length1 = 0; + } else { + patch.start1--; + patch.length1 = parseInt(m[2], 10); + } + + patch.start2 = parseInt(m[3], 10); + if (m[4] === '') { + patch.start2--; + patch.length2 = 1; + } else if (m[4] === '0') { + patch.length2 = 0; + } else { + patch.start2--; + patch.length2 = parseInt(m[4], 10); + } + textPointer++; + + while (textPointer < text.length) { + let sign = text[textPointer].charAt(0); + try { + let line = decodeURI(text[textPointer].substring(1)); + } catch (ex) { + // Malformed URI sequence. + throw new Error('Illegal escape in patch_fromText: ' + line); + } + if (sign === '-') { + // Deletion. + patch.diffs.push(new diff_match_patch.Diff(DIFF_DELETE, line)); + } else if (sign === '+') { + // Insertion. + patch.diffs.push(new diff_match_patch.Diff(DIFF_INSERT, line)); + } else if (sign === ' ') { + // Minor equality. + patch.diffs.push(new diff_match_patch.Diff(DIFF_EQUAL, line)); + } else if (sign === '@') { + // Start of next patch. + break; + } else if (sign === '') { + // Blank line? Whatever. + } else { + // WTF? + throw new Error('Invalid patch mode "' + sign + '" in: ' + line); + } + textPointer++; + } + } + return patches; +}; + + +/** + * Class representing one patch operation. + * @constructor + */ +diff_match_patch.patch_obj = function () { + /** @type {!Array.} */ + this.diffs = []; + /** @type {?number} */ + this.start1 = null; + /** @type {?number} */ + this.start2 = null; + /** @type {number} */ + this.length1 = 0; + /** @type {number} */ + this.length2 = 0; +}; + + +/** + * Emulate GNU diff's format. + * Header: @@ -382,8 +481,9 @@ + * Indices are printed as 1-based, not 0-based. + * @return {string} The GNU diff string. + */ +diff_match_patch.patch_obj.prototype.toString = function () { + let coords1, coords2; + if (this.length1 === 0) { + coords1 = this.start1 + ',0'; + } else if (this.length1 === 1) { + coords1 = this.start1 + 1; + } else { + coords1 = (this.start1 + 1) + ',' + this.length1; + } + if (this.length2 === 0) { + coords2 = this.start2 + ',0'; + } else if (this.length2 === 1) { + coords2 = this.start2 + 1; + } else { + coords2 = (this.start2 + 1) + ',' + this.length2; + } + let text = ['@@ -' + coords1 + ' +' + coords2 + ' @@\n']; + let op; + // Escape the body of the patch with %xx notation. + for (let x = 0; x < this.diffs.length; x++) { + switch (this.diffs[x][0]) { + case DIFF_INSERT: + op = '+'; + break; + case DIFF_DELETE: + op = '-'; + break; + case DIFF_EQUAL: + op = ' '; + break; + } + text[x + 1] = op + encodeURI(this.diffs[x][1]) + '\n'; + } + return text.join('').replace(/%20/g, ' '); +}; + + +// CommonJS exports (guarded) +if (typeof module !== 'undefined' && module.exports) { + module.exports = diff_match_patch; + module.exports['diff_match_patch'] = diff_match_patch; + module.exports['DIFF_DELETE'] = DIFF_DELETE; + module.exports['DIFF_INSERT'] = DIFF_INSERT; + module.exports['DIFF_EQUAL'] = DIFF_EQUAL; +} + +// ESM exports for static import compatibility +export default diff_match_patch; +export { diff_match_patch, DIFF_DELETE, DIFF_INSERT, DIFF_EQUAL }; diff --git a/src/vs/workbench/contrib/void/browser/media/void.css b/src/vs/workbench/contrib/void/browser/media/void.css index d732c4bf08a..f19841bb1b8 100644 --- a/src/vs/workbench/contrib/void/browser/media/void.css +++ b/src/vs/workbench/contrib/void/browser/media/void.css @@ -17,10 +17,12 @@ .void-greenBG { background-color: var(--vscode-void-greenBG); + box-shadow: inset 2px 0 0 var(--vscode-void-greenBorder); } .void-redBG { background-color: var(--vscode-void-redBG); + box-shadow: inset 2px 0 0 var(--vscode-void-redBorder); } diff --git a/src/vs/workbench/contrib/void/browser/metricsPollService.ts b/src/vs/workbench/contrib/void/browser/metricsPollService.ts index 493c7edaccd..329dc5553e2 100644 --- a/src/vs/workbench/contrib/void/browser/metricsPollService.ts +++ b/src/vs/workbench/contrib/void/browser/metricsPollService.ts @@ -8,7 +8,7 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import * as dom from '../../../../base/browser/dom.js'; -import { IMetricsService } from '../common/metricsService.js'; +import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; diff --git a/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts b/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts index 83b3ed7b7fd..e4ca7a21b26 100644 --- a/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts +++ b/src/vs/workbench/contrib/void/browser/miscWokrbenchContrib.ts @@ -6,10 +6,8 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; import { IExtensionTransferService } from './extensionTransferService.js'; -import { os } from '../common/helpers/systemInfo.js'; +import { os } from '../../../../platform/void/common/helpers/systemInfo.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { timeout } from '../../../../base/common/async.js'; -import { getActiveWindow } from '../../../../base/browser/dom.js'; // Onboarding contribution that mounts the component at startup export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContribution { @@ -33,16 +31,6 @@ export class MiscWorkbenchContribs extends Disposable implements IWorkbenchContr this.extensionTransferService.deleteBlacklistExtensions(os) } - - // after some time, trigger a resize event for the blank screen error - timeout(5_000).then(() => { - // Get the active window reference for multi-window support - const targetWindow = getActiveWindow(); - // Trigger a window resize event to ensure proper layout calculations - targetWindow.dispatchEvent(new Event('resize')) - - }) - } } diff --git a/src/vs/workbench/contrib/void/browser/quickEditActions.ts b/src/vs/workbench/contrib/void/browser/quickEditActions.ts index 63deba31de8..22bef713aef 100644 --- a/src/vs/workbench/contrib/void/browser/quickEditActions.ts +++ b/src/vs/workbench/contrib/void/browser/quickEditActions.ts @@ -12,8 +12,8 @@ import { IEditCodeService } from './editCodeServiceInterface.js'; import { roundRangeToLines } from './sidebarActions.js'; import { VOID_CTRL_K_ACTION_ID } from './actionIDs.js'; import { localize2 } from '../../../../nls.js'; -import { IMetricsService } from '../common/metricsService.js'; -import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IMetricsService } from '../../../../platform/void/common/metricsService.js'; + export type QuickEditPropsType = { diffareaid: number, @@ -42,7 +42,6 @@ registerAction2(class extends Action2 { keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyK, weight: KeybindingWeight.VoidExtension, - when: ContextKeyExpr.deserialize('editorFocus && !terminalFocus'), } }); } diff --git a/src/vs/workbench/contrib/void/browser/react/build.js b/src/vs/workbench/contrib/void/browser/react/build.js index 9507aa59f26..4d21f840e2d 100755 --- a/src/vs/workbench/contrib/void/browser/react/build.js +++ b/src/vs/workbench/contrib/void/browser/react/build.js @@ -3,10 +3,11 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { execSync } from 'child_process'; +import { execFileSync } from 'child_process'; import { spawn } from 'cross-spawn' // Added lines below import fs from 'fs'; +import os from 'os'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -56,6 +57,44 @@ function findDesiredPathFromLocalPath(localDesiredPath, currentPath) { return globalDesiredPath; } +function requireRepoPath(localDesiredPath) { + const desiredPath = findDesiredPathFromLocalPath(localDesiredPath, __dirname); + if (!desiredPath) { + throw new Error(`Could not resolve required path: ${localDesiredPath}`); + } + return desiredPath; +} + +function createTailwindShim(tailwindCliPath) { + const shimDir = fs.mkdtempSync(path.join(os.tmpdir(), 'void-tailwind-')); + const shimPath = path.join(shimDir, 'tailwindcss'); + const script = `#!/usr/bin/env sh\nexec ${JSON.stringify(process.execPath)} ${JSON.stringify(tailwindCliPath)} "$@"\n`; + fs.writeFileSync(shimPath, script, { mode: 0o755 }); + + return { + shimDir, + cleanup: () => { + try { + fs.rmSync(shimDir, { recursive: true, force: true }); + } catch { + // ignore cleanup errors + } + } + }; +} + +const scopeTailwindCliPath = requireRepoPath('./node_modules/scope-tailwind/dist/main.js'); +const nodemonCliPath = requireRepoPath('./node_modules/nodemon/bin/nodemon.js'); +const tsupCliPath = requireRepoPath('./node_modules/tsup/dist/cli-default.js'); +const tailwindCliPath = requireRepoPath('./node_modules/tailwindcss/lib/cli.js'); + +const { shimDir: tailwindShimDir, cleanup: cleanupTailwindShim } = createTailwindShim(tailwindCliPath); +const buildEnv = { + ...process.env, + PATH: `${tailwindShimDir}${path.delimiter}${process.env.PATH ?? ''}`, +}; +process.on('exit', cleanupTailwindShim); + // hack to refresh styles automatically function saveStylesFile() { setTimeout(() => { @@ -86,10 +125,14 @@ if (isWatch) { if (!fs.existsSync('src2')) { try { console.log('🔨 Running initial scope-tailwind build to create src2 folder...'); - execSync( - 'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "void-"', - { stdio: 'inherit' } - ); + execFileSync(process.execPath, [ + scopeTailwindCliPath, + './src', + '-o', 'src2/', + '-s', 'void-scope', + '-c', 'styles.css', + '-p', 'void-', + ], { stdio: 'inherit', env: buildEnv }); console.log('✅ src2/ created successfully.'); } catch (err) { console.error('❌ Error running initial scope-tailwind build:', err); @@ -98,18 +141,18 @@ if (isWatch) { } // Watch mode - const scopeTailwindWatcher = spawn('npx', [ - 'nodemon', + const scopeTailwindWatcher = spawn(process.execPath, [ + nodemonCliPath, '--watch', 'src', '--ext', 'ts,tsx,css', '--exec', - 'npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "void-"' - ]); + `${JSON.stringify(process.execPath)} ${JSON.stringify(scopeTailwindCliPath)} ./src -o src2/ -s void-scope -c styles.css -p void-` + ], { env: buildEnv }); - const tsupWatcher = spawn('npx', [ - 'tsup', + const tsupWatcher = spawn(process.execPath, [ + tsupCliPath, '--watch' - ]); + ], { env: buildEnv }); scopeTailwindWatcher.stdout.on('data', (data) => { console.log(`[scope-tailwind] ${data}`); @@ -136,6 +179,7 @@ if (isWatch) { process.on('SIGINT', () => { scopeTailwindWatcher.kill(); tsupWatcher.kill(); + cleanupTailwindShim(); process.exit(); }); @@ -145,10 +189,18 @@ if (isWatch) { console.log('📦 Building...'); // Run scope-tailwind once - execSync('npx scope-tailwind ./src -o src2/ -s void-scope -c styles.css -p "void-"', { stdio: 'inherit' }); + execFileSync(process.execPath, [ + scopeTailwindCliPath, + './src', + '-o', 'src2/', + '-s', 'void-scope', + '-c', 'styles.css', + '-p', 'void-', + ], { stdio: 'inherit', env: buildEnv }); // Run tsup once - execSync('npx tsup', { stdio: 'inherit' }); + execFileSync(process.execPath, [tsupCliPath], { stdio: 'inherit', env: buildEnv }); console.log('✅ Build complete!'); + cleanupTailwindShim(); } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx index 93e26b0d7b4..d4baa06cd75 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ApplyBlockHoverButtons.tsx @@ -3,15 +3,23 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { useState, useEffect, useCallback, useRef, Fragment } from 'react' -import { useAccessor, useChatThreadsState, useChatThreadsStreamState, useCommandBarState, useCommandBarURIListener, useSettingsState } from '../util/services.js' -import { usePromise, useRefState } from '../util/helpers.js' -import { isFeatureNameDisabled } from '../../../../common/voidSettingsTypes.js' +import { useState, useEffect, useCallback, Fragment, } from 'react' +import { useAccessor, useChatThreadsStreamState, useCommandBarURIListener, useSettingsState } from '../util/services.js' +import { useRefState } from '../util/helpers.js' +import { isFeatureNameDisabled } from '../../../../../../../platform/void/common/voidSettingsTypes.js' import { URI } from '../../../../../../../base/common/uri.js' -import { FileSymlink, LucideIcon, RotateCw, Terminal } from 'lucide-react' +import { FileSymlink, LucideIcon, Terminal } from 'lucide-react' import { Check, X, Square, Copy, Play, } from 'lucide-react' -import { getBasename, ListableToolItem, voidOpenFileFn, ToolChildrenWrapper } from '../sidebar-tsx/SidebarChat.js' -import { PlacesType, VariantType } from 'react-tooltip' +import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChatShared.js' +import { ListableToolItem, ToolChildrenWrapper } from '../sidebar-tsx/SidebarChatUI.js' +import { IChatThreadService } from '../../../chatThreadService.js' +import { IModelService } from '../../../../../../../editor/common/language/services/model.js' +import { EndOfLinePreference } from '../../../../../../../editor/common/language/model.js' +import { StagingSelectionItem } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js' +import { PlacesType } from 'react-tooltip' +import { QueryType } from '../../../../../../services/search/common/search.js' +import { ToolName } from '../../../../common/prompt/prompts.js' +import type { IEditCodeService } from '../../../editCodeServiceInterface.js' enum CopyButtonText { Idle = 'Copy', @@ -49,25 +57,6 @@ export const IconShell1 = ({ onClick, Icon, disabled, className, ...props }: Ico } - -// export const IconShell2 = ({ onClick, title, Icon, disabled, className }: IconButtonProps) => ( -// -// ) - const COPY_FEEDBACK_TIMEOUT = 1500 // amount of time to say 'Copied!' export const CopyButton = ({ codeStr, toolTipName }: { codeStr: string | (() => Promise | string), toolTipName: string }) => { @@ -88,7 +77,7 @@ export const CopyButton = ({ codeStr, toolTipName }: { codeStr: string | (() => clipboardService.writeText(typeof codeStr === 'string' ? codeStr : await codeStr()) .then(() => { setCopyButtonText(CopyButtonText.Copied) }) .catch(() => { setCopyButtonText(CopyButtonText.Error) }) - metricsService.capture('Copy Code', { length: codeStr.length }) // capture the length only + metricsService.capture('Copy Code', { length: codeStr.length }) }, [metricsService, clipboardService, codeStr, setCopyButtonText]) return /> } - - - export const JumpToFileButton = ({ uri, ...props }: { uri: URI | 'current' } & React.ButtonHTMLAttributes) => { const accessor = useAccessor() const commandService = accessor.get('ICommandService') @@ -133,22 +119,28 @@ export const JumpToTerminalButton = ({ onClick }: { onClick: () => void }) => { // state persisted for duration of react only // TODO change this to use type `ChatThreads.applyBoxState[applyBoxId]` const _applyingURIOfApplyBoxIdRef: { current: { [applyBoxId: string]: URI | undefined } } = { current: {} } +const _fileOrdinalOfApplyBoxIdRef: { current: { [applyBoxId: string]: number | undefined } } = { current: {} } const getUriBeingApplied = (applyBoxId: string) => { return _applyingURIOfApplyBoxIdRef.current[applyBoxId] ?? null } - -export const useApplyStreamState = ({ applyBoxId }: { applyBoxId: string }) => { +export function useApplyStreamState({ applyBoxId, boundUri }: { applyBoxId: string; boundUri?: URI }) { const accessor = useAccessor() const voidCommandBarService = accessor.get('IVoidCommandBarService') + const editCodeService = accessor.get('IEditCodeService') as IEditCodeService const getStreamState = useCallback(() => { - const uri = getUriBeingApplied(applyBoxId) - if (!uri) return 'idle-no-changes' - return voidCommandBarService.getStreamState(uri) - }, [voidCommandBarService, applyBoxId]) + const effective = boundUri ?? getUriBeingApplied(applyBoxId) + if (!effective) return 'idle-no-changes' as const + const fromSvc = voidCommandBarService.getStreamState(effective) + if (fromSvc === 'streaming') return fromSvc + + return editCodeService.hasIdleDiffZoneForApplyBox(effective, applyBoxId) + ? ('idle-has-changes' as const) + : ('idle-no-changes' as const) + }, [voidCommandBarService, editCodeService, applyBoxId, boundUri]) const [currStreamStateRef, setStreamState] = useRefState(getStreamState()) @@ -157,19 +149,30 @@ export const useApplyStreamState = ({ applyBoxId }: { applyBoxId: string }) => { setStreamState(getStreamState()) }, [setStreamState, getStreamState, applyBoxId]) - // listen for stream updates on this box - useCommandBarURIListener(useCallback((uri_) => { - const uri = getUriBeingApplied(applyBoxId) - if (uri?.fsPath === uri_.fsPath) { - setStreamState(getStreamState()) - } - }, [setStreamState, applyBoxId, getStreamState])) + useCommandBarURIListener(useCallback((uri_: URI) => { + const effective = boundUri ?? getUriBeingApplied(applyBoxId) + if (effective?.fsPath === uri_.fsPath) setStreamState(getStreamState()) + }, [setStreamState, applyBoxId, getStreamState, boundUri])) + useEffect(() => { + const d1 = editCodeService.onDidAddOrDeleteDiffZones((event) => { + const eff = boundUri ?? getUriBeingApplied(applyBoxId) + if (eff && event.uri && eff.fsPath === event.uri.fsPath) setStreamState(getStreamState()) + }) + const d2 = editCodeService.onDidChangeDiffsInDiffZoneNotStreaming((event) => { + const eff = boundUri ?? getUriBeingApplied(applyBoxId) + if (eff && event.uri && eff.fsPath === event.uri.fsPath) setStreamState(getStreamState()) + }) + const d3 = editCodeService.onDidChangeStreamingInDiffZone((event) => { + const eff = boundUri ?? getUriBeingApplied(applyBoxId) + if (eff && event.uri && eff.fsPath === event.uri.fsPath) setStreamState(getStreamState()) + }) + return () => { d1?.dispose?.(); d2?.dispose?.(); d3?.dispose?.(); } + }, [editCodeService, setStreamState, getStreamState, applyBoxId, boundUri]) return { currStreamStateRef, setApplying } } - type IndicatorColor = 'green' | 'orange' | 'dark' | 'yellow' | null export const StatusIndicator = ({ indicatorColor, title, className, ...props }: { indicatorColor: IndicatorColor, title?: React.ReactNode, className?: string } & React.HTMLAttributes) => { return ( @@ -199,19 +202,42 @@ const tooltipPropsForApplyBlock = ({ tooltipName, color = undefined, position = export const useEditToolStreamState = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI }) => { const accessor = useAccessor() const voidCommandBarService = accessor.get('IVoidCommandBarService') - const [streamState, setStreamState] = useState(voidCommandBarService.getStreamState(uri)) - // listen for stream updates on this box + const editCodeService = accessor.get('IEditCodeService') as IEditCodeService + + const compute = useCallback(() => { + const fromSvc = voidCommandBarService.getStreamState(uri) + if (fromSvc === 'streaming') return fromSvc + return editCodeService.hasIdleDiffZoneForApplyBox(uri, applyBoxId) + ? ('idle-has-changes' as const) + : ('idle-no-changes' as const) + }, [voidCommandBarService, editCodeService, uri, applyBoxId]) + + const [streamState, setStreamState] = useState(compute()) + useCommandBarURIListener(useCallback((uri_) => { - const shouldUpdate = uri.fsPath === uri_.fsPath - if (shouldUpdate) { setStreamState(voidCommandBarService.getStreamState(uri)) } - }, [voidCommandBarService, applyBoxId, uri])) + if (uri.fsPath === uri_.fsPath) setStreamState(compute()) + }, [compute, uri])) + + useEffect(() => { + const d1 = editCodeService.onDidAddOrDeleteDiffZones((e) => { if (e?.uri?.fsPath === uri.fsPath) setStreamState(compute()) }) + const d2 = editCodeService.onDidChangeDiffsInDiffZoneNotStreaming((e) => { if (e?.uri?.fsPath === uri.fsPath) setStreamState(compute()) }) + const d3 = editCodeService.onDidChangeStreamingInDiffZone((e) => { if (e?.uri?.fsPath === uri.fsPath) setStreamState(compute()) }) + return () => { d1?.dispose?.(); d2?.dispose?.(); d3?.dispose?.(); } + }, [editCodeService, compute, uri]) - return { streamState, } + return { streamState } } export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId: string, uri: URI | 'current' } & React.HTMLAttributes) => { - const { currStreamStateRef } = useApplyStreamState({ applyBoxId }) + const accessor = useAccessor() + const editCodeService = accessor.get('IEditCodeService') as IEditCodeService + const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId, boundUri: uri !== 'current' ? uri : undefined }) + useEffect(() => { + if (uri !== 'current') { + editCodeService.bindApplyBoxUri?.(applyBoxId, uri) + } + }, [uri, applyBoxId, editCodeService]) const currStreamState = currStreamStateRef.current @@ -239,131 +265,215 @@ export const StatusIndicatorForApplyButton = ({ applyBoxId, uri }: { applyBoxId: } -const terminalLanguages = new Set([ - 'bash', - 'shellscript', - 'shell', - 'powershell', - 'bat', - 'zsh', - 'sh', - 'fish', - 'nushell', - 'ksh', - 'xonsh', - 'elvish', -]) - -const ApplyButtonsForTerminal = ({ +export const ApplyButtonsHTML = ({ codeStr, applyBoxId, uri, - language, }: { codeStr: string, applyBoxId: string, - language?: string, +} & ({ uri: URI | 'current'; -}) => { +}) +) => { const accessor = useAccessor() + const editCodeService = accessor.get('IEditCodeService') as IEditCodeService const metricsService = accessor.get('IMetricsService') - const terminalToolService = accessor.get('ITerminalToolService') + const notificationService = accessor.get('INotificationService') + const chatThreadsService = accessor.get('IChatThreadService') as IChatThreadService + const modelService = accessor.get('IModelService') as IModelService + const fileService = accessor.get('IFileService') as any const settingsState = useSettingsState() + const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId - const [isShellRunning, setIsShellRunning] = useState(false) - const interruptToolRef = useRef<(() => void) | null>(null) - const isDisabled = isShellRunning - - const onClickSubmit = useCallback(async () => { - if (isShellRunning) return - try { - setIsShellRunning(true) - const terminalId = await terminalToolService.createPersistentTerminal({ cwd: null }) - const { interrupt } = await terminalToolService.runCommand( - codeStr, - { type: 'persistent', persistentTerminalId: terminalId } - ); - interruptToolRef.current = interrupt - metricsService.capture('Execute Shell', { length: codeStr.length }) - } catch (e) { - setIsShellRunning(false) - console.error('Failed to execute in terminal:', e) + const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId, boundUri: uri !== 'current' ? uri : undefined }) + useEffect(() => { + if (uri !== 'current') { + editCodeService.bindApplyBoxUri?.(applyBoxId, uri) } - }, [codeStr, uri, applyBoxId, metricsService, terminalToolService, isShellRunning]) - - if (isShellRunning) { - return ( - { - interruptToolRef.current?.(); - setIsShellRunning(false); - }} - {...tooltipPropsForApplyBlock({ tooltipName: 'Stop' })} - /> - ); - } - if (isDisabled) { - return null - } - return -} - - - -const ApplyButtonsForEdit = ({ - codeStr, - applyBoxId, - uri, - language, -}: { - codeStr: string, - applyBoxId: string, - language?: string, - uri: URI | 'current'; -}) => { - const accessor = useAccessor() - const editCodeService = accessor.get('IEditCodeService') - const metricsService = accessor.get('IMetricsService') - const notificationService = accessor.get('INotificationService') + }, [uri, applyBoxId, editCodeService]) - const settingsState = useSettingsState() - const isDisabled = !!isFeatureNameDisabled('Apply', settingsState) || !applyBoxId + // Detect if provided code snippet is already present in the target file; disable Apply if so + const [srStatus, setSrStatus] = useState<'already' | 'notpresent' | 'unknown'>('unknown') + useEffect(() => { + const effective = (uri !== 'current' ? uri : (getUriBeingApplied(applyBoxId) ?? null)) as URI | null + const normalize = (s: string) => s.replace(/\r/g, '').trim() + const collapse = (s: string) => normalize(s).replace(/[\t ]+/g, ' ').replace(/\n+/g, '\n') + const run = async () => { + try { + if (!effective) { setSrStatus('unknown'); return } + let snippet = codeStr || '' + snippet = normalize(snippet) + if (!snippet) { setSrStatus('unknown'); return } + const model = modelService.getModel(effective) + let fileText = model ? model.getValue(EndOfLinePreference.LF) : '' + if (!fileText) { + try { + const data = await (fileService as any).readFile(effective) + fileText = (data?.value?.toString ? data.value.toString() : new TextDecoder('utf-8').decode(data?.value)) || '' + } catch { fileText = '' } + } + if (!fileText) { setSrStatus('unknown'); return } + const textNorm = normalize(fileText) + const textCollapsed = collapse(fileText) + const snippetCollapsed = collapse(snippet) + const present = textNorm.includes(snippet) || textCollapsed.includes(snippetCollapsed) + setSrStatus(present ? 'already' : 'notpresent') + } catch { setSrStatus('unknown') } + } + run() + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [codeStr, uri, applyBoxId]) - const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId }) const onClickSubmit = useCallback(async () => { if (currStreamStateRef.current === 'streaming') return - await editCodeService.callBeforeApplyOrEdit(uri) + // Prefer explicit uri; if 'current', try a pre-linked URI for this box (future UI could set it) + const maybeLinkedUri = getUriBeingApplied(applyBoxId) + const effectiveUri = uri !== 'current' ? uri : (maybeLinkedUri ?? 'current') + await editCodeService.callBeforeApplyOrEdit(effectiveUri) + + // Build SELECTIONS from the previous user message selections for this card + const buildSelectionsForApply = async (targetUri?: URI): Promise => { + try { + // parse applyBoxId: threadId-messageIdx-tokenIdx; threadId may include '-' + const parts = applyBoxId.split('-') + if (parts.length < 3) return [] + const tokenIdxStr = parts.pop()! + const tokenIdx = parseInt(tokenIdxStr, 10) + const messageIdxStr = parts.pop()! + const threadId = parts.join('-') + const messageIdx = parseInt(messageIdxStr, 10) + if (Number.isNaN(messageIdx)) return [] + const thread = chatThreadsService.state.allThreads[threadId] + if (!thread) return [] + const fileTextCache = new Map() + const getFileTextCached = async (fileUri: URI): Promise<{ text: string; lines: string[] } | null> => { + const cached = fileTextCache.get(fileUri.fsPath) + if (cached) return cached + + const model = modelService.getModel(fileUri) + let fileText = model ? model.getValue(EndOfLinePreference.LF) : '' + if (!fileText) { + try { + const data = await fileService.readFile(fileUri) + fileText = (data?.value?.toString ? data.value.toString() : new TextDecoder('utf-8').decode(data?.value)) || '' + } catch { fileText = '' } + } + if (!fileText) return null + + const result = { text: fileText, lines: fileText.split('\n') } + fileTextCache.set(fileUri.fsPath, result) + return result + } + // find nearest previous user message + let prevUserSelections: StagingSelectionItem[] | null = null + for (let i = messageIdx - 1; i >= 0; i -= 1) { + const m = thread.messages[i] + if (m?.role === 'user') { prevUserSelections = m.selections ?? null; break } + } + if (!prevUserSelections || prevUserSelections.length === 0) { + if (!targetUri) return [] + const fileState = await getFileTextCached(targetUri) + if (!fileState) return [] + const inferred = await editCodeService.inferSelectionForApply({ uri: targetUri, codeStr, fileText: fileState.text }) + return inferred ? [inferred.text] : [] + } + // if no explicit target, infer unique file from selections + if (!targetUri) { + const fileSet = new Set(prevUserSelections.filter(s => s.type !== 'Folder').map(s => s.uri.fsPath)) + if (fileSet.size !== 1) return [] + const onlyFsPath = Array.from(fileSet)[0] + targetUri = prevUserSelections.find(s => s.type !== 'Folder' && s.uri.fsPath === onlyFsPath)!.uri + } + const candidates: string[] = [] + const seenCandidateTexts = new Set() + const addCandidate = (text: string) => { + if (seenCandidateTexts.has(text)) return + seenCandidateTexts.add(text) + candidates.push(text) + } + const ctx = 4 + for (const sel of prevUserSelections) { + if (sel.type === 'Folder') continue + if (sel.uri.fsPath !== targetUri.fsPath) continue + const fileState = await getFileTextCached(sel.uri) + if (!fileState) continue + const { text: fileText, lines } = fileState + if (sel.type === 'CodeSelection') { + const [startLine, endLine] = sel.range + const start = Math.max(1, startLine - ctx) + const end = Math.min(lines.length, endLine + ctx) + addCandidate(lines.slice(start - 1, end).join('\n')) + } + else if (sel.type === 'File') { + addCandidate(fileText) + } + } + if (candidates.length === 0) return [] + const normalize = (s: string) => s.split('\n').map(l => l.trim().toLowerCase()).filter(l => l.length > 0) + const codeLines = new Set(normalize(codeStr)) + let bestIdx = -1, bestScore = -1 + for (let i = 0; i < candidates.length; i += 1) { + const candLines = new Set(normalize(candidates[i])) + let score = 0 + for (const ln of candLines) { if (codeLines.has(ln)) score += 1 } + if (score > bestScore) { bestScore = score; bestIdx = i } + } + if (bestScore <= 0) { + const fallbackIdx = Number.isFinite(tokenIdx) ? (Math.abs(tokenIdx) % candidates.length) : 0 + bestIdx = fallbackIdx + } + return [candidates[bestIdx]] + } + catch { return [] } + } + const selectionsForApply = await buildSelectionsForApply(effectiveUri === 'current' ? undefined : effectiveUri) + + if (effectiveUri && effectiveUri !== 'current') { + setApplying(effectiveUri) + try { + const applied = await editCodeService.applyEditFileSimpleForApplyBox({ uri: effectiveUri, applyBoxId }) + if (applied) { + metricsService.capture('Apply Code', { length: codeStr.length }) + return + } + } catch (e: any) { + notificationService.warn?.(`Apply failed: ${e?.message ?? String(e)}`) + console.error('applyEditFileSimpleForApplyBox error:', e) + } finally { + setApplying(undefined) + } + } + + // Fallback: use existing startApplying flow const [newApplyingUri, applyDonePromise] = editCodeService.startApplying({ from: 'ClickApply', applyStr: codeStr, - uri: uri, + selections: selectionsForApply, + uri: effectiveUri, startBehavior: 'reject-conflicts', + applyBoxId: applyBoxId, }) ?? [] setApplying(newApplyingUri) if (!applyDonePromise) { - notificationService.info(`Void Error: We couldn't run Apply here. ${uri === 'current' ? 'This Apply block wants to run on the current file, but you might not have a file open.' : `This Apply block wants to run on ${uri.fsPath}, but it might not exist.`}`) + notificationService.info(`Void Error: We couldn't run Apply here. ${effectiveUri === 'current' ? 'Specify the target file path as the first line of the code block (absolute path), then try again.' : `This Apply block wants to run on ${effectiveUri.fsPath}, but it might not exist.`}`) } // catch any errors by interrupting the stream - applyDonePromise?.catch(e => { + applyDonePromise?.then(() => { + }).catch(e => { const uri = getUriBeingApplied(applyBoxId) if (uri) editCodeService.interruptURIStreaming({ uri: uri }) notificationService.info(`Void Error: There was a problem running Apply: ${e}.`) - }) metricsService.capture('Apply Code', { length: codeStr.length }) // capture the length only - }, [setApplying, currStreamStateRef, editCodeService, codeStr, uri, applyBoxId, metricsService, notificationService]) + }, [setApplying, currStreamStateRef, editCodeService, codeStr, uri, applyBoxId, metricsService]) const onClickStop = useCallback(() => { @@ -375,17 +485,23 @@ const ApplyButtonsForEdit = ({ metricsService.capture('Stop Apply', {}) }, [currStreamStateRef, applyBoxId, editCodeService, metricsService]) - const onAccept = useCallback(() => { - const uri = getUriBeingApplied(applyBoxId) - if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri: uri, behavior: 'accept', removeCtrlKs: false }) + const onAccept = useCallback(async () => { + const target = getUriBeingApplied(applyBoxId) ?? (uri !== 'current' ? uri : undefined) + if (target) { + await editCodeService.acceptOrRejectDiffAreasByApplyBox({ uri: target, applyBoxId, behavior: 'accept' }) + } }, [uri, applyBoxId, editCodeService]) - const onReject = useCallback(() => { - const uri = getUriBeingApplied(applyBoxId) - if (uri) editCodeService.acceptOrRejectAllDiffAreas({ uri: uri, behavior: 'reject', removeCtrlKs: false }) + const onReject = useCallback(async () => { + const target = getUriBeingApplied(applyBoxId) ?? (uri !== 'current' ? uri : undefined) + if (target) { + await editCodeService.acceptOrRejectDiffAreasByApplyBox({ uri: target, applyBoxId, behavior: 'reject' }) + } }, [uri, applyBoxId, editCodeService]) + const currStreamState = currStreamStateRef.current + if (currStreamState === 'streaming') { return } + if (isDisabled) { return null } + + if (currStreamState === 'idle-no-changes') { - return + return + + {srStatus === 'already' && Already applied — no changes detected} + } - if (currStreamState === 'idle-has-changes') { + + if (currStreamState === 'idle-has-changes') { return } -} - - - - -export const ApplyButtonsHTML = (params: { - codeStr: string, - applyBoxId: string, - language?: string, - uri: URI | 'current'; -}) => { - const { language } = params - const isShellLanguage = !!language && terminalLanguages.has(language) - - if (isShellLanguage) { - return - } - else { - return - } + return null } - - - export const EditToolAcceptRejectButtonsHTML = ({ codeStr, applyBoxId, @@ -455,7 +557,7 @@ export const EditToolAcceptRejectButtonsHTML = ({ applyBoxId: string, } & ({ uri: URI, - type: 'edit_file' | 'rewrite_file', + type: ToolName, threadId: string, }) ) => { @@ -485,7 +587,7 @@ export const EditToolAcceptRejectButtonsHTML = ({ return null } - if (streamState === 'idle-has-changes') { + if (streamState === 'idle-has-changes') { if (isRunning === 'LLM' || isRunning === 'tool') return null return <> @@ -501,7 +603,7 @@ export const EditToolAcceptRejectButtonsHTML = ({ /> } - + return null } export const BlockCodeApplyWrapper = ({ @@ -511,6 +613,7 @@ export const BlockCodeApplyWrapper = ({ language, canApply, uri, + fileOrdinalIdx, }: { codeStr: string; children: React.ReactNode; @@ -518,39 +621,258 @@ export const BlockCodeApplyWrapper = ({ canApply: boolean; language: string; uri: URI | 'current', + fileOrdinalIdx?: number, }) => { const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const { currStreamStateRef } = useApplyStreamState({ applyBoxId }) + const chatThreadsService = accessor.get('IChatThreadService') as IChatThreadService + const modelService = accessor.get('IModelService') as IModelService + const editCodeService = accessor.get('IEditCodeService') as IEditCodeService + const { currStreamStateRef, setApplying } = useApplyStreamState({ applyBoxId, boundUri: uri !== 'current' ? uri : undefined }) + useEffect(() => { + if (uri !== 'current') { + editCodeService.bindApplyBoxUri?.(applyBoxId, uri) + } + }, [uri, applyBoxId, editCodeService]) const currStreamState = currStreamStateRef.current + const [showPicker, setShowPicker] = useState(false) + const [manualPath, setManualPath] = useState('') + const [suggestions, setSuggestions] = useState([]) + const [isSearching, setIsSearching] = useState(false) + + const workspaceService = accessor.get('IWorkspaceContextService') + const fileService = accessor.get('IFileService') + const searchService = accessor.get('ISearchService') as any + const commandBarService = accessor.get('IVoidCommandBarService') - const name = uri !== 'current' ? + useEffect(() => { + if (!(uri === 'current' && showPicker)) return + const recent = commandBarService.sortedURIs ?? [] + if (recent.length > 0) { + setSuggestions(recent.slice(0, 200)) + return + } + setSuggestions([]) + }, [uri, showPicker]) + + useEffect(() => { + if (!(uri === 'current' && showPicker)) return + const q = manualPath.trim() + if (q.length === 0) return + let didCancel = false + const h = setTimeout(async () => { + try { + setIsSearching(true) + const folders = workspaceService.getWorkspace()?.folders ?? [] + if (folders.length === 0) { setSuggestions([]); return } + const folderQueries = folders.map((f: any) => ({ folder: f.uri })) + const res = await searchService.fileSearch({ + type: QueryType.File, + folderQueries, + filePattern: q, + sortByScore: true, + onlyFileScheme: true, + excludePattern: { + '**/node_modules/**': true, + '**/bower_components/**': true, + '**/.yarn/**': true, + '**/.pnp/**': true, + '**/.parcel-cache/**': true, + '**/.turbo/**': true, + '**/.cache/**': true, + '**/.next/**': true, + '**/.nuxt/**': true, + '**/.svelte-kit/**': true, + '**/dist/**': true, + '**/build/**': true, + '**/out/**': true, + '**/coverage/**': true, + '**/target/**': true, + '**/.git/**': true, + // Python + '**/.venv/**': true, + '**/venv/**': true, + '**/__pycache__/**': true, + '**/.mypy_cache/**': true, + '**/.pytest_cache/**': true, + '**/.tox/**': true, + '**/.ruff_cache/**': true, + // Java / Kotlin / Android + '**/.gradle/**': true, + '**/.idea/**': true, + '**/.settings/**': true, + '**/Pods/**': true, + // .NET / C# + '**/bin/**': true, + '**/obj/**': true, + // Go / PHP / Ruby + '**/vendor/**': true, + '**/pkg/**': true, + '**/.bundle/**': true, + '**/vendor/bundle/**': true, + // Haskell / Stack + '**/dist-newstyle/**': true, + '**/.stack-work/**': true, + // Elixir / Erlang + '**/_build/**': true, + '**/deps/**': true, + '**/ebin/**': true, + // C/C++ / CMake + '**/CMakeFiles/**': true, + '**/cmake-build-*/**': true, + }, + maxResults: 200, + }) + if (didCancel) return + const items: URI[] = (res?.results || []).map((r: any) => r.resource).filter(Boolean) + setSuggestions(items) + } + catch { + if (!didCancel) setSuggestions([]) + } + finally { if (!didCancel) setIsSearching(false) } + }, 200) + return () => { didCancel = true; clearTimeout(h) } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [manualPath, uri, showPicker]) + + const resolvePathToUri = useCallback((pathStr: string): URI | null => { + const trimmed = pathStr.trim() + if (!trimmed) return null + const isWindowsAbs = /^[a-zA-Z]:[\\\/]/.test(trimmed) + if (isWindowsAbs || trimmed.startsWith('/')) { + try { return URI.file(trimmed) } catch { return null } + } + const folders = workspaceService.getWorkspace()?.folders ?? [] + if (folders.length === 0) return null + const normalized = trimmed.replace(/^\.\/[\\/]?/, '') + return URI.joinPath(folders[0].uri, normalized) + }, [workspaceService]) + + const highlightName = useCallback((name: string, query: string) => { + const q = query.trim() + if (!q) return <>{name} + const lowerName = name.toLowerCase() + const lowerQ = q.toLowerCase() + let i = 0, j = 0 + const matchedIdxs: number[] = [] + while (i < lowerName.length && j < lowerQ.length) { + if (lowerName[i] === lowerQ[j]) { matchedIdxs.push(i); j += 1 } + i += 1 + } + if (matchedIdxs.length === 0) return <>{name} + const parts: React.ReactNode[] = [] + for (let k = 0; k < name.length; k++) { + const ch = name[k] + const isMatch = matchedIdxs.includes(k) + parts.push(isMatch ? {ch} : {ch}) + } + return <>{parts} + }, []) + + const onPick = useCallback((picked: URI) => { + setApplying(picked) + setShowPicker(false) + }, [setApplying]) + + const onSubmitManual = useCallback(() => { + const u = resolvePathToUri(manualPath) + if (u) { + setApplying(u) + setShowPicker(false) + } + }, [manualPath, resolvePathToUri, setApplying]) + + const selectedUri = getUriBeingApplied(applyBoxId) + + const name = (uri !== 'current' ? uri : selectedUri) ? {getBasename(uri.fsPath)}} + name={{getBasename(((uri !== 'current' ? uri : selectedUri) as URI).fsPath)}} isSmall={true} showDot={false} - onClick={() => { voidOpenFileFn(uri, accessor) }} + onClick={() => { const target = (uri !== 'current' ? uri : selectedUri) as URI; if (target) voidOpenFileFn(target, accessor) }} /> : {language} + const canRunApply = canApply || !!selectedUri + + // remember per-block ordinal to disambiguate selections per file across multiple code blocks + useEffect(() => { + if (typeof fileOrdinalIdx === 'number' && Number.isFinite(fileOrdinalIdx)) { + _fileOrdinalOfApplyBoxIdRef.current[applyBoxId] = fileOrdinalIdx + } + return () => { + // do not clear on unmount to keep stable mapping during interactions + } + }, [applyBoxId, fileOrdinalIdx]) return
{/* header */}
- + {name}
-
- +
+ {(uri === 'current') && ( + <> + setShowPicker(v => !v)} + {...tooltipPropsForApplyBlock({ tooltipName: selectedUri ? 'Change target file' : 'Select target file' })} + /> + {selectedUri && ( + setApplying(undefined)} + {...tooltipPropsForApplyBlock({ tooltipName: 'Clear selection' })} + /> + )} + + )} + {(uri !== 'current' || !!selectedUri) && } {currStreamState === 'idle-no-changes' && } - + {canRunApply && }
+ {/* selections spoiler (temporarily hidden — can re-enable if fallback needed) */} + {/* */} + + {/* picker */} + {uri === 'current' && showPicker && ( +
+
+ setManualPath(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') onSubmitManual() }} /> + +
+
+ {isSearching &&
Searching…
} + {suggestions.map((u, i) => { + const name = getBasename(u.fsPath) + let rel = getRelative(u, accessor) || '' + // normalize slashes and drop leading slashes + rel = rel.replace(/[/\\]+/g, '/').replace(/^\/+/, '') + // remove filename from the tail of relative path + const baseUnix = name.replace(/[/\\]+/g, '/') + if (rel.endsWith('/' + baseUnix)) { + rel = rel.slice(0, -('/' + baseUnix).length) + } + return ( +
onPick(u)} title={u.fsPath}> +
+ {highlightName(name, manualPath)}{rel ? {rel} : null} +
+
+ ) + })} + {!isSearching && suggestions.length === 0 &&
No results
} +
+
+ )} + {/* contents */} {children} diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx index 97214330b2f..7731f6df41a 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/ChatMarkdownRender.tsx @@ -3,7 +3,7 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { JSX, useMemo, useState } from 'react' +import React, { JSX, useState } from 'react' import { marked, MarkedToken, Token } from 'marked' import { convertToVscodeLang, detectLanguage } from '../../../../common/helpers/languageHelpers.js' @@ -11,10 +11,10 @@ import { BlockCodeApplyWrapper } from './ApplyBlockHoverButtons.js' import { useAccessor } from '../util/services.js' import { URI } from '../../../../../../../base/common/uri.js' import { isAbsolute } from '../../../../../../../base/common/path.js' -import { separateOutFirstLine } from '../../../../common/helpers/util.js' +import { separateOutFirstLine } from '../../../../../../../platform/void/common/helpers/util.js' import { BlockCode } from '../util/inputs.js' -import { CodespanLocationLink } from '../../../../common/chatThreadServiceTypes.js' -import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChat.js' +import { CodespanLocationLink } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js' +import { getBasename, getRelative, voidOpenFileFn } from '../sidebar-tsx/SidebarChatShared.js' export type ChatMessageLocation = { @@ -29,64 +29,16 @@ export const getApplyBoxId = ({ threadId, messageIdx, tokenIdx }: ApplyBoxLocati } function isValidUri(s: string): boolean { - return s.length > 5 && isAbsolute(s) && !s.includes('//') && !s.includes('/*') // common case that is a false positive is comments like // + const trimmed = s.trim() + if (!trimmed) return false + if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(trimmed)) return false + if (trimmed.includes('/*')) return false + return isAbsolute(trimmed) } // renders contiguous string of latex eg $e^{i\pi}$ const LatexRender = ({ latex }: { latex: string }) => { return {latex} - // try { - // let formula = latex; - // let displayMode = false; - - // // Extract the formula from delimiters - // if (latex.startsWith('$') && latex.endsWith('$')) { - // // Check if it's display math $$...$$ - // if (latex.startsWith('$$') && latex.endsWith('$$')) { - // formula = latex.slice(2, -2); - // displayMode = true; - // } else { - // formula = latex.slice(1, -1); - // } - // } else if (latex.startsWith('\\(') && latex.endsWith('\\)')) { - // formula = latex.slice(2, -2); - // } else if (latex.startsWith('\\[') && latex.endsWith('\\]')) { - // formula = latex.slice(2, -2); - // displayMode = true; - // } - - // // Render LaTeX - // const html = katex.renderToString(formula, { - // displayMode: displayMode, - // throwOnError: false, - // output: 'html' - // }); - - // // Sanitize the HTML output with DOMPurify - // const sanitizedHtml = dompurify.sanitize(html, { - // RETURN_TRUSTED_TYPE: true, - // USE_PROFILES: { html: true, svg: true, mathMl: true } - // }); - - // // Add proper styling based on mode - // const className = displayMode - // ? 'katex-block my-2 text-center' - // : 'katex-inline'; - - // // Use the ref approach to avoid dangerouslySetInnerHTML - // const mathRef = React.useRef(null); - - // React.useEffect(() => { - // if (mathRef.current) { - // mathRef.current.innerHTML = sanitizedHtml as unknown as string; - // } - // }, [sanitizedHtml]); - - // return ; - // } catch (error) { - // console.error('KaTeX rendering error:', error); - // return {latex}; - // } } const Codespan = ({ text, className, onClick, tooltip }: { text: string, className?: string, onClick?: () => void, tooltip?: string }) => { @@ -129,12 +81,18 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string link = chatThreadService.getCodespanLink({ codespanStr: text, messageIdx, threadId }) if (link === undefined) { - // if no link, generate link and add to cache chatThreadService.generateCodespanLink({ codespanStr: text, threadId }) - .then(link => { - chatThreadService.addCodespanLink({ newLinkText: text, newLinkLocation: link, messageIdx, threadId }) - setDidComputeCodespanLink(true) // rerender - }) + .then(newLink => { + if (newLink) { + chatThreadService.addCodespanLink({ + newLinkText: text, + newLinkLocation: newLink, + messageIdx, + threadId + }) + setDidComputeCodespanLink(true) + } + }) } if (link?.displayText) { @@ -142,7 +100,7 @@ const CodespanWithLink = ({ text, rawText, chatMessageLocation }: { text: string } if (isValidUri(displayText)) { - tooltip = getRelative(URI.file(displayText), accessor) // Full path as tooltip + tooltip = getRelative(URI.file(displayText), accessor) displayText = getBasename(displayText) } } @@ -252,15 +210,10 @@ const paragraphToLatexSegments = (paragraphText: string) => { ); } - segments.push(...inlineSegments); } - - } } - - return segments } @@ -283,57 +236,89 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. if (t.type === 'code') { const [firstLine, remainingContents] = separateOutFirstLine(t.text) - const firstLineIsURI = isValidUri(firstLine) && !codeURI - const contents = firstLineIsURI ? (remainingContents?.trimStart() || '') : t.text // exclude first-line URI from contents - if (!contents) return null - // figure out langauge and URI - let uri: URI | null - let language: string - if (codeURI) { - uri = codeURI - } - else if (firstLineIsURI) { // get lang from the uri in the first line of the markdown - uri = URI.file(firstLine) - } - else { - uri = null + const looksLikeFilePath = (s: string) => { + const fl = (s || '').trim() + if (!fl) return false + + if (/^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(fl)) return false + // Windows absolute + if (/^[a-zA-Z]:[\\\/]/.test(fl)) return true + // workspace-relative + if (fl.startsWith('./') || fl.startsWith('../')) return true + // POSIX absolute + if (isAbsolute(fl)) return true + return false } - if (t.lang) { // a language was provided. empty string is common so check truthy, not just undefined - language = convertToVscodeLang(languageService, t.lang) // convert markdown language to language that vscode recognizes (eg markdown doesn't know bash but it does know shell) + + const tryResolveUriFromFirstLine = (): URI | null => { + if (codeURI) return codeURI + let fl = (firstLine || '').trim() + fl = fl.replace(/\s*\([\s\S]*?\)\s*:?\s*$/, '') + const isWindowsAbs = /^[a-zA-Z]:[\\\/]/.test(fl) + if (isWindowsAbs || isAbsolute(fl)) { + try { return URI.file(fl) } catch { } + } + // workspace-relative heuristic + try { + const workspaceService = accessor.get('IWorkspaceContextService') as any + const folders = workspaceService?.getWorkspace?.()?.folders ?? [] + if (folders.length > 0) { + const looksLikePath = (fl.startsWith('./') || fl.startsWith('../')) + if (looksLikePath) { + const normalized = fl.replace(/^\.\/[\\\/]?/, '') + return URI.joinPath(folders[0].uri, normalized) + } + } + } catch { } + return null } - else { // no language provided - fallback - get lang from the uri and contents + + + const shouldStripFirst = looksLikeFilePath(firstLine) + const uriFromFirstLine = shouldStripFirst ? tryResolveUriFromFirstLine() : null + + + const uri: URI | null = codeURI ?? uriFromFirstLine ?? null + + + const contents = shouldStripFirst ? (remainingContents?.trimStart() || '') : t.text + if (!contents) return null + + + let language: string + if (t.lang) { + language = convertToVscodeLang(languageService, t.lang) + } else { language = detectLanguage(languageService, { uri, fileContents: contents }) } if (options.isApplyEnabled && chatMessageLocation) { - const isCodeblockClosed = t.raw.trimEnd().endsWith('```') // user should only be able to Apply when the code has been closed (t.raw ends with '```') - + const isCodeblockClosed = t.raw.trimEnd().endsWith('```') const applyBoxId = getApplyBoxId({ threadId: chatMessageLocation.threadId, messageIdx: chatMessageLocation.messageIdx, tokenIdx: tokenIdx, }) + const hasTargetUri = !!uri + return } - return + return } if (t.type === 'heading') { @@ -373,40 +358,6 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, ..
) - // return ( - //
- // - // - // - // {t.header.map((cell: any, index: number) => ( - // - // ))} - // - // - // - // {t.rows.map((row: any[], rowIndex: number) => ( - // - // {row.map((cell: any, cellIndex: number) => ( - // - // ))} - // - // ))} - // - //
- // {cell.raw} - //
- // {cell.raw} - //
- //
- // ) } if (t.type === 'hr') { @@ -546,11 +497,98 @@ const RenderToken = ({ token, inPTag, codeURI, chatMessageLocation, tokenIdx, .. export const ChatMarkdownRender = ({ string, inPTag = false, chatMessageLocation, ...options }: { string: string, inPTag?: boolean, codeURI?: URI, chatMessageLocation: ChatMessageLocation | undefined } & RenderTokenOptions) => { string = string.replaceAll('\n•', '\n\n•') const tokens = marked.lexer(string); // https://marked.js.org/using_pro#renderer + + // Infer codeURI for the next code block from preceding text tokens + const accessor = useAccessor() + const modelService: any = accessor.get('IModelService') + const commandBarService: any = accessor.get('IVoidCommandBarService') + const workspaceService: any = accessor.get('IWorkspaceContextService') + + const getBase = (p: string) => p.split(/[\\\/]/).pop() || p + const sanitizeHint = (s: string) => { + let out = (s || '').trim() + // strip surrounding backticks/quotes + out = out.replace(/^\s*[`'\"]/, '').replace(/[`'\"]\s*$/, '') + // strip list bullets like "- ", "• ", "1. " + out = out.replace(/^\s*(?:[-•]|\d+\.)\s+/, '') + out = out.replace(/\(.*?\)\s*:?$/, '').replace(/[:;,]+$/, '') + return out.trim() + } + const resolveUriFromHint = (raw: string): URI | null => { + const hint = sanitizeHint(raw) + if (!hint) return null + // absolute (unix/win) + const isWinAbs = /^[a-zA-Z]:[\\\/]/.test(hint) + if (isWinAbs || isAbsolute(hint)) { + try { return URI.file(hint) } catch { /* noop */ } + } + // contains path separators → workspace-relative join + if (/[\\\/]/.test(hint)) { + try { + const folders = workspaceService?.getWorkspace?.()?.folders ?? [] + if (folders.length > 0) { + const normalized = hint.replace(/^\.\/[\\\/]?/, '') + return URI.joinPath(folders[0].uri, normalized) + } + } catch { /* noop */ } + } + // bare filename → try open models then recent URIs + if (/^[\w.-]+\.[\w0-9.-]+$/.test(hint)) { + try { + const models: any[] = modelService?.getModels?.() ?? [] + const modelMatches = models.map(m => m?.uri).filter((u: any) => u?.fsPath && getBase(u.fsPath) === hint) + if (modelMatches.length === 1) return modelMatches[0] + const recent: any[] = commandBarService?.sortedURIs ?? [] + const recentMatches = recent.filter((u: any) => u?.fsPath && getBase(u.fsPath) === hint) + if (recentMatches.length === 1) return recentMatches[0] + } catch { /* noop */ } + } + return null + } + + const elements: React.ReactNode[] = [] + let pendingUri: URI | null = options.codeURI ?? null + + for (let index = 0; index < tokens.length; index += 1) { + const token = tokens[index] as any + let codeURIForThisToken: URI | undefined = undefined + + // If this token is a code block, pass the pendingUri once, then clear it + if (token.type === 'code') { + codeURIForThisToken = pendingUri ?? undefined + pendingUri = null + } + else { + // Try to infer URI from text-like tokens to apply to the next code block + let rawText: string | null = null + if (token.type === 'paragraph' || token.type === 'heading') rawText = token.text || token.raw || '' + else if (token.type === 'text') rawText = token.raw || token.text || '' + else if (token.type === 'list_item') rawText = token.text || '' + if (rawText) { + // find first plausible path/filename in the text + const match = rawText.match(/[`'\"]?([A-Za-z]:[\\\/][^\s:()]+|\/[^^\s:()]+|\.{0,2}\/[^^\s:()]+|(?:[\w.-]+[\\\/])+[\w.-]+\.[A-Za-z0-9.-]+|[\w.-]+\.[A-Za-z0-9.-]+)[`'\"]?/) + if (match && match[1]) { + const uri = resolveUriFromHint(match[1]) + if (uri) pendingUri = uri + } + } + } + + elements.push( + + ) + } + return ( <> - {tokens.map((token, index) => ( - - ))} + {elements} ) } diff --git a/src/vs/workbench/contrib/void/browser/react/src/markdown/inferSelection.ts b/src/vs/workbench/contrib/void/browser/react/src/markdown/inferSelection.ts new file mode 100644 index 00000000000..b47c8fdcc3d --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/markdown/inferSelection.ts @@ -0,0 +1,381 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +export type InferredSelection = { text: string; range: [number, number] }; +export type AstCandidateRange = { + startOffset: number; + endOffset: number; + nodeType?: string; +}; +export type InferenceAstContext = { + candidates: AstCandidateRange[]; + languageId?: string; + source?: 'service' | 'bundled'; +}; + +const isInformativeLine = (line: string): boolean => { + const s = line.trim(); + if (s.length < 8) return false; + if (/^\s*(\/\/|#|'|\*\s)/.test(line)) return false; // comments + if (/^[{}()[\];,:]*$/.test(s)) return false; // only punctuation + return /[A-Za-z0-9]/.test(s); +}; + +const normalizeLine = (line: string): string => line.trim().toLowerCase(); + +const buildLineMatchPrefix = (lines: string[], codeSet: Set): number[] => { + const prefix = new Array(lines.length + 1); + prefix[0] = 0; + + for (let i = 0; i < lines.length; i += 1) { + prefix[i + 1] = prefix[i] + (codeSet.has(normalizeLine(lines[i])) ? 1 : 0); + } + + return prefix; +}; + +const countLineMatchesInRange = (prefix: number[], startLine: number, endLine: number): number => { + const startIdx = Math.max(0, startLine - 1); + const endIdxExclusive = Math.min(prefix.length - 1, endLine); + if (endIdxExclusive <= startIdx) return 0; + return prefix[endIdxExclusive] - prefix[startIdx]; +}; + +const pickBestAstCandidate = ({ + codeStr, + fileText, + astContext +}: { + codeStr: string; + fileText: string; + astContext?: InferenceAstContext; +}): { startOffset: number; endOffset: number; score: number; overlap: number; anchorHits: number } | null => { + const candidates = astContext?.candidates; + if (!candidates || candidates.length === 0) return null; + + const codeLinesRaw = codeStr.split('\n'); + const codeSet = new Set(codeLinesRaw.map(normalizeLine).filter(s => s.length >= 4)); + const anchors = codeLinesRaw + .map(l => l.trim()) + .filter(isInformativeLine) + .sort((a, b) => b.length - a.length) + .slice(0, 8); + const lowerAnchors = anchors.map(anchor => anchor.toLowerCase()); + + let best: { startOffset: number; endOffset: number; score: number; overlap: number; anchorHits: number } | null = null; + const maxCandidates = Math.min(1500, candidates.length); + + for (let i = 0; i < maxCandidates; i += 1) { + const rawStart = Math.max(0, Math.min(fileText.length, Math.floor(candidates[i].startOffset))); + const rawEnd = Math.max(0, Math.min(fileText.length, Math.floor(candidates[i].endOffset))); + const startOffset = Math.min(rawStart, rawEnd); + const endOffset = Math.max(rawStart, rawEnd); + if (endOffset - startOffset < 8) continue; + + const candidateText = fileText.slice(startOffset, endOffset); + const candidateLines = candidateText.split('\n'); + + let overlap = 0; + for (const ln of candidateLines) { + if (codeSet.has(normalizeLine(ln))) overlap += 1; + } + + const candidateTextLower = candidateText.toLowerCase(); + let anchorHits = 0; + for (const lowerAnchor of lowerAnchors) { + if (candidateTextLower.includes(lowerAnchor)) anchorHits += 1; + } + + const expectedLines = Math.max(1, codeLinesRaw.length); + const lineSpan = Math.max(1, candidateLines.length); + const sizePenalty = lineSpan > expectedLines * 10 + ? Math.min(6, Math.floor(lineSpan / Math.max(1, expectedLines * 3))) + : 0; + + const score = overlap * 5 + anchorHits * 9 - sizePenalty; + if (!best || score > best.score) { + best = { startOffset, endOffset, score, overlap, anchorHits }; + } + } + + if (!best) return null; + + const minOverlap = Math.max(1, Math.floor(codeLinesRaw.length * 0.12)); + if (best.overlap < minOverlap && best.anchorHits === 0) return null; + if (best.score < 4) return null; + + return best; +}; + +export const inferSelectionFromCode = ({ + codeStr, + fileText, + astContext +}: { + codeStr: string; + fileText: string; + astContext?: InferenceAstContext; +}): InferredSelection | null => { + if (!codeStr || !fileText) return null; + + const astBest = pickBestAstCandidate({ codeStr, fileText, astContext }); + if (astBest) { + const fileLineOffsets = buildLineOffsets(fileText); + const startLine = charIdxToLine(astBest.startOffset, fileLineOffsets); + const endLine = charIdxToLine(astBest.endOffset - 1, fileLineOffsets); + const text = fileText.slice(astBest.startOffset, astBest.endOffset); + return { text, range: [startLine, endLine] }; + } + + const fileLines = fileText.split('\n'); + const codeLinesRaw = codeStr.split('\n'); + const codeLinesNorm = codeLinesRaw.map(normalizeLine); + + const anchors: { text: string; codeIdx: number }[] = []; + for (let i = 0; i < codeLinesRaw.length; i += 1) { + const ln = codeLinesRaw[i]; + if (!isInformativeLine(ln)) continue; + anchors.push({ text: ln.trim(), codeIdx: i }); + } + // prefer longer anchors; cap to 5 + anchors.sort((a, b) => b.text.length - a.text.length); + const topAnchors = anchors.slice(0, 5); + if (topAnchors.length === 0) return null; + + const fileLineOffsets = buildLineOffsets(fileText); + + const codeSet = new Set(codeLinesNorm.filter(s => s.length >= 5)); + const lineMatchPrefix = buildLineMatchPrefix(fileLines, codeSet); + + let best: { start: number; end: number; score: number } | null = null; + + for (const a of topAnchors) { + const anchor = a.text; + let fromIdx = 0; + while (fromIdx <= fileText.length) { + const hit = fileText.indexOf(anchor, fromIdx); + if (hit === -1) break; + const hitLine = charIdxToLine(hit, fileLineOffsets); // 1-indexed + + // align window by anchor's position in code + const startLine = Math.max(1, hitLine - a.codeIdx); + const endLine = Math.min(fileLines.length, startLine + codeLinesRaw.length - 1); + + // score overlap in O(1) using prefix sums + const score = countLineMatchesInRange(lineMatchPrefix, startLine, endLine); + + if (!best || score > best.score) { + best = { start: startLine, end: endLine, score }; + } + + fromIdx = hit + Math.max(1, Math.floor(anchor.length / 2)); + } + } + + if (!best) return null; + + // require minimal confidence: at least 2 overlapping lines or 15% of code lines + const minOverlap = Math.max(2, Math.floor(codeLinesRaw.length * 0.15)); + if (best.score < minOverlap) return null; + + const text = fileLines.slice(best.start - 1, best.end).join('\n'); + return { text, range: [best.start, best.end] }; +}; + +export type InferredBlock = { + text: string + range: [number, number] + offsets: [number, number] // [startOffset, endOffsetExclusive] + occurrence: number // 1-based +} + +const buildLineOffsets = (text: string): number[] => { + const offs: number[] = [0] + for (let i = 0; i < text.length; i++) { + if (text.charCodeAt(i) === 10 /* \n */) offs.push(i + 1) + } + return offs +} + +const charIdxToLine = (charIdx: number, lineOffsets: number[]): number => { + let lo = 0, hi = lineOffsets.length - 1 + while (lo <= hi) { + const mid = (lo + hi) >> 1 + if (lineOffsets[mid] <= charIdx) lo = mid + 1; else hi = mid - 1 + } + return Math.max(1, Math.min(lineOffsets.length, hi + 1)) +} + +const countOccurrencesBefore = (haystack: string, needle: string, endOffsetExclusive: number): number => { + if (!needle) return 0 + let count = 0 + let idx = 0 + while (idx < endOffsetExclusive) { + const hit = haystack.indexOf(needle, idx) + if (hit === -1 || hit >= endOffsetExclusive) break + count++ + idx = hit + Math.max(1, Math.floor(needle.length / 2)) + } + return count +} + +export const inferExactBlockFromCode = ({ + codeStr, + fileText, + astContext +}: { + codeStr: string; + fileText: string; + astContext?: InferenceAstContext; +}): InferredBlock | null => { + if (!codeStr || !fileText) return null; + + const fileLF = fileText.replace(/\r\n/g, '\n'); + + const astBest = pickBestAstCandidate({ codeStr, fileText, astContext }); + if (astBest) { + const text = fileText.substring(astBest.startOffset, astBest.endOffset); + const lineOffsets = buildLineOffsets(fileLF); + const startLine = charIdxToLine(astBest.startOffset, lineOffsets); + const endLine = charIdxToLine(astBest.endOffset - 1, lineOffsets); + const occurrence = countOccurrencesBefore(fileText, text, astBest.startOffset) + 1; + return { + text, + range: [startLine, endLine], + offsets: [astBest.startOffset, astBest.endOffset], + occurrence + }; + } + + + const codeLines = codeStr.split('\n'); + let anchor = ''; + for (const line of codeLines) { + const t = line.trim(); + if (t.length < 5) continue; + const patterns = [ + /^(function|class|const|let|var|export|async|interface|type|enum|namespace|declare|abstract)\s/, + /^(public|private|protected|static|readonly|override)\s/, + /^(def|class|async def|@\w+)[\s(]/, + /^(public|private|protected|internal|static|final|abstract|sealed|virtual|override|partial)\s/, + /^(class|interface|enum|struct|record)\s/, + /^(void|int|char|float|double|bool|auto|const|static|extern|inline|virtual|template|typename)\s/, + /^(class|struct|enum|union|namespace|using)\s/, + /^(func|type|interface|struct|package|var|const)\s/, + /^(fn|pub|impl|trait|struct|enum|mod|use|const|static|async|unsafe|extern)\s/, + /^(def|class|module|begin|if|unless|case|while|until|for)\s/, + /^(function|class|interface|trait|namespace|use|public|private|protected|static|abstract|final)\s/, + /^<\?php/, + /^(func|class|struct|enum|protocol|extension|var|let|init|deinit|typealias)\s/, + /^(public|private|internal|fileprivate|open|static|final|lazy|weak|unowned)\s/, + /^(fun|class|interface|object|enum|data class|sealed class|companion object|val|var)\s/, + /^(public|private|protected|internal|override|abstract|final|open|lateinit|inline)\s/, + /^(def|class|object|trait|case class|sealed|abstract|override|implicit|lazy)\s/, + + /^\w+\s*(<[^>]+>)?\s*\([^)]*\)\s*\{/, + + /^\w+\s*(<[^>]+>)?\s*=\s*(?:\([^)]*\)|[A-Za-z0-9_$]+)\s*=>/ + ]; + if (patterns.some(rx => rx.test(t)) || t.length > 10) { anchor = t; break; } + } + if (!anchor) { + anchor = codeLines.find(l => { + const t = l.trim(); + return t.length > 5 && !/^(\/\/|#|\*|\/\*)/.test(t); + })?.trim() || ''; + } + if (!anchor) return null; + + + const startIdx = fileText.indexOf(anchor); + if (startIdx === -1) return null; + + let startOffset = startIdx; + let endOffsetExclusive = startIdx; + + + const anchorEnd = startIdx + anchor.length; + const afterAnchorRaw = fileText.slice(anchorEnd); + const anchorHasBrace = anchor.includes('{'); + const nextNonWsIsBrace = /^\s*\{/.test(afterAnchorRaw); + const arrowNear = anchor.includes('=>') || /^\s*=>/.test(afterAnchorRaw); + const usesArrowWithoutBrace = arrowNear && !anchorHasBrace && !nextNonWsIsBrace; + + if (usesArrowWithoutBrace) { + + const scanFrom = anchorEnd; + const semi = fileText.indexOf(';', scanFrom); + const nl = fileText.indexOf('\n', scanFrom); + if (semi !== -1 && (nl === -1 || semi < nl)) endOffsetExclusive = semi + 1; + else if (nl !== -1) endOffsetExclusive = nl; + else endOffsetExclusive = fileText.length; + } else { + + let inS = false, inD = false, inT = false, inSL = false, inML = false; + let openPos = -1; + for (let pos = startIdx; pos < fileText.length; pos++) { + const c = fileText[pos]; + const next = pos + 1 < fileText.length ? fileText[pos + 1] : ''; + + if (!inS && !inD && !inT) { + if (!inML && !inSL && c === '/' && next === '/') { inSL = true; pos++; continue; } + if (!inML && !inSL && c === '/' && next === '*') { inML = true; pos++; continue; } + if (inSL && c === '\n') { inSL = false; continue; } + if (inML && c === '*' && next === '/') { inML = false; pos++; continue; } + if (inSL || inML) continue; + } + if (!inML && !inSL) { + if (!inD && !inT && c === '\'') { inS = !inS; continue; } + if (!inS && !inT && c === '"') { inD = !inD; continue; } + if (!inS && !inD && c === '`') { inT = !inT; continue; } + } + if (inS || inD || inT) continue; + + if (c === '{') { openPos = pos; break; } + } + if (openPos === -1) return null; + + + let depth = 0; + inS = inD = inT = inSL = inML = false; + for (let pos = openPos; pos < fileText.length; pos++) { + const c = fileText[pos]; + const next = pos + 1 < fileText.length ? fileText[pos + 1] : ''; + + if (!inS && !inD && !inT) { + if (!inML && !inSL && c === '/' && next === '/') { inSL = true; pos++; continue; } + if (!inML && !inSL && c === '/' && next === '*') { inML = true; pos++; continue; } + if (inSL && c === '\n') { inSL = false; continue; } + if (inML && c === '*' && next === '/') { inML = false; pos++; continue; } + if (inSL || inML) continue; + } + if (!inML && !inSL) { + if (!inD && !inT && c === '\'') { inS = !inS; continue; } + if (!inS && !inT && c === '"') { inD = !inD; continue; } + if (!inS && !inD && c === '`') { inT = !inT; continue; } + } + if (inS || inD || inT) continue; + + if (c === '{') { + if (depth === 0) startOffset = startIdx; + depth++; + } else if (c === '}') { + depth--; + if (depth === 0) { endOffsetExclusive = pos + 1; break; } + } + } + if (endOffsetExclusive <= startOffset) return null; + } + + const text = fileText.substring(startOffset, endOffsetExclusive); + + const lineOffsets = buildLineOffsets(fileLF); + const startLine = charIdxToLine(startOffset, lineOffsets); + const endLine = charIdxToLine(endOffsetExclusive - 1, lineOffsets); + + const occurrence = countOccurrencesBefore(fileText, text, startOffset) + 1; + + return { text, range: [startLine, endLine], offsets: [startOffset, endOffsetExclusive], occurrence }; +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx index 30762d89d0c..69696277bc7 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/quick-edit-tsx/QuickEditChat.tsx @@ -3,16 +3,14 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { useCallback, useEffect, useRef, useState } from 'react'; import { useSettingsState, useAccessor, useCtrlKZoneStreamingState } from '../util/services.js'; import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js'; import { QuickEditPropsType } from '../../../quickEditActions.js'; -import { ButtonStop, ButtonSubmit, IconX, VoidChatArea } from '../sidebar-tsx/SidebarChat.js'; +import { VoidChatArea } from '../sidebar-tsx/SidebarChatUI.js'; import { VOID_CTRL_K_ACTION_ID } from '../../../actionIDs.js'; import { useRefState } from '../util/helpers.js'; -import { isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'; - - +import { isFeatureNameDisabled } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; export const QuickEditChat = ({ @@ -133,6 +131,4 @@ export const QuickEditChat = ({ />
- - } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx index 9e882240dfa..32fcaee8fad 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorBoundary.tsx @@ -18,7 +18,7 @@ interface State { errorInfo: ErrorInfo | null; } -class ErrorBoundary extends Component { +export class ErrorBoundary extends Component { constructor(props: Props) { super(props); this.state = { @@ -28,38 +28,20 @@ class ErrorBoundary extends Component { }; } - static getDerivedStateFromError(error: Error): Partial { - return { - hasError: true, - error - }; - } - - componentDidCatch(error: Error, errorInfo: ErrorInfo): void { + override componentDidCatch(error: Error, errorInfo: ErrorInfo): void { this.setState({ error, errorInfo }); } - render(): ReactNode { + override render(): ReactNode { if (this.state.hasError && this.state.error) { - // If a custom fallback is provided, use it if (this.props.fallback) { return this.props.fallback; } - - // Use ErrorDisplay component as the default error UI - return ( - - // - ); + return ; } - return this.props.children; } } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx index e8aec93777d..50aac396a62 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/ErrorDisplay.tsx @@ -3,10 +3,9 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { useEffect, useState } from 'react'; +import { useState } from 'react'; import { AlertCircle, ChevronDown, ChevronUp, X } from 'lucide-react'; -import { useSettingsState } from '../util/services.js'; -import { errorDetails } from '../../../../common/sendLLMMessageTypes.js'; +import { errorDetails } from '../../../../../../../platform/void/common/sendLLMMessageTypes.js'; export const ErrorDisplay = ({ diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx index 44df32b0ac1..b9d0f9a75c1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/Sidebar.tsx @@ -2,11 +2,7 @@ * Copyright 2025 Glass Devtools, Inc. All rights reserved. * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ - import { useIsDark } from '../util/services.js'; -// import { SidebarThreadSelector } from './SidebarThreadSelector.js'; -// import { SidebarChat } from './SidebarChat.js'; - import '../styles.css' import { SidebarChat } from './SidebarChat.js'; import ErrorBoundary from './ErrorBoundary.js'; @@ -19,14 +15,12 @@ export const Sidebar = ({ className }: { className: string }) => { style={{ width: '100%', height: '100%' }} >
-
@@ -35,7 +29,5 @@ export const Sidebar = ({ className }: { className: string }) => {
- - } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx index 97add942891..5b849407b55 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChat.tsx @@ -3,2882 +3,190 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { ButtonHTMLAttributes, FormEvent, FormHTMLAttributes, Fragment, KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { Fragment, KeyboardEvent, useCallback, useLayoutEffect, useEffect, useMemo, useRef, useState } from 'react'; +import { useAccessor, useChatThreadsState, useChatThreadsStreamState, useSettingsState, } from '../util/services.js'; - -import { useAccessor, useChatThreadsState, useChatThreadsStreamState, useSettingsState, useActiveURI, useCommandBarState, useFullChatThreadsStreamState } from '../util/services.js'; -import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'; - -import { ChatMarkdownRender, ChatMessageLocation, getApplyBoxId } from '../markdown/ChatMarkdownRender.js'; import { URI } from '../../../../../../../base/common/uri.js'; -import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; -import { ErrorDisplay } from './ErrorDisplay.js'; -import { BlockCode, TextAreaFns, VoidCustomDropdownBox, VoidInputBox2, VoidSlider, VoidSwitch, VoidDiffEditor } from '../util/inputs.js'; -import { ModelDropdown, } from '../void-settings-tsx/ModelDropdown.js'; -import { PastThreadsList } from './SidebarThreadSelector.js'; -import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js'; -import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js'; -import { ChatMode, displayInfoOfProviderName, FeatureName, isFeatureNameDisabled } from '../../../../../../../workbench/contrib/void/common/voidSettingsTypes.js'; -import { ICommandService } from '../../../../../../../platform/commands/common/commands.js'; -import { WarningBox } from '../void-settings-tsx/WarningBox.js'; -import { getModelCapabilities, getIsReasoningEnabledState } from '../../../../common/modelCapabilities.js'; -import { AlertTriangle, File, Ban, Check, ChevronRight, Dot, FileIcon, Pencil, Undo, Undo2, X, Flag, Copy as CopyIcon, Info, CirclePlus, Ellipsis, CircleEllipsis, Folder, ALargeSmall, TypeOutline, Text } from 'lucide-react'; -import { ChatMessage, CheckpointEntry, StagingSelectionItem, ToolMessage } from '../../../../common/chatThreadServiceTypes.js'; -import { approvalTypeOfBuiltinToolName, BuiltinToolCallParams, BuiltinToolName, ToolName, LintErrorItem, ToolApprovalType, toolApprovalTypes } from '../../../../common/toolsServiceTypes.js'; -import { CopyButton, EditToolAcceptRejectButtonsHTML, IconShell1, JumpToFileButton, JumpToTerminalButton, StatusIndicator, StatusIndicatorForApplyButton, useApplyStreamState, useEditToolStreamState } from '../markdown/ApplyBlockHoverButtons.js'; -import { IsRunningType } from '../../../chatThreadService.js'; -import { acceptAllBg, acceptBorder, buttonFontSize, buttonTextColor, rejectAllBg, rejectBg, rejectBorder } from '../../../../common/helpers/colors.js'; -import { builtinToolNames, isABuiltinToolName, MAX_FILE_CHARS_PAGE, MAX_TERMINAL_INACTIVE_TIME } from '../../../../common/prompt/prompts.js'; -import { RawToolCallObj } from '../../../../common/sendLLMMessageTypes.js'; -import ErrorBoundary from './ErrorBoundary.js'; -import { ToolApprovalTypeSwitch } from '../void-settings-tsx/Settings.js'; - -import { persistentTerminalNameOfId } from '../../../terminalToolService.js'; -import { removeMCPToolNamePrefix } from '../../../../common/mcpServiceTypes.js'; - - - -export const IconX = ({ size, className = '', ...props }: { size: number, className?: string } & React.SVGProps) => { - return ( - - - - ); -}; - -const IconArrowUp = ({ size, className = '' }: { size: number, className?: string }) => { - return ( - - - - ); -}; - - -const IconSquare = ({ size, className = '' }: { size: number, className?: string }) => { - return ( - - - - ); -}; - - -export const IconWarning = ({ size, className = '' }: { size: number, className?: string }) => { - return ( - - - - ); -}; - - -export const IconLoading = ({ className = '' }: { className?: string }) => { - - const [loadingText, setLoadingText] = useState('.'); - - useEffect(() => { - let intervalId; - - // Function to handle the animation - const toggleLoadingText = () => { - if (loadingText === '...') { - setLoadingText('.'); - } else { - setLoadingText(loadingText + '.'); - } - }; - - // Start the animation loop - intervalId = setInterval(toggleLoadingText, 300); - - // Cleanup function to clear the interval when component unmounts - return () => clearInterval(intervalId); - }, [loadingText, setLoadingText]); - - return
{loadingText}
; - -} - - - -// SLIDER ONLY: -const ReasoningOptionSlider = ({ featureName }: { featureName: FeatureName }) => { - const accessor = useAccessor() - - const voidSettingsService = accessor.get('IVoidSettingsService') - const voidSettingsState = useSettingsState() - - const modelSelection = voidSettingsState.modelSelectionOfFeature[featureName] - const overridesOfModel = voidSettingsState.overridesOfModel - - if (!modelSelection) return null - - const { modelName, providerName } = modelSelection - const { reasoningCapabilities } = getModelCapabilities(providerName, modelName, overridesOfModel) - const { canTurnOffReasoning, reasoningSlider: reasoningBudgetSlider } = reasoningCapabilities || {} - - const modelSelectionOptions = voidSettingsState.optionsOfModelSelection[featureName][providerName]?.[modelName] - const isReasoningEnabled = getIsReasoningEnabledState(featureName, providerName, modelName, modelSelectionOptions, overridesOfModel) - - if (canTurnOffReasoning && !reasoningBudgetSlider) { // if it's just a on/off toggle without a power slider - return
- Thinking - { - const isOff = canTurnOffReasoning && !newVal - voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff }) - }} - /> -
- } - - if (reasoningBudgetSlider?.type === 'budget_slider') { // if it's a slider - const { min: min_, max, default: defaultVal } = reasoningBudgetSlider - - const nSteps = 8 // only used in calculating stepSize, stepSize is what actually matters - const stepSize = Math.round((max - min_) / nSteps) - - const valueIfOff = min_ - stepSize - const min = canTurnOffReasoning ? valueIfOff : min_ - const value = isReasoningEnabled ? voidSettingsState.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]?.reasoningBudget ?? defaultVal - : valueIfOff - - return
- Thinking - { - const isOff = canTurnOffReasoning && newVal === valueIfOff - voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff, reasoningBudget: newVal }) - }} - /> - {isReasoningEnabled ? `${value} tokens` : 'Thinking disabled'} -
- } - - if (reasoningBudgetSlider?.type === 'effort_slider') { - - const { values, default: defaultVal } = reasoningBudgetSlider - - const min = canTurnOffReasoning ? -1 : 0 - const max = values.length - 1 - - const currentEffort = voidSettingsState.optionsOfModelSelection[featureName][modelSelection.providerName]?.[modelSelection.modelName]?.reasoningEffort ?? defaultVal - const valueIfOff = -1 - const value = isReasoningEnabled && currentEffort ? values.indexOf(currentEffort) : valueIfOff - - const currentEffortCapitalized = currentEffort.charAt(0).toUpperCase() + currentEffort.slice(1, Infinity) - - return
- Thinking - { - const isOff = canTurnOffReasoning && newVal === valueIfOff - voidSettingsService.setOptionsOfModelSelection(featureName, modelSelection.providerName, modelSelection.modelName, { reasoningEnabled: !isOff, reasoningEffort: values[newVal] ?? undefined }) - }} - /> - {isReasoningEnabled ? `${currentEffortCapitalized}` : 'Thinking disabled'} -
- } - - return null -} - - - -const nameOfChatMode = { - 'normal': 'Chat', - 'gather': 'Gather', - 'agent': 'Agent', -} - -const detailOfChatMode = { - 'normal': 'Normal chat', - 'gather': 'Reads files, but can\'t edit', - 'agent': 'Edits files and uses tools', -} - - -const ChatModeDropdown = ({ className }: { className: string }) => { - const accessor = useAccessor() - - const voidSettingsService = accessor.get('IVoidSettingsService') - const settingsState = useSettingsState() - - const options: ChatMode[] = useMemo(() => ['normal', 'gather', 'agent'], []) - - const onChangeOption = useCallback((newVal: ChatMode) => { - voidSettingsService.setGlobalSetting('chatMode', newVal) - }, [voidSettingsService]) - - return nameOfChatMode[val]} - getOptionDropdownName={(val) => nameOfChatMode[val]} - getOptionDropdownDetail={(val) => detailOfChatMode[val]} - getOptionsEqual={(a, b) => a === b} - /> - -} - - - - - -interface VoidChatAreaProps { - // Required - children: React.ReactNode; // This will be the input component - - // Form controls - onSubmit: () => void; - onAbort: () => void; - isStreaming: boolean; - isDisabled?: boolean; - divRef?: React.RefObject; - - // UI customization - className?: string; - showModelDropdown?: boolean; - showSelections?: boolean; - showProspectiveSelections?: boolean; - loadingIcon?: React.ReactNode; - - selections?: StagingSelectionItem[] - setSelections?: (s: StagingSelectionItem[]) => void - // selections?: any[]; - // onSelectionsChange?: (selections: any[]) => void; - - onClickAnywhere?: () => void; - // Optional close button - onClose?: () => void; - - featureName: FeatureName; -} - -export const VoidChatArea: React.FC = ({ - children, - onSubmit, - onAbort, - onClose, - onClickAnywhere, - divRef, - isStreaming = false, - isDisabled = false, - className = '', - showModelDropdown = true, - showSelections = false, - showProspectiveSelections = false, - selections, - setSelections, - featureName, - loadingIcon, -}) => { - return ( -
{ - onClickAnywhere?.() - }} - > - {/* Selections section */} - {showSelections && selections && setSelections && ( - - )} - - {/* Input section */} -
- {children} - - {/* Close button (X) if onClose is provided */} - {onClose && ( -
- -
- )} -
- - {/* Bottom row */} -
- {showModelDropdown && ( -
- - -
- {featureName === 'Chat' && } - -
-
- )} - -
- - {isStreaming && loadingIcon} - - {isStreaming ? ( - - ) : ( - - )} -
- -
-
- ); -}; - - - - -type ButtonProps = ButtonHTMLAttributes -const DEFAULT_BUTTON_SIZE = 22; -export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Required>) => { - - return -} - -export const ButtonStop = ({ className, ...props }: ButtonHTMLAttributes) => { - return -} - - - -const scrollToBottom = (divRef: { current: HTMLElement | null }) => { - if (divRef.current) { - divRef.current.scrollTop = divRef.current.scrollHeight; - } -}; - - - -const ScrollToBottomContainer = ({ children, className, style, scrollContainerRef }: { children: React.ReactNode, className?: string, style?: React.CSSProperties, scrollContainerRef: React.MutableRefObject }) => { - const [isAtBottom, setIsAtBottom] = useState(true); // Start at bottom - - const divRef = scrollContainerRef - - const onScroll = () => { - const div = divRef.current; - if (!div) return; - - const isBottom = Math.abs( - div.scrollHeight - div.clientHeight - div.scrollTop - ) < 4; - - setIsAtBottom(isBottom); - }; - - // When children change (new messages added) - useEffect(() => { - if (isAtBottom) { - scrollToBottom(divRef); - } - }, [children, isAtBottom]); // Dependency on children to detect new messages - - // Initial scroll to bottom - useEffect(() => { - scrollToBottom(divRef); - }, []); - - return ( -
- {children} -
- ); -}; - -export const getRelative = (uri: URI, accessor: ReturnType) => { - const workspaceContextService = accessor.get('IWorkspaceContextService') - let path: string - const isInside = workspaceContextService.isInsideWorkspace(uri) - if (isInside) { - const f = workspaceContextService.getWorkspace().folders.find(f => uri.fsPath?.startsWith(f.uri.fsPath)) - if (f) { path = uri.fsPath.replace(f.uri.fsPath, '') } - else { path = uri.fsPath } - } - else { - path = uri.fsPath - } - return path || undefined -} - -export const getFolderName = (pathStr: string) => { - // 'unixify' path - pathStr = pathStr.replace(/[/\\]+/g, '/') // replace any / or \ or \\ with / - const parts = pathStr.split('/') // split on / - // Filter out empty parts (the last element will be empty if path ends with /) - const nonEmptyParts = parts.filter(part => part.length > 0) - if (nonEmptyParts.length === 0) return '/' // Root directory - if (nonEmptyParts.length === 1) return nonEmptyParts[0] + '/' // Only one folder - // Get the last two parts - const lastTwo = nonEmptyParts.slice(-2) - return lastTwo.join('/') + '/' -} - -export const getBasename = (pathStr: string, parts: number = 1) => { - // 'unixify' path - pathStr = pathStr.replace(/[/\\]+/g, '/') // replace any / or \ or \\ with / - const allParts = pathStr.split('/') // split on / - if (allParts.length === 0) return pathStr - return allParts.slice(-parts).join('/') -} - - - -// Open file utility function -export const voidOpenFileFn = ( - uri: URI, - accessor: ReturnType, - range?: [number, number] -) => { - const commandService = accessor.get('ICommandService') - const editorService = accessor.get('ICodeEditorService') - - // Get editor selection from CodeSelection range - let editorSelection = undefined; - - // If we have a selection, create an editor selection from the range - if (range) { - editorSelection = { - startLineNumber: range[0], - startColumn: 1, - endLineNumber: range[1], - endColumn: Number.MAX_SAFE_INTEGER, - }; - } - - // open the file - commandService.executeCommand('vscode.open', uri).then(() => { - - // select the text - setTimeout(() => { - if (!editorSelection) return; - - const editor = editorService.getActiveCodeEditor() - if (!editor) return; - - editor.setSelection(editorSelection) - editor.revealRange(editorSelection, ScrollType.Immediate) - - }, 50) // needed when document was just opened and needs to initialize - - }) - -}; - - -export const SelectedFiles = ( - { type, selections, setSelections, showProspectiveSelections, messageIdx, }: - | { type: 'past', selections: StagingSelectionItem[]; setSelections?: undefined, showProspectiveSelections?: undefined, messageIdx: number, } - | { type: 'staging', selections: StagingSelectionItem[]; setSelections: ((newSelections: StagingSelectionItem[]) => void), showProspectiveSelections?: boolean, messageIdx?: number } -) => { - - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const modelReferenceService = accessor.get('IVoidModelService') - - - - - // state for tracking prospective files - const { uri: currentURI } = useActiveURI() - const [recentUris, setRecentUris] = useState([]) - const maxRecentUris = 10 - const maxProspectiveFiles = 3 - useEffect(() => { // handle recent files - if (!currentURI) return - setRecentUris(prev => { - const withoutCurrent = prev.filter(uri => uri.fsPath !== currentURI.fsPath) // remove duplicates - const withCurrent = [currentURI, ...withoutCurrent] - return withCurrent.slice(0, maxRecentUris) - }) - }, [currentURI]) - const [prospectiveSelections, setProspectiveSelections] = useState([]) - - - // handle prospective files - useEffect(() => { - const computeRecents = async () => { - const prospectiveURIs = recentUris - .filter(uri => !selections.find(s => s.type === 'File' && s.uri.fsPath === uri.fsPath)) - .slice(0, maxProspectiveFiles) - - const answer: StagingSelectionItem[] = [] - for (const uri of prospectiveURIs) { - answer.push({ - type: 'File', - uri: uri, - language: (await modelReferenceService.getModelSafe(uri)).model?.getLanguageId() || 'plaintext', - state: { wasAddedAsCurrentFile: false }, - }) - } - return answer - } - - // add a prospective file if type === 'staging' and if the user is in a file, and if the file is not selected yet - if (type === 'staging' && showProspectiveSelections) { - computeRecents().then((a) => setProspectiveSelections(a)) - } - else { - setProspectiveSelections([]) - } - }, [recentUris, selections, type, showProspectiveSelections]) - - - const allSelections = [...selections, ...prospectiveSelections] - - if (allSelections.length === 0) { - return null - } - - return ( -
- - {allSelections.map((selection, i) => { - - const isThisSelectionProspective = i > selections.length - 1 - - const thisKey = selection.type === 'CodeSelection' ? selection.type + selection.language + selection.range + selection.state.wasAddedAsCurrentFile + selection.uri.fsPath - : selection.type === 'File' ? selection.type + selection.language + selection.state.wasAddedAsCurrentFile + selection.uri.fsPath - : selection.type === 'Folder' ? selection.type + selection.language + selection.state + selection.uri.fsPath - : i - - const SelectionIcon = ( - selection.type === 'File' ? File - : selection.type === 'Folder' ? Folder - : selection.type === 'CodeSelection' ? Text - : (undefined as never) - ) - - return
- {/* tooltip for file path */} - - {/* summarybox */} -
{ - if (type !== 'staging') return; // (never) - if (isThisSelectionProspective) { // add prospective selection to selections - setSelections([...selections, selection]) - } - else if (selection.type === 'File') { // open files - voidOpenFileFn(selection.uri, accessor); - - const wasAddedAsCurrentFile = selection.state.wasAddedAsCurrentFile - if (wasAddedAsCurrentFile) { - // make it so the file is added permanently, not just as the current file - const newSelection: StagingSelectionItem = { ...selection, state: { ...selection.state, wasAddedAsCurrentFile: false } } - setSelections([ - ...selections.slice(0, i), - newSelection, - ...selections.slice(i + 1) - ]) - } - } - else if (selection.type === 'CodeSelection') { - voidOpenFileFn(selection.uri, accessor, selection.range); - } - else if (selection.type === 'Folder') { - // TODO!!! reveal in tree - } - }} - > - {} - - { // file name and range - getBasename(selection.uri.fsPath) - + (selection.type === 'CodeSelection' ? ` (${selection.range[0]}-${selection.range[1]})` : '') - } - - {selection.type === 'File' && selection.state.wasAddedAsCurrentFile && messageIdx === undefined && currentURI?.fsPath === selection.uri.fsPath ? - - {`(Current File)`} - - : null - } - - {type === 'staging' && !isThisSelectionProspective ? // X button -
{ - e.stopPropagation(); // don't open/close selection - if (type !== 'staging') return; - setSelections([...selections.slice(0, i), ...selections.slice(i + 1)]) - }} - > - -
- : <> - } -
-
-
- - })} - - -
- - ) -} - - -type ToolHeaderParams = { - icon?: React.ReactNode; - title: React.ReactNode; - desc1: React.ReactNode; - desc1OnClick?: () => void; - desc2?: React.ReactNode; - isError?: boolean; - info?: string; - desc1Info?: string; - isRejected?: boolean; - numResults?: number; - hasNextPage?: boolean; - children?: React.ReactNode; - bottomChildren?: React.ReactNode; - onClick?: () => void; - desc2OnClick?: () => void; - isOpen?: boolean; - className?: string; -} - -const ToolHeaderWrapper = ({ - icon, - title, - desc1, - desc1OnClick, - desc1Info, - desc2, - numResults, - hasNextPage, - children, - info, - bottomChildren, - isError, - onClick, - desc2OnClick, - isOpen, - isRejected, - className, // applies to the main content -}: ToolHeaderParams) => { - - const [isOpen_, setIsOpen] = useState(false); - const isExpanded = isOpen !== undefined ? isOpen : isOpen_ - - const isDropdown = children !== undefined // null ALLOWS dropdown - const isClickable = !!(isDropdown || onClick) - - const isDesc1Clickable = !!desc1OnClick - - const desc1HTML = {desc1} - - return (
-
- {/* header */} -
-
- {/* left */} -
- {/* title eg "> Edited File" */} -
{ - if (isDropdown) { setIsOpen(v => !v); } - if (onClick) { onClick(); } - }} - > - {isDropdown && ()} - {title} - - {!isDesc1Clickable && desc1HTML} -
- {isDesc1Clickable && desc1HTML} -
- - {/* right */} -
- - {info && } - - {isError && } - {isRejected && } - {desc2 && - {desc2} - } - {numResults !== undefined && ( - - {`${numResults}${hasNextPage ? '+' : ''} result${numResults !== 1 ? 's' : ''}`} - - )} -
-
-
- {/* children */} - {
- {children} -
} -
- {bottomChildren} -
); -}; - - - -const EditTool = ({ toolMessage, threadId, messageIdx, content }: Parameters>[0] & { content: string }) => { - const accessor = useAccessor() - const isError = false - const isRejected = toolMessage.type === 'rejected' - - const title = getTitle(toolMessage) - - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - const { rawParams, params, name } = toolMessage - const desc1OnClick = () => voidOpenFileFn(params.uri, accessor) - const componentParams: ToolHeaderParams = { title, desc1, desc1OnClick, desc1Info, isError, icon, isRejected, } - - - const editToolType = toolMessage.name === 'edit_file' ? 'diff' : 'rewrite' - if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { - componentParams.children = - - - // JumpToFileButton removed in favor of FileLinkText - } - else if (toolMessage.type === 'success' || toolMessage.type === 'rejected' || toolMessage.type === 'tool_error') { - // add apply box - const applyBoxId = getApplyBoxId({ - threadId: threadId, - messageIdx: messageIdx, - tokenIdx: 'N/A', - }) - componentParams.desc2 = - - // add children - componentParams.children = - - - - if (toolMessage.type === 'success' || toolMessage.type === 'rejected') { - const { result } = toolMessage - componentParams.bottomChildren = - {result?.lintErrors?.map((error, i) => ( -
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
- ))} -
- } - else if (toolMessage.type === 'tool_error') { - // error - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - } - - return -} - -const SimplifiedToolHeader = ({ - title, - children, -}: { - title: string; - children?: React.ReactNode; -}) => { - const [isOpen, setIsOpen] = useState(false); - const isDropdown = children !== undefined; - return ( -
-
- {/* header */} -
{ - if (isDropdown) { setIsOpen(v => !v); } - }} - > - {isDropdown && ( - - )} -
- {title} -
-
- {/* children */} - {
- {children} -
} -
-
- ); -}; - - - - -const UserMessageComponent = ({ chatMessage, messageIdx, isCheckpointGhost, currCheckpointIdx, _scrollToBottom }: { chatMessage: ChatMessage & { role: 'user' }, messageIdx: number, currCheckpointIdx: number | undefined, isCheckpointGhost: boolean, _scrollToBottom: (() => void) | null }) => { - - const accessor = useAccessor() - const chatThreadsService = accessor.get('IChatThreadService') - - // global state - let isBeingEdited = false - let stagingSelections: StagingSelectionItem[] = [] - let setIsBeingEdited = (_: boolean) => { } - let setStagingSelections = (_: StagingSelectionItem[]) => { } - - if (messageIdx !== undefined) { - const _state = chatThreadsService.getCurrentMessageState(messageIdx) - isBeingEdited = _state.isBeingEdited - stagingSelections = _state.stagingSelections - setIsBeingEdited = (v) => chatThreadsService.setCurrentMessageState(messageIdx, { isBeingEdited: v }) - setStagingSelections = (s) => chatThreadsService.setCurrentMessageState(messageIdx, { stagingSelections: s }) - } - - - // local state - const mode: ChatBubbleMode = isBeingEdited ? 'edit' : 'display' - const [isFocused, setIsFocused] = useState(false) - const [isHovered, setIsHovered] = useState(false) - const [isDisabled, setIsDisabled] = useState(false) - const [textAreaRefState, setTextAreaRef] = useState(null) - const textAreaFnsRef = useRef(null) - // initialize on first render, and when edit was just enabled - const _mustInitialize = useRef(true) - const _justEnabledEdit = useRef(false) - useEffect(() => { - const canInitialize = mode === 'edit' && textAreaRefState - const shouldInitialize = _justEnabledEdit.current || _mustInitialize.current - if (canInitialize && shouldInitialize) { - setStagingSelections( - (chatMessage.selections || []).map(s => { // quick hack so we dont have to do anything more - if (s.type === 'File') return { ...s, state: { ...s.state, wasAddedAsCurrentFile: false, } } - else return s - }) - ) - - if (textAreaFnsRef.current) - textAreaFnsRef.current.setValue(chatMessage.displayContent || '') - - textAreaRefState.focus(); - - _justEnabledEdit.current = false - _mustInitialize.current = false - } - - }, [chatMessage, mode, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current]) - - const onOpenEdit = () => { - setIsBeingEdited(true) - chatThreadsService.setCurrentlyFocusedMessageIdx(messageIdx) - _justEnabledEdit.current = true - } - const onCloseEdit = () => { - setIsFocused(false) - setIsHovered(false) - setIsBeingEdited(false) - chatThreadsService.setCurrentlyFocusedMessageIdx(undefined) - - } - - const EditSymbol = mode === 'display' ? Pencil : X - - - let chatbubbleContents: React.ReactNode - if (mode === 'display') { - chatbubbleContents = <> - - {chatMessage.displayContent} - - } - else if (mode === 'edit') { - - const onSubmit = async () => { - - if (isDisabled) return; - if (!textAreaRefState) return; - if (messageIdx === undefined) return; - - // cancel any streams on this thread - const threadId = chatThreadsService.state.currentThreadId - - await chatThreadsService.abortRunning(threadId) - - // update state - setIsBeingEdited(false) - chatThreadsService.setCurrentlyFocusedMessageIdx(undefined) - - // stream the edit - const userMessage = textAreaRefState.value; - try { - await chatThreadsService.editUserMessageAndStreamResponse({ userMessage, messageIdx, threadId }) - } catch (e) { - console.error('Error while editing message:', e) - } - await chatThreadsService.focusCurrentChat() - requestAnimationFrame(() => _scrollToBottom?.()) - } - - const onAbort = async () => { - const threadId = chatThreadsService.state.currentThreadId - await chatThreadsService.abortRunning(threadId) - } - - const onKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - onCloseEdit() - } - if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { - onSubmit() - } - } - - if (!chatMessage.content) { // don't show if empty and not loading (if loading, want to show). - return null - } - - chatbubbleContents = - setIsDisabled(!text)} - onFocus={() => { - setIsFocused(true) - chatThreadsService.setCurrentlyFocusedMessageIdx(messageIdx); - }} - onBlur={() => { - setIsFocused(false) - }} - onKeyDown={onKeyDown} - fnsRef={textAreaFnsRef} - multiline={true} - /> - - } - - const isMsgAfterCheckpoint = currCheckpointIdx !== undefined && currCheckpointIdx === messageIdx - 1 - - return
setIsHovered(true)} - onMouseLeave={() => setIsHovered(false)} - > -
{ if (mode === 'display') { onOpenEdit() } }} - > - {chatbubbleContents} -
- - - -
- { - if (mode === 'display') { - onOpenEdit() - } else if (mode === 'edit') { - onCloseEdit() - } - }} - /> -
- - -
- -} - -const SmallProseWrapper = ({ children }: { children: React.ReactNode }) => { - return
- {children} -
-} - -const ProseWrapper = ({ children }: { children: React.ReactNode }) => { - return
- {children} -
-} -const AssistantMessageComponent = ({ chatMessage, isCheckpointGhost, isCommitted, messageIdx }: { chatMessage: ChatMessage & { role: 'assistant' }, isCheckpointGhost: boolean, messageIdx: number, isCommitted: boolean }) => { - - const accessor = useAccessor() - const chatThreadsService = accessor.get('IChatThreadService') - - const reasoningStr = chatMessage.reasoning?.trim() || null - const hasReasoning = !!reasoningStr - const isDoneReasoning = !!chatMessage.displayContent - const thread = chatThreadsService.getCurrentThread() - - - const chatMessageLocation: ChatMessageLocation = { - threadId: thread.id, - messageIdx: messageIdx, - } - - const isEmpty = !chatMessage.displayContent && !chatMessage.reasoning - if (isEmpty) return null - - return <> - {/* reasoning token */} - {hasReasoning && -
- - - - - -
- } - - {/* assistant message */} - {chatMessage.displayContent && -
- - - -
- } - - -} - -const ReasoningWrapper = ({ isDoneReasoning, isStreaming, children }: { isDoneReasoning: boolean, isStreaming: boolean, children: React.ReactNode }) => { - const isDone = isDoneReasoning || !isStreaming - const isWriting = !isDone - const [isOpen, setIsOpen] = useState(isWriting) - useEffect(() => { - if (!isWriting) setIsOpen(false) // if just finished reasoning, close - }, [isWriting]) - return : ''} isOpen={isOpen} onClick={() => setIsOpen(v => !v)}> - -
- {children} -
-
-
-} - - - - -// should either be past or "-ing" tense, not present tense. Eg. when the LLM searches for something, the user expects it to say "I searched for X" or "I am searching for X". Not "I search X". - -const loadingTitleWrapper = (item: React.ReactNode): React.ReactNode => { - return - {item} - - -} - -const titleOfBuiltinToolName = { - 'read_file': { done: 'Read file', proposed: 'Read file', running: loadingTitleWrapper('Reading file') }, - 'ls_dir': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') }, - 'get_dir_tree': { done: 'Inspected folder tree', proposed: 'Inspect folder tree', running: loadingTitleWrapper('Inspecting folder tree') }, - 'search_pathnames_only': { done: 'Searched by file name', proposed: 'Search by file name', running: loadingTitleWrapper('Searching by file name') }, - 'search_for_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') }, - 'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) }, - 'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) }, - 'edit_file': { done: `Edited file`, proposed: 'Edit file', running: loadingTitleWrapper('Editing file') }, - 'rewrite_file': { done: `Wrote file`, proposed: 'Write file', running: loadingTitleWrapper('Writing file') }, - 'run_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') }, - 'run_persistent_command': { done: `Ran terminal`, proposed: 'Run terminal', running: loadingTitleWrapper('Running terminal') }, - - 'open_persistent_terminal': { done: `Opened terminal`, proposed: 'Open terminal', running: loadingTitleWrapper('Opening terminal') }, - 'kill_persistent_terminal': { done: `Killed terminal`, proposed: 'Kill terminal', running: loadingTitleWrapper('Killing terminal') }, - - 'read_lint_errors': { done: `Read lint errors`, proposed: 'Read lint errors', running: loadingTitleWrapper('Reading lint errors') }, - 'search_in_file': { done: 'Searched in file', proposed: 'Search in file', running: loadingTitleWrapper('Searching in file') }, -} as const satisfies Record - - -const getTitle = (toolMessage: Pick): React.ReactNode => { - const t = toolMessage - - // non-built-in title - if (!builtinToolNames.includes(t.name as BuiltinToolName)) { - // descriptor of Running or Ran etc - const descriptor = - t.type === 'success' ? 'Called' - : t.type === 'running_now' ? 'Calling' - : t.type === 'tool_request' ? 'Call' - : t.type === 'rejected' ? 'Call' - : t.type === 'invalid_params' ? 'Call' - : t.type === 'tool_error' ? 'Call' - : 'Call' - - - const title = `${descriptor} ${toolMessage.mcpServerName || 'MCP'}` - if (t.type === 'running_now' || t.type === 'tool_request') - return loadingTitleWrapper(title) - return title - } - - // built-in title - else { - const toolName = t.name as BuiltinToolName - if (t.type === 'success') return titleOfBuiltinToolName[toolName].done - if (t.type === 'running_now') return titleOfBuiltinToolName[toolName].running - return titleOfBuiltinToolName[toolName].proposed - } -} - - -const toolNameToDesc = (toolName: BuiltinToolName, _toolParams: BuiltinToolCallParams[BuiltinToolName] | undefined, accessor: ReturnType): { - desc1: React.ReactNode, - desc1Info?: string, -} => { - - if (!_toolParams) { - return { desc1: '', }; - } - - const x = { - 'read_file': () => { - const toolParams = _toolParams as BuiltinToolCallParams['read_file'] - return { - desc1: getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - }; - }, - 'ls_dir': () => { - const toolParams = _toolParams as BuiltinToolCallParams['ls_dir'] - return { - desc1: getFolderName(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - }; - }, - 'search_pathnames_only': () => { - const toolParams = _toolParams as BuiltinToolCallParams['search_pathnames_only'] - return { - desc1: `"${toolParams.query}"`, - } - }, - 'search_for_files': () => { - const toolParams = _toolParams as BuiltinToolCallParams['search_for_files'] - return { - desc1: `"${toolParams.query}"`, - } - }, - 'search_in_file': () => { - const toolParams = _toolParams as BuiltinToolCallParams['search_in_file']; - return { - desc1: `"${toolParams.query}"`, - desc1Info: getRelative(toolParams.uri, accessor), - }; - }, - 'create_file_or_folder': () => { - const toolParams = _toolParams as BuiltinToolCallParams['create_file_or_folder'] - return { - desc1: toolParams.isFolder ? getFolderName(toolParams.uri.fsPath) ?? '/' : getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - } - }, - 'delete_file_or_folder': () => { - const toolParams = _toolParams as BuiltinToolCallParams['delete_file_or_folder'] - return { - desc1: toolParams.isFolder ? getFolderName(toolParams.uri.fsPath) ?? '/' : getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - } - }, - 'rewrite_file': () => { - const toolParams = _toolParams as BuiltinToolCallParams['rewrite_file'] - return { - desc1: getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - } - }, - 'edit_file': () => { - const toolParams = _toolParams as BuiltinToolCallParams['edit_file'] - return { - desc1: getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - } - }, - 'run_command': () => { - const toolParams = _toolParams as BuiltinToolCallParams['run_command'] - return { - desc1: `"${toolParams.command}"`, - } - }, - 'run_persistent_command': () => { - const toolParams = _toolParams as BuiltinToolCallParams['run_persistent_command'] - return { - desc1: `"${toolParams.command}"`, - } - }, - 'open_persistent_terminal': () => { - const toolParams = _toolParams as BuiltinToolCallParams['open_persistent_terminal'] - return { desc1: '' } - }, - 'kill_persistent_terminal': () => { - const toolParams = _toolParams as BuiltinToolCallParams['kill_persistent_terminal'] - return { desc1: toolParams.persistentTerminalId } - }, - 'get_dir_tree': () => { - const toolParams = _toolParams as BuiltinToolCallParams['get_dir_tree'] - return { - desc1: getFolderName(toolParams.uri.fsPath) ?? '/', - desc1Info: getRelative(toolParams.uri, accessor), - } - }, - 'read_lint_errors': () => { - const toolParams = _toolParams as BuiltinToolCallParams['read_lint_errors'] - return { - desc1: getBasename(toolParams.uri.fsPath), - desc1Info: getRelative(toolParams.uri, accessor), - } - } - } - - try { - return x[toolName]?.() || { desc1: '' } - } - catch { - return { desc1: '' } - } -} - -const ToolRequestAcceptRejectButtons = ({ toolName }: { toolName: ToolName }) => { - const accessor = useAccessor() - const chatThreadsService = accessor.get('IChatThreadService') - const metricsService = accessor.get('IMetricsService') - const voidSettingsService = accessor.get('IVoidSettingsService') - const voidSettingsState = useSettingsState() - - const onAccept = useCallback(() => { - try { // this doesn't need to be wrapped in try/catch anymore - const threadId = chatThreadsService.state.currentThreadId - chatThreadsService.approveLatestToolRequest(threadId) - metricsService.capture('Tool Request Accepted', {}) - } catch (e) { console.error('Error while approving message in chat:', e) } - }, [chatThreadsService, metricsService]) - - const onReject = useCallback(() => { - try { - const threadId = chatThreadsService.state.currentThreadId - chatThreadsService.rejectLatestToolRequest(threadId) - } catch (e) { console.error('Error while approving message in chat:', e) } - metricsService.capture('Tool Request Rejected', {}) - }, [chatThreadsService, metricsService]) - - const approveButton = ( - - ) - - const cancelButton = ( - - ) - - const approvalType = isABuiltinToolName(toolName) ? approvalTypeOfBuiltinToolName[toolName] : 'MCP tools' - const approvalToggle = approvalType ?
- -
: null - - return
- {approveButton} - {cancelButton} - {approvalToggle} -
-} - -export const ToolChildrenWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => { - return
-
- {children} -
-
-} -export const CodeChildren = ({ children, className }: { children: React.ReactNode, className?: string }) => { - return
-
- {children} -
-
-} - -export const ListableToolItem = ({ name, onClick, isSmall, className, showDot }: { name: React.ReactNode, onClick?: () => void, isSmall?: boolean, className?: string, showDot?: boolean }) => { - return
- {showDot === false ? null :
} -
{name}
-
-} - - - -const EditToolChildren = ({ uri, code, type }: { uri: URI | undefined, code: string, type: 'diff' | 'rewrite' }) => { - - const content = type === 'diff' ? - - : - - return
- - {content} - -
- -} - - -const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => { - return
- {lintErrors.map((error, i) => ( -
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
- ))} -
-} - -const BottomChildren = ({ children, title }: { children: React.ReactNode, title: string }) => { - const [isOpen, setIsOpen] = useState(false); - if (!children) return null; - return ( -
-
setIsOpen(o => !o)} - style={{ background: 'none' }} - > - - {title} -
-
-
- {children} -
-
-
- ); -} - - -const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr, toolName, threadId }: { threadId: string, applyBoxId: string, uri: URI, codeStr: string, toolName: 'edit_file' | 'rewrite_file' }) => { - const { streamState } = useEditToolStreamState({ applyBoxId, uri }) - return
- {/* */} - {/* */} - {streamState === 'idle-no-changes' && } - -
-} - - - -const InvalidTool = ({ toolName, message, mcpServerName }: { toolName: ToolName, message: string, mcpServerName: string | undefined }) => { - const accessor = useAccessor() - const title = getTitle({ name: toolName, type: 'invalid_params', mcpServerName }) - const desc1 = 'Invalid parameters' - const icon = null - const isError = true - const componentParams: ToolHeaderParams = { title, desc1, isError, icon } - - componentParams.children = - - {message} - - - return -} - -const CanceledTool = ({ toolName, mcpServerName }: { toolName: ToolName, mcpServerName: string | undefined }) => { - const accessor = useAccessor() - const title = getTitle({ name: toolName, type: 'rejected', mcpServerName }) - const desc1 = '' - const icon = null - const isRejected = true - const componentParams: ToolHeaderParams = { title, desc1, icon, isRejected } - return -} - - -const CommandTool = ({ toolMessage, type, threadId }: { threadId: string } & ({ - toolMessage: Exclude, { type: 'invalid_params' }> - type: 'run_command' -} | { - toolMessage: Exclude, { type: 'invalid_params' }> - type: | 'run_persistent_command' -})) => { - const accessor = useAccessor() - - const commandService = accessor.get('ICommandService') - const terminalToolsService = accessor.get('ITerminalToolService') - const toolsService = accessor.get('IToolsService') - const isError = false - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - const streamState = useChatThreadsStreamState(threadId) - - const divRef = useRef(null) - - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - - const effect = async () => { - if (streamState?.isRunning !== 'tool') return - if (type !== 'run_command' || toolMessage.type !== 'running_now') return; - - // wait for the interruptor so we know it's running - - await streamState?.interrupt - const container = divRef.current; - if (!container) return; - - const terminal = terminalToolsService.getTemporaryTerminal(toolMessage.params.terminalId); - if (!terminal) return; - - try { - terminal.attachToElement(container); - terminal.setVisible(true) - } catch { - } - - // Listen for size changes of the container and keep the terminal layout in sync. - const resizeObserver = new ResizeObserver((entries) => { - const height = entries[0].borderBoxSize[0].blockSize; - const width = entries[0].borderBoxSize[0].inlineSize; - if (typeof terminal.layout === 'function') { - terminal.layout({ width, height }); - } - }); - - resizeObserver.observe(container); - return () => { terminal.detachFromElement(); resizeObserver?.disconnect(); } - } - - useEffect(() => { - effect() - }, [terminalToolsService, toolMessage, toolMessage.type, type]); - - if (toolMessage.type === 'success') { - const { result } = toolMessage - - // it's unclear that this is a button and not an icon. - // componentParams.desc2 = { terminalToolsService.openTerminal(terminalId) }} - // /> - - let msg: string - if (type === 'run_command') msg = toolsService.stringOfResult['run_command'](toolMessage.params, result) - else msg = toolsService.stringOfResult['run_persistent_command'](toolMessage.params, result) - - if (type === 'run_persistent_command') { - componentParams.info = persistentTerminalNameOfId(toolMessage.params.persistentTerminalId) - } - - componentParams.children = -
- -
-
- } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - else if (toolMessage.type === 'running_now') { - if (type === 'run_command') - componentParams.children =
- } - else if (toolMessage.type === 'rejected' || toolMessage.type === 'tool_request') { - } - - return <> - - -} - -type WrapperProps = { toolMessage: Exclude, { type: 'invalid_params' }>, messageIdx: number, threadId: string } -const MCPToolWrapper = ({ toolMessage }: WrapperProps) => { - const accessor = useAccessor() - const mcpService = accessor.get('IMCPService') - - const title = getTitle(toolMessage) - const desc1 = removeMCPToolNamePrefix(toolMessage.name) - const icon = null - - - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, isError, icon, isRejected, } - - const paramsStr = JSON.stringify(params, null, 2) - componentParams.desc2 = - - componentParams.info = !toolMessage.mcpServerName ? 'MCP tool not found' : undefined - - // Add copy inputs button in desc2 - - - if (toolMessage.type === 'success' || toolMessage.type === 'tool_request') { - const { result } = toolMessage - const resultStr = result ? mcpService.stringifyResult(result) : 'null' - componentParams.children = - - - - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - - return - -} - -type ResultWrapper = (props: WrapperProps) => React.ReactNode - -const builtinToolNameToComponent: { [T in BuiltinToolName]: { resultWrapper: ResultWrapper, } } = { - 'read_file': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - - const title = getTitle(toolMessage) - - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - let range: [number, number] | undefined = undefined - if (toolMessage.params.startLine !== null || toolMessage.params.endLine !== null) { - const start = toolMessage.params.startLine === null ? `1` : `${toolMessage.params.startLine}` - const end = toolMessage.params.endLine === null ? `` : `${toolMessage.params.endLine}` - const addStr = `(${start}-${end})` - componentParams.desc1 += ` ${addStr}` - range = [params.startLine || 1, params.endLine || 1] - } - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor, range) } - if (result.hasNextPage && params.pageNumber === 1) // first page - componentParams.desc2 = `(truncated after ${Math.round(MAX_FILE_CHARS_PAGE) / 1000}k)` - else if (params.pageNumber > 1) // subsequent pages - componentParams.desc2 = `(part ${params.pageNumber})` - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - // JumpToFileButton removed in favor of FileLinkText - componentParams.bottomChildren = - - {result} - - - } - - return - }, - }, - 'get_dir_tree': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - if (params.uri) { - const rel = getRelative(params.uri, accessor) - if (rel) componentParams.info = `Only search in ${rel}` - } - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.children = - - - - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - - return - - } - }, - 'ls_dir': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const explorerService = accessor.get('IExplorerService') - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - if (params.uri) { - const rel = getRelative(params.uri, accessor) - if (rel) componentParams.info = `Only search in ${rel}` - } - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.numResults = result.children?.length - componentParams.hasNextPage = result.hasNextPage - componentParams.children = !result.children || (result.children.length ?? 0) === 0 ? undefined - : - {result.children.map((child, i) => ( { - voidOpenFileFn(child.uri, accessor) - // commandService.executeCommand('workbench.view.explorer'); // open in explorer folders view instead - // explorerService.select(child.uri, true); - }} - />))} - {result.hasNextPage && - - } - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - - return - } - }, - 'search_pathnames_only': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const isError = false - const isRejected = toolMessage.type === 'rejected' - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - if (params.includePattern) { - componentParams.info = `Only search in ${params.includePattern}` - } - - if (toolMessage.type === 'success') { - const { result, rawParams } = toolMessage - componentParams.numResults = result.uris.length - componentParams.hasNextPage = result.hasNextPage - componentParams.children = result.uris.length === 0 ? undefined - : - {result.uris.map((uri, i) => ( { voidOpenFileFn(uri, accessor) }} - />))} - {result.hasNextPage && - - } - - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - - return - } - }, - 'search_for_files': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const isError = false - const isRejected = toolMessage.type === 'rejected' - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - if (params.searchInFolder || params.isRegex) { - let info: string[] = [] - if (params.searchInFolder) { - const rel = getRelative(params.searchInFolder, accessor) - if (rel) info.push(`Only search in ${rel}`) - } - if (params.isRegex) { info.push(`Uses regex search`) } - componentParams.info = info.join('; ') - } - - if (toolMessage.type === 'success') { - const { result, rawParams } = toolMessage - componentParams.numResults = result.uris.length - componentParams.hasNextPage = result.hasNextPage - componentParams.children = result.uris.length === 0 ? undefined - : - {result.uris.map((uri, i) => ( { voidOpenFileFn(uri, accessor) }} - />))} - {result.hasNextPage && - - } - - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - return - } - }, - - 'search_in_file': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor(); - const toolsService = accessor.get('IToolsService'); - const title = getTitle(toolMessage); - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); - const icon = null; - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const { rawParams, params } = toolMessage; - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected }; - - const infoarr: string[] = [] - const uriStr = getRelative(params.uri, accessor) - if (uriStr) infoarr.push(uriStr) - if (params.isRegex) infoarr.push('Uses regex search') - componentParams.info = infoarr.join('; ') - - if (toolMessage.type === 'success') { - const { result } = toolMessage; // result is array of snippets - componentParams.numResults = result.lines.length; - componentParams.children = result.lines.length === 0 ? undefined : - - -
-								{toolsService.stringOfResult['search_in_file'](params, result)}
-							
-
-
- } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage; - componentParams.bottomChildren = - - {result} - - - } - - return ; - } - }, - - 'read_lint_errors': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - - const title = getTitle(toolMessage) - - const { uri } = toolMessage.params ?? {} - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - componentParams.info = getRelative(uri, accessor) // full path - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - if (result.lintErrors) - componentParams.children = - else - componentParams.children = `No lint errors found.` - - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - // JumpToFileButton removed in favor of FileLinkText - componentParams.bottomChildren = - - {result} - - - } - - return - }, - }, - - // --- - - 'create_file_or_folder': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const isError = false - const isRejected = toolMessage.type === 'rejected' - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - componentParams.info = getRelative(params.uri, accessor) // full path - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - else if (toolMessage.type === 'rejected') { - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - if (params) { componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } } - componentParams.bottomChildren = - - {result} - - - } - else if (toolMessage.type === 'running_now') { - // nothing more is needed - } - else if (toolMessage.type === 'tool_request') { - // nothing more is needed - } - - return - } - }, - 'delete_file_or_folder': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const isFolder = toolMessage.params?.isFolder ?? false - const isError = false - const isRejected = toolMessage.type === 'rejected' - const title = getTitle(toolMessage) - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const icon = null - - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - componentParams.info = getRelative(params.uri, accessor) // full path - - if (toolMessage.type === 'success') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - else if (toolMessage.type === 'rejected') { - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - if (params) { componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } } - componentParams.bottomChildren = - - {result} - - - } - else if (toolMessage.type === 'running_now') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - else if (toolMessage.type === 'tool_request') { - const { result } = toolMessage - componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } - } - - return - } - }, - 'rewrite_file': { - resultWrapper: (params) => { - return - } - }, - 'edit_file': { - resultWrapper: (params) => { - return - } - }, - - // --- - - 'run_command': { - resultWrapper: (params) => { - return - } - }, - - 'run_persistent_command': { - resultWrapper: (params) => { - return - } - }, - 'open_persistent_terminal': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const terminalToolsService = accessor.get('ITerminalToolService') - - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const title = getTitle(toolMessage) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - const relativePath = params.cwd ? getRelative(URI.file(params.cwd), accessor) : '' - componentParams.info = relativePath ? `Running in ${relativePath}` : undefined - - if (toolMessage.type === 'success') { - const { result } = toolMessage - const { persistentTerminalId } = result - componentParams.desc1 = persistentTerminalNameOfId(persistentTerminalId) - componentParams.onClick = () => terminalToolsService.focusPersistentTerminal(persistentTerminalId) - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } +import { ErrorDisplay } from './ErrorDisplay.js'; +import { TextAreaFns, VoidInputBox2, } from '../util/inputs.js'; +import { PastThreadsList } from './SidebarThreadSelector.js'; +import { VOID_CTRL_L_ACTION_ID } from '../../../actionIDs.js'; +import { VOID_OPEN_SETTINGS_ACTION_ID } from '../../../voidSettingsPane.js'; +import { isFeatureNameDisabled } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; +import { ProviderName } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; +import { WarningBox } from '../void-settings-tsx/WarningBox.js'; +import { getModelCapabilities } from '../../../../../../../platform/void/common/modelInference.js'; +import { Check, Image, X } from 'lucide-react'; +import { ChatAttachment, StagingSelectionItem } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; +import ErrorBoundary from './ErrorBoundary.js'; +import { getBasename } from './SidebarChatShared.js'; +import { IconLoading, VoidChatArea } from './SidebarChatUI.js'; +import { EditToolSoFar } from './SidebarChatTools.js'; +import { ChatBubble } from './SidebarChatBubbles.js'; +import { CommandBarInChat, TokenUsageSpoiler, HistoryCompressionIndicator } from './SidebarChatCommandBar.js'; - return - }, - }, - 'kill_persistent_terminal': { - resultWrapper: ({ toolMessage }) => { - const accessor = useAccessor() - const commandService = accessor.get('ICommandService') - const terminalToolsService = accessor.get('ITerminalToolService') - - const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) - const title = getTitle(toolMessage) - const icon = null - - if (toolMessage.type === 'tool_request') return null // do not show past requests - if (toolMessage.type === 'running_now') return null // do not show running - - const isError = false - const isRejected = toolMessage.type === 'rejected' - const { rawParams, params } = toolMessage - const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } - - if (toolMessage.type === 'success') { - const { persistentTerminalId } = params - componentParams.desc1 = persistentTerminalNameOfId(persistentTerminalId) - componentParams.onClick = () => terminalToolsService.focusPersistentTerminal(persistentTerminalId) - } - else if (toolMessage.type === 'tool_error') { - const { result } = toolMessage - componentParams.bottomChildren = - - {result} - - - } - return - }, - }, +const scrollToBottom = (divRef: { current: HTMLElement | null }) => { + if (divRef.current) { + divRef.current.scrollTop = divRef.current.scrollHeight; + } }; +const ScrollToBottomContainer = ({ + children, + className, + style, + scrollContainerRef +}: { + children: React.ReactNode; + className?: string; + style?: React.CSSProperties; + scrollContainerRef: React.MutableRefObject; +}) => { + const BOTTOM_THRESHOLD_PX = 32; -const Checkpoint = ({ message, threadId, messageIdx, isCheckpointGhost, threadIsRunning }: { message: CheckpointEntry, threadId: string; messageIdx: number, isCheckpointGhost: boolean, threadIsRunning: boolean }) => { - const accessor = useAccessor() - const chatThreadService = accessor.get('IChatThreadService') - const streamState = useFullChatThreadsStreamState() - - const isRunning = useChatThreadsStreamState(threadId)?.isRunning - const isDisabled = useMemo(() => { - if (isRunning) return true - return !!Object.keys(streamState).find((threadId2) => streamState[threadId2]?.isRunning) - }, [isRunning, streamState]) + const [isAtBottom, setIsAtBottom] = useState(true); + const isAtBottomRef = useRef(true); - return
-
{ - if (threadIsRunning) return - if (isDisabled) return - chatThreadService.jumpToCheckpointBeforeMessageIdx({ - threadId, - messageIdx, - jumpToUserModified: messageIdx === (chatThreadService.state.allThreads[threadId]?.messages.length ?? 0) - 1 - }) - }} - {...isDisabled ? { - 'data-tooltip-id': 'void-tooltip', - 'data-tooltip-content': `Disabled ${isRunning ? 'when running' : 'because another thread is running'}`, - 'data-tooltip-place': 'top', - } : {}} - > - Checkpoint -
-
-} + const divRef = scrollContainerRef; + const contentRef = useRef(null); + const computeIsAtBottom = useCallback(() => { + const div = divRef.current; + if (!div) return true; + return (div.scrollHeight - div.clientHeight - div.scrollTop) <= BOTTOM_THRESHOLD_PX; + }, [divRef]); -type ChatBubbleMode = 'display' | 'edit' -type ChatBubbleProps = { - chatMessage: ChatMessage, - messageIdx: number, - isCommitted: boolean, - chatIsRunning: IsRunningType, - threadId: string, - currCheckpointIdx: number | undefined, - _scrollToBottom: (() => void) | null, -} + const setBottomState = useCallback((v: boolean) => { + isAtBottomRef.current = v; + setIsAtBottom(v); + }, []); -const ChatBubble = (props: ChatBubbleProps) => { - return - <_ChatBubble {...props} /> - -} + const scrollToBottomNow = useCallback(() => { + const div = divRef.current; + if (!div) return; -const _ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, messageIdx, chatIsRunning, _scrollToBottom }: ChatBubbleProps) => { - const role = chatMessage.role + + requestAnimationFrame(() => { + const d = divRef.current; + if (!d) return; + d.scrollTop = d.scrollHeight; + }); + }, [divRef]); - const isCheckpointGhost = messageIdx > (currCheckpointIdx ?? Infinity) && !chatIsRunning // whether to show as gray (if chat is running, for good measure just dont show any ghosts) + const onScroll = useCallback(() => { + setBottomState(computeIsAtBottom()); + }, [computeIsAtBottom, setBottomState]); - if (role === 'user') { - return - } - else if (role === 'assistant') { - return - } - else if (role === 'tool') { + + useLayoutEffect(() => { + scrollToBottomNow(); + setBottomState(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); - if (chatMessage.type === 'invalid_params') { - return
- -
+ + useLayoutEffect(() => { + if (isAtBottomRef.current) { + scrollToBottomNow(); } + }, [children, scrollToBottomNow]); - const toolName = chatMessage.name - const isBuiltInTool = isABuiltinToolName(toolName) - const ToolResultWrapper = isBuiltInTool ? builtinToolNameToComponent[toolName]?.resultWrapper as ResultWrapper - : MCPToolWrapper as ResultWrapper - - if (ToolResultWrapper) - return <> -
- -
- {chatMessage.type === 'tool_request' ? -
- -
: null} - - return null - } - - else if (role === 'interrupted_streaming_tool') { - return
- -
- } - - else if (role === 'checkpoint') { - return - } - -} - -const CommandBarInChat = () => { - const { stateOfURI: commandBarStateOfURI, sortedURIs: sortedCommandBarURIs } = useCommandBarState() - const numFilesChanged = sortedCommandBarURIs.length - - const accessor = useAccessor() - const editCodeService = accessor.get('IEditCodeService') - const commandService = accessor.get('ICommandService') - const chatThreadsState = useChatThreadsState() - const commandBarState = useCommandBarState() - const chatThreadsStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId) - - // ( - // - // ) - - const [fileDetailsOpenedState, setFileDetailsOpenedState] = useState<'auto-opened' | 'auto-closed' | 'user-opened' | 'user-closed'>('auto-closed'); - const isFileDetailsOpened = fileDetailsOpenedState === 'auto-opened' || fileDetailsOpenedState === 'user-opened'; - - + + useEffect(() => { - // close the file details if there are no files - // this converts 'user-closed' to 'auto-closed' - if (numFilesChanged === 0) { - setFileDetailsOpenedState('auto-closed') - } - // open the file details if it hasnt been closed - if (numFilesChanged > 0 && fileDetailsOpenedState !== 'user-closed') { - setFileDetailsOpenedState('auto-opened') - } - }, [fileDetailsOpenedState, setFileDetailsOpenedState, numFilesChanged]) - - - const isFinishedMakingThreadChanges = ( - // there are changed files - commandBarState.sortedURIs.length !== 0 - // none of the files are streaming - && commandBarState.sortedURIs.every(uri => !commandBarState.stateOfURI[uri.fsPath]?.isStreaming) - ) - - // ======== status of agent ======== - // This icon answers the question "is the LLM doing work on this thread?" - // assume it is single threaded for now - // green = Running - // orange = Requires action - // dark = Done - - const threadStatus = ( - chatThreadsStreamState?.isRunning === 'awaiting_user' ? { title: 'Needs Approval', color: 'yellow', } as const - : chatThreadsStreamState?.isRunning ? { title: 'Running', color: 'orange', } as const - : { title: 'Done', color: 'dark', } as const - ) - - - const threadStatusHTML = - - - // ======== info about changes ======== - // num files changed - // acceptall + rejectall - // popup info about each change (each with num changes + acceptall + rejectall of their own) - - const numFilesChangedStr = numFilesChanged === 0 ? 'No files with changes' - : `${sortedCommandBarURIs.length} file${numFilesChanged === 1 ? '' : 's'} with changes` - - + const div = divRef.current; + const content = contentRef.current; + if (!div || !content) return; + const handleContentChange = () => { + if (isAtBottomRef.current) { + scrollToBottomNow(); + } + }; - const acceptRejectAllButtons =
- { - sortedCommandBarURIs.forEach(uri => { - editCodeService.acceptOrRejectAllDiffAreas({ - uri, - removeCtrlKs: true, - behavior: "reject", - _addToHistory: true, - }); - }); - }} - data-tooltip-id='void-tooltip' - data-tooltip-place='top' - data-tooltip-content='Reject all' - /> - - { - sortedCommandBarURIs.forEach(uri => { - editCodeService.acceptOrRejectAllDiffAreas({ - uri, - removeCtrlKs: true, - behavior: "accept", - _addToHistory: true, - }); - }); - }} - data-tooltip-id='void-tooltip' - data-tooltip-place='top' - data-tooltip-content='Accept all' - /> - - - -
- - - // !select-text cursor-auto - const fileDetailsContent =
- {sortedCommandBarURIs.map((uri, i) => { - const basename = getBasename(uri.fsPath) - - const { sortedDiffIds, isStreaming } = commandBarStateOfURI[uri.fsPath] ?? {} - const isFinishedMakingFileChanges = !isStreaming - - const numDiffs = sortedDiffIds?.length || 0 - - const fileStatus = (isFinishedMakingFileChanges - ? { title: 'Done', color: 'dark', } as const - : { title: 'Running', color: 'orange', } as const - ) - - const fileNameHTML =
voidOpenFileFn(uri, accessor)} - > - {/* */} - {basename} -
- + let mo: MutationObserver | null = null; + if (typeof MutationObserver !== 'undefined') { + mo = new MutationObserver(handleContentChange); + mo.observe(content, { childList: true, subtree: true, characterData: true }); + } + return () => { + ro?.disconnect(); + mo?.disconnect(); + }; + }, [divRef, scrollToBottomNow]); - const detailsContent =
- {numDiffs} diff{numDiffs !== 1 ? 's' : ''} + return ( +
+ {} +
+ {children}
+
+ ); +}; - const acceptRejectButtons =
- {/* */} - { editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "reject", _addToHistory: true, }); }} - data-tooltip-id='void-tooltip' - data-tooltip-place='top' - data-tooltip-content='Reject file' - - /> - { editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "accept", _addToHistory: true, }); }} - data-tooltip-id='void-tooltip' - data-tooltip-place='top' - data-tooltip-content='Accept file' - /> - -
+const ProseWrapper = ({ children }: { children: React.ReactNode }) => { + return
+prose-p:leading-normal +prose-ol:leading-normal +prose-ul:leading-normal - return ( - // name, details -
-
- {fileNameHTML} - {detailsContent} -
-
- {acceptRejectButtons} - {fileStatusHTML} -
-
- ) - })} +max-w-none +' + > + {children}
+} - const fileDetailsButton = ( - - ) +export const SidebarChat = () => { - return ( - <> - {/* file details */} -
-
- {fileDetailsContent} -
-
- {/* main content */} + const initiallySuggestedPromptsHTML =
+ {[ + 'Summarize my codebase', + 'How do types work in Rust?', + 'Create a .voidrules file for me' + ].map((text, index) => (
onSubmit(text)} > -
- {fileDetailsButton} -
-
- {acceptRejectAllButtons} - {threadStatusHTML} -
+ {text}
- - ) -} - - - -const EditToolSoFar = ({ toolCallSoFar, }: { toolCallSoFar: RawToolCallObj }) => { - - if (!isABuiltinToolName(toolCallSoFar.name)) return null - - const accessor = useAccessor() - - const uri = toolCallSoFar.rawParams.uri ? URI.file(toolCallSoFar.rawParams.uri) : undefined - - const title = titleOfBuiltinToolName[toolCallSoFar.name].proposed - - const uriDone = toolCallSoFar.doneParams.includes('uri') - const desc1 = - {uriDone ? - getBasename(toolCallSoFar.rawParams['uri'] ?? 'unknown') - : `Generating`} - - - - const desc1OnClick = () => { uri && voidOpenFileFn(uri, accessor) } - - // If URI has not been specified - return - - - - -} - + ))} +
-export const SidebarChat = () => { const textAreaRef = useRef(null) const textAreaFnsRef = useRef(null) @@ -2909,11 +217,57 @@ export const SidebarChat = () => { // ----- SIDEBAR CHAT state (local) ----- + const [attachments, setAttachments] = useState([]); + // state of current message const initVal = '' const [instructionsAreEmpty, setInstructionsAreEmpty] = useState(!initVal) - const isDisabled = instructionsAreEmpty || !!isFeatureNameDisabled('Chat', settingsState) + const chatModelSelection = settingsState.modelSelectionOfFeature['Chat']; + const overridesOfModel = settingsState.overridesOfModel; + const supportsImages = useMemo(() => { + if (!chatModelSelection) return false; + try { + const caps = getModelCapabilities(chatModelSelection.providerName as ProviderName, chatModelSelection.modelName, overridesOfModel); + return !!caps.inputModalities?.includes('image'); + } catch { + return false; + } + }, [chatModelSelection, overridesOfModel]); + + const hideEncryptedReasoning = useMemo(() => { + if (!chatModelSelection) return false; + try { + const providerName = chatModelSelection.providerName as ProviderName; + const modelName = chatModelSelection.modelName; + + + let fromCaps = false; + try { + const caps = getModelCapabilities(providerName, modelName, overridesOfModel); + const rc = caps.reasoningCapabilities as any; + if (rc && typeof rc === 'object' && rc.hideEncryptedReasoning !== undefined) { + fromCaps = !!rc.hideEncryptedReasoning; + } + } catch { /* ignore */ } + + + try { + const cp: any = (settingsState as any).customProviders?.[providerName]; + const ov: any = cp?.modelCapabilityOverrides?.[modelName]; + const rcOv: any = ov?.reasoningCapabilities; + if (rcOv && typeof rcOv === 'object' && 'hideEncryptedReasoning' in rcOv) { + return !!rcOv.hideEncryptedReasoning; + } + } catch { /* ignore */ } + + return fromCaps; + } catch { + return false; + } + }, [chatModelSelection, overridesOfModel, settingsState.customProviders]); + + const isDisabled = (instructionsAreEmpty && attachments.length === 0) || !!isFeatureNameDisabled('Chat', settingsState) const sidebarRef = useRef(null) const scrollContainerRef = useRef(null) @@ -2926,18 +280,20 @@ export const SidebarChat = () => { // send message to LLM const userMessage = _forceSubmit || textAreaRef.current?.value || '' + const attachmentsToSend = _forceSubmit ? [] : attachments; try { - await chatThreadsService.addUserMessageAndStreamResponse({ userMessage, threadId }) + await chatThreadsService.addUserMessageAndStreamResponse({ userMessage, threadId, attachments: attachmentsToSend.length ? attachmentsToSend : undefined }) } catch (e) { console.error('Error while sending message in chat:', e) } - setSelections([]) // clear staging + setSelections([]) + if (!_forceSubmit) setAttachments([]) textAreaFnsRef.current?.setValue('') - textAreaRef.current?.focus() // focus input after submit + textAreaRef.current?.focus() - }, [chatThreadsService, isDisabled, isRunning, textAreaRef, textAreaFnsRef, setSelections, settingsState]) + }, [attachments, chatThreadsService, isDisabled, isRunning, textAreaRef, textAreaFnsRef, setSelections, settingsState, selections]) const onAbort = async () => { const threadId = currentThread.id @@ -2948,9 +304,6 @@ export const SidebarChat = () => { const threadId = currentThread.id const currCheckpointIdx = chatThreadsState.allThreads[threadId]?.state?.currCheckpointIdx ?? undefined // if not exist, treat like checkpoint is last message (infinity) - - - // resolve mount info const isResolved = chatThreadsState.allThreads[threadId]?.state.mountedInfo?.mountedIsResolvedRef.current useEffect(() => { @@ -2959,15 +312,9 @@ export const SidebarChat = () => { textAreaRef: textAreaRef, scrollToBottom: () => scrollToBottom(scrollContainerRef), }) - }, [chatThreadsState, threadId, textAreaRef, scrollContainerRef, isResolved]) - - - const previousMessagesHTML = useMemo(() => { - // const lastMessageIdx = previousMessages.findLastIndex(v => v.role !== 'checkpoint') - // tool request shows up as Editing... if in progress return previousMessages.map((message, i) => { return { chatIsRunning={isRunning} threadId={threadId} _scrollToBottom={() => scrollToBottom(scrollContainerRef)} + hideEncryptedReasoning={hideEncryptedReasoning} /> }) - }, [previousMessages, threadId, currCheckpointIdx, isRunning]) + }, [previousMessages, threadId, currCheckpointIdx, isRunning, hideEncryptedReasoning]) const streamingChatIdx = previousMessagesHTML.length - const currStreamingMessageHTML = reasoningSoFar || displayContentSoFar || isRunning ? + const currStreamingMessageHTML = displayContentSoFar || isRunning ? { threadId={threadId} _scrollToBottom={null} + hideEncryptedReasoning={hideEncryptedReasoning} /> : null - // the tool currently being generated const generatingTool = toolIsGenerating ? toolCallSoFar.name === 'edit_file' || toolCallSoFar.name === 'rewrite_file' ? { {/* previous messages */} {previousMessagesHTML} {currStreamingMessageHTML} - {/* Generating tool */} {generatingTool} @@ -3035,7 +382,6 @@ export const SidebarChat = () => { {} : null} - {/* error message */} {latestError === undefined ? null :
@@ -3051,18 +397,93 @@ export const SidebarChat = () => { } - const onChangeText = useCallback((newStr: string) => { setInstructionsAreEmpty(!newStr) }, [setInstructionsAreEmpty]) + const onKeyDown = useCallback((e: KeyboardEvent) => { - if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { + if (e.key === 'Enter' && !e.shiftKey) { onSubmit() } else if (e.key === 'Escape' && isRunning) { onAbort() } }, [onSubmit, onAbort, isRunning]) + const fileDialogService = accessor.get('IFileDialogService'); + + const getImageMimeTypeForUri = useCallback((uri: URI): string => { + const path = (uri.fsPath || uri.path || '').toLowerCase(); + if (path.endsWith('.png')) return 'image/png'; + if (path.endsWith('.jpg') || path.endsWith('.jpeg')) return 'image/jpeg'; + if (path.endsWith('.webp')) return 'image/webp'; + if (path.endsWith('.gif')) return 'image/gif'; + if (path.endsWith('.bmp')) return 'image/bmp'; + if (path.endsWith('.svg')) return 'image/svg+xml'; + return 'application/octet-stream'; + }, []) + + const onAttachImages = useCallback(async () => { + if (!supportsImages) return; + try { + const uris = await fileDialogService.showOpenDialog({ + canSelectFiles: true, + canSelectFolders: false, + canSelectMany: true, + filters: [{ name: 'Images', extensions: ['png', 'jpg', 'jpeg', 'webp', 'gif', 'bmp', 'svg'] }], + }); + if (!uris || !uris.length) return; + setAttachments(prev => { + const existing = new Set(prev.map(a => a.uri.toString())); + const next = [...prev]; + for (const uri of uris) { + const key = uri.toString(); + if (existing.has(key)) continue; + next.push({ + kind: 'image', + uri, + mimeType: getImageMimeTypeForUri(uri), + name: getBasename(uri.fsPath || uri.path || '', 1), + }); + } + return next; + }); + } catch (err) { + console.error('Failed to attach images', err); + } + }, [fileDialogService, getImageMimeTypeForUri, setAttachments, supportsImages]) + + const onRemoveAttachment = useCallback((uri: URI) => { + setAttachments(prev => prev.filter(a => a.uri.toString() !== uri.toString())); + }, [setAttachments]) + + const attachmentsPreview = attachments.length ? ( +
+ {attachments.map(att => ( + + ))} +
+ ) : null + + const attachButton = supportsImages ? ( + + ) : null + const inputChatArea = onSubmit()} @@ -3070,11 +491,12 @@ export const SidebarChat = () => { isStreaming={!!isRunning} isDisabled={isDisabled} showSelections={true} - // showProspectiveSelections={previousMessagesHTML.length === 0} selections={selections} setSelections={setSelections} onClickAnywhere={() => { textAreaRef.current?.focus() }} + rightBottomExtras={attachButton} > + {attachmentsPreview} { fnsRef={textAreaFnsRef} multiline={true} /> - - const isLandingPage = previousMessages.length === 0 - - const initiallySuggestedPromptsHTML =
- {[ - 'Summarize my codebase', - 'How do types work in Rust?', - 'Create a .voidrules file for me' - ].map((text, index) => ( -
onSubmit(text)} - > - {text} + // ======== pinned ACP plan (shown above command bar) ======== + const currentThreadForPlan = chatThreadsState.allThreads[threadId] + const pinnedPlanItems = settingsState.globalSettings.showAcpPlanInChat === false + ? [] + : (currentThreadForPlan?.state?.acpPlan?.items ?? []) + + const pinnedPlanHTML = pinnedPlanItems.length ? ( +
+
Plan
+
+ {pinnedPlanItems.map((it, idx) => ( +
+
+ {it.state === 'pending' &&
} + {it.state === 'running' &&
} + {it.state === 'done' && } + {it.state === 'error' && } +
+ {it.text} +
+ ))}
- ))} -
- - +
+ ) : null const threadPageInput =
+ {pinnedPlanHTML} + +
@@ -3134,7 +566,7 @@ export const SidebarChat = () => { {landingPageInput} - {Object.keys(chatThreadsState.allThreads).length > 1 ? // show if there are threads + {Object.keys(chatThreadsState.allThreads).length > 1 ?
Previous Threads
@@ -3147,25 +579,10 @@ export const SidebarChat = () => { }
- - // const threadPageContent =
- // {/* Thread content */} - //
- //
- // - // {messagesHTML} - // - //
- // - // {inputForm} - // - //
- //
const threadPageContent =
- {messagesHTML} @@ -3174,13 +591,9 @@ export const SidebarChat = () => {
- return ( - - {isLandingPage ? - landingPageContent - : threadPageContent} + + {isLandingPage ? landingPageContent : threadPageContent} ) } diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatBubbles.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatBubbles.tsx new file mode 100644 index 00000000000..5841c575aa1 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatBubbles.tsx @@ -0,0 +1,609 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ +import React, { KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useAccessor, useChatThreadsStreamState, useFullChatThreadsStreamState } from '../util/services.js'; +import { ChatMarkdownRender, ChatMessageLocation } from '../markdown/ChatMarkdownRender.js'; +import { ProseWrapper } from './SidebarChatUI.js'; +import { ChatMessage, StagingSelectionItem, CheckpointEntry } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; +import { TextAreaFns, VoidInputBox2 } from '../util/inputs.js'; +import { + ToolHeaderWrapper, + InvalidTool, + toolNameToComponent, + ResultWrapper, + ToolRequestAcceptRejectButtons, + DynamicToolHeader, + CanceledTool, + SkippedTool +} from './SidebarChatTools.js'; +import { ErrorBoundary } from './ErrorBoundary.js'; +import { getChatMessageMarkdown, getAssistantTurnInfo, getAssistantTurnMarkdown } from './SidebarChatShared.js'; +import { SelectedFiles, VoidChatArea } from './SidebarChatUI.js'; +import { CopyButton } from '../markdown/ApplyBlockHoverButtons.js'; +import { IsRunningType } from '../../../ChatExecutionEngine.js'; +import { ToolName, isAToolName } from '../../../../common/prompt/prompts.js'; +import { Pencil, X, Image } from 'lucide-react'; + +export const ENCRYPTED_REASONING_PLACEHOLDER = 'Reasoning content is encrypted by the provider and cannot be displayed'; + +export const ReasoningSpoiler = ({ reasoning, anthropicReasoning }: { reasoning: string; anthropicReasoning: any[] | null }) => { + const [open, setOpen] = useState(false); + + const text = useMemo(() => { + if (reasoning && reasoning.trim()) return reasoning; + if (anthropicReasoning && anthropicReasoning.length) { + return anthropicReasoning.map((r: any) => (r && typeof r.thinking === 'string') ? r.thinking : '').join('\n').trim(); + } + return ''; + }, [reasoning, anthropicReasoning]); + + if (!text) return null; + + const preview = text.slice(0, 120).replace(/\s+/g, ' '); + + return ( +
+ + {open && ( +
+ {text} +
+ )} +
+ ); +}; + +export const UserMessageComponent = ({ + chatMessage, + messageIdx, + isCheckpointGhost, + currCheckpointIdx, + _scrollToBottom, +}: { + chatMessage: ChatMessage & { role: 'user' }, + messageIdx: number, + currCheckpointIdx: number | undefined, + isCheckpointGhost: boolean, + _scrollToBottom: (() => void) | null +}) => { + + // Hidden messages (like skipped tool notifications) + const isHidden = 'hidden' in chatMessage && !!(chatMessage as any).hidden; + const [isHiddenOpen, setIsHiddenOpen] = useState(false); + const toggleHidden = useCallback(() => { + setIsHiddenOpen(v => !v); + _scrollToBottom?.(); + }, [_scrollToBottom]); + + const accessor = useAccessor(); + const chatThreadsService = accessor.get('IChatThreadService'); + + // global state + let isBeingEdited = false; + let stagingSelections: StagingSelectionItem[] = []; + let setIsBeingEdited = (_: boolean) => { }; + let setStagingSelections = (_: StagingSelectionItem[]) => { }; + + if (messageIdx !== undefined) { + const _state = chatThreadsService.getCurrentMessageState(messageIdx); + isBeingEdited = _state.isBeingEdited; + stagingSelections = _state.stagingSelections; + setIsBeingEdited = (v) => chatThreadsService.setCurrentMessageState(messageIdx, { isBeingEdited: v }); + setStagingSelections = (s) => chatThreadsService.setCurrentMessageState(messageIdx, { stagingSelections: s }); + } + + // local state + const mode: ChatBubbleMode = isBeingEdited ? 'edit' : 'display'; + const [isFocused, setIsFocused] = useState(false); + const [isHovered, setIsHovered] = useState(false); + const [isDisabled, setIsDisabled] = useState(false); + const [textAreaRefState, setTextAreaRef] = useState(null); + const textAreaFnsRef = useRef(null); + + // initialize on first render, and when edit was just enabled + const _mustInitialize = useRef(true); + const _justEnabledEdit = useRef(false); + + useEffect(() => { + const canInitialize = mode === 'edit' && textAreaRefState; + const shouldInitialize = !isHidden && (_justEnabledEdit.current || _mustInitialize.current); + if (canInitialize && shouldInitialize) { + setStagingSelections( + (chatMessage.selections || []).map(s => { // quick hack so we dont have to do anything more + if (s.type === 'File') return { ...s, state: { ...s.state, wasAddedAsCurrentFile: false } }; + else return s; + }) + ); + + if (textAreaFnsRef.current) + textAreaFnsRef.current.setValue(chatMessage.displayContent || ''); + + textAreaRefState.focus(); + + _justEnabledEdit.current = false; + _mustInitialize.current = false; + } + + }, [chatMessage, mode, isHidden, _justEnabledEdit, textAreaRefState, textAreaFnsRef.current, _justEnabledEdit.current, _mustInitialize.current]); + + // Render hidden variant after hooks are declared to keep hook order stable + if (isHidden) { + const body = typeof chatMessage.content === 'string' + ? chatMessage.content + : (chatMessage.displayContent ?? ''); + + // Hide the special hidden "skip" user message entirely (both ACP and non-ACP), + // because we show the skip outcome in the tool output instead. + const normalized = String(body ?? '').trim().toLowerCase(); + if (normalized === 'skip' || normalized.startsWith('skip ')) { + return null; + } + + // Fallback: still render other hidden user messages (if any) + return ( +
+ +
+
{body}
+
+
+
+ ); + } + + const onOpenEdit = () => { + setIsBeingEdited(true); + chatThreadsService.setCurrentlyFocusedMessageIdx(messageIdx); + _justEnabledEdit.current = true; + }; + const onCloseEdit = () => { + setIsFocused(false); + setIsHovered(false); + setIsBeingEdited(false); + chatThreadsService.setCurrentlyFocusedMessageIdx(undefined); + }; + + const EditSymbol = mode === 'display' ? Pencil : X; + const messageMarkdown = getChatMessageMarkdown(chatMessage); + const showControls = isHovered || (isFocused && mode === 'edit'); + + let chatbubbleContents: React.ReactNode; + if (mode === 'display') { + chatbubbleContents = <> + + {chatMessage.displayContent} + {chatMessage.attachments && chatMessage.attachments.length > 0 && ( +
+ {chatMessage.attachments.map((att, i) => { + return ( +
+ + {att.name || att.uri?.fsPath || 'Unnamed attachment'} +
+ ); + })} +
+ )} + ; + } + else if (mode === 'edit') { + + const onSubmit = async () => { + + if (isDisabled) return; + if (!textAreaRefState) return; + if (messageIdx === undefined) return; + + // cancel any streams on this thread + const threadId = chatThreadsService.state.currentThreadId; + + await chatThreadsService.abortRunning(threadId); + + // update state + setIsBeingEdited(false); + chatThreadsService.setCurrentlyFocusedMessageIdx(undefined); + + // stream the edit + const userMessage = textAreaRefState.value; + try { + await chatThreadsService.editUserMessageAndStreamResponse({ userMessage, messageIdx, threadId }); + } catch (e) { + console.error('Error while editing message:', e); + } + await chatThreadsService.focusCurrentChat(); + requestAnimationFrame(() => _scrollToBottom?.()); + }; + + const onAbort = async () => { + const threadId = chatThreadsService.state.currentThreadId; + await chatThreadsService.abortRunning(threadId); + }; + + const onKeyDown = (e: KeyboardEvent) => { + if (e.key === 'Escape') { + onCloseEdit(); + } + if (e.key === 'Enter' && !e.shiftKey) { + onSubmit(); + } + }; + + if (!chatMessage.content) { // don't show if empty and not loading (if loading, want to show). + return null; + } + + chatbubbleContents = + setIsDisabled(!text)} + onFocus={() => { + setIsFocused(true); + chatThreadsService.setCurrentlyFocusedMessageIdx(messageIdx); + }} + onBlur={() => { + setIsFocused(false); + }} + onKeyDown={onKeyDown} + fnsRef={textAreaFnsRef} + multiline={true} + /> + ; + } + + const isMsgAfterCheckpoint = currCheckpointIdx !== undefined && currCheckpointIdx === messageIdx - 1; + + return
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > +
{ if (mode === 'display') { onOpenEdit(); } }} + > + {chatbubbleContents} +
+ +
+ {mode === 'display' && messageMarkdown && ( +
+ +
+ )} + { + if (mode === 'display') { + onOpenEdit(); + } else if (mode === 'edit') { + onCloseEdit(); + } + }} + /> +
+
; +}; + +export const AssistantMessageComponent = ({ + chatMessage, + isCheckpointGhost, + isCommitted, + messageIdx, + hideEncryptedReasoning, +}: { + chatMessage: ChatMessage & { role: 'assistant' }; + isCheckpointGhost: boolean; + messageIdx: number; + isCommitted: boolean; + hideEncryptedReasoning?: boolean; +}) => { + const accessor = useAccessor(); + const chatThreadsService = accessor.get('IChatThreadService'); + const thread = chatThreadsService.getCurrentThread(); + + const chatMessageLocation: ChatMessageLocation = { + threadId: thread.id, + messageIdx, + }; + + const displayContent = (chatMessage.displayContent || '').trimEnd(); + + const hasText = !!displayContent; + + const hasReasoning = + !!(chatMessage.reasoning && chatMessage.reasoning.trim()) || + !!(chatMessage.anthropicReasoning && chatMessage.anthropicReasoning.length); + + const reasoningIsEncryptedPlaceholder = + typeof chatMessage.reasoning === 'string' && + chatMessage.reasoning.trim() === ENCRYPTED_REASONING_PLACEHOLDER; + + // Only hide reasoning when it's the provider "encrypted" placeholder (not normal reasoning) + const showReasoning = hasReasoning && !(hideEncryptedReasoning && reasoningIsEncryptedPlaceholder); + + if (!hasText && !showReasoning) return null; + + return ( +
+ {showReasoning ? ( + + ) : null} + + {hasText && ( + +
+ +
+
+ )} +
+ ); +}; + +export const Checkpoint = ({ message, threadId, messageIdx, isCheckpointGhost, threadIsRunning }: { message: CheckpointEntry, threadId: string; messageIdx: number, isCheckpointGhost: boolean, threadIsRunning: boolean }) => { + const accessor = useAccessor(); + const chatThreadService = accessor.get('IChatThreadService'); + const streamState = useFullChatThreadsStreamState(); + + const isRunning = useChatThreadsStreamState(threadId)?.isRunning; + const isDisabled = useMemo(() => { + if (isRunning) return true; + return !!Object.keys(streamState).find((threadId2) => streamState[threadId2]?.isRunning); + }, [isRunning, streamState]); + + return
+
{ + if (threadIsRunning) return; + if (isDisabled) return; + chatThreadService.jumpToCheckpointBeforeMessageIdx({ + threadId, + messageIdx, + jumpToUserModified: messageIdx === (chatThreadService.state.allThreads[threadId]?.messages.length ?? 0) - 1 + }); + }} + {...isDisabled ? { + 'data-tooltip-id': 'void-tooltip', + 'data-tooltip-content': `Disabled ${isRunning ? 'when running' : 'because another thread is running'}`, + 'data-tooltip-place': 'top', + } : {}} + > + Checkpoint +
+
; +}; + +type ChatBubbleMode = 'display' | 'edit'; +type ChatBubbleProps = { + chatMessage: ChatMessage, + messageIdx: number, + isCommitted: boolean, + chatIsRunning: IsRunningType, + threadId: string, + currCheckpointIdx: number | undefined, + _scrollToBottom: (() => void) | null, + hideEncryptedReasoning?: boolean, +}; + +export const ChatBubble = (props: ChatBubbleProps) => { + return + <_ChatBubble {...props} /> + ; +}; + +const _ChatBubble = ({ threadId, chatMessage, currCheckpointIdx, isCommitted, messageIdx, chatIsRunning, _scrollToBottom, hideEncryptedReasoning }: ChatBubbleProps) => { + const accessor = useAccessor(); + const chatThreadsService = accessor.get('IChatThreadService'); + const thread = chatThreadsService.getCurrentThread(); + + const role = chatMessage.role; + + const isCheckpointGhost = + messageIdx > (currCheckpointIdx ?? Infinity) && !chatIsRunning; // whether to show as gray + + + const turnInfo = getAssistantTurnInfo(thread.messages, messageIdx); + const showCopyFooter = + !!turnInfo && + turnInfo.lastNonCheckpointIdx === messageIdx && + !chatIsRunning; + + const copyMarkdown = showCopyFooter ? getAssistantTurnMarkdown(thread.messages, messageIdx) : ''; + + const copyFooter = showCopyFooter && copyMarkdown ? ( +
+ +
+ ) : null; + + let bubble: React.ReactNode = null; + + if (role === 'user') { + bubble = ( + + ); + } + else if (role === 'assistant') { + bubble = ( + + ); + } + else if (role === 'tool') { + + const isSkipped = + (chatMessage as any).type === 'skipped' || + (!!(chatMessage as any).result && typeof (chatMessage as any).result === 'object' && ( + (chatMessage as any).result._skipped === true || (chatMessage as any).result.skipped === true + )) || + (!!(chatMessage as any).rawOutput && typeof (chatMessage as any).rawOutput === 'object' && ( + (chatMessage as any).rawOutput._skipped === true || (chatMessage as any).rawOutput.skipped === true + )); + + if (isSkipped) { + bubble = ( +
+ +
+ ); + } + else if ((chatMessage as any).type === 'invalid_params') { + bubble = ( +
+ +
+ ); + } + else { + // Narrow the chatMessage.name to ToolName for indexing typed maps + const nameAsTool = (chatMessage as any).name as ToolName; + const ToolResultWrapper = toolNameToComponent[nameAsTool]?.resultWrapper as ResultWrapper; + + // Check if this is a dynamic (MCP) tool + const isDynamicTool = !isAToolName((chatMessage as any).name); + + if (ToolResultWrapper || isDynamicTool) { + bubble = ( + <> +
+ {ToolResultWrapper ? + + : + // For dynamic tools, show a simple tool header + + } +
+ {(chatMessage as any).type === 'tool_request' ? +
+ +
: null} + + ); + } else { + bubble = null; + } + } + } + else if (role === 'interrupted_streaming_tool') { + bubble = ( +
+ +
+ ); + } + else if (role === 'checkpoint') { + bubble = ( + + ); + } + + if (!bubble) return null; + + return ( + <> + {bubble} + {copyFooter} + + ); +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatCommandBar.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatCommandBar.tsx new file mode 100644 index 00000000000..38ea50cee63 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatCommandBar.tsx @@ -0,0 +1,310 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import React, { useState, useEffect, useCallback } from 'react'; +import { useAccessor } from '../util/services.js'; +import { useChatThreadsState, useChatThreadsStreamState, useCommandBarState } from '../util/services.js'; +import { IconShell1 } from '../markdown/ApplyBlockHoverButtons.js'; +import { Check, X } from 'lucide-react'; +import { LLMTokenUsage } from '../../../../../../../platform/void/common/sendLLMMessageTypes.js'; +import { getBasename, voidOpenFileFn } from './SidebarChatShared.js'; +import { StatusIndicator } from '../markdown/ApplyBlockHoverButtons.js'; + + +export const HistoryCompressionIndicator = () => { + const chatThreadsState = useChatThreadsState(); + const threadId = chatThreadsState.currentThreadId; + const thread = chatThreadsState.allThreads[threadId]; + const info = thread?.state?.historyCompression; + if (!info || !info.hasCompressed) return null; + + const before = info.approxTokensBefore; + const after = info.approxTokensAfter; + const ratio = before > 0 ? Math.round((after / before) * 100) : null; + const format = (n: number) => n.toLocaleString?.() ?? String(n); + + return ( +
+
+ History compressed + + {info.summarizedMessageCount} msg → ~{format(after)} tokens{ratio !== null ? ` (${ratio}% of original)` : ''} + +
+
+ ); +}; + +export const TokenUsageSpoiler = () => { + const chatThreadsState = useChatThreadsState(); + const threadId = chatThreadsState.currentThreadId; + const thread = chatThreadsState.allThreads[threadId]; + const usage = thread?.state?.tokenUsageSession; + const last = (thread?.state as any)?.tokenUsageLastRequest as (LLMTokenUsage | undefined); + const limits = (thread?.state as any)?.tokenUsageLastRequestLimits as ({ maxInputTokens: number } | undefined); + const total = usage ? (usage.input + usage.cacheCreation + usage.cacheRead + usage.output) : 0; + const hasUsage = !!usage && total > 0; + + const [isOpen, setIsOpen] = useState(false); + + useEffect(() => { + setIsOpen(false); + }, [threadId]); + + if (!hasUsage) return null; + + const format = (n: number) => n.toLocaleString?.() ?? String(n); + const formatPct = (v: number) => `${(Math.round(v * 10) / 10).toFixed(1)}%`; + const lastPct = (last && limits && limits.maxInputTokens > 0) + ? (last.input / limits.maxInputTokens) * 100 + : null; + + return ( +
+ + {isOpen && ( +
+ {last && ( +
+ Last request + + input {format(last.input)} + {limits?.maxInputTokens && lastPct !== null + ? ` (~${formatPct(lastPct)} of ${format(limits.maxInputTokens)})` + : ''} + +
+ )} +
Input{format(usage!.input)}
+
Cache creation{format(usage!.cacheCreation)}
+
Cache read{format(usage!.cacheRead)}
+
Output{format(usage!.output)}
+
+ )} +
+ ); +}; + +export const CommandBarInChat = (): React.ReactElement => { + const { stateOfURI: commandBarStateOfURI, sortedURIs: sortedCommandBarURIs } = useCommandBarState(); + const numFilesChanged = sortedCommandBarURIs.length; + + const accessor = useAccessor(); + const editCodeService = accessor.get('IEditCodeService'); + const commandService = accessor.get('ICommandService'); + const chatThreadsState = useChatThreadsState(); + const commandBarState = useCommandBarState(); + const chatThreadsStreamState = useChatThreadsStreamState(chatThreadsState.currentThreadId); + + const [fileDetailsOpenedState, setFileDetailsOpenedState] = useState<'auto-opened' | 'auto-closed' | 'user-opened' | 'user-closed'>('auto-closed'); + const isFileDetailsOpened = fileDetailsOpenedState === 'auto-opened' || fileDetailsOpenedState === 'user-opened'; + + useEffect(() => { + if (numFilesChanged === 0) { + setFileDetailsOpenedState('auto-closed'); + } + if (numFilesChanged > 0 && fileDetailsOpenedState !== 'user-closed') { + setFileDetailsOpenedState('auto-opened'); + } + }, [fileDetailsOpenedState, setFileDetailsOpenedState, numFilesChanged]); + + const isFinishedMakingThreadChanges = ( + commandBarState.sortedURIs.length !== 0 + && commandBarState.sortedURIs.every(uri => !commandBarState.stateOfURI[uri.fsPath]?.isStreaming) + ); + + const threadStatus = ( + chatThreadsStreamState?.isRunning === 'awaiting_user' ? { title: 'Needs Approval', color: 'yellow', } as const + : chatThreadsStreamState?.isRunning ? { title: 'Running', color: 'orange', } as const + : { title: 'Done', color: 'dark', } as const + ); + + const threadStatusHTML = ; + + const numFilesChangedStr = numFilesChanged === 0 ? 'No files with changes' + : `${sortedCommandBarURIs.length} file${numFilesChanged === 1 ? '' : 's'} with changes`; + + const acceptRejectAllButtons =
+ { + sortedCommandBarURIs.forEach(uri => { + editCodeService.acceptOrRejectAllDiffAreas({ + uri, + removeCtrlKs: true, + behavior: "reject", + _addToHistory: true, + }); + }); + }} + data-tooltip-id='void-tooltip' + data-tooltip-place='top' + data-tooltip-content='Reject all' + /> + + { + sortedCommandBarURIs.forEach(uri => { + editCodeService.acceptOrRejectAllDiffAreas({ + uri, + removeCtrlKs: true, + behavior: "accept", + _addToHistory: true, + }); + }); + }} + data-tooltip-id='void-tooltip' + data-tooltip-place='top' + data-tooltip-content='Accept all' + /> +
; + + const fileDetailsContent =
+ {sortedCommandBarURIs.map((uri, i) => { + const basename = getBasename(uri.fsPath); + + const { sortedDiffIds, isStreaming } = commandBarStateOfURI[uri.fsPath] ?? {}; + const isFinishedMakingFileChanges = !isStreaming; + + const numDiffs = sortedDiffIds?.length || 0; + + const fileStatus = (isFinishedMakingFileChanges + ? { title: 'Done', color: 'dark', } as const + : { title: 'Running', color: 'orange', } as const + ); + + const fileNameHTML =
voidOpenFileFn(uri, accessor)} + > + {basename} +
; + + const detailsContent =
+ {numDiffs} diff{numDiffs !== 1 ? 's' : ''} +
; + + const acceptRejectButtons =
+ { editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "reject", _addToHistory: true, }); }} + data-tooltip-id='void-tooltip' + data-tooltip-place='top' + data-tooltip-content='Reject file' + /> + { editCodeService.acceptOrRejectAllDiffAreas({ uri, removeCtrlKs: true, behavior: "accept", _addToHistory: true, }); }} + data-tooltip-id='void-tooltip' + data-tooltip-place='top' + data-tooltip-content='Accept file' + /> +
; + + const fileStatusHTML = ; + + return ( +
+
+ {fileNameHTML} + {detailsContent} +
+
+ {acceptRejectButtons} + {fileStatusHTML} +
+
+ ); + })} +
; + + const fileDetailsButton = ( + + ); + + return ( + <> +
+
+ {fileDetailsContent} +
+
+
+
+ {fileDetailsButton} +
+
+ {acceptRejectAllButtons} + {threadStatusHTML} +
+
+ + ); +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatShared.ts b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatShared.ts new file mode 100644 index 00000000000..52a023168c6 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatShared.ts @@ -0,0 +1,171 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ +import { URI } from '../../../../../../../base/common/uri.js'; +import { ScrollType } from '../../../../../../../editor/common/editorCommon.js'; +import { ChatMessage } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; + +export type AccessorLike = { get: (serviceId: string) => any }; + +export const getRelative = (uri: URI, accessor: AccessorLike) => { + const workspaceContextService = accessor.get('IWorkspaceContextService'); + let path: string = ''; + + const isInside = workspaceContextService.isInsideWorkspace(uri); + if (isInside) { + const f = workspaceContextService + .getWorkspace() + .folders.find((f: any) => uri.fsPath?.startsWith(f.uri.fsPath)); + if (f) { + path = uri.fsPath?.replace(f.uri.fsPath, '') || ''; + } else { + path = uri.fsPath || ''; + } + } else { + path = uri.fsPath || ''; + } + return path || undefined; +}; + +export const getFolderName = (pathStr: string | undefined) => { + if (!pathStr) return ''; + pathStr = pathStr.replace(/[/\\]+/g, '/'); + const parts = pathStr.split('/'); + const nonEmptyParts = parts.filter(p => p.length > 0); + if (nonEmptyParts.length === 0) return '/'; + if (nonEmptyParts.length === 1) return nonEmptyParts[0] + '/'; + return nonEmptyParts.slice(-2).join('/') + '/'; +}; + +export const getBasename = (pathStr: string | undefined, parts: number = 1) => { + if (!pathStr) return ''; + pathStr = pathStr.replace(/[/\\]+/g, '/'); + const allParts = pathStr.split('/'); + if (allParts.length === 0) return pathStr; + return allParts.slice(-parts).join('/'); +}; + +export const getChatMessageMarkdown = (chatMessage: ChatMessage): string => { + const display = (chatMessage as any).displayContent; + if (typeof display === 'string' && display.length > 0) { + return display; + } + const content = (chatMessage as any).content; + if (typeof content === 'string') { + return content; + } + return ''; +}; + + +export const getAssistantTurnInfo = (messages: ChatMessage[], idx: number) => { + if (!Array.isArray(messages) || idx < 0 || idx >= messages.length) return undefined; + + + let prevUserIdx = -1; + for (let i = idx; i >= 0; i--) { + if (messages[i]?.role === 'user') { + prevUserIdx = i; + break; + } + } + + + let nextUserIdx = messages.length; + for (let i = idx + 1; i < messages.length; i++) { + if (messages[i]?.role === 'user') { + nextUserIdx = i; + break; + } + } + + const start = prevUserIdx + 1; + const end = nextUserIdx; // end exclusive + + + const containsAssistant = messages.slice(start, end).some(m => m?.role === 'assistant'); + if (!containsAssistant) return undefined; + + + let lastNonCheckpointIdx = -1; + for (let i = end - 1; i >= start; i--) { + if (messages[i]?.role !== 'checkpoint') { + lastNonCheckpointIdx = i; + break; + } + } + if (lastNonCheckpointIdx === -1) return undefined; + + return { start, end, lastNonCheckpointIdx }; +}; + + +export const getAssistantTurnMarkdown = (messages: ChatMessage[], idx: number): string => { + const info = getAssistantTurnInfo(messages, idx); + if (!info) return ''; + + let markdown = ''; + + for (let i = info.start; i < info.end; i++) { + const m = messages[i]; + if (!m) continue; + + if (m.role === 'assistant') { + const text = getChatMessageMarkdown(m).trim(); + if (text) markdown += `${text}\n\n`; + continue; + } + + if (m.role === 'tool') { + const toolName = (m as any).name || 'tool'; + const toolContent = getChatMessageMarkdown(m); + markdown += `**Tool (${toolName}):**\n\`\`\`\n${toolContent}\n\`\`\`\n\n`; + continue; + } + + if (m.role === 'interrupted_streaming_tool') { + const toolName = (m as any).name || 'tool'; + markdown += `**Tool (${toolName}) canceled**\n\n`; + continue; + } + } + + return markdown.trim(); +}; + + +export const getAssistantResponseMarkdown = (messages: ChatMessage[], startIdx: number): string => { + return getAssistantTurnMarkdown(messages, startIdx); +}; + +export const voidOpenFileFn = ( + uri: URI, + accessor: AccessorLike, + range?: [number, number], + _scrollToBottom?: () => void, +) => { + const commandService = accessor.get('ICommandService'); + const editorService = accessor.get('ICodeEditorService'); + + let editorSelection = undefined as any; + if (range) { + editorSelection = { + startLineNumber: range[0], + startColumn: 1, + endLineNumber: range[1], + endColumn: Number.MAX_SAFE_INTEGER, + }; + } + + commandService.executeCommand('vscode.open', uri).then(() => { + setTimeout(() => { + if (!editorSelection) return; + const editor = editorService.getActiveCodeEditor(); + if (!editor) return; + editor.setSelection(editorSelection); + editor.revealRange(editorSelection, ScrollType.Immediate); + _scrollToBottom?.(); + }, 50); + }); +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatTools.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatTools.tsx new file mode 100644 index 00000000000..02852c04378 --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatTools.tsx @@ -0,0 +1,2104 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + + +import React, { useState, useEffect, useLayoutEffect, useCallback, useMemo, useRef } from 'react'; +import { useAccessor, useChatThreadsStreamState, } from '../util/services.js'; + +import { AlertTriangle, Ban, ChevronRight, CircleEllipsis } from 'lucide-react'; + +import { ToolApprovalTypeSwitch } from '../void-settings-tsx/Settings.js'; +import { VoidSwitch } from '../util/inputs.js'; +import { ToolName, toolNames } from '../../../../common/prompt/prompts.js'; +import { approvalTypeOfToolName } from '../../../../../../../platform/void/common/toolsServiceTypes.js'; +import { isDangerousTerminalCommand } from '../../../../common/toolsService.js'; + +import { CopyButton, EditToolAcceptRejectButtonsHTML, useEditToolStreamState } from '../markdown/ApplyBlockHoverButtons.js'; +import { ChatMessage, ToolMessage, } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; +import { URI } from '../../../../../../../base/common/uri.js'; +import { getBasename, getRelative, getFolderName, voidOpenFileFn } from './SidebarChatShared.js'; +import { IconLoading, ToolChildrenWrapper, CodeChildren, ListableToolItem } from './SidebarChatUI.js'; +import { LintErrorItem, ToolCallParams, ShallowDirectoryItem } from '../../../../../../../platform/void/common/toolsServiceTypes.js'; +import { ChatMarkdownRender, getApplyBoxId } from '../markdown/ChatMarkdownRender.js'; +import { RawToolCallObj } from '../../../../../../../platform/void/common/sendLLMMessageTypes.js'; +import { persistentTerminalNameOfId } from '../../../terminalToolService.js'; +import { BlockCode } from '../util/inputs.js'; +import { MAX_FILE_CHARS_PAGE } from '../../../../../../../platform/void/common/prompt/constants.js'; + +const USER_CANCELED_TOOL_LABEL = 'User canceled tool'; + +const applyCanceledUi = (componentParams: ToolHeaderParams, toolMessage: any) => { + if (toolMessage?.type !== 'rejected') return; + componentParams.isRejected = true; + componentParams.rejectedTooltip = USER_CANCELED_TOOL_LABEL; + // show a visible label in header if nothing else is shown on the right + if (componentParams.desc2 === undefined) { + componentParams.desc2 = USER_CANCELED_TOOL_LABEL; + } +}; + +export const getReadFileRange = (params: any): string => { + const startLine = params?.start_line ?? params?.startLine; + const endLine = params?.end_line ?? params?.endLine; + const linesCount = params?.lines_count ?? params?.linesCount; + + if (!startLine && !endLine && !linesCount) { + return '[all]'; + } + + if (linesCount) { + const start = startLine ? Number(startLine) : 1; + const end = start + Number(linesCount) - 1; + return `[${start}-${end}]`; + } + + if (startLine || endLine) { + const start = startLine ? Number(startLine) : 1; + const end = endLine ? Number(endLine) : '...'; + return `[${start}-${end}]`; + } + + return ''; +}; + +export const loadingTitleWrapper = (item: React.ReactNode): React.ReactNode => { + return + {item} + + ; +}; + +export const titleOfToolName = { + 'read_file': { + done: (params: any) => `Read file ${getReadFileRange(params)}`, + proposed: (params: any) => `Read file ${getReadFileRange(params)}`, + running: (params: any) => loadingTitleWrapper(`Reading file ${getReadFileRange(params)}`) + }, + 'ls_dir': { done: 'Inspected folder', proposed: 'Inspect folder', running: loadingTitleWrapper('Inspecting folder') }, + 'get_dir_tree': { done: 'Inspected folder tree', proposed: 'Inspect folder tree', running: loadingTitleWrapper('Inspecting folder tree') }, + 'search_pathnames_only': { done: 'Searched by file name', proposed: 'Search by file name', running: loadingTitleWrapper('Searching by file name') }, + 'search_for_files': { done: 'Searched', proposed: 'Search', running: loadingTitleWrapper('Searching') }, + 'create_file_or_folder': { done: `Created`, proposed: `Create`, running: loadingTitleWrapper(`Creating`) }, + 'delete_file_or_folder': { done: `Deleted`, proposed: `Delete`, running: loadingTitleWrapper(`Deleting`) }, + 'rewrite_file': { done: `Wrote file`, proposed: 'Write file', running: loadingTitleWrapper('Writing file') }, + 'run_command': { + done: 'Run terminal', + proposed: 'Run terminal', + running: loadingTitleWrapper('Run terminal'), + }, + 'run_persistent_command': { + done: 'Run terminal', + proposed: 'Run terminal', + running: loadingTitleWrapper('Run terminal'), + }, + + 'open_persistent_terminal': { done: `Opened terminal`, proposed: 'Open terminal', running: loadingTitleWrapper('Opening terminal') }, + 'kill_persistent_terminal': { done: `Killed terminal`, proposed: 'Kill terminal', running: loadingTitleWrapper('Killing terminal') }, + + 'read_lint_errors': { done: `Read lint errors`, proposed: 'Read lint errors', running: loadingTitleWrapper('Reading lint errors') }, + 'search_in_file': { done: 'Searched in file', proposed: 'Search in file', running: loadingTitleWrapper('Searching in file') }, + 'edit_file': { done: 'Previewed edit', proposed: 'Edit file (preview)', running: loadingTitleWrapper('Preparing preview') }, +} as const; + +export const getTitle = (toolMessage: Pick): React.ReactNode => { + const t = toolMessage; + if (!toolNames.includes(t.name as ToolName)) return t.name; + + const toolName = t.name as ToolName; + const toolConfig = titleOfToolName[toolName]; + + if (t.type === 'success') { + if (typeof toolConfig.done === 'function') { + return toolConfig.done(t.rawParams); + } + return toolConfig.done; + } + + if (t.type === 'running_now') { + if (typeof toolConfig.running === 'function') { + return toolConfig.running(t.rawParams); + } + return toolConfig.running; + } + + return typeof toolConfig.proposed === 'function' ? toolConfig.proposed(t.rawParams) : toolConfig.proposed; +}; + +export const toolNameToDesc = (toolName: ToolName, _toolParams: any, accessor: any): { + desc1: React.ReactNode, + desc1Info?: string, +} => { + if (!_toolParams) { + return { desc1: '' }; + } + + const x = { + 'read_file': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? getBasename(fsPath) : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + 'ls_dir': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? (getFolderName(fsPath) ?? '/') : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + + // --- SEARCH TOOLS: do NOT show query in header (params will be shown in children area) + 'search_pathnames_only': () => { + return { desc1: '' }; + }, + 'search_for_files': () => { + return { desc1: '' }; + }, + 'search_in_file': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? getBasename(fsPath) : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + + 'create_file_or_folder': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + const isFolder = toolParams?.isFolder ?? false; + return { + desc1: fsPath + ? (isFolder ? (getFolderName(fsPath) ?? '/') : getBasename(fsPath)) + : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + 'delete_file_or_folder': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + const isFolder = toolParams?.isFolder ?? false; + return { + desc1: fsPath + ? (isFolder ? (getFolderName(fsPath) ?? '/') : getBasename(fsPath)) + : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + 'rewrite_file': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? getBasename(fsPath) : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + + 'run_command': () => ({ desc1: '' }), + 'run_persistent_command': () => ({ desc1: '' }), + 'open_persistent_terminal': () => ({ desc1: '' }), + 'kill_persistent_terminal': () => { + const toolParams = _toolParams as any; + return { desc1: toolParams?.persistentTerminalId ?? '' }; + }, + + 'get_dir_tree': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? (getFolderName(fsPath) ?? '/') : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + 'read_lint_errors': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + return { + desc1: fsPath ? getBasename(fsPath) : '', + desc1Info: uri ? getRelative(uri, accessor) : undefined, + }; + }, + + 'edit_file': () => { + const toolParams = _toolParams as any; + const uri = getUriFromToolParams(toolParams, accessor); + const fsPath = uri?.fsPath; + const relPath = uri ? (getRelative(uri, accessor) ?? undefined) : undefined; + const displayPath = relPath ? String(relPath).replace(/^[\\/]+/, '') || '.' : undefined; + return { + desc1: displayPath ?? (fsPath ? getBasename(fsPath) : ''), + desc1Info: displayPath ?? (uri ? getRelative(uri, accessor) : undefined), + }; + }, + }; + + try { + return (x as any)[toolName]?.() || { desc1: '' }; + } catch { + return { desc1: '' }; + } +}; + +export const ProseWrapper = ({ children }: { children: React.ReactNode }) => { + return
+ {children} +
+}; + +export type ToolHeaderParams = { + icon?: React.ReactNode; + title: React.ReactNode; + desc1: React.ReactNode; + desc1OnClick?: () => void; + desc2?: React.ReactNode; + isError?: boolean; + info?: string; + desc1Info?: string; + + isRejected?: boolean; + rejectedTooltip?: string; + + numResults?: number; + hasNextPage?: boolean; + + subChildren?: React.ReactNode; + children?: React.ReactNode; + bottomChildren?: React.ReactNode; + + onClick?: () => void; + desc2OnClick?: () => void; + + /** Controlled open state (optional). */ + isOpen?: boolean; + + /** If flips false->true, uncontrolled spoiler will auto-open once. */ + defaultIsOpen?: boolean; + + className?: string; +}; + +export const ToolHeaderWrapper = ({ + icon, + title, + desc1, + desc1OnClick, + desc1Info, + desc2, + numResults, + hasNextPage, + subChildren, + children, + info, + bottomChildren, + isError, + onClick, + desc2OnClick, + isOpen, + defaultIsOpen, + isRejected, + rejectedTooltip, + className, +}: ToolHeaderParams) => { + + const hasDropdownChildren = children !== undefined && children !== null; + + const [isOpen_, setIsOpen] = useState(() => !!defaultIsOpen); + const isExpanded = isOpen !== undefined ? isOpen : isOpen_; + + const isDropdown = hasDropdownChildren; + const isClickable = !!(isDropdown || onClick); + const isDesc1Clickable = !!desc1OnClick; + + // Auto-open once when defaultIsOpen flips false -> true (uncontrolled only) + const prevDefaultRef = useRef(!!defaultIsOpen); + useEffect(() => { + if (isOpen !== undefined) return; + const prev = prevDefaultRef.current; + const next = !!defaultIsOpen; + prevDefaultRef.current = next; + if (!prev && next) setIsOpen(true); + }, [defaultIsOpen, isOpen]); + + const desc1HTML = ( + { + e.stopPropagation(); + desc1OnClick?.(); + }) : undefined} + {...desc1Info ? { + 'data-tooltip-id': 'void-tooltip', + 'data-tooltip-content': desc1Info, + 'data-tooltip-place': 'top', + 'data-tooltip-delay-show': 1000, + } : {}} + > + {desc1} + + ); + + return ( +
+
+ {/* header */} +
+
+ {/* left */} +
+
{ + if (isDropdown && isOpen === undefined) { setIsOpen(v => !v); } + onClick?.(); + }} + > + {isDropdown && ( + + )} + {title} + {!isDesc1Clickable && desc1HTML} +
+ {isDesc1Clickable && desc1HTML} +
+ + {/* right */} +
+ {info && ( + + )} + + {isError && ( + + )} + {isRejected && ( + + )} + {desc2 && ( + { e.stopPropagation(); desc2OnClick?.(); }}> + {desc2} + + )} + {numResults !== undefined && ( + + {`${numResults}${hasNextPage ? '+' : ''} result${numResults !== 1 ? 's' : ''}`} + + )} +
+
+
+ + {/* always-visible under-header block */} + {subChildren !== undefined && subChildren !== null && ( +
+ {subChildren} +
+ )} + + {/* children (collapsible result) */} + {hasDropdownChildren && isExpanded && ( +
+ {children} +
+ )} +
+ + {bottomChildren} +
+ ); +}; + +export const ToolRequestAcceptRejectButtons = ({ toolName }: { toolName: ToolName }) => { + const accessor = useAccessor(); + const chatThreadsService = accessor.get('IChatThreadService'); + const metricsService = accessor.get('IMetricsService'); + const voidSettingsService = accessor.get('IVoidSettingsService'); + + const isAcp = !!voidSettingsService.state.globalSettings.useAcp; + + const onAccept = useCallback(() => { + try { + const threadId = chatThreadsService.state.currentThreadId; + chatThreadsService.approveLatestToolRequest(threadId); + metricsService.capture('Tool Request Accepted', {}); + } catch (e) { + console.error('Error while approving message in chat:', e); + } + }, [chatThreadsService, metricsService]); + + const onReject = useCallback(() => { + try { + const threadId = chatThreadsService.state.currentThreadId; + + // Always mark tool as rejected so it is struck-through in UI + chatThreadsService.rejectLatestToolRequest(threadId); + + // ACP: additionally abort the run (old behavior) + if (isAcp) { + void chatThreadsService.abortRunning(threadId); + } + } catch (e) { + console.error('Error while rejecting tool request:', e); + } + metricsService.capture('Tool Request Rejected', {}); + }, [chatThreadsService, metricsService, isAcp]); + + const onSkip = useCallback(() => { + try { + const threadId = chatThreadsService.state.currentThreadId; + + // Skip != Cancel: + // skip should mark tool as "skipped" (NOT "rejected"), so it won't show "User canceled tool". + // This works for both ACP and non-ACP. + chatThreadsService.skipLatestToolRequest(threadId); + } catch (e) { + console.error('Error while skipping tool request:', e); + } + metricsService.capture('Tool Request Skipped', {}); + }, [chatThreadsService, metricsService]); + + const [showSkipButton, setShowSkipButton] = useState(false); + + useEffect(() => { + const timeoutId = setTimeout(() => { + setShowSkipButton(true); + }, 10000); + + return () => clearTimeout(timeoutId); + }, []); + + const approveButton = ( + + ); + + const cancelButton = ( + + ); + + const skipButton = ( + + ); + + const approvalType = approvalTypeOfToolName[toolName]; + let alwaysRequireManualApproval = false; + if (approvalType === 'terminal' && (toolName === 'run_command' || toolName === 'run_persistent_command')) { + try { + const threadId = chatThreadsService.state.currentThreadId; + const thread = chatThreadsService.state.allThreads[threadId]; + const lastMsg = thread?.messages[thread.messages.length - 1]; + if (lastMsg && lastMsg.role === 'tool' && lastMsg.type === 'tool_request' && lastMsg.name === toolName) { + const cmd = typeof (lastMsg.params as any)?.command === 'string' ? (lastMsg.params as any).command : undefined; + if (cmd && isDangerousTerminalCommand(cmd)) { + alwaysRequireManualApproval = true; + } + } + } catch { + // best-effort only + } + } + + if (!approvalType && voidSettingsService.state.globalSettings.mcpAutoApprove) { + return
+ {toolName} + (auto-approved) +
; + } + + if (approvalType && !alwaysRequireManualApproval && voidSettingsService.state.globalSettings.autoApprove?.[approvalType]) { + return
+ {toolName} + (auto-approved) +
; + } + + const approvalToggle = approvalType ? +
+ { + const threadId = chatThreadsService.state.currentThreadId; + chatThreadsService.approveLatestToolRequest(threadId); + metricsService.capture('Tool Request Accepted', {}); + }} + /> +
: + (!approvalType &&
+ { + voidSettingsService.setGlobalSetting('mcpAutoApprove', newVal); + if (newVal) { + const threadId = chatThreadsService.state.currentThreadId; + chatThreadsService.approveLatestToolRequest(threadId); + metricsService.capture('Tool Request Accepted', {}); + } + }} + /> + Auto-approve +
); + + const shouldShowSkipButton = approvalTypeOfToolName[toolName] !== undefined || showSkipButton; + + return
+ {approveButton} + {cancelButton} + {shouldShowSkipButton && skipButton} + {approvalToggle} +
; +}; + +export const BottomChildren = ({ children, title }: { children: React.ReactNode, title: string }) => { + const [isOpen, setIsOpen] = useState(false); + if (!children) return null; + + return ( +
+
setIsOpen(o => !o)} + style={{ background: 'none' }} + > + + {title} +
+ + {isOpen && ( +
+
+ {children} +
+
+ )} +
+ ); +}; + +export const DynamicToolHeader = ({ toolMessage }: { toolMessage: any }) => { + const title = getTitle(toolMessage); + const desc1 = ''; + const icon = null; + const isError = toolMessage.type === 'tool_error'; + const isRejected = toolMessage.type === 'rejected'; + const componentParams: ToolHeaderParams = { title, desc1, isError, icon, isRejected }; + applyCanceledUi(componentParams, toolMessage); + + if (toolMessage.type === 'success') { + componentParams.children = ( + + + {toolMessage.displayContent || toolMessage.content || (typeof toolMessage.result === 'string' ? toolMessage.result : JSON.stringify(toolMessage.result, null, 2))} + + + ); + } else if (toolMessage.type === 'tool_error') { + componentParams.children = ( + + + {toolMessage.result} + + + ); + } else if (toolMessage.type === 'running_now') { + componentParams.children = ( + +
+ {toolMessage.displayContent || toolMessage.content} +
+
+ ); + } + return ; +}; + +export const InvalidTool = ({ toolName, message }: { toolName: ToolName, message: string }) => { + const accessor = useAccessor(); + const title = getTitle({ name: toolName, type: 'invalid_params', rawParams: {} }); + const desc1 = 'Invalid parameters'; + const icon = null; + const isError = true; + const componentParams: ToolHeaderParams = { title, desc1, isError, icon }; + + componentParams.children = + + {message} + + ; + return ; +}; + +export const CanceledTool = ({ toolName }: { toolName: ToolName }) => { + const accessor = useAccessor(); + const title = getTitle({ name: toolName, type: 'rejected', rawParams: {} }); + const desc1 = ''; + const icon = null; + + const componentParams: ToolHeaderParams = { + title, + desc1, + icon, + isRejected: true, + desc2: USER_CANCELED_TOOL_LABEL, + rejectedTooltip: USER_CANCELED_TOOL_LABEL, + }; + + return ; +}; + +export const SkippedTool = ({ toolMessage }: { toolMessage: any }) => { + const accessor = useAccessor(); + + const title = getTitle(toolMessage); + + // Try to show the same desc1/tooltip as normal tools (file name, folder, etc.) + let desc1: React.ReactNode = ''; + let desc1Info: string | undefined = undefined; + try { + const name = toolMessage?.name; + if (toolNames.includes(name as ToolName)) { + const tn = name as ToolName; + const paramsAny = (toolMessage as any).params ?? (toolMessage as any).rawParams ?? {}; + const d = toolNameToDesc(tn, paramsAny, accessor); + desc1 = d.desc1; + desc1Info = d.desc1Info; + } + } catch { + // best-effort only + } + + return ( + + ); +}; + +export const LintErrorChildren = ({ lintErrors }: { lintErrors: LintErrorItem[] }) => { + return
+ {lintErrors.map((error, i) => ( +
Lines {error.startLineNumber}-{error.endLineNumber}: {error.message}
+ ))} +
; +}; + +export const EditToolChildren = ({ uri, code }: { uri: URI | undefined, code: string }) => { + return
+ + + +
; +}; + +export const EditToolHeaderButtons = ({ applyBoxId, uri, codeStr, toolName, threadId }: { threadId: string, applyBoxId: string, uri: URI | undefined, codeStr: string, toolName: 'edit_file' | 'rewrite_file' }) => { + const { streamState } = uri ? useEditToolStreamState({ applyBoxId, uri }) : { streamState: 'idle-no-changes' }; + return
+ {streamState === 'idle-no-changes' && } + {uri && } +
; +}; + +export const EditToolSoFar = ({ toolCallSoFar, }: { toolCallSoFar: RawToolCallObj }) => { + const accessor = useAccessor(); + const uri = getUriFromToolParams(toolCallSoFar.rawParams, accessor); + const streamingToolName = toolCallSoFar.name as ToolName; + const toolConfig = titleOfToolName[streamingToolName]; + const title = typeof toolConfig.proposed === 'function' ? toolConfig.proposed(toolCallSoFar.rawParams) : toolConfig.proposed; + const uriDone = toolCallSoFar.doneParams.includes('uri'); + const desc1 = + {uriDone ? getBasename(toolCallSoFar.rawParams['uri'] ?? 'unknown') : `Generating`} + + ; + const desc1OnClick = () => { uri && voidOpenFileFn(uri, accessor); }; + return + { + const raw = toolCallSoFar.rawParams as any; + return raw?.updated_snippet ?? raw?.original_snippet ?? raw?.new_content ?? ''; + })()} /> + + ; +}; + + +export const CommandTool = ({ toolMessage, type, threadId }: { threadId: string } & ({ + toolMessage: Exclude, { type: 'invalid_params' }>; + type: 'run_command'; +} | { + toolMessage: Exclude, { type: 'invalid_params' }>; + type: 'run_persistent_command'; +})) => { + const accessor = useAccessor(); + const terminalToolsService = accessor.get('ITerminalToolService'); + const toolsService = accessor.get('IToolsService'); + const chatThreadsService = accessor.get('IChatThreadService'); + + + + const streamHookAny = useChatThreadsStreamState(threadId) as any; + const threadStreamState: any = streamHookAny?.streamState ?? streamHookAny; + + const title = getTitle(toolMessage); + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); + const isRejected = toolMessage.type === 'rejected'; + + const componentParams: ToolHeaderParams = { + title, + desc1, + desc1Info, + isError: false, + icon: null, + isRejected + }; + applyCanceledUi(componentParams, toolMessage); + + const commandStr = useMemo(() => { + try { + if (type === 'run_command') { + return (toolMessage.params as ToolCallParams['run_command']).command; + } + return (toolMessage.params as ToolCallParams['run_persistent_command']).command; + } catch { + return ''; + } + }, [toolMessage, type]); + + const onSkipRunningCommand = useCallback(() => { + try { + chatThreadsService.skipRunningTool(threadId); + } catch (e) { + console.error('Error while skipping running tool:', e); + } + }, [chatThreadsService, threadId]); + + // ---- IMPORTANT: force rerender while running (non-ACP can update terminalId/output by mutation) ---- + const [pollTick, setPollTick] = useState(0); + useEffect(() => { + if (toolMessage.type !== 'running_now') return; + const id = setInterval(() => setPollTick(v => v + 1), 250); + return () => clearInterval(id); + }, [toolMessage.type]); + + const [attachFailed, setAttachFailed] = useState(false); + const terminalContainerRef = useRef(null); + + + const streamStateContent = useMemo((): string => { + try { + if (!threadStreamState) return ''; + if (threadStreamState.isRunning !== 'tool') return ''; + + const toolInfo = threadStreamState.toolInfo; + if (!toolInfo) return ''; + if (toolInfo.toolName !== type) return ''; + + + const msgId = (toolMessage as any)?.id; + const infoId = toolInfo?.id; + + if (msgId && infoId) { + if (msgId !== infoId) return ''; + } else { + const infoCmd = toolInfo?.toolParams?.command; + if (typeof infoCmd === 'string' && typeof commandStr === 'string' && infoCmd !== commandStr) return ''; + } + + const c = toolInfo?.content; + return typeof c === 'string' ? c : ''; + } catch { + return ''; + } + }, [threadStreamState, toolMessage, type, commandStr, pollTick]); + + const tmpTerminalId: string | undefined = useMemo(() => { + const p: any = (toolMessage as any)?.params ?? {}; + const r: any = (toolMessage as any)?.result ?? {}; + const ro: any = (toolMessage as any)?.rawOutput ?? {}; + const rp: any = (toolMessage as any)?.rawParams ?? {}; + + + const toolInfoParams: any = threadStreamState?.toolInfo?.toolParams ?? {}; + + const candidates = [ + p.terminalId, p.tmpTerminalId, p.temporaryTerminalId, + r.terminalId, r.tmpTerminalId, r.temporaryTerminalId, + ro.terminalId, ro.tmpTerminalId, ro.temporaryTerminalId, + rp.terminalId, rp.tmpTerminalId, rp.temporaryTerminalId, + + toolInfoParams.terminalId, + toolInfoParams.tmpTerminalId, + toolInfoParams.temporaryTerminalId, + ]; + + for (const c of candidates) { + if (typeof c === 'string' && c.trim()) return c.trim(); + } + return undefined; + // pollTick forces re-evaluation even if objects were mutated without state updates + }, [toolMessage, pollTick, threadStreamState]); + + const attachableTerminal = useMemo(() => { + if (type !== 'run_command') return undefined; + if (toolMessage.type !== 'running_now') return undefined; + if (!tmpTerminalId) return undefined; + return terminalToolsService.getTemporaryTerminal(tmpTerminalId); + }, [terminalToolsService, tmpTerminalId, toolMessage.type, type]); + + useEffect(() => { + if (!attachableTerminal) return; + + const container = terminalContainerRef.current; + if (!container) return; + + try { + if (typeof (attachableTerminal as any).attachToElement !== 'function') { + setAttachFailed(true); + return; + } + (attachableTerminal as any).attachToElement(container); + (attachableTerminal as any).setVisible(true); + setAttachFailed(false); + } catch { + setAttachFailed(true); + return; + } + + const resizeObserver = new ResizeObserver((entries) => { + const height = entries[0].borderBoxSize[0].blockSize; + const width = entries[0].borderBoxSize[0].inlineSize; + if (typeof (attachableTerminal as any).layout === 'function') { + (attachableTerminal as any).layout({ width, height }); + } + }); + resizeObserver.observe(container); + + return () => { + try { (attachableTerminal as any).detachFromElement?.(); } catch { } + try { resizeObserver.disconnect(); } catch { } + }; + }, [attachableTerminal]); + const commandBlock = commandStr + ?
{commandStr}
+ : null; + + // Avoid showing engine placeholder as "output" + const sanitizeRunningPlaceholder = (s: unknown): string => { + if (typeof s !== 'string') return ''; + const t = s.trim(); + if (!t) return ''; + if (t === 'value not received yet...' || t === 'running...') return ''; + return s; + }; + + const streamingText = useMemo(() => { + const candidates: unknown[] = [ + + streamStateContent, + + (toolMessage as any)?.result?.output, + (toolMessage as any)?.rawOutput?.output, + sanitizeRunningPlaceholder((toolMessage as any)?.displayContent), + sanitizeRunningPlaceholder((toolMessage as any)?.content), + (toolMessage as any)?.result?.text, + (toolMessage as any)?.rawOutput?.text, + ]; + + for (const c of candidates) { + if (typeof c === 'string' && c.length > 0) return c; + } + return ''; + }, [toolMessage, pollTick, streamStateContent]); + + const TAIL_LIMIT = 6000; + const displayStreamingText = useMemo(() => { + if (!streamingText) return ''; + if (toolMessage.type !== 'running_now') return streamingText; + if (streamingText.length <= TAIL_LIMIT) return streamingText; + + const tail = streamingText.slice(streamingText.length - TAIL_LIMIT); + return ( + `[showing last ${TAIL_LIMIT} chars of ${streamingText.length}]\n` + + `…\n` + + tail + ); + }, [streamingText, toolMessage.type]); + + const outputScrollRef = useRef(null); + useEffect(() => { + if (toolMessage.type !== 'running_now') return; + if (attachableTerminal) return; + + const el = outputScrollRef.current; + if (!el) return; + el.scrollTop = el.scrollHeight; + }, [attachableTerminal, displayStreamingText, toolMessage.type]); + + if (toolMessage.type === 'success') { + const { result } = toolMessage; + + let msg: string = + toolMessage.displayContent + ?? toolMessage.content + ?? (type === 'run_command' + ? toolsService.stringOfResult['run_command'](toolMessage.params, result) + : toolsService.stringOfResult['run_persistent_command'](toolMessage.params, result)); + + componentParams.children = ( + +
+ +
+
+ ); + + componentParams.bottomChildren = commandBlock; + return ; + } + + if (toolMessage.type === 'tool_error') { + componentParams.bottomChildren = ( + <> + {commandBlock} + + {String((toolMessage as any).result ?? '')} + + + ); + return ; + } + + if (toolMessage.type === 'running_now') { + if (type === 'run_command') { + componentParams.children = (attachableTerminal && !attachFailed) + ?
+ : ( + + +
+
+									{displayStreamingText || '(waiting for output...)'}
+								
+
+
+
+ ); + } else { + componentParams.children = ( + + +
+
+								{displayStreamingText || '(running...)'}
+							
+
+
+
+ ); + } + + componentParams.bottomChildren = ( + <> + {commandBlock} +
+ +
+ + ); + return ; + } + + componentParams.bottomChildren = commandBlock; + return ; +}; + +interface EditToolResult { + patch_unified?: string; + preview?: { + patch_unified?: string; + }; + applied?: boolean; + error?: string; + debug_cmd?: string | null; + debug_cmd_alt?: string | null; + lintErrors?: LintErrorItem[]; +} + +const resolvePathLikeToUri = (pathLike: unknown, accessor: any): URI | undefined => { + if (!pathLike) return undefined; + + // URI instance + if (URI.isUri(pathLike)) return pathLike; + + // URI DTO-like object (e.g. after serialization) + if (typeof pathLike === 'object') { + try { + const obj = pathLike as any; + if (typeof obj.scheme === 'string' && typeof obj.path === 'string') { + return URI.from(obj); + } + if (typeof obj.fsPath === 'string' && obj.fsPath.trim()) { + return URI.file(obj.fsPath.trim()); + } + } catch { + // ignore and continue fallback parsing + } + } + + if (typeof pathLike !== 'string') return undefined; + const raw = pathLike.trim(); + if (!raw) return undefined; + + // Real URI with scheme (file://, vscode-remote://, etc) + const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(raw) && !/^[a-zA-Z]:[\\/]/.test(raw); + if (hasScheme) { + try { return URI.parse(raw); } catch { /* ignore */ } + } + + const workspaceFolders = accessor.get('IWorkspaceContextService').getWorkspace()?.folders ?? []; + const root = workspaceFolders[0]?.uri as URI | undefined; + + const isWindowsAbs = /^[a-zA-Z]:[\\/]/.test(raw); + const isPosixAbs = raw.startsWith('/'); + const normalizeFsPath = (p: string) => String(p ?? '').replace(/\\/g, '/').replace(/\/+$/g, ''); + const rootNorm = root ? normalizeFsPath(root.fsPath) : ''; + const rawNorm = normalizeFsPath(raw); + + if (root && rootNorm && (rawNorm === rootNorm || rawNorm.startsWith(rootNorm + '/'))) { + if (root.scheme === 'file') { + return URI.file(raw); + } + const rel = rawNorm === rootNorm ? '' : rawNorm.slice(rootNorm.length + 1); + return rel ? URI.joinPath(root, rel) : root; + } + + // Keep behavior consistent with tool path normalization: in a workspace, + // "/src/..." is treated as workspace-relative, not filesystem root. + if (root) { + let rel = raw; + if (rel.startsWith('./') || rel.startsWith('.\\')) rel = rel.slice(2); + rel = rel.replace(/^[\\/]+/, ''); + return rel ? URI.joinPath(root, rel) : root; + } + + // No workspace: best effort as local file path + if (isWindowsAbs || isPosixAbs) { + try { return URI.file(raw); } catch { return undefined; } + } + try { return URI.file(raw); } catch { return undefined; } +}; + +const getUriFromToolParams = (paramsAny: any, accessor: any): URI | undefined => { + if (!paramsAny) return undefined; + return resolvePathLikeToUri(paramsAny.uri, accessor) + ?? resolvePathLikeToUri(paramsAny.path, accessor) + ?? resolvePathLikeToUri(paramsAny.filePath, accessor); +}; + +export type ResultWrapper = (props: { toolMessage: Exclude, { type: 'invalid_params' }>, messageIdx: number, threadId: string }) => React.ReactNode +const EditTool = ( + { toolMessage, threadId, messageIdx, content }: Parameters>[0] & { content: string } +) => { + const accessor = useAccessor(); + const editCodeService = accessor.get('IEditCodeService'); + const languageService = accessor.get('ILanguageService'); + + const isError = false; + const isRejected = toolMessage.type === 'rejected'; + const isCanceled = toolMessage.type === 'rejected'; + + const title = getTitle(toolMessage); + + const paramsAny = (toolMessage as any).params ?? {}; + const uri = getUriFromToolParams(paramsAny, accessor); + const fsPath = uri?.fsPath ?? ''; + + // IMPORTANT: toolNameToDesc is now defensive too + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, paramsAny, accessor); + const icon = null; + + const { name } = toolMessage as any; + + const desc1OnClick = uri ? () => voidOpenFileFn(uri, accessor) : undefined; + + const componentParams: ToolHeaderParams = { + title, + desc1, + desc1OnClick, + desc1Info, + isError, + icon, + isRejected, + }; + + // Apply common canceled-UI (strike-through + label "User chancel by tool") + applyCanceledUi(componentParams, toolMessage); + + const [fallbackMsg, setFallbackMsg] = useState(() => { + if (!uri) return null; + return editCodeService?.getLastFallbackMessage?.(uri) ?? null; + }); + + useEffect(() => { + if (!uri) { + setFallbackMsg(null); + return; + } + setFallbackMsg(editCodeService?.getLastFallbackMessage?.(uri) ?? null); + }, [editCodeService, fsPath]); + + useEffect(() => { + if (!editCodeService?.onDidUseFallback) return; + if (!uri) return; + + const sub = editCodeService.onDidUseFallback((e: { uri: URI; message?: string }) => { + if (e?.uri?.fsPath && e.uri.fsPath === uri.fsPath) { + setFallbackMsg(e.message ?? 'LLM did not correctly provide an ORIGINAL code block'); + } + }); + return () => { sub?.dispose?.(); }; + }, [editCodeService, fsPath]); + + const language = uri ? (languageService.guessLanguageIdByFilepathOrFirstLine(uri) || 'plaintext') : 'plaintext'; + + if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { + componentParams.children = ( + + {content} + + ); + } else if ( + toolMessage.type === 'success' || + toolMessage.type === 'rejected' || + toolMessage.type === 'tool_error' || + toolMessage.type === 'skipped' + ) { + const applyBoxId = getApplyBoxId({ + threadId, + messageIdx, + tokenIdx: 'N/A', + }); + + // For skipped/canceled tools, do not show apply buttons + if (toolMessage.type !== 'skipped' && !isCanceled) { + componentParams.desc2 = ( + + ); + } + + if (toolMessage.type === 'success' || toolMessage.type === 'rejected') { + const blocks: React.ReactNode[] = []; + const result = toolMessage.result as EditToolResult | null; + const shouldShowFallback = !!fallbackMsg && result?.applied === false; + + if (shouldShowFallback) { + const cmd = result?.debug_cmd; + const cmdAlt = result?.debug_cmd_alt; + + blocks.push( + +
{fallbackMsg}
+ {cmd ?
{cmd}
: null} + {cmdAlt ?
{cmdAlt}
: null} +
+ ); + } + + if (result?.lintErrors && Array.isArray(result.lintErrors) && result.lintErrors.length > 0) { + blocks.push( + + {result.lintErrors.map((error: LintErrorItem, i: number) => ( +
+ Lines {error.startLineNumber}-{error.endLineNumber}: {error.message} +
+ ))} +
+ ); + } + + const patchUnified = result?.patch_unified || result?.preview?.patch_unified; + if (patchUnified && uri) { + const rel = getRelative(uri, accessor) || getBasename(uri.fsPath); + const normalizeRel = String(rel).replace(/^[\\/]+/, ''); + + const patchUnifiedWithRelativePaths = String(patchUnified) + .replace(/^---\s+a\/.*$/m, `--- a/${normalizeRel}`) + .replace(/^\+\+\+\s+b\/.*$/m, `+++ b/${normalizeRel}`); + + blocks.push( + + {patchUnifiedWithRelativePaths} + + ); + } + + if (blocks.length > 0) { + componentParams.bottomChildren = <>{blocks}; + } else if (result && 'error' in result && typeof result.error === 'string' && result.error.includes('original_snippet and updated_snippet are identical')) { + componentParams.bottomChildren = ( + +
+ No changes were made. The original and updated snippets are identical. +
+
+ ); + } + } else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage as any; + componentParams.bottomChildren = ( + + {String(result ?? '')} + + ); + } else if (toolMessage.type === 'skipped') { + componentParams.isRejected = true; + componentParams.desc2 = 'Skipped by user'; + componentParams.rejectedTooltip = 'Skipped by user'; + } + } + + return ; +}; + +type AnyResultWrapper = (props: any) => React.ReactNode +export const toolNameToComponent: Partial> = { + 'read_file': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor(); + const languageService = accessor.get('ILanguageService'); + + const isRejected = toolMessage.type === 'rejected'; + const isError = toolMessage.type === 'tool_error'; + + const paramsAny = (toolMessage as any).params ?? {}; + + // Robust URI extraction (ACP/dynamic tools can deliver different shapes). + const uri = getUriFromToolParams(paramsAny, accessor); + + const normalizeRelPath = (s: string | undefined): string | undefined => { + if (!s) return undefined; + let t = String(s); + + // remove leading slashes/backslashes ("/src/..." -> "src/...") + t = t.replace(/^[\\/]+/, ''); + + // if empty -> workspace root + if (!t) return '.'; + return t; + }; + + const relRaw = uri ? (getRelative(uri, accessor) ?? undefined) : undefined; + const relPath = normalizeRelPath(relRaw); + + // If getRelative() fails, fall back to basename. + const displayPath = relPath ?? (uri ? getBasename(uri.fsPath) : 'unknown'); + + // Parse numeric params (ACP might send numbers as strings) + const asNum = (v: any): number | undefined => { + if (typeof v === 'number' && Number.isFinite(v)) return v; + if (typeof v === 'string' && v.trim() && Number.isFinite(Number(v))) return Number(v); + return undefined; + }; + + const startLine = asNum(paramsAny?.startLine); + const endLine = asNum(paramsAny?.endLine); + const linesCount = asNum(paramsAny?.linesCount); + + let rangeLabel = 'all'; + let range: [number, number] | undefined = undefined; + + if (typeof linesCount === 'number' && linesCount > 0) { + const s = startLine ?? 1; + const e = s + linesCount - 1; + rangeLabel = `${s} - ${e}`; + range = [s, e]; + } else if (typeof startLine === 'number' || typeof endLine === 'number') { + const s = startLine ?? 1; + const totalNumLines = asNum((toolMessage as any)?.result?.totalNumLines); + const e = endLine ?? totalNumLines ?? s; + rangeLabel = `${s} - ${e}`; + range = [s, e]; + } + + // HEADER FORMAT: + // Read file [N - K] relative/path + // Read file [all] relative/path + const title = `Read file [${rangeLabel}]`; + + const language = uri + ? (languageService.guessLanguageIdByFilepathOrFirstLine(uri) || 'plaintext') + : 'plaintext'; + + const componentParams: ToolHeaderParams = { + title, + desc1: displayPath, + desc1Info: displayPath, + isError, + icon: null, + isRejected + }; + applyCanceledUi(componentParams, toolMessage); + + // Click header -> open file, and if we have a line range, reveal it + if (uri) { + componentParams.onClick = () => { voidOpenFileFn(uri, accessor, range); }; + } + + if (toolMessage.type === 'success') { + const resultAny = (toolMessage as any).result ?? {}; + + const textToShow = + (toolMessage as any).displayContent + || (toolMessage as any).content + || resultAny?.text + || resultAny?.fileContents + || ''; + + componentParams.children = {textToShow}; + + // Pagination hints (keep existing behavior) + if (resultAny.hasNextPage) { + if (typeof linesCount === 'number' && linesCount > 0) { + const s = startLine ?? 1; + const actualEnd = s + linesCount - 1; + const total = asNum(resultAny.totalNumLines) ?? actualEnd; + const nextStart = Math.min(actualEnd + 1, total); + componentParams.desc2 = `(more...) Next: start_line=${nextStart}, lines_count=${linesCount}. Total lines: ${total}.`; + } else if (paramsAny.pageNumber && paramsAny.pageNumber > 1) { + componentParams.desc2 = `(part ${paramsAny.pageNumber}) Next: page_number=${paramsAny.pageNumber + 1}.`; + } else { + componentParams.desc2 = `(truncated after ${Math.round(MAX_FILE_CHARS_PAGE / 1000)}k) Next: page_number=${(paramsAny.pageNumber ?? 1) + 1}.`; + } + } else if (paramsAny.pageNumber && paramsAny.pageNumber > 1) { + componentParams.desc2 = `(part ${paramsAny.pageNumber})`; + } + } else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage as any; + componentParams.bottomChildren = ( + + {String(result ?? '')} + + ); + } else if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { + const txt = (toolMessage as any).displayContent || (toolMessage as any).content || ''; + componentParams.children = ( + +
{txt}
+
+ ); + } + + return ; + }, + }, + 'edit_file': { + resultWrapper: ({ toolMessage, messageIdx, threadId }: any) => { + const accessor = useAccessor(); + const languageService = accessor.get('ILanguageService'); + + const paramsAny = (toolMessage as any).params ?? {}; + const uri = getUriFromToolParams(paramsAny, accessor); + + const language = uri + ? (languageService.guessLanguageIdByFilepathOrFirstLine(uri) || 'plaintext') + : 'plaintext'; + + if (toolMessage.type === 'tool_request' || toolMessage.type === 'running_now') { + const previewContent = + (toolMessage.result as any)?.preview?.after + ?? paramsAny?.updatedSnippet + ?? paramsAny?.originalSnippet + ?? toolMessage.content + ?? ''; + + return ; + } + + if (toolMessage.type === 'success') { + const resultAny = toolMessage.result as any; + const previewAfter = resultAny?.preview?.after ?? resultAny?.previewSample?.after ?? null; + + const contentToShow = + previewAfter + ?? paramsAny?.updatedSnippet + ?? paramsAny?.originalSnippet + ?? toolMessage.content + ?? ''; + + return ; + } + + if (toolMessage.type === 'tool_error') { + const fsPath = uri?.fsPath; + return ( + + ); + } + // rejected / skipped: let EditTool handle safely + const fallbackContent = + paramsAny?.updatedSnippet + ?? paramsAny?.originalSnippet + ?? toolMessage.content + ?? ''; + + return ; + } + }, + 'get_dir_tree': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + + const title = getTitle(toolMessage) + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const icon = null + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + if (params.uri) { + const rel = getRelative(params.uri, accessor) + if (rel) componentParams.info = `Only search in ${rel}` + } + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.children = + + + + + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + componentParams.bottomChildren = + + {result} + + + } + + return + + } + }, + 'ls_dir': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + const explorerService = accessor.get('IExplorerService') + const title = getTitle(toolMessage) + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const icon = null + + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + if (params.uri) { + const rel = getRelative(params.uri, accessor) + if (rel) componentParams.info = `Only search in ${rel}` + } + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.numResults = result.children?.length + componentParams.hasNextPage = result.hasNextPage + componentParams.children = !result.children || (result.children.length ?? 0) === 0 ? undefined + : + {result.children.map((child: ShallowDirectoryItem, i: number) => ( + { + voidOpenFileFn(child.uri, accessor) + // commandService.executeCommand('workbench.view.explorer'); // open in explorer folders view instead + // explorerService.select(child.uri, true); + }} + /> + ))} + {result.hasNextPage && + + } + + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + componentParams.bottomChildren = + + {result} + + + } + + return + } + }, + + 'search_pathnames_only': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor(); + + const title = getTitle(toolMessage); + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); + const icon = null; + + const isRejected = toolMessage.type === 'rejected'; + const isError = toolMessage.type === 'tool_error'; + + const params = (toolMessage as any).params ?? {}; + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected }; + applyCanceledUi(componentParams, toolMessage); + const searchParams = + `Query: "${params?.query ?? ''}"` + + (params?.includePattern ? `, Pattern: "${params.includePattern}"` : ''); + + componentParams.subChildren = ( +
+ {searchParams} +
+ ); + + if (toolMessage.type === 'success') { + const result = (toolMessage as any).result ?? {}; + const uris: URI[] = Array.isArray(result?.uris) ? result.uris : []; + + componentParams.numResults = uris.length; + componentParams.hasNextPage = !!result?.hasNextPage; + + componentParams.children = ( + + {uris.length === 0 ? ( +
No results.
+ ) : ( + <> + {uris.map((uri: URI, i: number) => ( + { voidOpenFileFn(uri, accessor); }} + /> + ))} + {result?.hasNextPage && ( + + )} + + )} +
+ ); + } else if (toolMessage.type === 'tool_error') { + const result = (toolMessage as any).result; + componentParams.children = ( + + {String(result ?? '')} + + ); + } else if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { + componentParams.children = ( + +
+ {(toolMessage as any).displayContent || (toolMessage as any).content || ''} +
+
+ ); + } + + return ; + } + }, + + 'search_for_files': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor(); + + const title = getTitle(toolMessage); + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); + const icon = null; + + const isRejected = toolMessage.type === 'rejected'; + const isError = toolMessage.type === 'tool_error'; + + const params = (toolMessage as any).params ?? {}; + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected }; + applyCanceledUi(componentParams, toolMessage); + const folderRel = params?.searchInFolder ? (getRelative(params.searchInFolder, accessor) ?? '') : ''; + const searchParams = + `Query: "${params?.query ?? ''}"` + + (folderRel ? `, Folder: "${folderRel}"` : '') + + (params?.isRegex ? `, Regex: true` : ''); + + componentParams.subChildren = ( +
+ {searchParams} +
+ ); + + if (toolMessage.type === 'success') { + const result = (toolMessage as any).result ?? {}; + const uris: URI[] = Array.isArray(result?.uris) ? result.uris : []; + + componentParams.numResults = uris.length; + componentParams.hasNextPage = !!result?.hasNextPage; + + componentParams.children = ( + + {uris.length === 0 ? ( +
No results.
+ ) : ( + <> + {uris.map((uri: URI, i: number) => ( + { voidOpenFileFn(uri, accessor); }} + /> + ))} + {result?.hasNextPage && ( + + )} + + )} +
+ ); + } else if (toolMessage.type === 'tool_error') { + const result = (toolMessage as any).result; + componentParams.children = ( + + {String(result ?? '')} + + ); + } else if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { + componentParams.children = ( + +
+ {(toolMessage as any).displayContent || (toolMessage as any).content || ''} +
+
+ ); + } + + return ; + } + }, + + 'search_in_file': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor(); + const toolsService = accessor.get('IToolsService'); + + const title = getTitle(toolMessage); + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor); + const icon = null; + + const isRejected = toolMessage.type === 'rejected'; + const isError = toolMessage.type === 'tool_error'; + + const params = (toolMessage as any).params ?? {}; + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected }; + applyCanceledUi(componentParams, toolMessage); + const uriStr = params?.uri ? (getRelative(params.uri, accessor) ?? '') : ''; + const searchParams = + `Query: "${params?.query ?? ''}"` + + (uriStr ? `, File: "${uriStr}"` : '') + + (params?.isRegex ? `, Regex: true` : ''); + + componentParams.subChildren = ( +
+ {searchParams} +
+ ); + + if (toolMessage.type === 'success') { + const result = (toolMessage as any).result ?? {}; + const lines = Array.isArray(result?.lines) ? result.lines : []; + + componentParams.numResults = lines.length; + + let resultStr = ''; + try { + resultStr = toolsService.stringOfResult['search_in_file'](params, result); + } catch { + resultStr = typeof result === 'string' ? result : JSON.stringify(result, null, 2); + } + + componentParams.children = ( + + {lines.length === 0 ? ( +
No matches.
+ ) : ( + +
{resultStr}
+
+ )} +
+ ); + } else if (toolMessage.type === 'tool_error') { + const result = (toolMessage as any).result; + componentParams.children = ( + + {String(result ?? '')} + + ); + } else if (toolMessage.type === 'running_now' || toolMessage.type === 'tool_request') { + componentParams.children = ( + +
+ {(toolMessage as any).displayContent || (toolMessage as any).content || ''} +
+
+ ); + } + + return ; + } + }, + + 'read_lint_errors': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + + const title = getTitle(toolMessage) + + const { uri } = toolMessage.params ?? {} + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const icon = null + + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + componentParams.info = getRelative(uri, accessor) + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + if (result.lintErrors) + componentParams.children = + else + componentParams.children = `No lint errors found.` + + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + // JumpToFileButton removed in favor of FileLinkText + componentParams.bottomChildren = + + {result} + + + } + + return + }, + }, + + // --- + + 'create_file_or_folder': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + const isError = false + const isRejected = toolMessage.type === 'rejected' + const title = getTitle(toolMessage) + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const icon = null + + + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + componentParams.info = getRelative(params.uri, accessor) + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + else if (toolMessage.type === 'rejected') { + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + if (params) { componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } } + componentParams.bottomChildren = + + {result} + + + } + return + } + }, + 'delete_file_or_folder': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + const isFolder = toolMessage.params?.isFolder ?? false + const isError = false + const isRejected = toolMessage.type === 'rejected' + const title = getTitle(toolMessage) + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const icon = null + + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + componentParams.info = getRelative(params.uri, accessor) + + if (toolMessage.type === 'success') { + const { result } = toolMessage + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + else if (toolMessage.type === 'rejected') { + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + if (params) { componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } } + componentParams.bottomChildren = + + {result} + + + } + else if (toolMessage.type === 'running_now') { + const { result } = toolMessage + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + else if (toolMessage.type === 'tool_request') { + const { result } = toolMessage + componentParams.onClick = () => { voidOpenFileFn(params.uri, accessor) } + } + + return + } + }, + 'rewrite_file': { + resultWrapper: (params) => { + return + } + }, + 'run_command': { + resultWrapper: (params) => { + return + } + }, + + 'run_persistent_command': { + resultWrapper: (params) => { + return + } + }, + 'open_persistent_terminal': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const terminalToolsService = accessor.get('ITerminalToolService') + + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const title = getTitle(toolMessage) + const icon = null + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + const cwdUri = resolvePathLikeToUri(params?.cwd, accessor) + const relativePath = cwdUri ? getRelative(cwdUri, accessor) : '' + componentParams.info = relativePath ? `Running in ${relativePath}` : undefined + + if (toolMessage.type === 'success') { + const { result } = toolMessage + const { persistentTerminalId } = result + componentParams.desc1 = persistentTerminalNameOfId(persistentTerminalId) + componentParams.onClick = () => terminalToolsService.focusPersistentTerminal(persistentTerminalId) + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + componentParams.bottomChildren = + + {result} + + + } + + return + }, + }, + 'kill_persistent_terminal': { + resultWrapper: ({ toolMessage }) => { + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + const terminalToolsService = accessor.get('ITerminalToolService') + + const { desc1, desc1Info } = toolNameToDesc(toolMessage.name, toolMessage.params, accessor) + const title = getTitle(toolMessage) + const icon = null + const isError = false + const isRejected = toolMessage.type === 'rejected' + const { rawParams, params } = toolMessage + const componentParams: ToolHeaderParams = { title, desc1, desc1Info, isError, icon, isRejected, } + applyCanceledUi(componentParams, toolMessage); + if (toolMessage.type === 'success') { + const { persistentTerminalId } = params + componentParams.desc1 = persistentTerminalNameOfId(persistentTerminalId) + componentParams.onClick = () => terminalToolsService.focusPersistentTerminal(persistentTerminalId) + } + else if (toolMessage.type === 'tool_error') { + const { result } = toolMessage + componentParams.bottomChildren = + + {result} + + + } + + return + }, + }, +}; diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatUI.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatUI.tsx new file mode 100644 index 00000000000..06f2a29e97b --- /dev/null +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarChatUI.tsx @@ -0,0 +1,563 @@ +/*-------------------------------------------------------------------------------------- + * Copyright 2025 Glass Devtools, Inc. All rights reserved. + * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. + *--------------------------------------------------------------------------------------*/ + +import React, { useState, useEffect, useMemo, useCallback, ButtonHTMLAttributes } from 'react'; +import { FeatureName } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; +import { File, Folder, Text } from 'lucide-react'; +import { StagingSelectionItem } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; +import { URI } from '../../../../../../../base/common/uri.js'; +import { getBasename, voidOpenFileFn } from './SidebarChatShared.js'; +import { useAccessor, useActiveURI, useSettingsState } from '../util/services.js'; +import { BlockCode } from '../util/inputs.js'; +import { ModelDropdown, } from '../void-settings-tsx/ModelDropdown.js'; +import { VoidCustomDropdownBox } from '../util/inputs.js'; +import { ChatMode } from '../../../../../../../platform/void/common/voidSettingsTypes.js'; + +export const IconX = ({ size, className = '', ...props }: { size: number, className?: string } & React.SVGProps) => { + return ( + + + + ); +}; + +const IconSquare = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + ); +}; + + +export const IconWarning = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + ); +}; + + +export const IconLoading = ({ className = '' }: { className?: string }) => { + + const [loadingText, setLoadingText] = useState('.'); + + useEffect(() => { + let intervalId; + + // Function to handle the animation + const toggleLoadingText = () => { + if (loadingText === '...') { + setLoadingText('.'); + } else { + setLoadingText(loadingText + '.'); + } + }; + + // Start the animation loop + intervalId = setInterval(toggleLoadingText, 300); + + // Cleanup function to clear the interval when component unmounts + return () => clearInterval(intervalId); + }, [loadingText, setLoadingText]); + + return
{loadingText}
; + +} + +const IconArrowUp = ({ size, className = '' }: { size: number, className?: string }) => { + return ( + + + + ); +}; + +type ButtonProps = ButtonHTMLAttributes +const DEFAULT_BUTTON_SIZE = 22; +export const ButtonSubmit = ({ className, disabled, ...props }: ButtonProps & Required>) => { + + return +} + +export const ButtonStop = ({ className, ...props }: ButtonHTMLAttributes) => { + return +} + +interface VoidChatAreaProps { + // Required + children: React.ReactNode; // This will be the input component + + // Form controls + onSubmit: () => void; + onAbort: () => void; + isStreaming: boolean; + isDisabled?: boolean; + divRef?: React.RefObject; + + // UI customization + className?: string; + showModelDropdown?: boolean; + showSelections?: boolean; + showProspectiveSelections?: boolean; + loadingIcon?: React.ReactNode; + + selections?: StagingSelectionItem[] + setSelections?: (s: StagingSelectionItem[]) => void + // selections?: any[]; + // onSelectionsChange?: (selections: any[]) => void; + + onClickAnywhere?: () => void; + // Optional close button + onClose?: () => void; + + featureName: FeatureName; + // Optional extra controls in the bottom-right (e.g., attach buttons) + rightBottomExtras?: React.ReactNode; +} + +export const VoidChatArea: React.FC = ({ + children, + onSubmit, + onAbort, + onClose, + onClickAnywhere, + divRef, + isStreaming = false, + isDisabled = false, + className = '', + showModelDropdown = true, + showSelections = false, + showProspectiveSelections = false, + selections, + setSelections, + featureName, + loadingIcon, + rightBottomExtras, +}) => { + return ( +
{ + onClickAnywhere?.() + }} + > + {/* Selections section */} + {showSelections && selections && setSelections && ( + + )} + + {/* Input section */} +
+ {children} + + {/* Close button (X) if onClose is provided */} + {onClose && ( +
+ +
+ )} +
+ + {/* Bottom row */} +
+ {showModelDropdown && ( +
+
+ {featureName === 'Chat' && } + +
+
+ )} + +
+ + {rightBottomExtras} + {isStreaming && loadingIcon} + + {isStreaming ? ( + + ) : ( + + )} +
+ +
+
+ ); +}; + +export const ToolChildrenWrapper = ({ children, className }: { children: React.ReactNode, className?: string }) => { + // NOTE: do NOT force select-none globally, it blocks copy/select in tool outputs. + return ( +
+
+ {children} +
+
+ ); +}; + +export const ProseWrapper = ({ children }: { children: React.ReactNode }) => { + return
+ {children} +
+}; + +export const CodeChildren = ({ children, className, language }: { children: React.ReactNode, className?: string, language?: string }) => { + const isString = typeof children === 'string'; + + if (!isString) { + return ( +
+ {children} +
+ ); + } + + // Otherwise use BlockCode for strings. + const codeString = children as string; + return ( +
+ +
+ ); +}; + +export const ListableToolItem = ({ name, onClick, isSmall, className, showDot }: { name: React.ReactNode, onClick?: () => void, isSmall?: boolean, className?: string, showDot?: boolean }) => { + return
+ {showDot === false ? null :
} +
{name}
+
+} + +export const SelectedFiles = ( + { type, selections, setSelections, showProspectiveSelections, messageIdx, }: + | { type: 'past', selections: StagingSelectionItem[]; setSelections?: undefined, showProspectiveSelections?: undefined, messageIdx: number, } + | { type: 'staging', selections: StagingSelectionItem[]; setSelections: ((newSelections: StagingSelectionItem[]) => void), showProspectiveSelections?: boolean, messageIdx?: number } +) => { + + const accessor = useAccessor() + const commandService = accessor.get('ICommandService') + const modelReferenceService = accessor.get('IVoidModelService') + const { uri: currentURI } = useActiveURI() + const [recentUris, setRecentUris] = useState([]) + const maxRecentUris = 10 + const maxProspectiveFiles = 3 + + useEffect(() => { + if (!currentURI) return + setRecentUris(prev => { + const withoutCurrent = prev.filter(uri => uri.fsPath !== currentURI.fsPath) + const withCurrent = [currentURI, ...withoutCurrent] + return withCurrent.slice(0, maxRecentUris) + }) + }, [currentURI]) + + const [prospectiveSelections, setProspectiveSelections] = useState([]) + + useEffect(() => { + const computeRecents = async () => { + const prospectiveURIs = recentUris + .filter(uri => !selections.find(s => s.type === 'File' && s.uri.fsPath === uri.fsPath)) + .slice(0, maxProspectiveFiles) + + const answer: StagingSelectionItem[] = [] + for (const uri of prospectiveURIs) { + answer.push({ + type: 'File', + uri: uri, + language: 'plaintext', + state: { wasAddedAsCurrentFile: false }, + }) + } + return answer + } + + if (type === 'staging' && showProspectiveSelections) { + computeRecents().then((a) => setProspectiveSelections(a)) + } + else { + setProspectiveSelections([]) + } + }, [recentUris, selections, type, showProspectiveSelections]) + + + const allSelections = [...selections, ...prospectiveSelections] + + if (allSelections.length === 0) { + return null + } + + return ( +
+ + {allSelections.map((selection, i) => { + + const isThisSelectionProspective = i > selections.length - 1 + + const thisKey = selection.type === 'CodeSelection' ? selection.type + selection.language + (selection as any)?.range + (selection as any)?.state?.wasAddedAsCurrentFile + selection.uri.fsPath + : selection.type === 'File' ? selection.type + selection.language + (selection as any)?.state?.wasAddedAsCurrentFile + selection.uri.fsPath + : selection.type === 'Folder' ? selection.type + selection.language + (selection as any)?.state + selection.uri.fsPath + : i + + const SelectionIcon = ( + selection.type === 'File' ? File + : selection.type === 'Folder' ? Folder + : selection.type === 'CodeSelection' ? Text + : (undefined as never) + ) + + return
+
{ + if (type !== 'staging') return; + if (isThisSelectionProspective) { + setSelections([...selections, selection]) + } + else if (selection.type === 'File') { + voidOpenFileFn(selection.uri, accessor); + + const wasAddedAsCurrentFile = (selection as any).state.wasAddedAsCurrentFile + if (wasAddedAsCurrentFile) { + const newSelection: StagingSelectionItem = { ...selection, state: { ...(selection as any).state, wasAddedAsCurrentFile: false } } + setSelections([ + ...selections.slice(0, i), + newSelection, + ...selections.slice(i + 1) + ]) + } + } + else if (selection.type === 'CodeSelection') { + voidOpenFileFn(selection.uri, accessor, (selection as any)?.range); + } + else if (selection.type === 'Folder') { + // TODO!!! reveal in tree + } + }} + > + {} + { + (() => { + if (selection.type === 'CodeSelection') { + return getBasename(selection.uri.path) + ` (${(selection as any)?.range?.[0]}-${(selection as any)?.range?.[1]})` + } else if (selection.type === 'File') { + return getBasename(selection.uri.path) + } else if (selection.type === 'Folder') { + return getBasename(selection.uri.path) + '/' + } + return (selection as any)?.uri?.path || 'Unknown' + })() + } + {selection.type === 'File' && (selection as any)?.state?.wasAddedAsCurrentFile && messageIdx === undefined ? + + {`(Current File)`} + + : null + } + + {type === 'staging' && !isThisSelectionProspective ? +
{ + e.stopPropagation(); + if (type !== 'staging') return; + setSelections([...selections.slice(0, i), ...selections.slice(i + 1)]) + }} + > + +
+ : <>} +
+
+ })} +
+ ) +} + +const nameOfChatMode = { + 'normal': 'Chat', + 'gather': 'Gather', + 'agent': 'Agent', +} + +const detailOfChatMode = { + 'normal': 'Normal chat', + 'gather': 'Reads files, but can\'t edit', + 'agent': 'Edits files and uses tools', +} + +const ChatModeDropdown = ({ className }: { className: string }) => { + const accessor = useAccessor() + + const voidSettingsService = accessor.get('IVoidSettingsService') + const settingsState = useSettingsState() + + const options: ChatMode[] = useMemo(() => ['normal', 'gather', 'agent'], []) + + const onChangeOption = useCallback((newVal: ChatMode) => { + voidSettingsService.setGlobalSetting('chatMode', newVal) + }, [voidSettingsService]) + + return nameOfChatMode[val]} + getOptionDropdownName={(val) => nameOfChatMode[val]} + getOptionDropdownDetail={(val) => detailOfChatMode[val]} + getOptionsEqual={(a, b) => a === b} + /> + +} diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx index a6147461a16..69349be3139 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/SidebarThreadSelector.tsx @@ -3,12 +3,12 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import { useMemo, useState } from 'react'; -import { CopyButton, IconShell1 } from '../markdown/ApplyBlockHoverButtons.js'; -import { useAccessor, useChatThreadsState, useChatThreadsStreamState, useFullChatThreadsStreamState, useSettingsState } from '../util/services.js'; -import { IconX } from './SidebarChat.js'; -import { Check, Copy, Icon, LoaderCircle, MessageCircleQuestion, Trash2, UserCheck, X } from 'lucide-react'; -import { IsRunningType, ThreadType } from '../../../chatThreadService.js'; +import { useState } from 'react'; +import { IconShell1 } from '../markdown/ApplyBlockHoverButtons.js'; +import { useAccessor, useChatThreadsState, useFullChatThreadsStreamState } from '../util/services.js'; +import { Check, Copy, LoaderCircle, MessageCircleQuestion, Trash2, X } from 'lucide-react'; +import { IsRunningType } from '../../../ChatExecutionEngine.js'; +import { ThreadType } from '../../../chatThreadService.js'; const numInitialThreads = 3 @@ -85,10 +85,6 @@ export const PastThreadsList = ({ className = '' }: { className?: string }) => { ); }; - - - - // Format date to display as today, yesterday, or date const formatDate = (date: Date) => { const now = new Date(); @@ -114,7 +110,6 @@ const formatTime = (date: Date) => { }); }; - const DuplicateButton = ({ threadId }: { threadId: string }) => { const accessor = useAccessor() const chatThreadsService = accessor.get('IChatThreadService') @@ -127,7 +122,6 @@ const DuplicateButton = ({ threadId }: { threadId: string }) => { data-tooltip-content='Duplicate thread' > - } const TrashButton = ({ threadId }: { threadId: string }) => { @@ -182,31 +176,6 @@ const PastThreadElement = ({ pastThread, idx, hoveredIdx, setHoveredIdx, isRunni const accessor = useAccessor() const chatThreadsService = accessor.get('IChatThreadService') - // const settingsState = useSettingsState() - // const convertService = accessor.get('IConvertToLLMMessageService') - // const chatMode = settingsState.globalSettings.chatMode - // const modelSelection = settingsState.modelSelectionOfFeature?.Chat ?? null - // const copyChatButton = { - // const { messages } = await convertService.prepareLLMChatMessages({ - // chatMessages: currentThread.messages, - // chatMode, - // modelSelection, - // }) - // return JSON.stringify(messages, null, 2) - // }} - // toolTipName={modelSelection === null ? 'Copy As Messages Payload' : `Copy As ${displayInfoOfProviderName(modelSelection.providerName).title} Payload`} - // /> - - - // const currentThread = chatThreadsService.getCurrentThread() - // const copyChatButton2 = { - // return JSON.stringify(currentThread.messages, null, 2) - // }} - // toolTipName={`Copy As Void Chat`} - // /> - let firstMsg = null; const firstUserMsgIdx = pastThread.messages.findIndex((msg) => msg.role === 'user'); @@ -220,9 +189,6 @@ const PastThreadElement = ({ pastThread, idx, hoveredIdx, setHoveredIdx, isRunni const numMessages = pastThread.messages.filter((msg) => msg.role === 'assistant' || msg.role === 'user').length; const detailsHTML = {numMessages} {` `} diff --git a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx index 64143bfdfdf..0a14e583837 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/sidebar-tsx/index.tsx @@ -7,5 +7,3 @@ import { mountFnGenerator } from '../util/mountFnGenerator.js' import { Sidebar } from './Sidebar.js' export const mountSidebar = mountFnGenerator(Sidebar) - - diff --git a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx index d877e1102fb..a739c4410f1 100644 --- a/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx +++ b/src/vs/workbench/contrib/void/browser/react/src/util/inputs.tsx @@ -3,28 +3,103 @@ * Licensed under the Apache License, Version 2.0. See LICENSE.txt for more information. *--------------------------------------------------------------------------------------*/ -import React, { forwardRef, ForwardRefExoticComponent, MutableRefObject, RefAttributes, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'; +import React, { forwardRef, ForwardRefExoticComponent, RefAttributes, useCallback, useEffect, useId, useMemo, useRef, useState } from 'react'; import { IInputBoxStyles, InputBox } from '../../../../../../../base/browser/ui/inputbox/inputBox.js'; import { defaultCheckboxStyles, defaultInputBoxStyles, defaultSelectBoxStyles } from '../../../../../../../platform/theme/browser/defaultStyles.js'; import { SelectBox } from '../../../../../../../base/browser/ui/selectBox/selectBox.js'; import { IDisposable } from '../../../../../../../base/common/lifecycle.js'; import { Checkbox } from '../../../../../../../base/browser/ui/toggle/toggle.js'; - import { CodeEditorWidget } from '../../../../../../../editor/browser/widget/codeEditor/codeEditorWidget.js' import { useAccessor } from './services.js'; -import { ITextModel } from '../../../../../../../editor/common/model.js'; +import { ITextModel } from '../../../../../../../editor/common/language/model.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { inputBackground, inputForeground } from '../../../../../../../platform/theme/common/colorRegistry.js'; -import { useFloating, autoUpdate, offset, flip, shift, size, autoPlacement } from '@floating-ui/react'; +import { useFloating, autoUpdate, offset, flip, shift, size,} from '@floating-ui/react'; import { URI } from '../../../../../../../base/common/uri.js'; -import { getBasename, getFolderName } from '../sidebar-tsx/SidebarChat.js'; -import { ChevronRight, File, Folder, FolderClosed, LucideProps } from 'lucide-react'; -import { StagingSelectionItem } from '../../../../common/chatThreadServiceTypes.js'; -import { DiffEditorWidget } from '../../../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; -import { extractSearchReplaceBlocks, ExtractedSearchReplaceBlock } from '../../../../common/helpers/extractCodeFromResult.js'; -import { IAccessibilitySignalService } from '../../../../../../../platform/accessibilitySignal/browser/accessibilitySignalService.js'; -import { IEditorProgressService } from '../../../../../../../platform/progress/common/progress.js'; -import { detectLanguage } from '../../../../common/helpers/languageHelpers.js'; +import { getBasename } from '../sidebar-tsx/SidebarChatShared.js'; +import { ChevronRight, File, Folder, LucideProps } from 'lucide-react'; +import { StagingSelectionItem } from '../../../../../../../platform/void/common/chatThreadServiceTypes.js'; + +const jetBrainsMonoFontFamily = `'JetBrains Mono', JetBrainsMono, monospace`; +const jetBrainsMonoFontStyleElementId = 'void-jetbrains-mono-font-face'; +let jetBrainsMonoFontStylesInjected = false; + +let _cachedWorkspaceFoldersKey: string | null = null; +let _cachedSortedWorkspaceFolders: readonly any[] = []; + +function getSortedWorkspaceFolders(accessor: ReturnType) { + const workspaceService = accessor.get('IWorkspaceContextService'); + const folders = workspaceService.getWorkspace().folders ?? []; + + const key = folders.map((f: any) => f.uri?.fsPath ?? '').join('|'); + if (key !== _cachedWorkspaceFoldersKey) { + _cachedWorkspaceFoldersKey = key; + _cachedSortedWorkspaceFolders = [...folders].sort( + (a: any, b: any) => (b.uri.fsPath.length - a.uri.fsPath.length) + ); + } + + return _cachedSortedWorkspaceFolders; +} + +const ensureJetBrainsMonoFontFace = () => { + if (jetBrainsMonoFontStylesInjected || typeof document === 'undefined') { + return; + } + + if (document.getElementById(jetBrainsMonoFontStyleElementId)) { + jetBrainsMonoFontStylesInjected = true; + return; + } + + const style = document.createElement('style'); + style.id = jetBrainsMonoFontStyleElementId; + style.textContent = ` +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-Regular.woff2') format('woff2'); + font-style: normal; + font-weight: 400; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-Italic.woff2') format('woff2'); + font-style: italic; + font-weight: 400; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-Medium.woff2') format('woff2'); + font-style: normal; + font-weight: 500; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-MediumItalic.woff2') format('woff2'); + font-style: italic; + font-weight: 500; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-Bold.woff2') format('woff2'); + font-style: normal; + font-weight: 700; + font-display: swap; +} +@font-face { + font-family: 'JetBrains Mono'; + src: url('assets/fonts/JetBrainsMono-BoldItalic.woff2') format('woff2'); + font-style: italic; + font-weight: 700; + font-display: swap; +}`; + document.head.appendChild(style); + jetBrainsMonoFontStylesInjected = true; +} // type guard @@ -71,116 +146,91 @@ type Option = { const isSubsequence = (text: string, pattern: string): boolean => { + const t = text.toLowerCase(); + const p = pattern.toLowerCase(); + if (p === '') return true; - text = text.toLowerCase() - pattern = pattern.toLowerCase() - - if (pattern === '') return true; - if (text === '') return false; - if (pattern.length > text.length) return false; - - const seq: boolean[][] = Array(pattern.length + 1) - .fill(null) - .map(() => Array(text.length + 1).fill(false)); - - for (let j = 0; j <= text.length; j++) { - seq[0][j] = true; + let j = 0; + for (let i = 0; i < t.length && j < p.length; i++) { + if (t[i] === p[j]) j++; } - - for (let i = 1; i <= pattern.length; i++) { - for (let j = 1; j <= text.length; j++) { - if (pattern[i - 1] === text[j - 1]) { - seq[i][j] = seq[i - 1][j - 1]; - } else { - seq[i][j] = seq[i][j - 1]; - } - } - } - return seq[pattern.length][text.length]; + return j === p.length; }; -const scoreSubsequence = (text: string, pattern: string): number => { - if (pattern === '') return 0; +const scoreSubsequenceLower = (textLower: string, patternLower: string): number => { + if (!patternLower) return 0; - text = text.toLowerCase(); - pattern = pattern.toLowerCase(); + const p = patternLower; + const m = p.length; - // We'll use dynamic programming to find the longest consecutive substring - const n = text.length; - const m = pattern.length; + const lps = new Array(m).fill(0); + for (let i = 1, len = 0; i < m; ) { + if (p[i] === p[len]) { + lps[i++] = ++len; + } else if (len > 0) { + len = lps[len - 1]; + } else { + lps[i++] = 0; + } + } - // This will track our maximum consecutive match length - let maxConsecutive = 0; + let j = 0; + let max = 0; - // For each starting position in the text - for (let i = 0; i < n; i++) { - // Check for matches starting from this position - let consecutiveCount = 0; + for (let i = 0; i < textLower.length; i++) { + const c = textLower[i]; - // For each character in the pattern - for (let j = 0; j < m; j++) { - // If we have a match and we're still within text bounds - if (i + j < n && text[i + j] === pattern[j]) { - consecutiveCount++; - } else { - // Break on first non-match - break; + while (j > 0 && p[j] !== c) { + j = lps[j - 1]; + } + if (p[j] === c) { + j++; + if (j > max) max = j; + if (j === m) { + j = lps[j - 1]; } } - - // Update our maximum - maxConsecutive = Math.max(maxConsecutive, consecutiveCount); } + return max; +}; - return maxConsecutive; -} - - -function getRelativeWorkspacePath(accessor: ReturnType, uri: URI): string { - const workspaceService = accessor.get('IWorkspaceContextService'); - const workspaceFolders = workspaceService.getWorkspace().folders; +const scoreSubsequence = (text: string, pattern: string): number => { + if (pattern === '') return 0; + return scoreSubsequenceLower(text.toLowerCase(), pattern.toLowerCase()); +}; - if (!workspaceFolders.length) { - return uri.fsPath; // No workspace folders, return original path +const getWorkspacePathInfo = ( + uri: URI, + sortedFolders: ReturnType +): { relativePath: string; workspaceFolderUri: URI | undefined } => { + if (!sortedFolders.length) { + return { relativePath: uri.fsPath, workspaceFolderUri: undefined }; } - // Sort workspace folders by path length (descending) to match the most specific folder first - const sortedFolders = [...workspaceFolders].sort((a, b) => - b.uri.fsPath.length - a.uri.fsPath.length - ); - - // Add trailing slash to paths for exact matching const uriPath = uri.fsPath.endsWith('/') ? uri.fsPath : uri.fsPath + '/'; - // Check if the URI is inside any workspace folder for (const folder of sortedFolders) { + const folderFsPath: string = folder.uri.fsPath; + const folderPath = folderFsPath.endsWith('/') ? folderFsPath : folderFsPath + '/'; - - const folderPath = folder.uri.fsPath.endsWith('/') ? folder.uri.fsPath : folder.uri.fsPath + '/'; if (uriPath.startsWith(folderPath)) { - // Calculate the relative path by removing the workspace folder path - let relativePath = uri.fsPath.slice(folder.uri.fsPath.length); - // Remove leading slash if present - if (relativePath.startsWith('/')) { - relativePath = relativePath.slice(1); - } - // console.log({ folderPath, relativePath, uriPath }); - - return relativePath; + let relativePath = uri.fsPath.slice(folderFsPath.length); + if (relativePath.startsWith('/')) relativePath = relativePath.slice(1); + return { relativePath, workspaceFolderUri: folder.uri }; } } - // URI is not in any workspace folder, return original path - return uri.fsPath; -} - + return { relativePath: uri.fsPath, workspaceFolderUri: undefined }; +}; +function getRelativeWorkspacePath(accessor: ReturnType, uri: URI): string { + const sortedFolders = getSortedWorkspaceFolders(accessor); + return getWorkspacePathInfo(uri, sortedFolders).relativePath; +} const numOptionsToShow = 100 - - // TODO make this unique based on other options const getAbbreviatedName = (relativePath: string) => { return getBasename(relativePath, 1) @@ -189,10 +239,7 @@ const getAbbreviatedName = (relativePath: string) => { const getOptionsAtPath = async (accessor: ReturnType, path: string[], optionText: string): Promise => { const toolsService = accessor.get('IToolsService') - - - - const searchForFilesOrFolders = async (t: string, searchFor: 'files' | 'folders') => { + const searchForFilesOrFolders = async (t: string, searchFor: 'files' | 'folders'): Promise => { try { const searchResults = (await (await toolsService.callTool.search_pathnames_only({ @@ -200,10 +247,11 @@ const getOptionsAtPath = async (accessor: ReturnType, path: includePattern: null, pageNumber: 1, })).result).uris + const sortedFolders = getSortedWorkspaceFolders(accessor); if (searchFor === 'files') { const res: Option[] = searchResults.map(uri => { - const relativePath = getRelativeWorkspacePath(accessor, uri) + const relativePath = getWorkspacePathInfo(uri, sortedFolders).relativePath return { leafNodeType: 'File', uri: uri, @@ -222,60 +270,35 @@ const getOptionsAtPath = async (accessor: ReturnType, path: for (const uri of searchResults) { if (!uri) continue; - // Get the full path and extract directories - const relativePath = getRelativeWorkspacePath(accessor, uri) + const { relativePath, workspaceFolderUri } = getWorkspacePathInfo(uri, sortedFolders); const pathParts = relativePath.split('/'); - // Get workspace info - const workspaceService = accessor.get('IWorkspaceContextService'); - const workspaceFolders = workspaceService.getWorkspace().folders; - - // Find the workspace folder containing this URI - let workspaceFolderUri: URI | undefined; - if (workspaceFolders.length) { - // Sort workspace folders by path length (descending) to match the most specific folder first - const sortedFolders = [...workspaceFolders].sort((a, b) => - b.uri.fsPath.length - a.uri.fsPath.length - ); - - // Find the containing workspace folder - for (const folder of sortedFolders) { - const folderPath = folder.uri.fsPath.endsWith('/') ? folder.uri.fsPath : folder.uri.fsPath + '/'; - const uriPath = uri.fsPath.endsWith('/') ? uri.fsPath : uri.fsPath + '/'; - - if (uriPath.startsWith(folderPath)) { - workspaceFolderUri = folder.uri; - break; - } - } - } - if (workspaceFolderUri) { // Add each directory and its parents to the map let currentPath = ''; for (let i = 0; i < pathParts.length - 1; i++) { currentPath = i === 0 ? `/${pathParts[i]}` : `${currentPath}/${pathParts[i]}`; - - // Create a proper directory URI const directoryUri = URI.joinPath( workspaceFolderUri, currentPath.startsWith('/') ? currentPath.substring(1) : currentPath ); - directoryMap.set(currentPath, directoryUri); } } } - // Convert map to array + return Array.from(directoryMap.entries()).map(([relativePath, uri]) => ({ leafNodeType: 'Folder', - uri: uri, - iconInMenu: Folder, // Folder + uri, + iconInMenu: Folder, fullName: relativePath, abbreviatedName: getAbbreviatedName(relativePath), })) satisfies Option[]; } + + // Fallback (should not reach due to narrow type), but keep TS happy + return [] } catch (error) { console.error('Error fetching directories:', error); return []; @@ -314,7 +337,6 @@ const getOptionsAtPath = async (accessor: ReturnType, path: } - if (generateNextOptionsAtPath) { nextOptionsAtPath = await generateNextOptionsAtPath(optionText) @@ -325,21 +347,25 @@ const getOptionsAtPath = async (accessor: ReturnType, path: nextOptionsAtPath = [...foldersResults, ...filesResults,] } + const qLower = optionText.trim().toLowerCase(); + const optionsAtPath = nextOptionsAtPath - .filter(o => isSubsequence(o.fullName, optionText)) - .sort((a, b) => { // this is a hack but good for now - const scoreA = scoreSubsequence(a.fullName, optionText); - const scoreB = scoreSubsequence(b.fullName, optionText); - return scoreB - scoreA; + .map(o => { + const fullLower = o.fullName.toLowerCase(); + return { + o, + fullLower, + score: scoreSubsequenceLower(fullLower, qLower), + }; }) - .slice(0, numOptionsToShow) // should go last because sorting/filtering should happen on all datapoints - - return optionsAtPath + .filter(x => isSubsequence(x.fullLower, qLower)) + .sort((a, b) => b.score - a.score) + .slice(0, numOptionsToShow) + .map(x => x.o); + return optionsAtPath; } - - export type TextAreaFns = { setValue: (v: string) => void, enable: () => void, disable: () => void } type InputBox2Props = { initValue?: string | null; @@ -354,9 +380,8 @@ type InputBox2Props = { onBlur?: (e: React.FocusEvent) => void; onChangeHeight?: (newHeight: number) => void; } -export const VoidInputBox2 = forwardRef(function X({ initValue, placeholder, multiline, enableAtToMention, fnsRef, className, onKeyDown, onFocus, onBlur, onChangeText }, ref) { - +export const VoidInputBox2 = forwardRef(function X({ initValue, placeholder, multiline, enableAtToMention, fnsRef, className, onKeyDown, onFocus, onBlur, onChangeText }, ref) { // mirrors whatever is in ref const accessor = useAccessor() @@ -446,8 +471,6 @@ export const VoidInputBox2 = forwardRef(fun chatThreadService.addNewStagingSelection(newSelection) } else { - - currentPathRef.current = JSON.stringify(newPath); const newOpts = await getOptionsAtPath(accessor, newPath, '') || [] if (currentPathRef.current !== JSON.stringify(newPath)) { return; } @@ -669,6 +692,7 @@ export const VoidInputBox2 = forwardRef(fun whileElementsMounted: autoUpdate, strategy: 'fixed', }); + useEffect(() => { if (!isMenuOpen) return; @@ -692,8 +716,6 @@ export const VoidInputBox2 = forwardRef(fun document.addEventListener('mousedown', handleClickOutside); return () => document.removeEventListener('mousedown', handleClickOutside); }, [isMenuOpen, refs.floating, refs.reference]); - // logic for @ to mention ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - const [isEnabled, setEnabled] = useState(true) @@ -703,14 +725,15 @@ export const VoidInputBox2 = forwardRef(fun r.style.height = 'auto' // set to auto to reset height, then set to new height - if (r.scrollHeight === 0) return requestAnimationFrame(adjustHeight) + if (r.scrollHeight === 0) { + requestAnimationFrame(adjustHeight) + return + } const h = r.scrollHeight const newHeight = Math.min(h + 1, 500) // plus one to avoid scrollbar appearing when it shouldn't r.style.height = `${newHeight}px` }, []); - - const fns: TextAreaFns = useMemo(() => ({ setValue: (val) => { const r = textAreaRef.current @@ -723,16 +746,11 @@ export const VoidInputBox2 = forwardRef(fun disable: () => { setEnabled(false) }, }), [onChangeText, adjustHeight]) - - useEffect(() => { if (initValue) fns.setValue(initValue) }, [initValue]) - - - return <>