diff --git a/.eslintignore b/.eslintignore index 1c49d5b8846..e3aedfea0d6 100644 --- a/.eslintignore +++ b/.eslintignore @@ -25,10 +25,6 @@ **/src/typings/**/*.d.ts **/src/vs/*/**/*.d.ts **/src/vs/base/test/common/filters.perf.data.js -**/src/vs/css.build.js -**/src/vs/css.js **/src/vs/loader.js -**/src/vs/nls.build.js -**/src/vs/nls.js **/test/unit/assert.js **/typings/** diff --git a/.eslintrc.json b/.eslintrc.json index 9e9b1ddabfb..4d49a16fbf0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -210,7 +210,8 @@ // - electron-browser "when": "hasBrowser", "allow": [ - "vs/css!./**/*" + "vs/css!./**/*", + "@microsoft/applicationinsights-web" ] }, { @@ -220,15 +221,12 @@ // - electron-main "when": "hasNode", "allow": [ - "@microsoft/applicationinsights-web", "@parcel/watcher", "@vscode/sqlite3", "@vscode/vscode-languagedetection", "@vscode/ripgrep", "@vscode/iconv-lite-umd", "applicationinsights", - "@microsoft/1ds-core-js", - "@microsoft/1ds-post-js", "assert", "child_process", "console", @@ -277,6 +275,7 @@ // imports that are allowed in all /test/ files "when": "test", "allow": [ + "vs/css.build", "assert", "sinon", "sinon-test" @@ -331,7 +330,9 @@ "vs/base/~", "vs/base/parts/*/~", "vs/platform/*/~", - "tas-client-umd" // node module allowed even in /common/ + "tas-client-umd", // node module allowed even in /common/ + "@microsoft/1ds-core-js",// node module allowed even in /common/ + "@microsoft/1ds-post-js" // node module allowed even in /common/ ] }, { @@ -445,8 +446,7 @@ }, // TODO@layers "tas-client-umd", // node module allowed even in /common/ "vscode-textmate", // node module allowed even in /common/ - "@vscode/vscode-languagedetection", // node module allowed even in /common/ - "@microsoft/applicationinsights-web" // node module allowed even in /common/ + "@vscode/vscode-languagedetection" // node module allowed even in /common/ ] }, { @@ -577,7 +577,7 @@ "restrictions": [] }, { - "target": "src/vs/{css.d.ts,monaco.d.ts,nls.d.ts,nls.mock.ts}", + "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.build.ts,nls.mock.ts}", "restrictions": [] }, { diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml index 3161158f947..b5fe086de05 100644 --- a/.github/workflows/basic.yml +++ b/.github/workflows/basic.yml @@ -62,7 +62,7 @@ jobs: run: yarn --frozen-lockfile --network-timeout 180000 - name: Compile and Download - run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" playwright-install download-builtin-extensions + run: yarn npm-run-all --max_old_space_size=4095 -lp compile "electron x64" - name: Run Unit Tests id: electron-unit-tests @@ -114,9 +114,6 @@ jobs: ELECTRON_SKIP_BINARY_DOWNLOAD: 1 run: yarn --frozen-lockfile --network-timeout 180000 - - name: Download Playwright - run: yarn playwright-install - - name: Run Hygiene Checks run: yarn gulp hygiene diff --git a/.yarnrc b/.yarnrc index c6a9765f61f..e6d816a2f56 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,4 +1,4 @@ disturl "https://electronjs.org/headers" -target "18.3.2" +target "18.3.4" runtime "electron" build_from_source "true" diff --git a/build/.moduleignore b/build/.moduleignore index a2e1b715e1b..438f9b575d1 100644 --- a/build/.moduleignore +++ b/build/.moduleignore @@ -122,10 +122,6 @@ vscode-windows-ca-certs/**/* node-addon-api/**/* prebuild-install/**/* -@microsoft/applicationinsights*/** -@microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js - # other node modules **/docs/** diff --git a/build/.webignore b/build/.webignore index f55882ba9d4..563dfb0000c 100644 --- a/build/.webignore +++ b/build/.webignore @@ -33,6 +33,3 @@ xterm-addon-webgl/out/** # This makes sure the model is included in the package !@vscode/vscode-languagedetection/model/** -@microsoft/applicationinsights*/** -@microsoft/dynamicproto-js/** -!@microsoft/applicationinsights-web/dist/applicationinsights-web.min.js \ No newline at end of file diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index 7feb8af4e98..65504f03ecd 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -127,6 +127,13 @@ steps: exec { node build/azure-pipelines/mixin } displayName: Mix in quality + - powershell: | + . build/azure-pipelines/win32/exec.ps1 + $ErrorActionPreference = "Stop" + exec { node build\lib\policies } + displayName: Generate Group Policy definitions + condition: and(succeeded(), ne(variables['VSCODE_PUBLISH'], 'false')) + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/filters.js b/build/filters.js index c2a0a52bdb9..bee295e70c3 100644 --- a/build/filters.js +++ b/build/filters.js @@ -66,10 +66,6 @@ module.exports.indentationFilter = [ '!**/LICENSE.{txt,rtf}', '!LICENSES.chromium.html', '!**/LICENSE', - '!src/vs/nls.js', - '!src/vs/nls.build.js', - '!src/vs/css.js', - '!src/vs/css.build.js', '!src/vs/loader.js', '!src/vs/base/browser/dompurify/*', '!src/vs/base/common/marked/marked.js', diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index c3049784fe4..6f4523eeae6 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -15,7 +15,7 @@ const compileBuildTask = task.define('compile-build', task.series( util.rimraf('out-build'), util.buildWebNodePaths('out-build'), - compilation.compileTask('src', 'out-build', true) + compilation.compileTask('src', 'out-build', true, false) ) ); gulp.task(compileBuildTask); diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 3d8a85fd7f9..fcc35c88b01 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -29,13 +29,17 @@ const editorEntryPoints = [ name: 'vs/editor/editor.main', include: [], exclude: ['vs/css', 'vs/nls'], - prepend: ['out-editor-build/vs/css.js', 'out-editor-build/vs/nls.js'], + prepend: [ + { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' }, + { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } + ], }, { name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], - prepend: ['vs/loader.js'], - append: ['vs/base/worker/workerMain'], + exclude: ['vs/nls'], + prepend: [{ path: 'vs/loader.js' }], + append: [{ path: 'vs/base/worker/workerMain' }], dest: 'vs/base/worker/workerMain.js' } ]; @@ -77,7 +81,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { }); }); -const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true)); +const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, false)); const optimizeEditorAMDTask = task.define('optimize-editor-amd', common.optimizeTask({ src: 'out-editor-build', @@ -109,12 +113,6 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => 'inlineEntryPoint:0.ts', 'inlineEntryPoint:1.ts', 'vs/loader.js', - 'vs/nls.ts', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/css.js', - 'vs/css.build.js', - 'vs/css.d.ts', 'vs/base/worker/workerMain.ts', ], renames: { diff --git a/build/gulpfile.extensions.js b/build/gulpfile.extensions.js index 3f9f19b4b94..71fa1ec425c 100644 --- a/build/gulpfile.extensions.js +++ b/build/gulpfile.extensions.js @@ -100,7 +100,7 @@ const tasks = compilations.map(function (tsconfigFile) { headerOut = relativeDirname.substr(index + 1) + '/out'; } - function createPipeline(build, emitError) { + function createPipeline(build, emitError, transpileOnly) { const nlsDev = require('vscode-nls-dev'); const tsb = require('./lib/tsb'); const sourcemaps = require('gulp-sourcemaps'); @@ -110,7 +110,7 @@ const tasks = compilations.map(function (tsconfigFile) { overrideOptions.inlineSources = Boolean(build); overrideOptions.base = path.dirname(absolutePath); - const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false }, err => reporter(err.toString())); + const compilation = tsb.create(absolutePath, overrideOptions, { verbose: false, transpileOnly, transpileOnlyIncludesDts: transpileOnly }, err => reporter(err.toString())); const pipeline = function () { const input = es.through(); @@ -152,6 +152,16 @@ const tasks = compilations.map(function (tsconfigFile) { const cleanTask = task.define(`clean-extension-${name}`, util.rimraf(out)); + const transpileTask = task.define(`transpile-extension:${name}`, task.series(cleanTask, () => { + const pipeline = createPipeline(false, true, true); + const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); + const input = es.merge(nonts, pipeline.tsProjectSrc()); + + return input + .pipe(pipeline()) + .pipe(gulp.dest(out)); + })); + const compileTask = task.define(`compile-extension:${name}`, task.series(cleanTask, () => { const pipeline = createPipeline(false, true); const nonts = gulp.src(src, srcOpts).pipe(filter(['**', '!**/*.ts'])); @@ -184,12 +194,16 @@ const tasks = compilations.map(function (tsconfigFile) { })); // Tasks + gulp.task(transpileTask); gulp.task(compileTask); gulp.task(watchTask); - return { compileTask, watchTask, compileBuildTask }; + return { transpileTask, compileTask, watchTask, compileBuildTask }; }); +const transpileExtensionsTask = task.define('transpile-extensions', task.parallel(...tasks.map(t => t.transpileTask))); +gulp.task(transpileExtensionsTask); + const compileExtensionsTask = task.define('compile-extensions', task.parallel(...tasks.map(t => t.compileTask))); gulp.task(compileExtensionsTask); exports.compileExtensionsTask = compileExtensionsTask; diff --git a/build/gulpfile.js b/build/gulpfile.js index 65dda505fa7..75d3755d8e5 100644 --- a/build/gulpfile.js +++ b/build/gulpfile.js @@ -19,8 +19,12 @@ const { compileExtensionsTask, watchExtensionsTask, compileExtensionMediaTask } gulp.task(compileApiProposalNamesTask); gulp.task(watchApiProposalNamesTask); +// Transpile only +const transpileClientTask = task.define('transpile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileTask('src', 'out', false, true))); +gulp.task(transpileClientTask); + // Fast compile for development time -const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false))); +const compileClientTask = task.define('compile-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), compileApiProposalNamesTask, compileTask('src', 'out', false, false))); gulp.task(compileClientTask); const watchClientTask = task.define('watch-client', task.series(util.rimraf('out'), util.buildWebNodePaths('out'), task.parallel(watchTask('out', false), watchApiProposalNamesTask))); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 3315caf4bff..4a47b63c823 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -331,6 +331,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op result = es.merge(result, gulp.src('resources/win32/VisualElementsManifest.xml', { base: 'resources/win32' }) .pipe(rename(product.nameShort + '.VisualElementsManifest.xml'))); + result = es.merge(result, gulp.src('.build/policies/win32/**', { base: '.build/policies/win32' }) + .pipe(rename(f => f.dirname = `policies/${f.dirname}`))); + } else if (platform === 'linux') { result = es.merge(result, gulp.src('resources/linux/bin/code.sh', { base: '.' }) .pipe(replace('@@PRODNAME@@', product.nameLong)) diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 8c1967d4c68..497ac4fb67e 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -42,14 +42,20 @@ function bundle(entryPoints, config, callback) { if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire) => { - const resolvePath = (path) => { - const r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; + const resolvePath = (entry) => { + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } - return r; + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + r = r.replace('vs/css.build.js', 'vs/css.js'); + return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { const entryPoint = entryPointsMap[moduleId]; @@ -298,9 +304,18 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); } - else { + else if (module.defineLocation) { mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); } + else { + const moduleCopy = { + id: module.id, + path: module.path, + defineLocation: module.defineLocation, + dependencies: module.dependencies + }; + throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); + } }); Object.keys(usedPlugins).forEach((pluginName) => { const plugin = usedPlugins[pluginName]; @@ -321,10 +336,13 @@ function emitEntryPoint(modulesMap, deps, entryPoint, includedModules, prepend, plugin.writeFile(pluginName, entryPoint, req, write, {}); } }); - const toIFile = (path) => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry) => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index a1130d4bbbd..c5fdc2da18c 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -15,7 +15,7 @@ interface IPosition { interface IBuildModuleInfo { id: string; path: string; - defineLocation: IPosition; + defineLocation: IPosition | null; dependencies: string[]; shim: string; exports: any; @@ -42,12 +42,17 @@ interface ILoaderPluginReqFunc { toUrl(something: string): string; } +export interface IExtraFile { + path: string; + amdModuleId?: string; +} + export interface IEntryPoint { name: string; include?: string[]; exclude?: string[]; - prepend?: string[]; - append?: string[]; + prepend?: IExtraFile[]; + append?: IExtraFile[]; dest?: string; } @@ -92,6 +97,13 @@ interface IPartialBundleResult { export interface ILoaderConfig { isBuild?: boolean; paths?: { [path: string]: any }; + /* + * Normally, during a build, no module factories are invoked. This can be used + * to forcefully execute a module's factory. + */ + buildForceInvokeFactory: { + [moduleId: string]: boolean; + }; } /** @@ -132,15 +144,21 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } + config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; + config.buildForceInvokeFactory['vs/nls'] = true; + config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire: any) => { - const resolvePath = (path: string) => { - const r = localRequire.toUrl(path); - if (!/\.js/.test(r)) { - return r + '.js'; + const resolvePath = (entry: IExtraFile) => { + let r = localRequire.toUrl(entry.path); + if (!r.endsWith('.js')) { + r += '.js'; } - return r; + // avoid packaging the build version of plugins: + r = r.replace('vs/nls.build.js', 'vs/nls.js'); + r = r.replace('vs/css.build.js', 'vs/css.js'); + return { path: r, amdModuleId: entry.amdModuleId }; }; for (const moduleId in entryPointsMap) { const entryPoint = entryPointsMap[moduleId]; @@ -403,8 +421,8 @@ function emitEntryPoint( deps: IGraph, entryPoint: string, includedModules: string[], - prepend: string[], - append: string[], + prepend: IExtraFile[], + append: IExtraFile[], dest: string | undefined ): IEmitEntryPointResult { if (!dest) { @@ -444,8 +462,16 @@ function emitEntryPoint( if (module.shim) { mainResult.sources.push(emitShimmedModule(c, deps[c], module.shim, module.path, contents)); - } else { + } else if (module.defineLocation) { mainResult.sources.push(emitNamedModule(c, module.defineLocation, module.path, contents)); + } else { + const moduleCopy = { + id: module.id, + path: module.path, + defineLocation: module.defineLocation, + dependencies: module.dependencies + }; + throw new Error(`Cannot bundle module '${module.id}' for entry point '${entryPoint}' because it has no shim and it lacks a defineLocation: ${JSON.stringify(moduleCopy)}`); } }); @@ -470,10 +496,13 @@ function emitEntryPoint( } }); - const toIFile = (path: string): IFile => { - const contents = readFileAndRemoveBOM(path); + const toIFile = (entry: IExtraFile): IFile => { + let contents = readFileAndRemoveBOM(entry.path); + if (entry.amdModuleId) { + contents = contents.replace(/^define\(/m, `define("${entry.amdModuleId}",`); + } return { - path: path, + path: entry.path, contents: contents }; }; diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 2c815b1ad9b..d21c5b87f36 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -18,6 +18,7 @@ const ansiColors = require("ansi-colors"); const os = require("os"); const File = require("vinyl"); const task = require("./task"); +const tsb = require("./tsb"); const watch = require('./watch'); const reporter = (0, reporter_1.createReporter)(); function getTypeScriptCompilerOptions(src) { @@ -34,15 +35,15 @@ function getTypeScriptCompilerOptions(src) { options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; return options; } -function createCompile(src, build, emitError) { - const tsb = require('./tsb'); +function createCompile(src, build, emitError, transpileOnly) { + // const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps'); const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); const overrideOptions = { ...getTypeScriptCompilerOptions(src), inlineSources: Boolean(build) }; if (!build) { overrideOptions.inlineSourceMap = true; } - const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err)); + const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err)); function pipeline(token) { const bom = require('gulp-bom'); const utf8Filter = util.filter(data => /(\/|\\)test(\/|\\).*utf8/.test(data.path)); @@ -73,12 +74,12 @@ function createCompile(src, build, emitError) { }; return pipeline; } -function compileTask(src, out, build) { +function compileTask(src, out, build, transpileOnly) { return function () { if (os.totalmem() < 4000000000) { throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, transpileOnly); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -93,7 +94,7 @@ function compileTask(src, out, build) { exports.compileTask = compileTask; function watchTask(out, build) { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); const generator = new MonacoGenerator(true); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 0cb12f4ad53..c72f7fdecda 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -19,6 +19,7 @@ import * as os from 'os'; import ts = require('typescript'); import * as File from 'vinyl'; import * as task from './task'; +import * as tsb from './tsb'; const watch = require('./watch'); @@ -39,8 +40,8 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { return options; } -function createCompile(src: string, build: boolean, emitError?: boolean) { - const tsb = require('./tsb') as typeof import('./tsb'); +function createCompile(src: string, build: boolean, emitError: boolean, transpileOnly: boolean) { + // const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); @@ -50,7 +51,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { overrideOptions.inlineSourceMap = true; } - const compilation = tsb.create(projectPath, overrideOptions, { verbose: false }, err => reporter(err)); + const compilation = tsb.create(projectPath, overrideOptions, { verbose: false, transpileOnly }, err => reporter(err)); function pipeline(token?: util.ICancellationToken) { const bom = require('gulp-bom') as typeof import('gulp-bom'); @@ -86,7 +87,7 @@ function createCompile(src: string, build: boolean, emitError?: boolean) { return pipeline; } -export function compileTask(src: string, out: string, build: boolean): () => NodeJS.ReadWriteStream { +export function compileTask(src: string, out: string, build: boolean, transpileOnly: boolean): () => NodeJS.ReadWriteStream { return function () { @@ -94,7 +95,7 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true); + const compile = createCompile(src, build, true, transpileOnly); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -111,7 +112,7 @@ export function compileTask(src: string, out: string, build: boolean): () => Nod export function watchTask(out: string, build: boolean): () => NodeJS.ReadWriteStream { return function () { - const compile = createCompile('src', build); + const compile = createCompile('src', build, false, false); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); diff --git a/build/lib/optimize.js b/build/lib/optimize.js index d5b957d1195..6a0b1804834 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -35,40 +35,58 @@ function loaderConfig() { } exports.loaderConfig = loaderConfig; const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { - let sources = [ - `${src}/vs/loader.js` - ]; - if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); - } - let isFirst = true; +function loaderPlugin(src, base, amdModuleId) { return (gulp - .src(sources, { base: `${src}` }) + .src(src, { base }) .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); } - else { - this.emit('data', data); + this.emit('data', data); + }))); +} +function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); + if (bundleLoader) { + loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls')); + } + const files = []; + const order = (f) => { + if (f.path.endsWith('loader.js')) { + return 0; } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; + return (loaderStream + .pipe(es.through(function (data) { + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js'))); diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index fc2a2f42661..96f002faae8 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -41,41 +41,68 @@ export function loaderConfig() { const IS_OUR_COPYRIGHT_REGEXP = /Copyright \(C\) Microsoft Corporation/i; -function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream { - let sources = [ - `${src}/vs/loader.js` - ]; - if (bundleLoader) { - sources = sources.concat([ - `${src}/vs/css.js`, - `${src}/vs/nls.js` - ]); - } - - let isFirst = true; +function loaderPlugin(src: string, base: string, amdModuleId: string | undefined): NodeJS.ReadWriteStream { return ( gulp - .src(sources, { base: `${src}` }) - .pipe(es.through(function (data) { - if (isFirst) { - isFirst = false; - this.emit('data', new VinylFile({ - path: 'fake', - base: '.', - contents: Buffer.from(bundledFileHeader) - })); - this.emit('data', data); - } else { - this.emit('data', data); + .src(src, { base }) + .pipe(es.through(function (data: VinylFile) { + if (amdModuleId) { + let contents = data.contents.toString('utf8'); + contents = contents.replace(/^define\(/m, `define("${amdModuleId}",`); + data.contents = Buffer.from(contents); } + this.emit('data', data); + })) + ); +} + +function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, externalLoaderInfo?: any): NodeJS.ReadWriteStream { + let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); + if (bundleLoader) { + loaderStream = es.merge( + loaderStream, + loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), + loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls'), + ); + } + + const files: VinylFile[] = []; + const order = (f: VinylFile) => { + if (f.path.endsWith('loader.js')) { + return 0; + } + if (f.path.endsWith('css.js')) { + return 1; + } + if (f.path.endsWith('nls.js')) { + return 2; + } + return 3; + }; + + return ( + loaderStream + .pipe(es.through(function (data) { + files.push(data); }, function () { + files.sort((a, b) => { + return order(a) - order(b); + }); + files.unshift(new VinylFile({ + path: 'fake', + base: '.', + contents: Buffer.from(bundledFileHeader) + })); if (externalLoaderInfo !== undefined) { - this.emit('data', new VinylFile({ + files.push(new VinylFile({ path: 'fake2', base: '.', contents: Buffer.from(`require.config(${JSON.stringify(externalLoaderInfo, undefined, 2)});`) })); } + for (const file of files) { + this.emit('data', file); + } this.emit('end'); })) .pipe(concat('vs/loader.js')) diff --git a/build/lib/standalone.js b/build/lib/standalone.js index 192a436899f..1121e56ff94 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -102,13 +102,12 @@ function extractEditor(options) { delete tsConfig.compilerOptions.moduleResolution; writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/loader.d.ts', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 992d1ab5288..f2181e6f274 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -115,13 +115,12 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str writeOutputFile('tsconfig.json', JSON.stringify(tsConfig, null, '\t')); [ - 'vs/css.build.js', - 'vs/css.d.ts', - 'vs/css.js', + 'vs/css.build.ts', + 'vs/css.ts', 'vs/loader.js', - 'vs/nls.build.js', - 'vs/nls.d.ts', - 'vs/nls.js', + 'vs/loader.d.ts', + 'vs/nls.build.ts', + 'vs/nls.ts', 'vs/nls.mock.ts', ].forEach(copyFile); } diff --git a/build/lib/tsb/index.js b/build/lib/tsb/index.js index 44b9bc862dd..adab557fe87 100644 --- a/build/lib/tsb/index.js +++ b/build/lib/tsb/index.js @@ -15,6 +15,7 @@ const utils_1 = require("./utils"); const fs_1 = require("fs"); const log = require("fancy-log"); const colors = require("ansi-colors"); +const transpiler_1 = require("./transpiler"); class EmptyDuplex extends stream_1.Duplex { _write(_chunk, _encoding, callback) { callback(); } _read() { this.push(null); } @@ -51,25 +52,21 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) } } // FULL COMPILE stream doing transpile, syntax and semantic diagnostics - let _builder; - function createCompileStream(token) { - if (!_builder) { - _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); - } + function createCompileStream(builder, token) { return through(function (file) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); return; } - _builder.file(file); + builder.file(file); }, function () { // start the compilation process - _builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null)); + builder.build(file => this.queue(file), printDiagnostic, token).catch(e => console.error(e)).then(() => this.queue(null)); }); } // TRANSPILE ONLY stream doing just TS to JS conversion - function createTranspileStream() { + function createTranspileStream(transpiler) { return through(function (file) { // give the file to the compiler if (file.isStream()) { @@ -79,27 +76,29 @@ function create(projectPath, existingOptions, config, onError = _defaultOnError) if (!file.contents) { return; } - const out = ts.transpileModule(String(file.contents), { - compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false } - }); - if (out.diagnostics) { - out.diagnostics.forEach(printDiagnostic); + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; } - const outFile = new Vinyl({ - path: file.path.replace(/\.ts$/, '.js'), - cwd: file.cwd, - base: file.base, - contents: Buffer.from(out.outputText), + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + transpiler.transpile(file); + }, function () { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; }); - this.push(outFile); - logFn('Transpiled', file.path); }); } - const result = (token) => { - return config.transplileOnly - ? createTranspileStream() - : createCompileStream(token); - }; + let result; + if (config.transpileOnly) { + const transpiler = new transpiler_1.Transpiler(logFn, printDiagnostic, cmdLine); + result = (() => createTranspileStream(transpiler)); + } + else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token) => createCompileStream(_builder, token)); + } result.src = (opts) => { let _pos = 0; const _fileNames = cmdLine.fileNames.slice(0); diff --git a/build/lib/tsb/index.ts b/build/lib/tsb/index.ts index 548094bbf03..1164efc9d03 100644 --- a/build/lib/tsb/index.ts +++ b/build/lib/tsb/index.ts @@ -13,6 +13,7 @@ import { strings } from './utils'; import { readFileSync, statSync } from 'fs'; import * as log from 'fancy-log'; import colors = require('ansi-colors'); +import { Transpiler } from './transpiler'; export interface IncrementalCompiler { (token?: any): Readable & Writable; @@ -35,7 +36,7 @@ const _defaultOnError = (err: string) => console.log(JSON.stringify(err, null, 4 export function create( projectPath: string, existingOptions: Partial, - config: { verbose?: boolean; transplileOnly?: boolean }, + config: { verbose?: boolean; transpileOnly?: boolean; transpileOnlyIncludesDts?: boolean }, onError: (message: string) => void = _defaultOnError ): IncrementalCompiler { @@ -73,13 +74,7 @@ export function create( } // FULL COMPILE stream doing transpile, syntax and semantic diagnostics - - let _builder!: builder.ITypeScriptBuilder; - function createCompileStream(token?: builder.CancellationToken): Readable & Writable { - - if (!_builder) { - _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); - } + function createCompileStream(builder: builder.ITypeScriptBuilder, token?: builder.CancellationToken): Readable & Writable { return through(function (this: through.ThroughStream, file: Vinyl) { // give the file to the compiler @@ -87,11 +82,11 @@ export function create( this.emit('error', 'no support for streams'); return; } - _builder.file(file); + builder.file(file); }, function (this: { queue(a: any): void }) { // start the compilation process - _builder.build( + builder.build( file => this.queue(file), printDiagnostic, token @@ -100,46 +95,43 @@ export function create( } // TRANSPILE ONLY stream doing just TS to JS conversion - function createTranspileStream(): Readable & Writable { - - return through(function (this: through.ThroughStream, file: Vinyl) { + function createTranspileStream(transpiler: Transpiler): Readable & Writable { + return through(function (this: through.ThroughStream & { queue(a: any): void }, file: Vinyl) { // give the file to the compiler if (file.isStream()) { this.emit('error', 'no support for streams'); return; } - if (!file.contents) { return; } - - const out = ts.transpileModule(String(file.contents), { - compilerOptions: { ...cmdLine.options, declaration: false, sourceMap: false } - }); - - if (out.diagnostics) { - out.diagnostics.forEach(printDiagnostic); + if (!config.transpileOnlyIncludesDts && file.path.endsWith('.d.ts')) { + return; } - const outFile = new Vinyl({ - path: file.path.replace(/\.ts$/, '.js'), - cwd: file.cwd, - base: file.base, - contents: Buffer.from(out.outputText), + if (!transpiler.onOutfile) { + transpiler.onOutfile = file => this.queue(file); + } + + transpiler.transpile(file); + + }, function (this: { queue(a: any): void }) { + transpiler.join().then(() => { + this.queue(null); + transpiler.onOutfile = undefined; }); - - this.push(outFile); - - logFn('Transpiled', file.path); }); } - const result = (token: builder.CancellationToken) => { - return config.transplileOnly - ? createTranspileStream() - : createCompileStream(token); - }; + let result: IncrementalCompiler; + if (config.transpileOnly) { + const transpiler = new Transpiler(logFn, printDiagnostic, cmdLine); + result = (() => createTranspileStream(transpiler)); + } else { + const _builder = builder.createTypeScriptBuilder({ logFn }, projectPath, cmdLine); + result = ((token: builder.CancellationToken) => createCompileStream(_builder, token)); + } result.src = (opts?: { cwd?: string; base?: string }) => { let _pos = 0; diff --git a/build/lib/tsb/transpiler.js b/build/lib/tsb/transpiler.js new file mode 100644 index 00000000000..406075bbf8e --- /dev/null +++ b/build/lib/tsb/transpiler.js @@ -0,0 +1,210 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.Transpiler = void 0; +const ts = require("typescript"); +const threads = require("node:worker_threads"); +const Vinyl = require("vinyl"); +const node_os_1 = require("node:os"); +function transpile(tsSrc, options) { + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req) => { + const res = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort.postMessage(res); + }); +} +class TranspileWorker { + constructor(outFileFn) { + this.id = TranspileWorker.pool++; + this._worker = new threads.Worker(__filename); + this._durations = []; + this._worker.addListener('message', (res) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + const [resolve, reject, files, options, t1] = this._pending; + const outFiles = []; + const diag = []; + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + if (diag.length > 0) { + diag.push(...diag); + continue; + } + let SuffixTypes; + (function (SuffixTypes) { + SuffixTypes[SuffixTypes["Dts"] = 5] = "Dts"; + SuffixTypes[SuffixTypes["Ts"] = 3] = "Ts"; + SuffixTypes[SuffixTypes["Unknown"] = 0] = "Unknown"; + })(SuffixTypes || (SuffixTypes = {})); + const suffixLen = file.path.endsWith('.d.ts') ? 5 /* SuffixTypes.Dts */ + : file.path.endsWith('.ts') ? 3 /* SuffixTypes.Ts */ + : 0 /* SuffixTypes.Unknown */; + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === 5 /* SuffixTypes.Dts */ && _isDefaultEmpty(jsSrc)) { + continue; + } + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + this._pending = undefined; + this._durations.push(Date.now() - t1); + if (diag.length > 0) { + reject(diag); + } + else { + resolve(outFiles); + } + }); + } + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + get isBusy() { + return this._pending !== undefined; + } + next(files, options) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} +TranspileWorker.pool = 1; +class Transpiler { + constructor(logFn, _onError, _cmdLine) { + this._onError = _onError; + this._cmdLine = _cmdLine; + this._workerPool = []; + this._queue = []; + this._allJobs = []; + this._tsApiInternalOutfileName = new class { + constructor(parsedCmd) { + const host = ts.createCompilerHost(parsedCmd.options); + const program = ts.createProgram({ options: parsedCmd.options, rootNames: parsedCmd.fileNames, host }); + const emitHost = { + getCompilerOptions: () => parsedCmd.options, + getCurrentDirectory: () => host.getCurrentDirectory(), + getCanonicalFileName: file => host.getCanonicalFileName(file), + getCommonSourceDirectory: () => program.getCommonSourceDirectory() + }; + this.getForInfile = file => { + return ts.getOwnEmitOutputFilePath(file, emitHost, '.js'); + }; + } + }(this._cmdLine); + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + } + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + transpile(file) { + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + _consumeQueue() { + if (this._queue.length === 0) { + // no work... + return; + } + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._tsApiInternalOutfileName.getForInfile(file))); + } + } + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + const job = new Promise(resolve => { + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + consume(); + }); + this._allJobs.push(job); + } + } +} +exports.Transpiler = Transpiler; +Transpiler.P = Math.floor((0, node_os_1.cpus)().length * .5); +function _isDefaultEmpty(src) { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/build/lib/tsb/transpiler.ts b/build/lib/tsb/transpiler.ts new file mode 100644 index 00000000000..ad515263f6c --- /dev/null +++ b/build/lib/tsb/transpiler.ts @@ -0,0 +1,276 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as ts from 'typescript'; +import * as threads from 'node:worker_threads'; +import * as Vinyl from 'vinyl'; +import { cpus } from 'node:os'; + +interface TranspileReq { + readonly tsSrcs: string[]; + readonly options: ts.TranspileOptions; +} + +interface TranspileRes { + readonly jsSrcs: string[]; + readonly diagnostics: ts.Diagnostic[][]; +} + +function transpile(tsSrc: string, options: ts.TranspileOptions): { jsSrc: string; diag: ts.Diagnostic[] } { + + const isAmd = /\n(import|export)/m.test(tsSrc); + if (!isAmd && options.compilerOptions?.module === ts.ModuleKind.AMD) { + // enforce NONE module-system for not-amd cases + options = { ...options, ...{ compilerOptions: { ...options.compilerOptions, module: ts.ModuleKind.None } } }; + } + const out = ts.transpileModule(tsSrc, options); + return { + jsSrc: out.outputText, + diag: out.diagnostics ?? [] + }; +} + +if (!threads.isMainThread) { + // WORKER + threads.parentPort?.addListener('message', (req: TranspileReq) => { + const res: TranspileRes = { + jsSrcs: [], + diagnostics: [] + }; + for (const tsSrc of req.tsSrcs) { + const out = transpile(tsSrc, req.options); + res.jsSrcs.push(out.jsSrc); + res.diagnostics.push(out.diag); + } + threads.parentPort!.postMessage(res); + }); +} + +class TranspileWorker { + + private static pool = 1; + + readonly id = TranspileWorker.pool++; + + private _worker = new threads.Worker(__filename); + private _pending?: [resolve: Function, reject: Function, file: Vinyl[], options: ts.TranspileOptions, t1: number]; + private _durations: number[] = []; + + constructor(outFileFn: (fileName: string) => string) { + + this._worker.addListener('message', (res: TranspileRes) => { + if (!this._pending) { + console.error('RECEIVING data WITHOUT request'); + return; + } + + const [resolve, reject, files, options, t1] = this._pending; + + const outFiles: Vinyl[] = []; + const diag: ts.Diagnostic[] = []; + + for (let i = 0; i < res.jsSrcs.length; i++) { + // inputs and outputs are aligned across the arrays + const file = files[i]; + const jsSrc = res.jsSrcs[i]; + const diag = res.diagnostics[i]; + + if (diag.length > 0) { + diag.push(...diag); + continue; + } + const enum SuffixTypes { + Dts = 5, + Ts = 3, + Unknown = 0 + } + const suffixLen = file.path.endsWith('.d.ts') ? SuffixTypes.Dts + : file.path.endsWith('.ts') ? SuffixTypes.Ts + : SuffixTypes.Unknown; + + // check if output of a DTS-files isn't just "empty" and iff so + // skip this file + if (suffixLen === SuffixTypes.Dts && _isDefaultEmpty(jsSrc)) { + continue; + } + + const outBase = options.compilerOptions?.outDir ?? file.base; + const outPath = outFileFn(file.path); + + outFiles.push(new Vinyl({ + path: outPath, + base: outBase, + contents: Buffer.from(jsSrc), + })); + } + + this._pending = undefined; + this._durations.push(Date.now() - t1); + + if (diag.length > 0) { + reject(diag); + } else { + resolve(outFiles); + } + }); + } + + terminate() { + // console.log(`Worker#${this.id} ENDS after ${this._durations.length} jobs (total: ${this._durations.reduce((p, c) => p + c, 0)}, avg: ${this._durations.reduce((p, c) => p + c, 0) / this._durations.length})`); + this._worker.terminate(); + } + + get isBusy() { + return this._pending !== undefined; + } + + next(files: Vinyl[], options: ts.TranspileOptions) { + if (this._pending !== undefined) { + throw new Error('BUSY'); + } + return new Promise((resolve, reject) => { + this._pending = [resolve, reject, files, options, Date.now()]; + const req: TranspileReq = { + options, + tsSrcs: files.map(file => String(file.contents)) + }; + this._worker.postMessage(req); + }); + } +} + + +export class Transpiler { + + static P = Math.floor(cpus().length * .5); + + public onOutfile?: (file: Vinyl) => void; + + private _workerPool: TranspileWorker[] = []; + private _queue: Vinyl[] = []; + private _allJobs: Promise[] = []; + + constructor( + logFn: (topic: string, message: string) => void, + private readonly _onError: (err: any) => void, + private readonly _cmdLine: ts.ParsedCommandLine + ) { + logFn('Transpile', `will use ${Transpiler.P} transpile worker`); + } + + async join() { + // wait for all penindg jobs + this._consumeQueue(); + await Promise.allSettled(this._allJobs); + this._allJobs.length = 0; + + // terminate all worker + this._workerPool.forEach(w => w.terminate()); + this._workerPool.length = 0; + } + + + transpile(file: Vinyl) { + + if (this._cmdLine.options.noEmit) { + // not doing ANYTHING here + return; + } + + const newLen = this._queue.push(file); + if (newLen > Transpiler.P ** 2) { + this._consumeQueue(); + } + } + + private _consumeQueue(): void { + + if (this._queue.length === 0) { + // no work... + return; + } + + // kinda LAZYily create workers + if (this._workerPool.length === 0) { + for (let i = 0; i < Transpiler.P; i++) { + this._workerPool.push(new TranspileWorker(file => this._tsApiInternalOutfileName.getForInfile(file))); + } + } + + const freeWorker = this._workerPool.filter(w => !w.isBusy); + if (freeWorker.length === 0) { + // OK, they will pick up work themselves + return; + } + + for (const worker of freeWorker) { + if (this._queue.length === 0) { + break; + } + + const job = new Promise(resolve => { + + const consume = () => { + const files = this._queue.splice(0, Transpiler.P); + if (files.length === 0) { + // DONE + resolve(undefined); + return; + } + // work on the NEXT file + // const [inFile, outFn] = req; + worker.next(files, { compilerOptions: this._cmdLine.options }).then(outFiles => { + if (this.onOutfile) { + outFiles.map(this.onOutfile, this); + } + consume(); + }).catch(err => { + this._onError(err); + }); + }; + + consume(); + }); + + this._allJobs.push(job); + } + } + + private _tsApiInternalOutfileName = new class { + + getForInfile: (file: string) => string; + + constructor(parsedCmd: ts.ParsedCommandLine) { + + type InternalTsHost = { + getCompilerOptions(): ts.CompilerOptions; + getCurrentDirectory(): string; + getCommonSourceDirectory(): string; + getCanonicalFileName(file: string): string; + }; + type InternalTsApi = { getOwnEmitOutputFilePath(fileName: string, host: InternalTsHost, extension: string): string } & typeof ts; + + const host = ts.createCompilerHost(parsedCmd.options); + const program = ts.createProgram({ options: parsedCmd.options, rootNames: parsedCmd.fileNames, host }); + const emitHost: InternalTsHost = { + getCompilerOptions: () => parsedCmd.options, + getCurrentDirectory: () => host.getCurrentDirectory(), + getCanonicalFileName: file => host.getCanonicalFileName(file), + getCommonSourceDirectory: () => (program).getCommonSourceDirectory() + }; + + this.getForInfile = file => { + return (ts).getOwnEmitOutputFilePath(file, emitHost, '.js'); + }; + } + }(this._cmdLine); +} + +function _isDefaultEmpty(src: string): boolean { + return src + .replace('"use strict";', '') + .replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, '$1') + .trim().length === 0; +} diff --git a/cgmanifest.json b/cgmanifest.json index 59dc01361ce..972ef7c833e 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "6839dd92b84057e79ddce06f8291cc56a0a51ff6" + "commitHash": "7162f641b5761e737ca5a9b3ef6d3e7454ee3dbd" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "18.3.2" + "version": "18.3.4" }, { "component": { diff --git a/extensions/cpp/cgmanifest.json b/extensions/cpp/cgmanifest.json index d0f49639167..29876c8523b 100644 --- a/extensions/cpp/cgmanifest.json +++ b/extensions/cpp/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-cpp-syntax", "repositoryUrl": "https://github.com/jeff-hykin/better-cpp-syntax", - "commitHash": "156fc0eef532928c9dbf22f622d4a8efc7d97a6b" + "commitHash": "ddcaa65af8a578881e0d38f3c1cf5259a1128ab5" } }, "license": "MIT", - "version": "1.15.13", + "version": "1.15.17", "description": "The original JSON grammars were derived from https://github.com/atom/language-c which was originally converted from the C TextMate bundle https://github.com/textmate/c.tmbundle." }, { diff --git a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json index 7f41320ec40..c85993386c5 100644 --- a/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json +++ b/extensions/cpp/syntaxes/cpp.embedded.macro.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/156fc0eef532928c9dbf22f622d4a8efc7d97a6b", + "version": "https://github.com/jeff-hykin/better-cpp-syntax/commit/ddcaa65af8a578881e0d38f3c1cf5259a1128ab5", "name": "C++", "scopeName": "source.cpp.embedded.macro", "patterns": [ @@ -519,7 +519,7 @@ "name": "comment.block.cpp" }, "builtin_storage_type_initilizer": { - "begin": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)|(?=(?))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -4240,7 +4240,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -5368,7 +5368,7 @@ }, "line": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))|(?=(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7350,7 +7351,7 @@ "include": "source.cpp#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -7702,7 +7703,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()|(?=(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "\\s*+((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -2176,7 +2176,7 @@ ] }, "control_flow_keywords": { - "match": "((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\{)", "end": "\\}", "beginCaptures": { "1": { @@ -3363,7 +3363,7 @@ ] }, "destructor_root": { - "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", + "begin": "((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)(((?>(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))::((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))~\\14((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\())", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -3655,7 +3655,7 @@ }, "diagnostic": { "begin": "(^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?((?:error|warning)))\\b(?:(?:\\s)+)?", - "end": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?(::))?(?:(?:\\s)+)?((?|\\?\\?>)(?:(?:\\s)+)?(;)|(;))|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -4519,7 +4519,7 @@ ] }, "function_call": { - "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", + "begin": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?(\\()", "end": "\\)", "beginCaptures": { "1": { @@ -4593,7 +4593,7 @@ ] }, "function_definition": { - "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", + "begin": "(?:(?:^|\\G|(?<=;|\\}))|(?<=>))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<60>?)+>)(?:\\s)*+)?::)*\\s*+)((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\b(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -5156,7 +5156,7 @@ ] }, { - "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", + "match": "(?<=^|\\))(?:(?:\\s)+)?(->)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<23>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "punctuation.definition.function.return-type.cpp" @@ -5404,7 +5404,7 @@ ] }, "function_pointer": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -5755,7 +5755,7 @@ ] }, "function_pointer_parameter": { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -6476,7 +6476,7 @@ "name": "storage.type.modifier.virtual.cpp" }, { - "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", + "match": "(?<=protected|virtual|private|public|,|:)(?:(?:\\s)+)?(?!(?:(?:(?:protected)|(?:private)|(?:public))|virtual))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -6674,7 +6674,7 @@ ] }, "inline_builtin_storage_type": { - "match": "(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?line\\b", - "end": "(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?define\\b)(?:(?:\\s)+)?((?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least16_t[^\\w]|uint_least32_t[^\\w]|uint_least64_t[^\\w]|int_least16_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|int_least8_t[^\\w]|int_fast16_t[^\\w]|int_fast32_t[^\\w]|int_fast64_t[^\\w]|uint_fast8_t[^\\w]|suseconds_t[^\\w]|int_fast8_t[^\\w]|useconds_t[^\\w]|blksize_t[^\\w]|in_addr_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|unsigned[^\\w]|u_quad_t[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|intptr_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|swblk_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|signed[^\\w]|double[^\\w]|u_char[^\\w]|u_long[^\\w]|ushort[^\\w]|quad_t[^\\w]|mode_t[^\\w]|size_t[^\\w]|time_t[^\\w]|int8_t[^\\w]|short[^\\w]|float[^\\w]|u_int[^\\w]|div_t[^\\w]|dev_t[^\\w]|gid_t[^\\w]|ino_t[^\\w]|key_t[^\\w]|pid_t[^\\w]|off_t[^\\w]|uid_t[^\\w]|auto[^\\w]|void[^\\w]|char[^\\w]|long[^\\w]|bool[^\\w]|uint[^\\w]|id_t[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", + "match": "(?:((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?\\*|->)))((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*(?:(?:\\s)+)?(?:(?:\\.\\*|\\.)|(?:->\\*|->))(?:(?:\\s)+)?)*)(?:(?:\\s)+)?(\\b(?!uint_least32_t[^\\w]|uint_least16_t[^\\w]|uint_least64_t[^\\w]|int_least32_t[^\\w]|int_least64_t[^\\w]|uint_fast32_t[^\\w]|uint_fast64_t[^\\w]|uint_least8_t[^\\w]|uint_fast16_t[^\\w]|int_least16_t[^\\w]|int_fast16_t[^\\w]|int_least8_t[^\\w]|uint_fast8_t[^\\w]|int_fast64_t[^\\w]|int_fast32_t[^\\w]|int_fast8_t[^\\w]|suseconds_t[^\\w]|useconds_t[^\\w]|in_addr_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|uintmax_t[^\\w]|in_port_t[^\\w]|uintptr_t[^\\w]|blksize_t[^\\w]|uint32_t[^\\w]|uint64_t[^\\w]|u_quad_t[^\\w]|intmax_t[^\\w]|intmax_t[^\\w]|unsigned[^\\w]|blkcnt_t[^\\w]|uint16_t[^\\w]|intptr_t[^\\w]|swblk_t[^\\w]|wchar_t[^\\w]|u_short[^\\w]|qaddr_t[^\\w]|caddr_t[^\\w]|daddr_t[^\\w]|fixpt_t[^\\w]|nlink_t[^\\w]|segsz_t[^\\w]|clock_t[^\\w]|ssize_t[^\\w]|int16_t[^\\w]|int32_t[^\\w]|int64_t[^\\w]|uint8_t[^\\w]|int8_t[^\\w]|mode_t[^\\w]|quad_t[^\\w]|ushort[^\\w]|u_long[^\\w]|u_char[^\\w]|double[^\\w]|signed[^\\w]|time_t[^\\w]|size_t[^\\w]|key_t[^\\w]|div_t[^\\w]|ino_t[^\\w]|uid_t[^\\w]|gid_t[^\\w]|off_t[^\\w]|pid_t[^\\w]|float[^\\w]|dev_t[^\\w]|u_int[^\\w]|short[^\\w]|bool[^\\w]|id_t[^\\w]|uint[^\\w]|long[^\\w]|char[^\\w]|void[^\\w]|auto[^\\w]|id_t[^\\w]|int[^\\w])(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b(?!\\())", "captures": { "1": { "patterns": [ @@ -7574,7 +7575,7 @@ ] }, "namespace_alias": { - "match": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<8>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<4>?)+>)(?:\\s)*+)?::)*\\s*+)(?:(?:\\s)+)?((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:new)|(?:\\->\\*)|(?:<<=)|(?:>>=)|(?:<=>)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:\\-\\-)|(?:<<)|(?:>>)|(?:<=)|(?:>=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|(?:\\/=)|(?:%=)|(?:&=)|(?:\\^=)|(?:\\|=)|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=|,))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", + "begin": "(?:(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:__cdecl|__clrcall|__stdcall|__fastcall|__thiscall|__vectorcall)?)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(operator)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<55>?)+>)(?:\\s)*+)?::)*+)(?:(?:((?:(?:delete\\[\\])|(?:delete)|(?:new\\[\\])|(?:<=>)|(?:<<=)|(?:new)|(?:>>=)|(?:\\->\\*)|(?:\\/=)|(?:%=)|(?:&=)|(?:>=)|(?:\\|=)|(?:\\+\\+)|(?:\\-\\-)|(?:\\(\\))|(?:\\[\\])|(?:\\->)|(?:\\+\\+)|(?:<<)|(?:>>)|(?:\\-\\-)|(?:<=)|(?:\\^=)|(?:==)|(?:!=)|(?:&&)|(?:\\|\\|)|(?:\\+=)|(?:\\-=)|(?:\\*=)|,|(?:\\+)|(?:\\-)|!|~|(?:\\*)|&|(?:\\*)|(?:\\/)|%|(?:\\+)|(?:\\-)|<|>|&|(?:\\^)|(?:\\|)|=))|((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?:\\[\\])?)))|(\"\")((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))((?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=\\<|\\()", "end": "(?:(?<=\\}|%>|\\?\\?>)|(?=[;>\\[\\]=]))", "beginCaptures": { "0": { @@ -10440,7 +10441,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -11478,7 +11479,7 @@ "include": "#vararg_ellipses" }, { - "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", + "match": "((?:((?:(?:thread_local)|(?:volatile)|(?:register)|(?:restrict)|(?:static)|(?:extern)|(?:const)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))+)((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:\\s)*+(?(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=,|\\)|=)", "captures": { "1": { "patterns": [ @@ -12488,7 +12489,7 @@ }, "pragma": { "begin": "^((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(#)(?:(?:\\s)+)?pragma\\b", - "end": "(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", + "match": "\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<11>?)+>)?(?![\\w<:.])", "captures": { "0": { "patterns": [ @@ -13083,7 +13084,7 @@ } }, "scope_resolution": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13105,7 +13106,7 @@ } }, "scope_resolution_function_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13127,7 +13128,7 @@ } }, "scope_resolution_function_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13165,7 +13166,7 @@ } }, "scope_resolution_function_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13187,7 +13188,7 @@ } }, "scope_resolution_function_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13225,7 +13226,7 @@ } }, "scope_resolution_function_definition_operator_overload": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13247,7 +13248,7 @@ } }, "scope_resolution_function_definition_operator_overload_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13285,7 +13286,7 @@ } }, "scope_resolution_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13323,7 +13324,7 @@ } }, "scope_resolution_namespace_alias": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13345,7 +13346,7 @@ } }, "scope_resolution_namespace_alias_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13383,7 +13384,7 @@ } }, "scope_resolution_namespace_block": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13405,7 +13406,7 @@ } }, "scope_resolution_namespace_block_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13443,7 +13444,7 @@ } }, "scope_resolution_namespace_using": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13465,7 +13466,7 @@ } }, "scope_resolution_namespace_using_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13503,7 +13504,7 @@ } }, "scope_resolution_parameter": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13525,7 +13526,7 @@ } }, "scope_resolution_parameter_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13563,7 +13564,7 @@ } }, "scope_resolution_template_call": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13585,7 +13586,7 @@ } }, "scope_resolution_template_call_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13623,7 +13624,7 @@ } }, "scope_resolution_template_definition": { - "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", + "match": "(::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<3>?)+>)(?:\\s)*+)?::)*\\s*+", "captures": { "0": { "patterns": [ @@ -13645,7 +13646,7 @@ } }, "scope_resolution_template_definition_inner_generated": { - "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", + "match": "((::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?::)*\\s*+)((?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<7>?)+>)(?:\\s)*+)?(::)", "captures": { "1": { "patterns": [ @@ -13687,7 +13688,7 @@ "name": "punctuation.terminator.statement.cpp" }, "simple_type": { - "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", + "match": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<12>?)+>)?(?![\\w<:.]))(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?", "captures": { "1": { "name": "meta.qualified_type.cpp", @@ -16491,7 +16492,7 @@ } }, "type_alias": { - "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", + "match": "(using)(?:(?:\\s)+)?(?!namespace)(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))(?:(?:\\s)+)?(\\=)(?:(?:\\s)+)?((?:typename)?)(?:(?:\\s)+)?((?:(?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<29>?)+>)?(?![\\w<:.]))|(.*(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)?(?:(?:\\s)+)?(?:(;)|\\n)", "captures": { "1": { "name": "keyword.other.using.directive.cpp" @@ -17581,7 +17582,7 @@ "endCaptures": {}, "patterns": [ { - "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", + "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)(?:\\s)*+)?::)*+)?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<18>?)+>)?(?![\\w<:.]))(((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))?(?:(?:&|(?:\\*))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*(?:&|(?:\\*)))?((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\()(\\*)(?:(?:\\s)+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:(?:\\s)+)?(?:(\\[)(\\w*)(\\])(?:(?:\\s)+)?)*(\\))(?:(?:\\s)+)?(\\()", "end": "(\\))((?:(?:(?:(?>(?:\\s)+)|(\\/\\*)((?:[^\\*]|(?:\\*)++[^\\/])*+((?:\\*)++\\/)))+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?=[{=,);>]|\\n)(?!\\()", "beginCaptures": { "1": { @@ -18866,7 +18867,7 @@ ] }, "typename": { - "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|__has_include|atomic_cancel|atomic_commit|dynamic_cast|thread_local|synchronized|static_cast|const_cast|co_return|constexpr|consteval|constexpr|constexpr|consteval|protected|namespace|constinit|co_return|noexcept|noexcept|continue|co_await|co_yield|volatile|register|restrict|explicit|volatile|noexcept|template|operator|decltype|typename|requires|co_await|co_yield|reflexpr|alignof|alignas|default|mutable|virtual|mutable|private|include|warning|_Pragma|defined|typedef|__asm__|concept|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|public|ifndef|define|pragma|export|import|module|compl|bitor|throw|or_eq|while|catch|break|class|union|const|const|endif|ifdef|undef|error|using|else|goto|case|enum|elif|else|line|this|not|new|xor|and|for|try|asm|or|do|if|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:__has_include)|(?:atomic_cancel)|(?:atomic_commit)|(?:dynamic_cast)|(?:thread_local)|(?:synchronized)|(?:static_cast)|(?:const_cast)|(?:co_return)|(?:constexpr)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:consteval)|(?:protected)|(?:namespace)|(?:constinit)|(?:co_return)|(?:noexcept)|(?:noexcept)|(?:continue)|(?:co_await)|(?:co_yield)|(?:volatile)|(?:register)|(?:restrict)|(?:explicit)|(?:override)|(?:volatile)|(?:noexcept)|(?:template)|(?:operator)|(?:decltype)|(?:typename)|(?:requires)|(?:co_await)|(?:co_yield)|(?:reflexpr)|(?:alignof)|(?:alignas)|(?:default)|(?:nullptr)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:sizeof)|(?:delete)|(?:not_eq)|(?:bitand)|(?:and_eq)|(?:xor_eq)|(?:typeid)|(?:switch)|(?:return)|(?:static)|(?:extern)|(?:inline)|(?:friend)|(?:public)|(?:ifndef)|(?:define)|(?:pragma)|(?:export)|(?:import)|(?:module)|(?:compl)|(?:bitor)|(?:throw)|(?:or_eq)|(?:while)|(?:catch)|(?:break)|(?:false)|(?:const)|(?:final)|(?:const)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:using)|(?:audit)|(?:axiom)|(?:else)|(?:goto)|(?:case)|(?:NULL)|(?:true)|(?:elif)|(?:else)|(?:line)|(?:this)|(?:not)|(?:new)|(?:xor)|(?:and)|(?:for)|(?:try)|(?:asm)|(?:or)|(?:do)|(?:if)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", + "match": "(((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z))))*((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)(?:\\s)*+)?::)*+)?((?:((?:(?>(?:\\s)+)|\\/\\*(?:[^\\*]|(?:\\*)++[^\\/])*+(?:\\*)++\\/)+)|(?:\\b)|(?=\\W)|(?<=\\W)|(?:\\A)|(?:\\Z)))(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<17>?)+>)?(?![\\w<:.]))", "captures": { "1": { "name": "storage.modifier.cpp" @@ -19755,7 +19756,7 @@ } }, "using_namespace": { - "begin": "(?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((?]*+|\"(?:[^\"]*|\\\\\")\")|'(?:[^']*|\\\\')')\\g<6>?)+>)(?:\\s)*+)?::)*\\s*+)?((? { const document = documents.get(textDocument.uri); if (document) { - console.log(JSON.stringify(options)); - const edits = getLanguageService(document).format(document, range ?? getFullRange(document), options as CSSFormatConfiguration); + const edits = getLanguageService(document).format(document, range ?? getFullRange(document), options); if (edits.length > formatterMaxNumberOfEdits) { const newText = TextDocument.applyEdits(document, edits); return [TextEdit.replace(getFullRange(document), newText)]; diff --git a/extensions/debug-auto-launch/src/extension.ts b/extensions/debug-auto-launch/src/extension.ts index 572c468d843..86de0ac6ea4 100644 --- a/extensions/debug-auto-launch/src/extension.ts +++ b/extensions/debug-auto-launch/src/extension.ts @@ -8,6 +8,12 @@ import { createServer, Server } from 'net'; import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; +const enum State { + Disabled = 'disabled', + OnlyWithFlag = 'onlyWithFlag', + Smart = 'smart', + Always = 'always', +} const localize = nls.loadMessageBundle(); const TEXT_STATUSBAR_LABEL = { [State.Disabled]: localize('status.text.auto.attach.disabled', 'Auto Attach: Disabled'), @@ -62,12 +68,6 @@ const SETTINGS_CAUSE_REFRESH = new Set( ['autoAttachSmartPattern', SETTING_STATE].map(s => `${SETTING_SECTION}.${s}`), ); -const enum State { - Disabled = 'disabled', - OnlyWithFlag = 'onlyWithFlag', - Smart = 'smart', - Always = 'always', -} let currentState: Promise<{ context: vscode.ExtensionContext; state: State | null }>; let statusItem: vscode.StatusBarItem | undefined; // and there is no status bar item diff --git a/extensions/git/package.json b/extensions/git/package.json index 00464a5d1e6..752304346ed 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -14,7 +14,6 @@ "contribMergeEditorToolbar", "contribViewsWelcome", "scmActionButton", - "scmInput", "scmSelectedProvider", "scmValidation", "timeline" @@ -2034,7 +2033,7 @@ "type": "boolean", "scope": "resource", "description": "%config.useEditorAsCommitInput%", - "default": false + "default": true }, "git.verboseCommit": { "type": "boolean", @@ -2364,6 +2363,12 @@ "default": true, "description": "%config.terminalAuthentication%" }, + "git.terminalGitEditor": { + "type": "boolean", + "scope": "resource", + "default": true, + "description": "%config.terminalGitEditor%" + }, "git.useCommitInputAsStashMessage": { "type": "boolean", "scope": "resource", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 5210e64de80..d17b31b71be 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -212,6 +212,7 @@ "config.requireGitUserConfig": "Controls whether to require explicit Git user configuration or allow Git to guess if missing.", "config.showCommitInput": "Controls whether to show the commit input in the Git source control panel.", "config.terminalAuthentication": "Controls whether to enable VS Code to be the authentication handler for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", + "config.terminalGitEditor": "Controls whether to enable VS Code to be git editor for git processes spawned in the integrated terminal. Note: terminals need to be restarted to pick up a change in this setting.", "config.timeline.showAuthor": "Controls whether to show the commit author in the Timeline view.", "config.timeline.showUncommitted": "Controls whether to show uncommitted changes in the Timeline view.", "config.timeline.date": "Controls which date to use for items in the Timeline view.", diff --git a/extensions/git/src/askpass.ts b/extensions/git/src/askpass.ts index d9d9378f8aa..cbf981eea19 100644 --- a/extensions/git/src/askpass.ts +++ b/extensions/git/src/askpass.ts @@ -8,9 +8,11 @@ import { IDisposable, EmptyDisposable, toDisposable } from './util'; import * as path from 'path'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; import { CredentialsProvider, Credentials } from './api/git'; +import { ITerminalEnvironmentProvider } from './terminal'; -export class Askpass implements IIPCHandler { +export class Askpass implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; private cache = new Map(); private credentialsProviders = new Set(); @@ -19,6 +21,13 @@ export class Askpass implements IIPCHandler { if (ipc) { this.disposable = ipc.registerHandler('askpass', this); } + + this.env = { + GIT_ASKPASS: path.join(__dirname, this.ipc ? 'askpass.sh' : 'askpass-empty.sh'), + VSCODE_GIT_ASKPASS_NODE: process.execPath, + VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js'), + }; } async handle({ request, host }: { request: string; host: string }): Promise { @@ -64,25 +73,13 @@ export class Askpass implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_ASKPASS: path.join(__dirname, 'askpass-empty.sh') - }; - } - - const env: { [key: string]: string } = { - ...this.ipc.getEnv(), - VSCODE_GIT_ASKPASS_NODE: process.execPath, - VSCODE_GIT_ASKPASS_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_ASKPASS_MAIN: path.join(__dirname, 'askpass-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useIntegratedAskPass')) { - env.GIT_ASKPASS = path.join(__dirname, 'askpass.sh'); - } + return config.get('useIntegratedAskPass') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useIntegratedAskPass') && config.get('terminalAuthentication') ? this.env : {}; } registerCredentialsProvider(provider: CredentialsProvider): Disposable { diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index dd5f2f9a029..c231c44ee94 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,7 +5,6 @@ import * as os from 'os'; import * as path from 'path'; -import * as picomatch from 'picomatch'; import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; @@ -27,24 +26,25 @@ const localize = nls.loadMessageBundle(); class CheckoutItem implements QuickPickItem { protected get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } - get label(): string { return this.ref.name || this.shortCommit; } + get label(): string { return this.ref.name ? `${this.ref.name}${this.repository.isBranchProtected(this.ref.name) ? ' $(lock-small)' : ''}` : this.shortCommit; } get description(): string { return this.shortCommit; } - constructor(protected ref: Ref) { } + constructor(protected repository: Repository, protected ref: Ref) { } - async run(repository: Repository, opts?: { detached?: boolean }): Promise { + async run(opts?: { detached?: boolean }): Promise { const ref = this.ref.name; if (!ref) { return; } - await repository.checkout(ref, opts); + await this.repository.checkout(ref, opts); } } class CheckoutTagItem extends CheckoutItem { + override get label(): string { return this.ref.name || this.shortCommit; } override get description(): string { return localize('tag at', "Tag at {0}", this.shortCommit); } @@ -52,21 +52,22 @@ class CheckoutTagItem extends CheckoutItem { class CheckoutRemoteHeadItem extends CheckoutItem { + override get label(): string { return this.ref.name || this.shortCommit; } override get description(): string { return localize('remote branch at', "Remote branch at {0}", this.shortCommit); } - override async run(repository: Repository, opts?: { detached?: boolean }): Promise { + override async run(opts?: { detached?: boolean }): Promise { if (!this.ref.name) { return; } - const branches = await repository.findTrackingBranches(this.ref.name); + const branches = await this.repository.findTrackingBranches(this.ref.name); if (branches.length > 0) { - await repository.checkout(branches[0].name!, opts); + await this.repository.checkout(branches[0].name!, opts); } else { - await repository.checkoutTracking(this.ref.name, opts); + await this.repository.checkoutTracking(this.ref.name, opts); } } } @@ -219,7 +220,7 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { checkoutTypes = checkoutTypeConfig; } - const processors = checkoutTypes.map(getCheckoutProcessor) + const processors = checkoutTypes.map(type => getCheckoutProcessor(repository, type)) .filter(p => !!p) as CheckoutProcessor[]; for (const ref of repository.refs) { @@ -234,8 +235,8 @@ function createCheckoutItems(repository: Repository): CheckoutItem[] { class CheckoutProcessor { private refs: Ref[] = []; - get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(r)); } - constructor(private type: RefType, private ctor: { new(ref: Ref): CheckoutItem }) { } + get items(): CheckoutItem[] { return this.refs.map(r => new this.ctor(this.repository, r)); } + constructor(private repository: Repository, private type: RefType, private ctor: { new(repository: Repository, ref: Ref): CheckoutItem }) { } onRef(ref: Ref): void { if (ref.type === this.type) { @@ -244,14 +245,14 @@ class CheckoutProcessor { } } -function getCheckoutProcessor(type: string): CheckoutProcessor | undefined { +function getCheckoutProcessor(repository: Repository, type: string): CheckoutProcessor | undefined { switch (type) { case 'local': - return new CheckoutProcessor(RefType.Head, CheckoutItem); + return new CheckoutProcessor(repository, RefType.Head, CheckoutItem); case 'remote': - return new CheckoutProcessor(RefType.RemoteHead, CheckoutRemoteHeadItem); + return new CheckoutProcessor(repository, RefType.RemoteHead, CheckoutRemoteHeadItem); case 'tags': - return new CheckoutProcessor(RefType.Tag, CheckoutTagItem); + return new CheckoutProcessor(repository, RefType.Tag, CheckoutTagItem); } return undefined; @@ -1584,11 +1585,8 @@ export class CommandCenter { } // Branch protection - const branchProtection = config.get('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== ''); const branchProtectionPrompt = config.get<'alwaysCommit' | 'alwaysCommitToNewBranch' | 'alwaysPrompt'>('branchProtectionPrompt')!; - const branchIsProtected = branchProtection.some(bp => picomatch.isMatch(repository.HEAD?.name ?? '', bp)); - - if (branchIsProtected && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { + if (repository.isBranchProtected() && (branchProtectionPrompt === 'alwaysPrompt' || branchProtectionPrompt === 'alwaysCommitToNewBranch')) { const commitToNewBranch = localize('commit to branch', "Commit to a New Branch"); let pick: string | undefined = commitToNewBranch; @@ -1859,7 +1857,7 @@ export class CommandCenter { const item = choice as CheckoutItem; try { - await item.run(repository, opts); + await item.run(opts); } catch (err) { if (err.gitErrorCode !== GitErrorCodes.DirtyWorkTree) { throw err; @@ -1871,10 +1869,10 @@ export class CommandCenter { if (choice === force) { await this.cleanAll(repository); - await item.run(repository, opts); + await item.run(opts); } else if (choice === stash) { await this.stash(repository); - await item.run(repository, opts); + await item.run(opts); await this.stashPopLatest(repository); } } diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index 0a2bb752afe..fb0688e4401 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -5,20 +5,29 @@ import * as path from 'path'; import { TabInputText, Uri, window, workspace } from 'vscode'; import { IIPCHandler, IIPCServer } from './ipc/ipcServer'; +import { ITerminalEnvironmentProvider } from './terminal'; import { EmptyDisposable, IDisposable } from './util'; interface GitEditorRequest { commitMessagePath?: string; } -export class GitEditor implements IIPCHandler { +export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { + private env: { [key: string]: string }; private disposable: IDisposable = EmptyDisposable; - constructor(private ipc?: IIPCServer) { + constructor(ipc?: IIPCServer) { if (ipc) { this.disposable = ipc.registerHandler('git-editor', this); } + + this.env = { + GIT_EDITOR: `"${path.join(__dirname, ipc ? 'git-editor.sh' : 'git-editor-empty.sh')}"`, + VSCODE_GIT_EDITOR_NODE: process.execPath, + VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', + VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') + }; } async handle({ commitMessagePath }: GitEditorRequest): Promise { @@ -39,24 +48,13 @@ export class GitEditor implements IIPCHandler { } getEnv(): { [key: string]: string } { - if (!this.ipc) { - return { - GIT_EDITOR: `"${path.join(__dirname, 'git-editor-empty.sh')}"` - }; - } - - const env: { [key: string]: string } = { - VSCODE_GIT_EDITOR_NODE: process.execPath, - VSCODE_GIT_EDITOR_EXTRA_ARGS: (process.versions['electron'] && process.versions['microsoft-build']) ? '--ms-enable-electron-run-as-node' : '', - VSCODE_GIT_EDITOR_MAIN: path.join(__dirname, 'git-editor-main.js') - }; - const config = workspace.getConfiguration('git'); - if (config.get('useEditorAsCommitInput')) { - env.GIT_EDITOR = `"${path.join(__dirname, 'git-editor.sh')}"`; - } + return config.get('useEditorAsCommitInput') ? this.env : {}; + } - return env; + getTerminalEnv(): { [key: string]: string } { + const config = workspace.getConfiguration('git'); + return config.get('useEditorAsCommitInput') && config.get('terminalGitEditor') ? this.env : {}; } dispose(): void { diff --git a/extensions/git/src/ipc/ipcServer.ts b/extensions/git/src/ipc/ipcServer.ts index ad4583cbfe1..c9de357b1a6 100644 --- a/extensions/git/src/ipc/ipcServer.ts +++ b/extensions/git/src/ipc/ipcServer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vscode'; +import { ITerminalEnvironmentProvider } from '../terminal'; import { toDisposable } from '../util'; import * as path from 'path'; import * as http from 'http'; @@ -27,7 +28,7 @@ export interface IIPCHandler { handle(request: any): Promise; } -export async function createIPCServer(context?: string): Promise { +export async function createIPCServer(context?: string): Promise { const server = http.createServer(); const hash = crypto.createHash('sha1'); @@ -65,7 +66,7 @@ export interface IIPCServer extends Disposable { registerHandler(name: string, handler: IIPCHandler): Disposable; } -class IPCServer implements IIPCServer, Disposable { +export class IPCServer implements IIPCServer, ITerminalEnvironmentProvider, Disposable { private handlers = new Map(); get ipcHandlePath(): string { return this._ipcHandlePath; } @@ -110,6 +111,10 @@ class IPCServer implements IIPCServer, Disposable { return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; } + getTerminalEnv(): { [key: string]: string } { + return { VSCODE_GIT_IPC_HANDLE: this.ipcHandlePath }; + } + dispose(): void { this.handlers.clear(); this.server.close(); diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index 46f612539fb..6fbc88cc9fd 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -25,7 +25,7 @@ import { GitTimelineProvider } from './timelineProvider'; import { registerAPICommands } from './api/api1'; import { TerminalEnvironmentManager } from './terminal'; import { OutputChannelLogger } from './log'; -import { createIPCServer, IIPCServer } from './ipc/ipcServer'; +import { createIPCServer, IPCServer } from './ipc/ipcServer'; import { GitEditor } from './gitEditor'; const deactivateTasks: { (): Promise }[] = []; @@ -62,22 +62,22 @@ async function createModel(context: ExtensionContext, outputChannelLogger: Outpu return !skip; }); - let ipc: IIPCServer | undefined = undefined; + let ipcServer: IPCServer | undefined = undefined; try { - ipc = await createIPCServer(context.storagePath); + ipcServer = await createIPCServer(context.storagePath); } catch (err) { outputChannelLogger.logError(`Failed to create git IPC: ${err}`); } - const askpass = new Askpass(ipc); + const askpass = new Askpass(ipcServer); disposables.push(askpass); - const gitEditor = new GitEditor(ipc); + const gitEditor = new GitEditor(ipcServer); disposables.push(gitEditor); - const environment = { ...askpass.getEnv(), ...gitEditor.getEnv() }; - const terminalEnvironmentManager = new TerminalEnvironmentManager(context, environment); + const environment = { ...askpass.getEnv(), ...gitEditor.getEnv(), ...ipcServer?.getEnv() }; + const terminalEnvironmentManager = new TerminalEnvironmentManager(context, [askpass, gitEditor, ipcServer]); disposables.push(terminalEnvironmentManager); outputChannelLogger.logInfo(localize('using git', "Using git {0} from {1}", info.version, info.path)); diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index 7a765bee11f..0190ce2c7be 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -5,6 +5,7 @@ import * as fs from 'fs'; import * as path from 'path'; +import * as picomatch from 'picomatch'; import { CancellationToken, Command, Disposable, Event, EventEmitter, Memento, ProgressLocation, ProgressOptions, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, ThemeColor, Uri, window, workspace, WorkspaceEdit, FileDecoration, commands, Tab, TabInputTextDiff, TabInputNotebookDiff, RelativePattern } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import * as nls from 'vscode-nls'; @@ -867,6 +868,7 @@ export class Repository implements Disposable { private isRepositoryHuge: false | { limit: number } = false; private didWarnAboutLimit = false; + private isBranchProtectedMatcher: picomatch.Matcher | undefined; private resourceCommandResolver = new ResourceCommandResolver(this); private disposables: Disposable[] = []; @@ -985,6 +987,10 @@ export class Repository implements Disposable { } }, null, this.disposables); + const onDidChangeBranchProtection = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git.branchProtection', root)); + onDidChangeBranchProtection(this.updateBranchProtectionMatcher, this, this.disposables); + this.updateBranchProtectionMatcher(); + const statusBar = new StatusBarCommands(this, remoteSourcePublisherRegistry); this.disposables.push(statusBar); statusBar.onDidChange(() => this._sourceControl.statusBarCommands = statusBar.commands, null, this.disposables); @@ -2211,6 +2217,21 @@ export class Repository implements Disposable { } } + private updateBranchProtectionMatcher(): void { + const scopedConfig = workspace.getConfiguration('git', Uri.file(this.repository.root)); + const branchProtectionGlobs = scopedConfig.get('branchProtection')!.map(bp => bp.trim()).filter(bp => bp !== ''); + + if (branchProtectionGlobs.length === 0) { + this.isBranchProtectedMatcher = undefined; + } else { + this.isBranchProtectedMatcher = picomatch(branchProtectionGlobs); + } + } + + public isBranchProtected(name: string = this.HEAD?.name ?? ''): boolean { + return this.isBranchProtectedMatcher ? this.isBranchProtectedMatcher(name) : false; + } + dispose(): void { this.disposables = dispose(this.disposables); } diff --git a/extensions/git/src/statusbar.ts b/extensions/git/src/statusbar.ts index 7cf06e0ed4f..f5b2e3e87c8 100644 --- a/extensions/git/src/statusbar.ts +++ b/extensions/git/src/statusbar.ts @@ -24,7 +24,8 @@ class CheckoutStatusBar { get command(): Command | undefined { const rebasing = !!this.repository.rebaseCommit; - const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}`; + const isBranchProtected = this.repository.isBranchProtected(); + const title = `$(git-branch) ${this.repository.headLabel}${rebasing ? ` (${localize('rebasing', 'Rebasing')})` : ''}${isBranchProtected ? ' $(lock-small)' : ''}`; return { command: 'git.checkout', diff --git a/extensions/git/src/terminal.ts b/extensions/git/src/terminal.ts index 2eda93151d2..9501cc88bba 100644 --- a/extensions/git/src/terminal.ts +++ b/extensions/git/src/terminal.ts @@ -6,27 +6,15 @@ import { ExtensionContext, workspace } from 'vscode'; import { filterEvent, IDisposable } from './util'; +export interface ITerminalEnvironmentProvider { + getTerminalEnv(): { [key: string]: string }; +} + export class TerminalEnvironmentManager { private readonly disposable: IDisposable; - private _enabled = false; - private set enabled(enabled: boolean) { - if (this._enabled === enabled) { - return; - } - - this._enabled = enabled; - this.context.environmentVariableCollection.clear(); - - if (enabled) { - for (const name of Object.keys(this.env)) { - this.context.environmentVariableCollection.replace(name, this.env[name]); - } - } - } - - constructor(private readonly context: ExtensionContext, private readonly env: { [key: string]: string }) { + constructor(private readonly context: ExtensionContext, private readonly envProviders: (ITerminalEnvironmentProvider | undefined)[]) { this.disposable = filterEvent(workspace.onDidChangeConfiguration, e => e.affectsConfiguration('git')) (this.refresh, this); @@ -35,7 +23,19 @@ export class TerminalEnvironmentManager { private refresh(): void { const config = workspace.getConfiguration('git', null); - this.enabled = config.get('enabled', true) && config.get('terminalAuthentication', true); + this.context.environmentVariableCollection.clear(); + + if (!config.get('enabled', true)) { + return; + } + + for (const envProvider of this.envProviders) { + const terminalEnv = envProvider?.getTerminalEnv() ?? {}; + + for (const name of Object.keys(terminalEnv)) { + this.context.environmentVariableCollection.replace(name, terminalEnv[name]); + } + } } dispose(): void { diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 53ab6c9e310..e36cebf97cf 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -51,9 +51,7 @@ export function anyEvent(...events: Event[]): Event { return (listener: (e: T) => any, thisArgs?: any, disposables?: Disposable[]) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); - if (disposables) { - disposables.push(result); - } + disposables?.push(result); return result; }; diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 1f1c02d3356..13997275056 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -12,7 +12,6 @@ "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", "../../src/vscode-dts/vscode.proposed.scmActionButton.d.ts", - "../../src/vscode-dts/vscode.proposed.scmInput.d.ts", "../../src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts", "../../src/vscode-dts/vscode.proposed.scmValidation.d.ts", "../../src/vscode-dts/vscode.proposed.tabs.d.ts", diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 16a38088f5b..51d53e8984a 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -119,12 +119,6 @@ "default": false, "markdownDescription": "%html.format.indentHandlebars.desc%" }, - "html.format.endWithNewline": { - "type": "boolean", - "scope": "resource", - "default": false, - "description": "%html.format.endWithNewline.desc%" - }, "html.format.extraLiners": { "type": [ "string", diff --git a/extensions/html-language-features/package.nls.json b/extensions/html-language-features/package.nls.json index f5795e33e2e..acb6474d63e 100644 --- a/extensions/html-language-features/package.nls.json +++ b/extensions/html-language-features/package.nls.json @@ -10,7 +10,6 @@ "html.format.preserveNewLines.desc": "Controls whether existing line breaks before elements should be preserved. Only works before elements, not inside tags or for text.", "html.format.maxPreserveNewLines.desc": "Maximum number of line breaks to be preserved in one chunk. Use `null` for unlimited.", "html.format.indentHandlebars.desc": "Format and indent `{{#foo}}` and `{{/foo}}`.", - "html.format.endWithNewline.desc": "End with a newline.", "html.format.extraLiners.desc": "List of tags, comma separated, that should have an extra newline before them. `null` defaults to `\"head, body, /html\"`.", "html.format.wrapAttributes.desc": "Wrap attributes.", "html.format.wrapAttributes.auto": "Wrap attributes only when line length is exceeded.", diff --git a/extensions/html-language-features/server/src/modes/htmlMode.ts b/extensions/html-language-features/server/src/modes/htmlMode.ts index 58a3ded2bee..baca7e0369f 100644 --- a/extensions/html-language-features/server/src/modes/htmlMode.ts +++ b/extensions/html-language-features/server/src/modes/htmlMode.ts @@ -49,6 +49,9 @@ export function getHTMLMode(htmlLanguageService: HTMLLanguageService, workspace: } else { formatSettings.contentUnformatted = 'script'; } + if (formatParams.insertFinalNewline) { + formatSettings.endWithNewline = true; + } merge(formatParams, formatSettings); return htmlLanguageService.format(document, range, formatSettings); }, diff --git a/extensions/html-language-features/server/src/test/formatting.test.ts b/extensions/html-language-features/server/src/test/formatting.test.ts index 775a7b260fd..ebc319d20b6 100644 --- a/extensions/html-language-features/server/src/test/formatting.test.ts +++ b/extensions/html-language-features/server/src/test/formatting.test.ts @@ -85,16 +85,12 @@ suite('HTML Embedded Formatting', () => { }); test('EndWithNewline', async () => { - const options = { - html: { - format: { - endWithNewline: true - } - } - }; - await assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', options); - await assertFormat('|

Hello

|', '\n

Hello

\n', options); - await assertFormat('', '\n\n\n \n\n\n\n', options); + const options : FormattingOptions = FormattingOptions.create(2, true); + options.insertFinalNewline = true; + + await assertFormat('

Hello

', '\n\n\n

Hello

\n\n\n\n', {}, options); + await assertFormat('|

Hello

|', '\n

Hello

\n', {}, options); + await assertFormat('', '\n\n\n \n\n\n\n', {}, options); }); test('Inside script', async () => { diff --git a/extensions/json/language-configuration.json b/extensions/json/language-configuration.json index 2dc97d50dbb..f9ec3fec781 100644 --- a/extensions/json/language-configuration.json +++ b/extensions/json/language-configuration.json @@ -16,7 +16,7 @@ { "open": "`", "close": "`", "notIn": ["string", "comment"] } ], "indentationRules": { - "increaseIndentPattern": "({+(?=([^\"]*\"[^\"]*\")*[^\"}]*$))|(\\[+(?=([^\"]*\"[^\"]*\")*[^\"\\]]*$))", + "increaseIndentPattern": "({+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"}]*)$)|(\\[+(?=((\\\\.|[^\"\\\\])*\"(\\\\.|[^\"\\\\])*\")*[^\"\\]]*)$)", "decreaseIndentPattern": "^\\s*[}\\]],?\\s*$" } } diff --git a/extensions/markdown-language-features/preview-src/loading.ts b/extensions/markdown-language-features/preview-src/loading.ts index fea2d653d4d..c6439188766 100644 --- a/extensions/markdown-language-features/preview-src/loading.ts +++ b/extensions/markdown-language-features/preview-src/loading.ts @@ -29,9 +29,7 @@ export class StyleLoadingMonitor { return; } this.finishedLoading = true; - if (this.poster) { - this.poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); - } + this.poster?.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); }); } @@ -41,4 +39,4 @@ export class StyleLoadingMonitor { poster.postMessage('previewStyleLoadError', { unloadedStyles: this.unloadedStyles }); } } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/extension.ts b/extensions/markdown-language-features/src/extension.ts index 59d337710a5..a97caf760a5 100644 --- a/extensions/markdown-language-features/src/extension.ts +++ b/extensions/markdown-language-features/src/extension.ts @@ -6,19 +6,19 @@ import * as vscode from 'vscode'; import { CommandManager } from './commandManager'; import * as commands from './commands/index'; -import { registerPasteProvider } from './languageFeatures/copyPaste'; -import { MdDefinitionProvider } from './languageFeatures/definitionProvider'; -import { register as registerDiagnostics } from './languageFeatures/diagnostics'; -import { MdLinkComputer, registerDocumentLinkProvider } from './languageFeatures/documentLinkProvider'; -import { MdDocumentSymbolProvider } from './languageFeatures/documentSymbolProvider'; -import { registerDropIntoEditor } from './languageFeatures/dropIntoEditor'; -import { registerFindFileReferences } from './languageFeatures/fileReferences'; -import { MdFoldingProvider } from './languageFeatures/foldingProvider'; -import { MdPathCompletionProvider } from './languageFeatures/pathCompletions'; -import { MdReferencesProvider } from './languageFeatures/references'; -import { MdRenameProvider } from './languageFeatures/rename'; -import { MdSmartSelect } from './languageFeatures/smartSelect'; -import { MdWorkspaceSymbolProvider } from './languageFeatures/workspaceSymbolProvider'; +import { registerPasteSupport } from './languageFeatures/copyPaste'; +import { registerDefinitionSupport } from './languageFeatures/definitionProvider'; +import { registerDiagnosticSupport } from './languageFeatures/diagnostics'; +import { MdLinkProvider, registerDocumentLinkSupport } from './languageFeatures/documentLinkProvider'; +import { MdDocumentSymbolProvider, registerDocumentSymbolSupport } from './languageFeatures/documentSymbolProvider'; +import { registerDropIntoEditorSupport } from './languageFeatures/dropIntoEditor'; +import { registerFindFileReferenceSupport } from './languageFeatures/fileReferences'; +import { registerFoldingSupport } from './languageFeatures/foldingProvider'; +import { registerPathCompletionSupport } from './languageFeatures/pathCompletions'; +import { MdReferencesProvider, registerReferencesSupport } from './languageFeatures/references'; +import { registerRenameSupport } from './languageFeatures/rename'; +import { registerSmartSelectSupport } from './languageFeatures/smartSelect'; +import { registerWorkspaceSymbolSupport } from './languageFeatures/workspaceSymbolProvider'; import { Logger } from './logger'; import { MarkdownEngine } from './markdownEngine'; import { getMarkdownExtensionContributions } from './markdownExtensions'; @@ -26,6 +26,7 @@ import { MarkdownContentProvider } from './preview/previewContentProvider'; import { MarkdownPreviewManager } from './preview/previewManager'; import { ContentSecurityPolicyArbiter, ExtensionContentSecurityPolicyArbiter, PreviewSecuritySelector } from './preview/security'; import { githubSlugifier } from './slugify'; +import { MdTableOfContentsProvider } from './tableOfContents'; import { loadDefaultTelemetryReporter, TelemetryReporter } from './telemetryReporter'; import { VsCodeMdWorkspaceContents } from './workspaceContents'; @@ -43,11 +44,10 @@ export function activate(context: vscode.ExtensionContext) { const commandManager = new CommandManager(); const contentProvider = new MarkdownContentProvider(engine, context, cspArbiter, contributions, logger); - const symbolProvider = new MdDocumentSymbolProvider(engine); const previewManager = new MarkdownPreviewManager(contentProvider, logger, contributions, engine); context.subscriptions.push(previewManager); - context.subscriptions.push(registerMarkdownLanguageFeatures(commandManager, symbolProvider, engine)); + context.subscriptions.push(registerMarkdownLanguageFeatures(commandManager, engine)); context.subscriptions.push(registerMarkdownCommands(commandManager, previewManager, telemetryReporter, cspArbiter, engine)); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration(() => { @@ -58,30 +58,37 @@ export function activate(context: vscode.ExtensionContext) { function registerMarkdownLanguageFeatures( commandManager: CommandManager, - symbolProvider: MdDocumentSymbolProvider, engine: MarkdownEngine ): vscode.Disposable { const selector: vscode.DocumentSelector = { language: 'markdown', scheme: '*' }; - const linkComputer = new MdLinkComputer(engine); const workspaceContents = new VsCodeMdWorkspaceContents(); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const linkProvider = new MdLinkProvider(engine, workspaceContents); + const tocProvider = new MdTableOfContentsProvider(engine, workspaceContents); + const referencesProvider = new MdReferencesProvider(engine, workspaceContents, tocProvider); + const symbolProvider = new MdDocumentSymbolProvider(tocProvider); + return vscode.Disposable.from( workspaceContents, - vscode.languages.registerDocumentSymbolProvider(selector, symbolProvider), - vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine)), - vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine)), - vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)), - vscode.languages.registerReferenceProvider(selector, referencesProvider), - vscode.languages.registerRenameProvider(selector, new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier)), - vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)), - MdPathCompletionProvider.register(selector, engine, linkComputer), - registerDocumentLinkProvider(selector, linkComputer), - registerDiagnostics(selector, engine, workspaceContents, linkComputer, commandManager), - registerDropIntoEditor(selector), - registerPasteProvider(selector), - registerFindFileReferences(commandManager, referencesProvider), + linkProvider, + referencesProvider, + tocProvider, + + // Language features + registerDefinitionSupport(selector, referencesProvider), + registerDiagnosticSupport(selector, engine, workspaceContents, linkProvider, commandManager, referencesProvider, tocProvider), + registerDocumentLinkSupport(selector, linkProvider), + registerDocumentSymbolSupport(selector, tocProvider), + registerDropIntoEditorSupport(selector), + registerFindFileReferenceSupport(commandManager, referencesProvider), + registerFoldingSupport(selector, engine, tocProvider), + registerPasteSupport(selector), + registerPathCompletionSupport(selector, engine, linkProvider), + registerReferencesSupport(selector, referencesProvider), + registerRenameSupport(selector, workspaceContents, referencesProvider, engine.slugifier), + registerSmartSelectSupport(selector, engine, tocProvider), + registerWorkspaceSymbolSupport(workspaceContents, symbolProvider), ); } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts index 242bc4a0f52..fe6c119b012 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyPaste.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { tryGetUriListSnippet } from './dropIntoEditor'; -export function registerPasteProvider(selector: vscode.DocumentSelector) { +export function registerPasteSupport(selector: vscode.DocumentSelector) { return vscode.languages.registerDocumentPasteEditProvider(selector, new class implements vscode.DocumentPasteEditProvider { async provideDocumentPasteEdits( diff --git a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts index 622d6bf87a6..06d9db8a3bc 100644 --- a/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/definitionProvider.ts @@ -3,19 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { Disposable } from '../util/dispose'; import { SkinnyTextDocument } from '../workspaceContents'; import { MdReferencesProvider } from './references'; -export class MdDefinitionProvider extends Disposable implements vscode.DefinitionProvider { +export class MdDefinitionProvider implements vscode.DefinitionProvider { - constructor(private readonly referencesProvider: MdReferencesProvider) { - super(); - } + constructor( + private readonly referencesProvider: MdReferencesProvider + ) { } async provideDefinition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const allRefs = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); + const allRefs = await this.referencesProvider.getReferencesAtPosition(document, position, token); return allRefs.find(ref => ref.kind === 'link' && ref.isDefinition)?.location; } } + +export function registerDefinitionSupport( + selector: vscode.DocumentSelector, + referencesProvider: MdReferencesProvider, +): vscode.Disposable { + return vscode.languages.registerDefinitionProvider(selector, new MdDefinitionProvider(referencesProvider)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts index c7237493eca..4a0a30a79a6 100644 --- a/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts +++ b/extensions/markdown-language-features/src/languageFeatures/diagnostics.ts @@ -8,15 +8,17 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { CommandManager } from '../commandManager'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; +import { MdTableOfContentsProvider } from '../tableOfContents'; +import { MdTableOfContentsWatcher } from '../test/tableOfContentsWatcher'; import { Delayer } from '../util/async'; +import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; -import { isMarkdownFile } from '../util/file'; +import { isMarkdownFile, looksLikeMarkdownPath } from '../util/file'; import { Limiter } from '../util/limiter'; import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -import { InternalHref, LinkDefinitionSet, MdLink, MdLinkComputer, MdLinkSource } from './documentLinkProvider'; -import { tryFindMdDocumentForLink } from './references'; +import { InternalHref, LinkDefinitionSet, MdLink, MdLinkProvider, MdLinkSource } from './documentLinkProvider'; +import { MdReferencesProvider, tryResolveLinkPath } from './references'; const localize = nls.loadMessageBundle(); @@ -93,19 +95,21 @@ class InflightDiagnosticRequests { private readonly inFlightRequests = new ResourceMap<{ readonly cts: vscode.CancellationTokenSource }>(); - public trigger(resource: vscode.Uri, compute: (token: vscode.CancellationToken) => Promise) { + public async trigger(resource: vscode.Uri, compute: (token: vscode.CancellationToken) => Promise): Promise { this.cancel(resource); const cts = new vscode.CancellationTokenSource(); const entry = { cts }; this.inFlightRequests.set(resource, entry); - compute(cts.token).finally(() => { + try { + return await compute(cts.token); + } finally { if (this.inFlightRequests.get(resource) === entry) { this.inFlightRequests.delete(resource); } cts.dispose(); - }); + } } public cancel(resource: vscode.Uri) { @@ -136,7 +140,7 @@ class LinkWatcher extends Disposable { */ public readonly onDidChangeLinkedToFile = this._onDidChangeLinkedToFile.event; - private readonly _watchers = new Map; + readonly documents: ResourceMap; }>(); override dispose() { @@ -168,21 +172,21 @@ class LinkWatcher extends Disposable { // First decrement watcher counter for previous document state for (const entry of this._watchers.values()) { - entry.documents.delete(document.toString()); + entry.documents.delete(document); } // Then create/update watchers for new document state for (const path of linkedToResource) { - let entry = this._watchers.get(path.toString()); + let entry = this._watchers.get(path); if (!entry) { entry = { watcher: this.startWatching(path), - documents: new Map(), + documents: new ResourceMap(), }; - this._watchers.set(path.toString(), entry); + this._watchers.set(path, entry); } - entry.documents.set(document.toString(), document); + entry.documents.set(document, document); } // Finally clean up watchers for links that are no longer are referenced anywhere @@ -209,7 +213,7 @@ class LinkWatcher extends Disposable { } private onLinkedResourceChanged(resource: vscode.Uri) { - const entry = this._watchers.get(resource.toString()); + const entry = this._watchers.get(resource); if (entry) { this._onDidChangeLinkedToFile.fire(entry.documents.values()); } @@ -226,96 +230,49 @@ class LinkDoesNotExistDiagnostic extends vscode.Diagnostic { } } -export class DiagnosticManager extends Disposable { +export abstract class DiagnosticReporter extends Disposable { + private readonly pending = new Set>(); + + public clear(): void { + this.pending.clear(); + } + + public abstract set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void; + + public abstract delete(uri: vscode.Uri): void; + + public addWorkItem(promise: Promise): Promise { + this.pending.add(promise); + promise.finally(() => this.pending.delete(promise)); + return promise; + } + + public async waitPendingWork(): Promise { + await Promise.all([...this.pending.values()]); + } +} + +export class DiagnosticCollectionReporter extends DiagnosticReporter { private readonly collection: vscode.DiagnosticCollection; - private readonly diagnosticDelayer: Delayer; - private readonly pendingDiagnostics = new Set(); - private readonly inFlightDiagnostics = this._register(new InflightDiagnosticRequests()); - - private readonly linkWatcher = this._register(new LinkWatcher()); - - constructor( - private readonly computer: DiagnosticComputer, - private readonly configuration: DiagnosticConfiguration, - ) { + constructor() { super(); - - this.diagnosticDelayer = this._register(new Delayer(300)); - this.collection = this._register(vscode.languages.createDiagnosticCollection('markdown')); - - this._register(this.configuration.onDidChange(() => { - this.rebuild(); - })); - - this._register(vscode.workspace.onDidOpenTextDocument(doc => { - this.triggerDiagnostics(doc); - })); - - this._register(vscode.workspace.onDidChangeTextDocument(e => { - this.triggerDiagnostics(e.document); - })); - - this._register(vscode.workspace.onDidCloseTextDocument(({ uri }) => { - this.pendingDiagnostics.delete(uri); - this.inFlightDiagnostics.cancel(uri); - this.linkWatcher.deleteDocument(uri); - this.collection.delete(uri); - })); - - this._register(this.linkWatcher.onDidChangeLinkedToFile(changedDocuments => { - for (const resource of changedDocuments) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); - if (doc) { - this.triggerDiagnostics(doc); - } - } - })); - - this.rebuild(); } - public override dispose() { - super.dispose(); - this.pendingDiagnostics.clear(); - } - - public async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> { - const config = this.configuration.getOptions(doc.uri); - if (!config.enabled) { - return { diagnostics: [], links: [], config }; - } - return { ...await this.computer.getDiagnostics(doc, config, token), config }; - } - - private async recomputePendingDiagnostics(): Promise { - const pending = [...this.pendingDiagnostics]; - this.pendingDiagnostics.clear(); - - for (const resource of pending) { - const doc = vscode.workspace.textDocuments.find(doc => doc.uri.fsPath === resource.fsPath); - if (doc) { - this.inFlightDiagnostics.trigger(doc.uri, async (token) => { - const state = await this.recomputeDiagnosticState(doc, token); - this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFileLinks ? state.links : []); - this.collection.set(doc.uri, state.diagnostics); - }); - } - } - } - - private async rebuild() { + public override clear(): void { + super.clear(); this.collection.clear(); - this.pendingDiagnostics.clear(); - this.inFlightDiagnostics.clear(); + } - const allOpenedTabResources = this.getAllTabResources(); - await Promise.all( - vscode.workspace.textDocuments - .filter(doc => allOpenedTabResources.has(doc.uri) && isMarkdownFile(doc)) - .map(doc => this.triggerDiagnostics(doc))); + public set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void { + const tabs = this.getAllTabResources(); + this.collection.set(uri, tabs.has(uri) ? diagnostics : []); + } + + public delete(uri: vscode.Uri): void { + this.collection.delete(uri); } private getAllTabResources(): ResourceMap { @@ -329,14 +286,125 @@ export class DiagnosticManager extends Disposable { } return openedTabDocs; } +} - private triggerDiagnostics(doc: vscode.TextDocument) { - this.inFlightDiagnostics.cancel(doc.uri); +export class DiagnosticManager extends Disposable { - if (isMarkdownFile(doc)) { - this.pendingDiagnostics.add(doc.uri); - this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics()); + private readonly diagnosticDelayer: Delayer; + private readonly pendingDiagnostics = new Set(); + private readonly inFlightDiagnostics = this._register(new InflightDiagnosticRequests()); + + private readonly linkWatcher = this._register(new LinkWatcher()); + private readonly tableOfContentsWatcher: MdTableOfContentsWatcher; + + public readonly ready: Promise; + + constructor( + engine: MarkdownEngine, + private readonly workspaceContents: MdWorkspaceContents, + private readonly computer: DiagnosticComputer, + private readonly configuration: DiagnosticConfiguration, + private readonly reporter: DiagnosticReporter, + private readonly referencesProvider: MdReferencesProvider, + delay = 300, + ) { + super(); + + this.diagnosticDelayer = this._register(new Delayer(delay)); + + this._register(this.configuration.onDidChange(() => { + this.rebuild(); + })); + + this._register(workspaceContents.onDidCreateMarkdownDocument(doc => { + this.triggerDiagnostics(doc.uri); + })); + + this._register(workspaceContents.onDidChangeMarkdownDocument(doc => { + this.triggerDiagnostics(doc.uri); + })); + + this._register(vscode.workspace.onDidCloseTextDocument(({ uri }) => { + this.pendingDiagnostics.delete(uri); + this.inFlightDiagnostics.cancel(uri); + this.linkWatcher.deleteDocument(uri); + this.reporter.delete(uri); + })); + + this._register(this.linkWatcher.onDidChangeLinkedToFile(changedDocuments => { + for (const resource of changedDocuments) { + const doc = vscode.workspace.textDocuments.find(doc => doc.uri.toString() === resource.toString()); + if (doc && isMarkdownFile(doc)) { + this.triggerDiagnostics(doc.uri); + } + } + })); + + this.tableOfContentsWatcher = this._register(new MdTableOfContentsWatcher(engine, workspaceContents)); + this._register(this.tableOfContentsWatcher.onTocChanged(async e => { + // When the toc of a document changes, revalidate every file that linked to it too + const triggered = new ResourceMap(); + for (const ref of await this.referencesProvider.getAllReferencesToFile(e.uri, noopToken)) { + const file = ref.location.uri; + if (!triggered.has(file)) { + this.triggerDiagnostics(file); + triggered.set(file); + } + } + })); + + this.ready = this.rebuild(); + } + + public override dispose() { + super.dispose(); + this.pendingDiagnostics.clear(); + } + + private async recomputeDiagnosticState(doc: SkinnyTextDocument, token: vscode.CancellationToken): Promise<{ diagnostics: readonly vscode.Diagnostic[]; links: readonly MdLink[]; config: DiagnosticOptions }> { + const config = this.configuration.getOptions(doc.uri); + if (!config.enabled) { + return { diagnostics: [], links: [], config }; } + return { ...await this.computer.getDiagnostics(doc, config, token), config }; + } + + private async recomputePendingDiagnostics(): Promise { + const pending = [...this.pendingDiagnostics]; + this.pendingDiagnostics.clear(); + + await Promise.all(pending.map(async resource => { + const doc = await this.workspaceContents.getOrLoadMarkdownDocument(resource); + if (doc) { + await this.inFlightDiagnostics.trigger(doc.uri, async (token) => { + const state = await this.recomputeDiagnosticState(doc, token); + this.linkWatcher.updateLinksForDocument(doc.uri, state.config.enabled && state.config.validateFileLinks ? state.links : []); + this.reporter.set(doc.uri, state.diagnostics); + }); + } + })); + } + + private rebuild(): Promise { + this.reporter.clear(); + this.pendingDiagnostics.clear(); + this.inFlightDiagnostics.clear(); + + return this.reporter.addWorkItem( + (async () => { + const allDocs = await this.workspaceContents.getAllMarkdownDocuments(); + await Promise.all(Array.from(allDocs, doc => this.triggerDiagnostics(doc.uri))); + })() + ); + } + + private async triggerDiagnostics(uri: vscode.Uri): Promise { + this.inFlightDiagnostics.cancel(uri); + + this.pendingDiagnostics.add(uri); + return this.reporter.addWorkItem( + this.diagnosticDelayer.trigger(() => this.recomputePendingDiagnostics()) + ); } } @@ -380,22 +448,22 @@ class FileLinkMap { export class DiagnosticComputer { constructor( - private readonly engine: MarkdownEngine, private readonly workspaceContents: MdWorkspaceContents, - private readonly linkComputer: MdLinkComputer, + private readonly linkProvider: MdLinkProvider, + private readonly tocProvider: MdTableOfContentsProvider, ) { } - public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: MdLink[] }> { - const links = await this.linkComputer.getAllLinks(doc, token); - if (token.isCancellationRequested) { + public async getDiagnostics(doc: SkinnyTextDocument, options: DiagnosticOptions, token: vscode.CancellationToken): Promise<{ readonly diagnostics: vscode.Diagnostic[]; readonly links: readonly MdLink[] }> { + const { links, definitions } = await this.linkProvider.getLinks(doc); + if (token.isCancellationRequested || !options.enabled) { return { links, diagnostics: [] }; } return { links, diagnostics: (await Promise.all([ - this.validateFileLinks(doc, options, links, token), - Array.from(this.validateReferenceLinks(options, links)), + this.validateFileLinks(options, links, token), + Array.from(this.validateReferenceLinks(options, links, definitions)), this.validateFragmentLinks(doc, options, links, token), ])).flat() }; @@ -407,7 +475,7 @@ export class DiagnosticComputer { return []; } - const toc = await TableOfContents.create(this.engine, doc); + const toc = await this.tocProvider.get(doc.uri); if (token.isCancellationRequested) { return []; } @@ -415,6 +483,7 @@ export class DiagnosticComputer { const diagnostics: vscode.Diagnostic[] = []; for (const link of links) { if (link.href.kind === 'internal' + && link.source.text.startsWith('#') && link.href.path.toString() === doc.uri.toString() && link.href.fragment && !toc.lookup(link.href.fragment) @@ -432,15 +501,14 @@ export class DiagnosticComputer { return diagnostics; } - private *validateReferenceLinks(options: DiagnosticOptions, links: readonly MdLink[]): Iterable { + private *validateReferenceLinks(options: DiagnosticOptions, links: readonly MdLink[], definitions: LinkDefinitionSet): Iterable { const severity = toSeverity(options.validateReferences); if (typeof severity === 'undefined') { return []; } - const definitionSet = new LinkDefinitionSet(links); for (const link of links) { - if (link.href.kind === 'reference' && !definitionSet.lookup(link.href.ref)) { + if (link.href.kind === 'reference' && !definitions.lookup(link.href.ref)) { yield new vscode.Diagnostic( link.source.hrefRange, localize('invalidReferenceLink', 'No link definition found: \'{0}\'', link.href.ref), @@ -449,14 +517,15 @@ export class DiagnosticComputer { } } - private async validateFileLinks(doc: SkinnyTextDocument, options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { + private async validateFileLinks(options: DiagnosticOptions, links: readonly MdLink[], token: vscode.CancellationToken): Promise { const pathErrorSeverity = toSeverity(options.validateFileLinks); if (typeof pathErrorSeverity === 'undefined') { return []; } const fragmentErrorSeverity = toSeverity(typeof options.validateMarkdownFileLinkFragments === 'undefined' ? options.validateFragmentLinks : options.validateMarkdownFileLinkFragments); - const linkSet = new FileLinkMap(links); + // We've already validated our own fragment links in `validateOwnHeaderLinks` + const linkSet = new FileLinkMap(links.filter(link => !link.source.text.startsWith('#'))); if (linkSet.size === 0) { return []; } @@ -471,24 +540,19 @@ export class DiagnosticComputer { return; } - const hrefDoc = await tryFindMdDocumentForLink({ kind: 'internal', path: path, fragment: '' }, this.workspaceContents); - if (hrefDoc && hrefDoc.uri.toString() === doc.uri.toString()) { - // We've already validated our own links in `validateOwnHeaderLinks` - return; - } - - if (!hrefDoc && !await this.workspaceContents.pathExists(path)) { + const resolvedHrefPath = await tryResolveLinkPath(path, this.workspaceContents); + if (!resolvedHrefPath) { const msg = localize('invalidPathLink', 'File does not exist at path: {0}', path.fsPath); for (const link of links) { if (!this.isIgnoredLink(options, link.source.pathText)) { diagnostics.push(new LinkDoesNotExistDiagnostic(link.source.hrefRange, msg, pathErrorSeverity, link.source.pathText)); } } - } else if (hrefDoc && typeof fragmentErrorSeverity !== 'undefined') { + } else if (typeof fragmentErrorSeverity !== 'undefined' && this.isMarkdownPath(resolvedHrefPath)) { // Validate each of the links to headers in the file const fragmentLinks = links.filter(x => x.fragment); if (fragmentLinks.length) { - const toc = await TableOfContents.create(this.engine, hrefDoc); + const toc = await this.tocProvider.get(resolvedHrefPath); for (const link of fragmentLinks) { if (!toc.lookup(link.fragment) && !this.isIgnoredLink(options, link.source.pathText) && !this.isIgnoredLink(options, link.source.text)) { const msg = localize('invalidLinkToHeaderInOtherFile', 'Header does not exist in file: {0}', link.fragment); @@ -503,6 +567,10 @@ export class DiagnosticComputer { return diagnostics; } + private isMarkdownPath(resolvedHrefPath: vscode.Uri) { + return this.workspaceContents.hasMarkdownDocument(resolvedHrefPath) || looksLikeMarkdownPath(resolvedHrefPath); + } + private isIgnoredLink(options: DiagnosticOptions, link: string): boolean { return options.ignoreLinks.some(glob => picomatch.isMatch(link, glob)); } @@ -555,15 +623,23 @@ class AddToIgnoreLinksQuickFixProvider implements vscode.CodeActionProvider { } } -export function register( +export function registerDiagnosticSupport( selector: vscode.DocumentSelector, engine: MarkdownEngine, workspaceContents: MdWorkspaceContents, - linkComputer: MdLinkComputer, + linkProvider: MdLinkProvider, commandManager: CommandManager, + referenceProvider: MdReferencesProvider, + tocProvider: MdTableOfContentsProvider, ): vscode.Disposable { const configuration = new VSCodeDiagnosticConfiguration(); - const manager = new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration); + const manager = new DiagnosticManager( + engine, + workspaceContents, + new DiagnosticComputer(workspaceContents, linkProvider, tocProvider), + configuration, + new DiagnosticCollectionReporter(), + referenceProvider); return vscode.Disposable.from( configuration, manager, diff --git a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts index 5331240e295..5d33055264e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentLinkProvider.ts @@ -9,8 +9,11 @@ import * as uri from 'vscode-uri'; import { OpenDocumentLinkCommand } from '../commands/openDocumentLink'; import { MarkdownEngine } from '../markdownEngine'; import { coalesce } from '../util/arrays'; +import { noopToken } from '../util/cancellation'; +import { Disposable } from '../util/dispose'; import { getUriForLinkWithKnownExternalScheme, isOfScheme, Schemes } from '../util/schemes'; -import { SkinnyTextDocument } from '../workspaceContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { MdDocumentInfoCache } from './workspaceCache'; const localize = nls.loadMessageBundle(); @@ -128,11 +131,14 @@ export type MdLink = MdInlineLink | MdLinkDefinition; function extractDocumentLink( document: SkinnyTextDocument, - pre: number, - link: string, + pre: string, + rawLink: string, matchIndex: number | undefined ): MdLink | undefined { - const offset = (matchIndex || 0) + pre; + const isAngleBracketLink = rawLink.startsWith('<'); + const link = stripAngleBrackets(rawLink); + + const offset = (matchIndex || 0) + pre.length + (isAngleBracketLink ? 1 : 0); const linkStart = document.positionAt(offset); const linkEnd = document.positionAt(offset + link.length); try { @@ -182,20 +188,36 @@ function stripAngleBrackets(link: string) { return link.replace(angleBracketLinkRe, '$1'); } -/** - * Matches `[text](link)` - */ -const linkPattern = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*)(([^\s\(\)]|\([^\s\(\)]*?\))+)\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const r = String.raw; /** - * Matches `[text]()` + * Matches `[text](link)` or `[text]()` */ -const linkPatternAngle = /(\[((!\[[^\]]*?\]\(\s*)([^\s\(\)]+?)\s*\)\]|(?:\\\]|[^\]]|\][^(])*\])\(\s*<)(([^<>]|\([^\s\(\)]*?\))+)>\s*("[^"]*"|'[^']*'|\([^\(\)]*\))?\s*\)/g; +const linkPattern = new RegExp( + // text + r`(\[` + // open prefix match --> + /**/r`(?:` + + /*****/r`[^\[\]\\]|` + // Non-bracket chars, or... + /*****/r`\\.|` + // Escaped char, or... + /*****/r`\[[^\[\]]*\]` + // Matched bracket pair + /**/r`)*` + + r`\]` + + // Destination + r`\(\s*)` + // <-- close prefix match + /**/r`(` + + /*****/r`[^\s\(\)\<](?:[^\s\(\)]|\([^\s\(\)]*?\))*|` + // Link without whitespace, or... + /*****/r`<[^<>]*>` + // In angle brackets + /**/r`)` + + + // Title + /**/r`\s*(?:"[^"]*"|'[^']*'|\([^\(\)]*\))?\s*` + + r`\)`, + 'g'); /** - * Matches `[text][ref]` or `[shorthand]` - */ +* Matches `[text][ref]` or `[shorthand]` +*/ const referenceLinkPattern = /(^|[^\]\\])(?:(?:(\[((?:\\\]|[^\]])+)\]\[\s*?)([^\s\]]*?)\]|\[\s*?([^\s\]]*?)\])(?![\:\(]))/gm; /** @@ -238,10 +260,13 @@ class NoLinkRanges { contains(range: vscode.Range): boolean { return this.multiline.some(interval => range.start.line >= interval[0] && range.start.line < interval[1]) || - this.inline.some(position => position.intersection(range)); + this.inline.some(inlineRange => inlineRange.contains(range.start)); } } +/** + * Stateless object that extracts link information from markdown files. + */ export class MdLinkComputer { constructor( @@ -257,43 +282,30 @@ export class MdLinkComputer { return Array.from([ ...this.getInlineLinks(document, noLinkRanges), ...this.getReferenceLinks(document, noLinkRanges), - ...this.getLinkDefinitions2(document, noLinkRanges), + ...this.getLinkDefinitions(document, noLinkRanges), ...this.getAutoLinks(document, noLinkRanges), ]); } private *getInlineLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); - - for (const match of text.matchAll(linkPatternAngle)) { - const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) { - yield matchImageData; - } - const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); - if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) { - yield matchLinkData; - } - } - for (const match of text.matchAll(linkPattern)) { - const matchImageData = match[4] && extractDocumentLink(document, match[3].length + 1, match[4], match.index); - if (matchImageData && !noLinkRanges.contains(matchImageData.source.hrefRange)) { - yield matchImageData; - } - - if (match[5] !== undefined && match[5].startsWith('<')) { - continue; - } - - const matchLinkData = extractDocumentLink(document, match[1].length, match[5], match.index); + const matchLinkData = extractDocumentLink(document, match[1], match[2], match.index); if (matchLinkData && !noLinkRanges.contains(matchLinkData.source.hrefRange)) { yield matchLinkData; + + // Also check link destination for links + for (const innerMatch of match[1].matchAll(linkPattern)) { + const innerData = extractDocumentLink(document, innerMatch[1], innerMatch[2], (match.index ?? 0) + (innerMatch.index ?? 0)); + if (innerData) { + yield innerData; + } + } } } } - private *getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { + private * getAutoLinks(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(autoLinkPattern)) { @@ -369,12 +381,7 @@ export class MdLinkComputer { } } - public async getLinkDefinitions(document: SkinnyTextDocument): Promise> { - const noLinkRanges = await NoLinkRanges.compute(document, this.engine); - return this.getLinkDefinitions2(document, noLinkRanges); - } - - private *getLinkDefinitions2(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { + private *getLinkDefinitions(document: SkinnyTextDocument, noLinkRanges: NoLinkRanges): Iterable { const text = document.getText(); for (const match of text.matchAll(definitionPattern)) { const pre = match[1]; @@ -419,7 +426,37 @@ export class MdLinkComputer { } } -export class LinkDefinitionSet { +/** + * Stateful object which provides links for markdown files the workspace. + */ +export class MdLinkProvider extends Disposable { + + private readonly _linkCache: MdDocumentInfoCache; + + private readonly linkComputer: MdLinkComputer; + + constructor( + engine: MarkdownEngine, + workspaceContents: MdWorkspaceContents, + ) { + super(); + this.linkComputer = new MdLinkComputer(engine); + this._linkCache = this._register(new MdDocumentInfoCache(workspaceContents, doc => this.linkComputer.getAllLinks(doc, noopToken))); + } + + public async getLinks(document: SkinnyTextDocument): Promise<{ + readonly links: readonly MdLink[]; + readonly definitions: LinkDefinitionSet; + }> { + const links = (await this._linkCache.get(document.uri)) ?? []; + return { + links, + definitions: new LinkDefinitionSet(links), + }; + } +} + +export class LinkDefinitionSet implements Iterable<[string, MdLinkDefinition]> { private readonly _map = new Map(); constructor(links: Iterable) { @@ -430,29 +467,31 @@ export class LinkDefinitionSet { } } + public [Symbol.iterator](): Iterator<[string, MdLinkDefinition]> { + return this._map.entries(); + } + public lookup(ref: string): MdLinkDefinition | undefined { return this._map.get(ref); } } -export class MdLinkProvider implements vscode.DocumentLinkProvider { +export class MdVsCodeLinkProvider implements vscode.DocumentLinkProvider { constructor( - private readonly _linkComputer: MdLinkComputer, + private readonly _linkProvider: MdLinkProvider, ) { } public async provideDocumentLinks( document: SkinnyTextDocument, token: vscode.CancellationToken ): Promise { - const allLinks = (await this._linkComputer.getAllLinks(document, token)) ?? []; + const { links, definitions } = await this._linkProvider.getLinks(document); if (token.isCancellationRequested) { return []; } - const definitionSet = new LinkDefinitionSet(allLinks); - return coalesce(allLinks - .map(data => this.toValidDocumentLink(data, definitionSet))); + return coalesce(links.map(data => this.toValidDocumentLink(data, definitions))); } private toValidDocumentLink(link: MdLink, definitionSet: LinkDefinitionSet): vscode.DocumentLink | undefined { @@ -482,9 +521,9 @@ export class MdLinkProvider implements vscode.DocumentLinkProvider { } } -export function registerDocumentLinkProvider( +export function registerDocumentLinkSupport( selector: vscode.DocumentSelector, - linkComputer: MdLinkComputer, + linkProvider: MdLinkProvider, ): vscode.Disposable { - return vscode.languages.registerDocumentLinkProvider(selector, new MdLinkProvider(linkComputer)); + return vscode.languages.registerDocumentLinkProvider(selector, new MdVsCodeLinkProvider(linkProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts index cdb1b8a79b8..e72c92f3554 100644 --- a/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/documentSymbolProvider.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; interface MarkdownSymbol { @@ -17,16 +16,16 @@ interface MarkdownSymbol { export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { constructor( - private readonly engine: MarkdownEngine + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async provideDocumentSymbolInformation(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); return toc.entries.map(entry => this.toSymbolInformation(entry)); } public async provideDocumentSymbols(document: SkinnyTextDocument): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); const root: MarkdownSymbol = { level: -Infinity, children: [], @@ -74,3 +73,10 @@ export class MdDocumentSymbolProvider implements vscode.DocumentSymbolProvider { return '#'.repeat(entry.level) + ' ' + entry.text; } } + +export function registerDocumentSymbolSupport( + selector: vscode.DocumentSelector, + tocProvider: MdTableOfContentsProvider, +): vscode.Disposable { + return vscode.languages.registerDocumentSymbolProvider(selector, new MdDocumentSymbolProvider(tocProvider)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts index 7217db35652..0947e16840e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts +++ b/extensions/markdown-language-features/src/languageFeatures/dropIntoEditor.ts @@ -23,7 +23,7 @@ const imageFileExtensions = new Set([ '.webp', ]); -export function registerDropIntoEditor(selector: vscode.DocumentSelector) { +export function registerDropIntoEditorSupport(selector: vscode.DocumentSelector) { return vscode.languages.registerDocumentOnDropEditProvider(selector, new class implements vscode.DocumentOnDropEditProvider { async provideDocumentOnDropEdits(document: vscode.TextDocument, _position: vscode.Position, dataTransfer: vscode.DataTransfer, token: vscode.CancellationToken): Promise { const enabled = vscode.workspace.getConfiguration('markdown', document).get('editor.drop.enabled', true); diff --git a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts index b9f1321cf06..9a2ecb9aa8e 100644 --- a/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts +++ b/extensions/markdown-language-features/src/languageFeatures/fileReferences.ts @@ -49,6 +49,9 @@ export class FindFileReferencesCommand implements Command { } } -export function registerFindFileReferences(commandManager: CommandManager, referencesProvider: MdReferencesProvider): vscode.Disposable { +export function registerFindFileReferenceSupport( + commandManager: CommandManager, + referencesProvider: MdReferencesProvider +): vscode.Disposable { return commandManager.register(new FindFileReferencesCommand(referencesProvider)); } diff --git a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts index 243a2ee12fd..a3b0fb44f1d 100644 --- a/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/foldingProvider.ts @@ -6,7 +6,7 @@ import Token = require('markdown-it/lib/token'); import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents } from '../tableOfContents'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; const rangeLimit = 5000; @@ -18,7 +18,8 @@ interface MarkdownItTokenWithMap extends Token { export class MdFoldingProvider implements vscode.FoldingRangeProvider { constructor( - private readonly engine: MarkdownEngine + private readonly engine: MarkdownEngine, + private readonly tocProvide: MdTableOfContentsProvider, ) { } public async provideFoldingRanges( @@ -54,8 +55,8 @@ export class MdFoldingProvider implements vscode.FoldingRangeProvider { .filter((region: vscode.FoldingRange | null): region is vscode.FoldingRange => !!region); } - private async getHeaderFoldingRanges(document: SkinnyTextDocument) { - const toc = await TableOfContents.create(this.engine, document); + private async getHeaderFoldingRanges(document: SkinnyTextDocument): Promise { + const toc = await this.tocProvide.get(document.uri); return toc.entries.map(entry => { let endLine = entry.sectionLocation.range.end.line; if (document.lineAt(endLine).isEmptyOrWhitespace && endLine >= entry.line + 1) { @@ -111,3 +112,11 @@ const isFoldableToken = (token: Token): token is MarkdownItTokenWithMap => { return false; } }; + +export function registerFoldingSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, +): vscode.Disposable { + return vscode.languages.registerFoldingRangeProvider(selector, new MdFoldingProvider(engine, tocProvider)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts index 1e3824da90d..b9a59ca4cec 100644 --- a/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts +++ b/extensions/markdown-language-features/src/languageFeatures/pathCompletions.ts @@ -9,7 +9,7 @@ import { MarkdownEngine } from '../markdownEngine'; import { TableOfContents } from '../tableOfContents'; import { resolveUriToMarkdownFile } from '../util/openDocumentLink'; import { SkinnyTextDocument } from '../workspaceContents'; -import { MdLinkComputer } from './documentLinkProvider'; +import { MdLinkProvider } from './documentLinkProvider'; enum CompletionContextKind { /** `[...](|)` */ @@ -76,19 +76,14 @@ function tryDecodeUriComponent(str: string): string { } } -export class MdPathCompletionProvider implements vscode.CompletionItemProvider { - - public static register( - selector: vscode.DocumentSelector, - engine: MarkdownEngine, - linkComputer: MdLinkComputer, - ): vscode.Disposable { - return vscode.languages.registerCompletionItemProvider(selector, new MdPathCompletionProvider(engine, linkComputer), '.', '/', '#'); - } +/** + * Adds path completions in markdown files by implementing {@link vscode.CompletionItemProvider}. + */ +export class MdVsCodePathCompletionProvider implements vscode.CompletionItemProvider { constructor( private readonly engine: MarkdownEngine, - private readonly linkComputer: MdLinkComputer, + private readonly linkProvider: MdLinkProvider, ) { } public async provideCompletionItems(document: SkinnyTextDocument, position: vscode.Position, _token: vscode.CancellationToken, _context: vscode.CompletionContext): Promise { @@ -240,8 +235,8 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider { const insertionRange = new vscode.Range(context.linkTextStartPosition, position); const replacementRange = new vscode.Range(insertionRange.start, position.translate({ characterDelta: context.linkSuffix.length })); - const definitions = await this.linkComputer.getLinkDefinitions(document); - for (const def of definitions) { + const { definitions } = await this.linkProvider.getLinks(document); + for (const [_, def] of definitions) { yield { kind: vscode.CompletionItemKind.Reference, label: def.ref.text, @@ -351,3 +346,11 @@ export class MdPathCompletionProvider implements vscode.CompletionItemProvider { return document.uri; } } + +export function registerPathCompletionSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, + linkProvider: MdLinkProvider, +): vscode.Disposable { + return vscode.languages.registerCompletionItemProvider(selector, new MdVsCodePathCompletionProvider(engine, linkProvider), '.', '/', '#'); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/references.ts b/extensions/markdown-language-features/src/languageFeatures/references.ts index 9d02154e649..f532a36bb2f 100644 --- a/extensions/markdown-language-features/src/languageFeatures/references.ts +++ b/extensions/markdown-language-features/src/languageFeatures/references.ts @@ -5,13 +5,13 @@ import * as vscode from 'vscode'; import * as uri from 'vscode-uri'; import { MarkdownEngine } from '../markdownEngine'; -import { Slugifier } from '../slugify'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { Disposable } from '../util/dispose'; +import { looksLikeMarkdownPath } from '../util/file'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref, MdLink, MdLinkComputer } from './documentLinkProvider'; -import { MdWorkspaceCache } from './workspaceCache'; +import { MdWorkspaceInfoCache } from './workspaceCache'; /** @@ -59,31 +59,27 @@ export interface MdHeaderReference { export type MdReference = MdLinkReference | MdHeaderReference; -export class MdReferencesProvider extends Disposable implements vscode.ReferenceProvider { +/** + * Stateful object that computes references for markdown files. + */ +export class MdReferencesProvider extends Disposable { - private readonly _linkCache: MdWorkspaceCache; + private readonly _linkCache: MdWorkspaceInfoCache; + private readonly _linkComputer: MdLinkComputer; public constructor( - private readonly linkComputer: MdLinkComputer, - private readonly workspaceContents: MdWorkspaceContents, private readonly engine: MarkdownEngine, - private readonly slugifier: Slugifier, + private readonly workspaceContents: MdWorkspaceContents, + private readonly tocProvider: MdTableOfContentsProvider, ) { super(); - this._linkCache = this._register(new MdWorkspaceCache(workspaceContents, doc => linkComputer.getAllLinks(doc, noopToken))); + this._linkComputer = new MdLinkComputer(engine); + this._linkCache = this._register(new MdWorkspaceInfoCache(workspaceContents, doc => this._linkComputer.getAllLinks(doc, noopToken))); } - async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { - const allRefs = await this.getAllReferencesAtPosition(document, position, token); - - return allRefs - .filter(ref => context.includeDeclaration || !ref.isDefinition) - .map(ref => ref.location); - } - - public async getAllReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const toc = await TableOfContents.create(this.engine, document); + public async getReferencesAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { + const toc = await this.tocProvider.get(document.uri); if (token.isCancellationRequested) { return []; } @@ -96,6 +92,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } + public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise { + const allLinksInWorkspace = (await this._linkCache.values()).flat(); + return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined)); + } + private async getReferencesToHeader(document: SkinnyTextDocument, header: TocEntry): Promise { const links = (await this._linkCache.values()).flat(); @@ -113,7 +114,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference for (const link of links) { if (link.href.kind === 'internal' && this.looksLikeLinkToDoc(link.href, document.uri) - && this.slugifier.fromHeading(link.href.fragment).value === header.slug.value + && this.engine.slugifier.fromHeading(link.href.fragment).value === header.slug.value ) { references.push({ kind: 'link', @@ -129,7 +130,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } private async getReferencesToLinkAtPosition(document: SkinnyTextDocument, position: vscode.Position, token: vscode.CancellationToken): Promise { - const docLinks = await this.linkComputer.getAllLinks(document, token); + const docLinks = await this._linkComputer.getAllLinks(document, token); for (const link of docLinks) { if (link.kind === 'definition') { @@ -177,15 +178,15 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference return references; } - const targetDoc = await tryFindMdDocumentForLink(sourceLink.href, this.workspaceContents); + const resolvedResource = await tryResolveLinkPath(sourceLink.href.path, this.workspaceContents); if (token.isCancellationRequested) { return []; } const references: MdReference[] = []; - if (targetDoc && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { - const toc = await TableOfContents.create(this.engine, targetDoc); + if (resolvedResource && this.isMarkdownPath(resolvedResource) && sourceLink.href.fragment && sourceLink.source.fragmentRange?.contains(triggerPosition)) { + const toc = await this.tocProvider.get(resolvedResource); const entry = toc.lookup(sourceLink.href.fragment); if (entry) { references.push({ @@ -199,11 +200,11 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } for (const link of allLinksInWorkspace) { - if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, targetDoc.uri)) { + if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resolvedResource)) { continue; } - if (this.slugifier.fromHeading(link.href.fragment).equals(this.slugifier.fromHeading(sourceLink.href.fragment))) { + if (this.engine.slugifier.fromHeading(link.href.fragment).equals(this.engine.slugifier.fromHeading(sourceLink.href.fragment))) { const isTriggerLocation = sourceLink.source.resource.fsPath === link.source.resource.fsPath && sourceLink.source.hrefRange.isEqual(link.source.hrefRange); references.push({ kind: 'link', @@ -215,23 +216,22 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } } else { // Triggered on a link without a fragment so we only require matching the file and ignore fragments - references.push(...this.findAllLinksToFile(targetDoc?.uri ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); + references.push(...this.findAllLinksToFile(resolvedResource ?? sourceLink.href.path, allLinksInWorkspace, sourceLink)); } return references; } + private isMarkdownPath(resolvedHrefPath: vscode.Uri) { + return this.workspaceContents.hasMarkdownDocument(resolvedHrefPath) || looksLikeMarkdownPath(resolvedHrefPath); + } + private looksLikeLinkToDoc(href: InternalHref, targetDoc: vscode.Uri) { return href.path.fsPath === targetDoc.fsPath || uri.Utils.extname(href.path) === '' && href.path.with({ path: href.path.path + '.md' }).fsPath === targetDoc.fsPath; } - public async getAllReferencesToFile(resource: vscode.Uri, _token: vscode.CancellationToken): Promise { - const allLinksInWorkspace = (await this._linkCache.values()).flat(); - return Array.from(this.findAllLinksToFile(resource, allLinksInWorkspace, undefined)); - } - - private * findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable { + private *findAllLinksToFile(resource: vscode.Uri, allLinksInWorkspace: readonly MdLink[], sourceLink: MdLink | undefined): Iterable { for (const link of allLinksInWorkspace) { if (link.href.kind !== 'internal' || !this.looksLikeLinkToDoc(link.href, resource)) { continue; @@ -254,7 +254,7 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } - private * getReferencesToLinkReference(allLinks: Iterable, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable { + private *getReferencesToLinkReference(allLinks: Iterable, refToFind: string, from: { resource: vscode.Uri; range: vscode.Range }): Iterable { for (const link of allLinks) { let ref: string; if (link.kind === 'definition') { @@ -291,18 +291,42 @@ export class MdReferencesProvider extends Disposable implements vscode.Reference } } -export async function tryFindMdDocumentForLink(href: InternalHref, workspaceContents: MdWorkspaceContents): Promise { - const targetDoc = await workspaceContents.getMarkdownDocument(href.path); - if (targetDoc) { - return targetDoc; +/** + * Implements {@link vscode.ReferenceProvider} for markdown documents. + */ +export class MdVsCodeReferencesProvider implements vscode.ReferenceProvider { + + public constructor( + private readonly referencesProvider: MdReferencesProvider + ) { } + + async provideReferences(document: SkinnyTextDocument, position: vscode.Position, context: vscode.ReferenceContext, token: vscode.CancellationToken): Promise { + const allRefs = await this.referencesProvider.getReferencesAtPosition(document, position, token); + return allRefs + .filter(ref => context.includeDeclaration || !ref.isDefinition) + .map(ref => ref.location); + } +} + +export function registerReferencesSupport( + selector: vscode.DocumentSelector, + referencesProvider: MdReferencesProvider, +): vscode.Disposable { + return vscode.languages.registerReferenceProvider(selector, new MdVsCodeReferencesProvider(referencesProvider)); +} + +export async function tryResolveLinkPath(originalUri: vscode.Uri, workspaceContents: MdWorkspaceContents): Promise { + if (await workspaceContents.pathExists(originalUri)) { + return originalUri; } // We don't think the file exists. If it doesn't already have an extension, try tacking on a `.md` and using that instead - if (uri.Utils.extname(href.path) === '') { - const dotMdResource = href.path.with({ path: href.path.path + '.md' }); - return workspaceContents.getMarkdownDocument(dotMdResource); + if (uri.Utils.extname(originalUri) === '') { + const dotMdResource = originalUri.with({ path: originalUri.path + '.md' }); + if (await workspaceContents.pathExists(dotMdResource)) { + return dotMdResource; + } } return undefined; } - diff --git a/extensions/markdown-language-features/src/languageFeatures/rename.ts b/extensions/markdown-language-features/src/languageFeatures/rename.ts index 955581b9d97..a88e3ff4689 100644 --- a/extensions/markdown-language-features/src/languageFeatures/rename.ts +++ b/extensions/markdown-language-features/src/languageFeatures/rename.ts @@ -11,7 +11,7 @@ import { Disposable } from '../util/dispose'; import { resolveDocumentLink } from '../util/openDocumentLink'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; import { InternalHref } from './documentLinkProvider'; -import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryFindMdDocumentForLink } from './references'; +import { MdHeaderReference, MdLinkReference, MdReference, MdReferencesProvider, tryResolveLinkPath } from './references'; const localize = nls.loadMessageBundle(); @@ -45,7 +45,7 @@ function tryDecodeUri(str: string): string { } } -export class MdRenameProvider extends Disposable implements vscode.RenameProvider { +export class MdVsCodeRenameProvider extends Disposable implements vscode.RenameProvider { private cachedRefs?: { readonly resource: vscode.Uri; @@ -58,8 +58,8 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide private readonly renameNotSupportedText = localize('invalidRenameLocation', "Rename not supported at location"); public constructor( - private readonly referencesProvider: MdReferencesProvider, private readonly workspaceContents: MdWorkspaceContents, + private readonly referencesProvider: MdReferencesProvider, private readonly slugifier: Slugifier, ) { super(); @@ -153,8 +153,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide const edit = new vscode.WorkspaceEdit(); const fileRenames: MdFileRenameEdit[] = []; - const targetDoc = await tryFindMdDocumentForLink(triggerHref, this.workspaceContents); - const targetUri = targetDoc?.uri ?? triggerHref.path; + const targetUri = await tryResolveLinkPath(triggerHref.path, this.workspaceContents) ?? triggerHref.path; const rawNewFilePath = resolveDocumentLink(newName, triggerDocument); let resolvedNewFilePath = rawNewFilePath; @@ -253,7 +252,7 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide return this.cachedRefs; } - const references = await this.referencesProvider.getAllReferencesAtPosition(document, position, token); + const references = await this.referencesProvider.getReferencesAtPosition(document, position, token); const triggerRef = references.find(ref => ref.isTriggerLocation); if (!triggerRef) { return undefined; @@ -270,3 +269,12 @@ export class MdRenameProvider extends Disposable implements vscode.RenameProvide } } + +export function registerRenameSupport( + selector: vscode.DocumentSelector, + workspaceContents: MdWorkspaceContents, + referencesProvider: MdReferencesProvider, + slugifier: Slugifier, +): vscode.Disposable { + return vscode.languages.registerRenameProvider(selector, new MdVsCodeRenameProvider(workspaceContents, referencesProvider, slugifier)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts index 3f69ea546d7..fd494a0f8fe 100644 --- a/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts +++ b/extensions/markdown-language-features/src/languageFeatures/smartSelect.ts @@ -5,7 +5,7 @@ import Token = require('markdown-it/lib/token'); import * as vscode from 'vscode'; import { MarkdownEngine } from '../markdownEngine'; -import { TableOfContents, TocEntry } from '../tableOfContents'; +import { MdTableOfContentsProvider, TocEntry } from '../tableOfContents'; import { SkinnyTextDocument } from '../workspaceContents'; interface MarkdownItTokenWithMap extends Token { @@ -15,7 +15,8 @@ interface MarkdownItTokenWithMap extends Token { export class MdSmartSelect implements vscode.SelectionRangeProvider { constructor( - private readonly engine: MarkdownEngine + private readonly engine: MarkdownEngine, + private readonly tocProvider: MdTableOfContentsProvider, ) { } public async provideSelectionRanges(document: SkinnyTextDocument, positions: vscode.Position[], _token: vscode.CancellationToken): Promise { @@ -54,7 +55,7 @@ export class MdSmartSelect implements vscode.SelectionRangeProvider { } private async getHeaderSelectionRange(document: SkinnyTextDocument, position: vscode.Position): Promise { - const toc = await TableOfContents.create(this.engine, document); + const toc = await this.tocProvider.get(document.uri); const headerInfo = getHeadersForPosition(toc.entries, position); @@ -249,3 +250,11 @@ function getFirstChildHeader(document: SkinnyTextDocument, header?: TocEntry, to } return undefined; } + +export function registerSmartSelectSupport( + selector: vscode.DocumentSelector, + engine: MarkdownEngine, + tocProvider: MdTableOfContentsProvider, +): vscode.Disposable { + return vscode.languages.registerSelectionRangeProvider(selector, new MdSmartSelect(engine, tocProvider)); +} diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts index 295771bfa60..8d7e3c81fa2 100644 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts +++ b/extensions/markdown-language-features/src/languageFeatures/workspaceCache.ts @@ -9,12 +9,89 @@ import { Lazy, lazy } from '../util/lazy'; import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; -/** - * Cache of information for markdown files in the workspace. - */ -export class MdWorkspaceCache extends Disposable { +class LazyResourceMap { + private readonly _map = new ResourceMap>>(); - private readonly _cache = new ResourceMap>>(); + public has(resource: vscode.Uri): boolean { + return this._map.has(resource); + } + + public get(resource: vscode.Uri): Promise | undefined { + return this._map.get(resource)?.value; + } + + public set(resource: vscode.Uri, value: Lazy>) { + this._map.set(resource, value); + } + + public delete(resource: vscode.Uri) { + this._map.delete(resource); + } + + public entries(): Promise> { + return Promise.all(Array.from(this._map.entries(), async ([key, entry]) => { + return [key, await entry.value]; + })); + } +} + +/** + * Cache of information per-document in the workspace. + * + * The values are computed lazily and invalidated when the document changes. + */ +export class MdDocumentInfoCache extends Disposable { + + private readonly _cache = new LazyResourceMap(); + + public constructor( + private readonly workspaceContents: MdWorkspaceContents, + private readonly getValue: (document: SkinnyTextDocument) => Promise, + ) { + super(); + + this._register(this.workspaceContents.onDidChangeMarkdownDocument(doc => this.onDidChangeDocument(doc))); + this._register(this.workspaceContents.onDidCreateMarkdownDocument(doc => this.onDidChangeDocument(doc))); + this._register(this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this)); + } + + public async get(resource: vscode.Uri): Promise { + const existing = this._cache.get(resource); + if (existing) { + return existing; + } + + const doc = await this.workspaceContents.getOrLoadMarkdownDocument(resource); + return doc && this.onDidChangeDocument(doc, true)?.value; + } + + public async entries(): Promise> { + return this._cache.entries(); + } + + private onDidChangeDocument(document: SkinnyTextDocument, forceAdd = false): Lazy> | undefined { + if (forceAdd || this._cache.has(document.uri)) { + const value = lazy(() => this.getValue(document)); + this._cache.set(document.uri, value); + return value; + } + return undefined; + } + + private onDidDeleteDocument(resource: vscode.Uri) { + this._cache.delete(resource); + } +} + +/** + * Cache of information across all markdown files in the workspace. + * + * Unlike {@link MdDocumentInfoCache}, the entries here are computed eagerly for every file in the workspace. + * However the computation of the values is still lazy. + */ +export class MdWorkspaceInfoCache extends Disposable { + + private readonly _cache = new LazyResourceMap(); private _init?: Promise; public constructor( @@ -26,14 +103,12 @@ export class MdWorkspaceCache extends Disposable { public async entries(): Promise> { await this.ensureInit(); - return Promise.all(Array.from(this._cache.entries(), async ([key, entry]) => { - return [key, await entry.value]; - })); + return this._cache.entries(); } public async values(): Promise> { await this.ensureInit(); - return Promise.all(Array.from(this._cache.values(), x => x.value)); + return Array.from(await this._cache.entries(), x => x[1]); } private async ensureInit(): Promise { diff --git a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts index 5906cc1cc72..41734606ffa 100644 --- a/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/languageFeatures/workspaceSymbolProvider.ts @@ -7,11 +7,11 @@ import * as vscode from 'vscode'; import { Disposable } from '../util/dispose'; import { MdWorkspaceContents } from '../workspaceContents'; import { MdDocumentSymbolProvider } from './documentSymbolProvider'; -import { MdWorkspaceCache } from './workspaceCache'; +import { MdWorkspaceInfoCache } from './workspaceCache'; export class MdWorkspaceSymbolProvider extends Disposable implements vscode.WorkspaceSymbolProvider { - private readonly _cache: MdWorkspaceCache; + private readonly _cache: MdWorkspaceInfoCache; public constructor( symbolProvider: MdDocumentSymbolProvider, @@ -19,7 +19,7 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work ) { super(); - this._cache = this._register(new MdWorkspaceCache(workspaceContents, doc => symbolProvider.provideDocumentSymbolInformation(doc))); + this._cache = this._register(new MdWorkspaceInfoCache(workspaceContents, doc => symbolProvider.provideDocumentSymbolInformation(doc))); } public async provideWorkspaceSymbols(query: string): Promise { @@ -27,3 +27,10 @@ export class MdWorkspaceSymbolProvider extends Disposable implements vscode.Work return allSymbols.filter(symbolInformation => symbolInformation.name.toLowerCase().indexOf(query.toLowerCase()) !== -1); } } + +export function registerWorkspaceSymbolSupport( + workspaceContents: MdWorkspaceContents, + symbolProvider: MdDocumentSymbolProvider, +): vscode.Disposable { + return vscode.languages.registerWorkspaceSymbolProvider(new MdWorkspaceSymbolProvider(symbolProvider, workspaceContents)); +} diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 3f32880d22f..400132d781f 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -98,10 +98,14 @@ export class MarkdownEngine { private _slugCount = new Map(); private _tokenCache = new TokenCache(); + public readonly slugifier: Slugifier; + public constructor( private readonly contributionProvider: MarkdownContributionProvider, - private readonly slugifier: Slugifier, + slugifier: Slugifier, ) { + this.slugifier = slugifier; + contributionProvider.onContributionsChanged(() => { // Markdown plugin contributions may have changed this.md = undefined; diff --git a/extensions/markdown-language-features/src/tableOfContents.ts b/extensions/markdown-language-features/src/tableOfContents.ts index 33a65d997b5..0f28f9fa635 100644 --- a/extensions/markdown-language-features/src/tableOfContents.ts +++ b/extensions/markdown-language-features/src/tableOfContents.ts @@ -4,10 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import { MdDocumentInfoCache } from './languageFeatures/workspaceCache'; import { MarkdownEngine } from './markdownEngine'; -import { githubSlugifier, Slug } from './slugify'; +import { githubSlugifier, Slug, Slugifier } from './slugify'; +import { Disposable } from './util/dispose'; import { isMarkdownFile } from './util/file'; -import { SkinnyTextDocument } from './workspaceContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from './workspaceContents'; export interface TocEntry { readonly slug: Slug; @@ -63,7 +65,7 @@ export class TableOfContents { public static async create(engine: MarkdownEngine, document: SkinnyTextDocument,): Promise { const entries = await this.buildToc(engine, document); - return new TableOfContents(entries); + return new TableOfContents(entries, engine.slugifier); } public static async createForDocumentOrNotebook(engine: MarkdownEngine, document: SkinnyTextDocument): Promise { @@ -80,7 +82,7 @@ export class TableOfContents { } } - return new TableOfContents(entries); + return new TableOfContents(entries, engine.slugifier); } } @@ -101,11 +103,11 @@ export class TableOfContents { const lineNumber = heading.map[0]; const line = document.lineAt(lineNumber); - let slug = githubSlugifier.fromHeading(line.text); + let slug = engine.slugifier.fromHeading(line.text); const existingSlugEntry = existingSlugEntries.get(slug.value); if (existingSlugEntry) { ++existingSlugEntry.count; - slug = githubSlugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); + slug = engine.slugifier.fromHeading(slug.value + '-' + existingSlugEntry.count); } else { existingSlugEntries.set(slug.value, { count: 0 }); } @@ -161,12 +163,34 @@ export class TableOfContents { return header.replace(/^\s*#+\s*(.*?)(\s+#+)?$/, (_, word) => word.trim()); } + public static readonly empty = new TableOfContents([], githubSlugifier); + private constructor( public readonly entries: readonly TocEntry[], + private readonly slugifier: Slugifier, ) { } public lookup(fragment: string): TocEntry | undefined { - const slug = githubSlugifier.fromHeading(fragment); + const slug = this.slugifier.fromHeading(fragment); return this.entries.find(entry => entry.slug.equals(slug)); } } + +export class MdTableOfContentsProvider extends Disposable { + + private readonly _cache: MdDocumentInfoCache; + + constructor( + engine: MarkdownEngine, + workspaceContents: MdWorkspaceContents, + ) { + super(); + this._cache = this._register(new MdDocumentInfoCache(workspaceContents, doc => { + return TableOfContents.create(engine, doc); + })); + } + + public async get(resource: vscode.Uri): Promise { + return (await this._cache.get(resource)) ?? TableOfContents.empty; + } +} diff --git a/extensions/markdown-language-features/src/test/definitionProvider.test.ts b/extensions/markdown-language-features/src/test/definitionProvider.test.ts index cbdf50637ec..9ff5d65c44a 100644 --- a/extensions/markdown-language-features/src/test/definitionProvider.test.ts +++ b/extensions/markdown-language-features/src/test/definitionProvider.test.ts @@ -7,9 +7,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdDefinitionProvider } from '../languageFeatures/definitionProvider'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; import { MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -18,10 +17,9 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { +function getDefinition(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); const provider = new MdDefinitionProvider(referencesProvider); return provider.provideDefinition(doc, pos, noopToken); } diff --git a/extensions/markdown-language-features/src/test/diagnostic.test.ts b/extensions/markdown-language-features/src/test/diagnostic.test.ts index cb6d32b244d..199c1d35ceb 100644 --- a/extensions/markdown-language-features/src/test/diagnostic.test.ts +++ b/extensions/markdown-language-features/src/test/diagnostic.test.ts @@ -6,46 +6,19 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions } from '../languageFeatures/diagnostics'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; +import { DiagnosticCollectionReporter, DiagnosticComputer, DiagnosticConfiguration, DiagnosticLevel, DiagnosticManager, DiagnosticOptions, DiagnosticReporter } from '../languageFeatures/diagnostics'; +import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; +import { disposeAll } from '../util/dispose'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { ResourceMap } from '../util/resourceMap'; import { MdWorkspaceContents } from '../workspaceContents'; import { createNewMarkdownEngine } from './engine'; import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { assertRangeEqual, joinLines, workspacePath } from './util'; - -async function getComputedDiagnostics(doc: InMemoryDocument, workspaceContents: MdWorkspaceContents): Promise { - const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const computer = new DiagnosticComputer(engine, workspaceContents, linkComputer); - return ( - await computer.getDiagnostics(doc, { - enabled: true, - validateFileLinks: DiagnosticLevel.warning, - validateFragmentLinks: DiagnosticLevel.warning, - validateMarkdownFileLinkFragments: DiagnosticLevel.warning, - validateReferences: DiagnosticLevel.warning, - ignoreLinks: [], - }, noopToken) - ).diagnostics; -} - -function createDiagnosticsManager(workspaceContents: MdWorkspaceContents, configuration = new MemoryDiagnosticConfiguration({})) { - const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - return new DiagnosticManager(new DiagnosticComputer(engine, workspaceContents, linkComputer), configuration); -} - -function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRanges: readonly vscode.Range[]) { - assert.strictEqual(actual.length, expectedRanges.length); - - for (let i = 0; i < actual.length; ++i) { - assertRangeEqual(actual[i].range, expectedRanges[i], `Range ${i} to be equal`); - } -} - const defaultDiagnosticsOptions = Object.freeze({ enabled: true, validateFileLinks: DiagnosticLevel.warning, @@ -55,25 +28,80 @@ const defaultDiagnosticsOptions = Object.freeze({ ignoreLinks: [], }); +async function getComputedDiagnostics(doc: InMemoryDocument, workspace: MdWorkspaceContents, options: Partial = {}): Promise { + const engine = createNewMarkdownEngine(); + const linkProvider = new MdLinkProvider(engine, workspace); + const tocProvider = new MdTableOfContentsProvider(engine, workspace); + const computer = new DiagnosticComputer(workspace, linkProvider, tocProvider); + return ( + await computer.getDiagnostics(doc, { ...defaultDiagnosticsOptions, ...options, }, noopToken) + ).diagnostics; +} + +function assertDiagnosticsEqual(actual: readonly vscode.Diagnostic[], expectedRanges: readonly vscode.Range[]) { + assert.strictEqual(actual.length, expectedRanges.length); + + for (let i = 0; i < actual.length; ++i) { + assertRangeEqual(actual[i].range, expectedRanges[i], `Range ${i} to be equal`); + } +} + +function orderDiagnosticsByRange(diagnostics: Iterable): readonly vscode.Diagnostic[] { + return Array.from(diagnostics).sort((a, b) => a.range.start.compareTo(b.range.start)); +} + class MemoryDiagnosticConfiguration implements DiagnosticConfiguration { private readonly _onDidChange = new vscode.EventEmitter(); public readonly onDidChange = this._onDidChange.event; - constructor( - private readonly _options: Partial, - ) { } + private _options: Partial; - getOptions(_resource: vscode.Uri): DiagnosticOptions { + constructor(options: Partial) { + this._options = options; + } + + public getOptions(_resource: vscode.Uri): DiagnosticOptions { return { ...defaultDiagnosticsOptions, ...this._options, }; } + + public update(newOptions: Partial) { + this._options = newOptions; + this._onDidChange.fire(); + } } +class MemoryDiagnosticReporter extends DiagnosticReporter { + private readonly diagnostics = new ResourceMap(); + + override dispose(): void { + super.clear(); + this.clear(); + } + + override clear(): void { + super.clear(); + this.diagnostics.clear(); + } + + set(uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]): void { + this.diagnostics.set(uri, diagnostics); + } + + delete(uri: vscode.Uri): void { + this.diagnostics.delete(uri); + } + + get(uri: vscode.Uri): readonly vscode.Diagnostic[] { + return orderDiagnosticsByRange(this.diagnostics.get(uri) ?? []); + } +} + +suite('markdown: Diagnostic Computer', () => { -suite('markdown: Diagnostics', () => { test('Should not return any diagnostics for empty document', async () => { const doc = new InMemoryDocument(workspacePath('doc.md'), joinLines( `text`, @@ -146,7 +174,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should generate diagnostics for non-existent link reference', async () => { @@ -169,9 +197,9 @@ suite('markdown: Diagnostics', () => { `[text][no-such-ref]`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ enabled: false })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, new MemoryDiagnosticConfiguration({ enabled: false }).getOptions(doc1.uri)); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not generate diagnostics for email autolink', async () => { @@ -180,7 +208,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not generate diagnostics for html tag that looks like an autolink', async () => { @@ -190,7 +218,7 @@ suite('markdown: Diagnostics', () => { )); const diagnostics = await getComputedDiagnostics(doc1, new InMemoryWorkspaceMarkdownDocuments([doc1])); - assert.deepStrictEqual(diagnostics.length, 0); + assertDiagnosticsEqual(diagnostics, []); }); test('Should allow ignoring invalid file link using glob', async () => { @@ -200,9 +228,9 @@ suite('markdown: Diagnostics', () => { `[text]: /no-such-file`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should be able to disable fragment validation for external files', async () => { @@ -211,11 +239,10 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateMarkdownFileLinkFragments: DiagnosticLevel.ignore })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateMarkdownFileLinkFragments: DiagnosticLevel.ignore }); + assertDiagnosticsEqual(diagnostics, []); }); test('Disabling own fragment validation should also disable path fragment validation by default', async () => { @@ -225,17 +252,15 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateFragmentLinks: DiagnosticLevel.ignore })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore }); + assertDiagnosticsEqual(diagnostics, []); } { // But we should be able to override the default - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { validateFragmentLinks: DiagnosticLevel.ignore, validateMarkdownFileLinkFragments: DiagnosticLevel.warning }); assertDiagnosticsEqual(diagnostics, [ new vscode.Range(1, 13, 1, 21), ]); @@ -247,9 +272,10 @@ suite('markdown: Diagnostics', () => { `[text](/no-such-file#header)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should not consider link fragment', async () => { @@ -257,9 +283,10 @@ suite('markdown: Diagnostics', () => { `[text](/no-such-file#header)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/no-such-file'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/no-such-file'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support globs', async () => { @@ -269,19 +296,19 @@ suite('markdown: Diagnostics', () => { `![i](/images/sub/sub2/ccc.png)`, )); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['/images/**/*.png'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/images/**/*.png'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support ignoring header', async () => { const doc1 = new InMemoryDocument(workspacePath('doc1.md'), joinLines( `![i](#no-such)`, )); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); - const manager = createDiagnosticsManager(new InMemoryWorkspaceMarkdownDocuments([doc1]), new MemoryDiagnosticConfiguration({ ignoreLinks: ['#no-such'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['#no-such'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('ignoreLinks should support ignoring header in file', async () => { @@ -290,16 +317,14 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md#no-such'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#no-such'] }); + assertDiagnosticsEqual(diagnostics, []); } { - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md#*'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md#*'] }); + assertDiagnosticsEqual(diagnostics, []); } }); @@ -309,10 +334,10 @@ suite('markdown: Diagnostics', () => { )); const doc2 = new InMemoryDocument(workspacePath('doc2.md'), joinLines('')); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should not detect checkboxes as invalid links', async () => { @@ -322,10 +347,10 @@ suite('markdown: Diagnostics', () => { `- [ ]`, )); - const contents = new InMemoryWorkspaceMarkdownDocuments([doc1]); - const manager = createDiagnosticsManager(contents, new MemoryDiagnosticConfiguration({ ignoreLinks: ['/doc2.md'] })); - const { diagnostics } = await manager.recomputeDiagnosticState(doc1, noopToken); - assert.deepStrictEqual(diagnostics.length, 0); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { ignoreLinks: ['/doc2.md'] }); + assertDiagnosticsEqual(diagnostics, []); }); test('Should detect invalid links with titles', async () => { @@ -347,4 +372,179 @@ suite('markdown: Diagnostics', () => { new vscode.Range(5, 7, 5, 17), ]); }); + + test('Should generate diagnostics for non-existent header using file link to own file', async () => { + const doc = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( + `[bad](doc.md#no-such)`, + `[bad](doc#no-such)`, + `[bad](/sub/doc.md#no-such)`, + `[bad](/sub/doc#no-such)`, + )); + + const diagnostics = await getComputedDiagnostics(doc, new InMemoryWorkspaceMarkdownDocuments([doc])); + assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [ + new vscode.Range(0, 12, 0, 20), + new vscode.Range(1, 9, 1, 17), + new vscode.Range(2, 17, 2, 25), + new vscode.Range(3, 14, 3, 22), + ]); + }); + + test('Own header link using file path link should be controlled by "validateMarkdownFileLinkFragments" instead of "validateFragmentLinks"', async () => { + const doc1 = new InMemoryDocument(workspacePath('sub', 'doc.md'), joinLines( + `[bad](doc.md#no-such)`, + `[bad](doc#no-such)`, + `[bad](/sub/doc.md#no-such)`, + `[bad](/sub/doc#no-such)`, + )); + + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1]); + + const diagnostics = await getComputedDiagnostics(doc1, workspace, { + validateFragmentLinks: DiagnosticLevel.ignore, + validateMarkdownFileLinkFragments: DiagnosticLevel.warning, + }); + assertDiagnosticsEqual(orderDiagnosticsByRange(diagnostics), [ + new vscode.Range(0, 12, 0, 20), + new vscode.Range(1, 9, 1, 17), + new vscode.Range(2, 17, 2, 25), + new vscode.Range(3, 14, 3, 22), + ]); + }); +}); + +suite('Markdown: Diagnostics manager', () => { + + const _disposables: vscode.Disposable[] = []; + + setup(() => { + disposeAll(_disposables); + }); + + teardown(() => { + disposeAll(_disposables); + }); + + function createDiagnosticsManager( + workspace: MdWorkspaceContents, + configuration = new MemoryDiagnosticConfiguration({}), + reporter: DiagnosticReporter = new DiagnosticCollectionReporter(), + ) { + const engine = createNewMarkdownEngine(); + const linkProvider = new MdLinkProvider(engine, workspace); + const tocProvider = new MdTableOfContentsProvider(engine, workspace); + const referencesProvider = new MdReferencesProvider(engine, workspace, tocProvider); + const manager = new DiagnosticManager( + engine, + workspace, + new DiagnosticComputer(workspace, linkProvider, tocProvider), + configuration, + reporter, + referencesProvider, + 0); + _disposables.push(manager, referencesProvider); + return manager; + } + + test('Changing enable/disable should recompute diagnostics', async () => { + const doc1Uri = workspacePath('doc1.md'); + const doc2Uri = workspacePath('doc2.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ + new InMemoryDocument(doc1Uri, joinLines( + `[text](#no-such-1)`, + )), + new InMemoryDocument(doc2Uri, joinLines( + `[text](#no-such-2)`, + )) + ]); + + const reporter = new MemoryDiagnosticReporter(); + const config = new MemoryDiagnosticConfiguration({ enabled: true }); + + const manager = createDiagnosticsManager(workspace, config, reporter); + await manager.ready; + + // Check initial state (Enabled) + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + + // Disable + config.update({ enabled: false }); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), []); + assertDiagnosticsEqual(reporter.get(doc2Uri), []); + + // Enable + config.update({ enabled: true }); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(0, 7, 0, 17), + ]); + }); + + test('Should revalidate linked files when header changes', async () => { + const doc1Uri = workspacePath('doc1.md'); + const doc1 = new InMemoryDocument(doc1Uri, joinLines( + `[text](#no-such)`, + `[text](/doc2.md#header)`, + )); + const doc2Uri = workspacePath('doc2.md'); + const doc2 = new InMemoryDocument(doc2Uri, joinLines( + `# Header`, + `[text](#header)`, + `[text](#no-such-2)`, + )); + + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc1, doc2]); + const reporter = new MemoryDiagnosticReporter(); + + const manager = createDiagnosticsManager(workspace, new MemoryDiagnosticConfiguration({}), reporter); + await manager.ready; + + // Check initial state + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 15), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(2, 7, 2, 17), + ]); + + // Edit header + workspace.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + `# new header`, + `[text](#new-header)`, + `[text](#no-such-2)`, + ))); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 15), + new vscode.Range(1, 15, 1, 22), + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(2, 7, 2, 17), + ]); + + // Revert to original file + workspace.updateDocument(new InMemoryDocument(doc2Uri, joinLines( + `# header`, + `[text](#header)`, + `[text](#no-such-2)`, + ))); + await reporter.waitPendingWork(); + assertDiagnosticsEqual(reporter.get(doc1Uri), [ + new vscode.Range(0, 7, 0, 15) + ]); + assertDiagnosticsEqual(reporter.get(doc2Uri), [ + new vscode.Range(2, 7, 2, 17), + ]); + }); }); diff --git a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts index 899641258d9..2b9ac060be1 100644 --- a/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentLinkProvider.test.ts @@ -6,19 +6,21 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer, MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdLinkProvider, MdVsCodeLinkProvider } from '../languageFeatures/documentLinkProvider'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; -import { assertRangeEqual, joinLines } from './util'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; +import { assertRangeEqual, joinLines, workspacePath } from './util'; -const testFile = vscode.Uri.joinPath(vscode.workspace.workspaceFolders![0].uri, 'x.md'); - function getLinksForFile(fileContents: string) { - const doc = new InMemoryDocument(testFile, fileContents); - const linkComputer = new MdLinkComputer(createNewMarkdownEngine()); - const provider = new MdLinkProvider(linkComputer); + const doc = new InMemoryDocument(workspacePath('x.md'), fileContents); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + + const engine = createNewMarkdownEngine(); + const linkProvider = new MdLinkProvider(engine, workspace); + const provider = new MdVsCodeLinkProvider(linkProvider); return provider.provideDocumentLinks(doc, noopToken); } @@ -30,7 +32,7 @@ function assertLinksEqual(actualLinks: readonly vscode.DocumentLink[], expectedR } } -suite('markdown.DocumentLinkProvider', () => { +suite('Markdown: DocumentLinkProvider', () => { test('Should not return anything for empty document', async () => { const links = await getLinksForFile(''); assert.strictEqual(links.length, 0); @@ -129,24 +131,24 @@ suite('markdown.DocumentLinkProvider', () => { { const links = await getLinksForFile('[![alt text](image.jpg)](https://example.com)'); assertLinksEqual(links, [ + new vscode.Range(0, 25, 0, 44), new vscode.Range(0, 13, 0, 22), - new vscode.Range(0, 25, 0, 44) ]); } { const links = await getLinksForFile('[![a]( whitespace.jpg )]( https://whitespace.com )'); assertLinksEqual(links, [ + new vscode.Range(0, 26, 0, 48), new vscode.Range(0, 7, 0, 21), - new vscode.Range(0, 26, 0, 48) ]); } { const links = await getLinksForFile('[![a](img1.jpg)](file1.txt) text [![a](img2.jpg)](file2.txt)'); assertLinksEqual(links, [ - new vscode.Range(0, 6, 0, 14), new vscode.Range(0, 17, 0, 26), - new vscode.Range(0, 39, 0, 47), + new vscode.Range(0, 6, 0, 14), new vscode.Range(0, 50, 0, 59), + new vscode.Range(0, 39, 0, 47), ]); } }); diff --git a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts index e29c7399e7a..aef737945d3 100644 --- a/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/documentSymbolProvider.test.ts @@ -5,22 +5,22 @@ import * as assert from 'assert'; import 'mocha'; -import * as vscode from 'vscode'; import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; -import { createNewMarkdownEngine } from './engine'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; - - -const testFileName = vscode.Uri.file('test.md'); +import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; +import { workspacePath } from './util'; function getSymbolsForFile(fileContents: string) { - const doc = new InMemoryDocument(testFileName, fileContents); - const provider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); + const doc = new InMemoryDocument(workspacePath('test.md'), fileContents); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace)); return provider.provideDocumentSymbols(doc); } - suite('markdown.DocumentSymbolProvider', () => { test('Should not return anything for empty document', async () => { const symbols = await getSymbolsForFile(''); diff --git a/extensions/markdown-language-features/src/test/fileReferences.test.ts b/extensions/markdown-language-features/src/test/fileReferences.test.ts index da5ed9db282..2a53245534e 100644 --- a/extensions/markdown-language-features/src/test/fileReferences.test.ts +++ b/extensions/markdown-language-features/src/test/fileReferences.test.ts @@ -6,9 +6,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; import { MdReference, MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -17,11 +16,10 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getFileReferences(resource: vscode.Uri, workspaceContents: MdWorkspaceContents) { +function getFileReferences(resource: vscode.Uri, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - return provider.getAllReferencesToFile(resource, noopToken); + const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); + return computer.getAllReferencesToFile(resource, noopToken); } function assertReferencesEqual(actualRefs: readonly MdReference[], ...expectedRefs: { uri: vscode.Uri; line: number }[]) { diff --git a/extensions/markdown-language-features/src/test/foldingProvider.test.ts b/extensions/markdown-language-features/src/test/foldingProvider.test.ts index bfb78f8a77b..34603af0344 100644 --- a/extensions/markdown-language-features/src/test/foldingProvider.test.ts +++ b/extensions/markdown-language-features/src/test/foldingProvider.test.ts @@ -7,8 +7,10 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; import { MdFoldingProvider } from '../languageFeatures/foldingProvider'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines } from './util'; const testFileName = vscode.Uri.file('test.md'); @@ -218,6 +220,8 @@ suite('markdown.FoldingProvider', () => { async function getFoldsForDocument(contents: string) { const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdFoldingProvider(createNewMarkdownEngine()); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdFoldingProvider(engine, new MdTableOfContentsProvider(engine, workspace)); return await provider.provideFoldingRanges(doc, {}, new vscode.CancellationTokenSource().token); } diff --git a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts index 53dab1a3e6d..2c53e0cde5e 100644 --- a/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts +++ b/extensions/markdown-language-features/src/test/inMemoryWorkspace.ts @@ -22,10 +22,14 @@ export class InMemoryWorkspaceMarkdownDocuments implements MdWorkspaceContents { return Array.from(this._documents.values()); } - public async getMarkdownDocument(resource: vscode.Uri): Promise { + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { return this._documents.get(resource); } + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documents.has(resolvedHrefPath); + } + public async pathExists(resource: vscode.Uri): Promise { return this._documents.has(resource); } diff --git a/extensions/markdown-language-features/src/test/pathCompletion.test.ts b/extensions/markdown-language-features/src/test/pathCompletion.test.ts index f50bb9126af..0cc98a65453 100644 --- a/extensions/markdown-language-features/src/test/pathCompletion.test.ts +++ b/extensions/markdown-language-features/src/test/pathCompletion.test.ts @@ -6,19 +6,22 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdPathCompletionProvider } from '../languageFeatures/pathCompletions'; +import { MdLinkProvider } from '../languageFeatures/documentLinkProvider'; +import { MdVsCodePathCompletionProvider } from '../languageFeatures/pathCompletions'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { createNewMarkdownEngine } from './engine'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { CURSOR, getCursorPositions, joinLines, workspacePath } from './util'; function getCompletionsAtCursor(resource: vscode.Uri, fileContents: string) { const doc = new InMemoryDocument(resource, fileContents); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const provider = new MdPathCompletionProvider(engine, linkComputer); + const linkProvider = new MdLinkProvider(engine, workspace); + const provider = new MdVsCodePathCompletionProvider(engine, linkProvider); const cursorPositions = getCursorPositions(fileContents, doc); return provider.provideCompletionItems(doc, cursorPositions[0], noopToken, { triggerCharacter: undefined, diff --git a/extensions/markdown-language-features/src/test/references.test.ts b/extensions/markdown-language-features/src/test/references.test.ts index 071088883d0..18e942e0076 100644 --- a/extensions/markdown-language-features/src/test/references.test.ts +++ b/extensions/markdown-language-features/src/test/references.test.ts @@ -6,9 +6,8 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; -import { MdReferencesProvider } from '../languageFeatures/references'; -import { githubSlugifier } from '../slugify'; +import { MdReferencesProvider, MdVsCodeReferencesProvider } from '../languageFeatures/references'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -17,10 +16,10 @@ import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; import { joinLines, workspacePath } from './util'; -function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents) { +function getReferences(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents) { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const provider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); + const computer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); + const provider = new MdVsCodeReferencesProvider(computer); return provider.provideReferences(doc, pos, { includeDeclaration: true }, noopToken); } diff --git a/extensions/markdown-language-features/src/test/rename.test.ts b/extensions/markdown-language-features/src/test/rename.test.ts index db523866b1c..d6dece78ea0 100644 --- a/extensions/markdown-language-features/src/test/rename.test.ts +++ b/extensions/markdown-language-features/src/test/rename.test.ts @@ -6,10 +6,10 @@ import * as assert from 'assert'; import 'mocha'; import * as vscode from 'vscode'; -import { MdLinkComputer } from '../languageFeatures/documentLinkProvider'; import { MdReferencesProvider } from '../languageFeatures/references'; -import { MdRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; +import { MdVsCodeRenameProvider, MdWorkspaceEdit } from '../languageFeatures/rename'; import { githubSlugifier } from '../slugify'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { noopToken } from '../util/cancellation'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { MdWorkspaceContents } from '../workspaceContents'; @@ -21,22 +21,20 @@ import { assertRangeEqual, joinLines, workspacePath } from './util'; /** * Get prepare rename info. */ -function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspaceContents: MdWorkspaceContents): Promise { +function prepareRename(doc: InMemoryDocument, pos: vscode.Position, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); + const referenceComputer = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); + const renameProvider = new MdVsCodeRenameProvider(workspace, referenceComputer, githubSlugifier); return renameProvider.prepareRename(doc, pos, noopToken); } /** * Get all the edits for the rename. */ -function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspaceContents: MdWorkspaceContents): Promise { +function getRenameEdits(doc: InMemoryDocument, pos: vscode.Position, newName: string, workspace: MdWorkspaceContents): Promise { const engine = createNewMarkdownEngine(); - const linkComputer = new MdLinkComputer(engine); - const referencesProvider = new MdReferencesProvider(linkComputer, workspaceContents, engine, githubSlugifier); - const renameProvider = new MdRenameProvider(referencesProvider, workspaceContents, githubSlugifier); + const referencesProvider = new MdReferencesProvider(engine, workspace, new MdTableOfContentsProvider(engine, workspace)); + const renameProvider = new MdVsCodeRenameProvider(workspace, referencesProvider, githubSlugifier); return renameProvider.provideRenameEditsImpl(doc, pos, newName, noopToken); } diff --git a/extensions/markdown-language-features/src/test/smartSelect.test.ts b/extensions/markdown-language-features/src/test/smartSelect.test.ts index 9d7f60bf481..9298d5972e4 100644 --- a/extensions/markdown-language-features/src/test/smartSelect.test.ts +++ b/extensions/markdown-language-features/src/test/smartSelect.test.ts @@ -9,6 +9,8 @@ import { MdSmartSelect } from '../languageFeatures/smartSelect'; import { createNewMarkdownEngine } from './engine'; import { InMemoryDocument } from '../util/inMemoryDocument'; import { CURSOR, getCursorPositions, joinLines } from './util'; +import { MdTableOfContentsProvider } from '../tableOfContents'; +import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; const testFileName = vscode.Uri.file('test.md'); @@ -720,7 +722,9 @@ function assertLineNumbersEqual(selectionRange: vscode.SelectionRange, startLine function getSelectionRangesForDocument(contents: string, pos?: vscode.Position[]): Promise { const doc = new InMemoryDocument(testFileName, contents); - const provider = new MdSmartSelect(createNewMarkdownEngine()); + const workspace = new InMemoryWorkspaceMarkdownDocuments([doc]); + const engine = createNewMarkdownEngine(); + const provider = new MdSmartSelect(engine, new MdTableOfContentsProvider(engine, workspace)); const positions = pos ? pos : getCursorPositions(contents, doc); return provider.provideSelectionRanges(doc, positions, new vscode.CancellationTokenSource().token); } diff --git a/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts b/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts new file mode 100644 index 00000000000..cef3c33816f --- /dev/null +++ b/extensions/markdown-language-features/src/test/tableOfContentsWatcher.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import { MarkdownEngine } from '../markdownEngine'; +import { TableOfContents } from '../tableOfContents'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { equals } from '../util/arrays'; +import { Disposable } from '../util/dispose'; +import { ResourceMap } from '../util/resourceMap'; + +/** + * Check if the items in a table of contents have changed. + * + * This only checks for changes in the entries themselves, not for any changes in their locations. + */ +function hasTableOfContentsChanged(a: TableOfContents, b: TableOfContents): boolean { + const aSlugs = a.entries.map(entry => entry.slug.value).sort(); + const bSlugs = b.entries.map(entry => entry.slug.value).sort(); + return !equals(aSlugs, bSlugs); +} + +export class MdTableOfContentsWatcher extends Disposable { + + private readonly _files = new ResourceMap<{ + readonly toc: TableOfContents; + }>(); + + private readonly _onTocChanged = this._register(new vscode.EventEmitter<{ readonly uri: vscode.Uri }>); + public readonly onTocChanged = this._onTocChanged.event; + + public constructor( + private readonly engine: MarkdownEngine, + private readonly workspaceContents: MdWorkspaceContents, + ) { + super(); + + this._register(this.workspaceContents.onDidChangeMarkdownDocument(this.onDidChangeDocument, this)); + this._register(this.workspaceContents.onDidCreateMarkdownDocument(this.onDidCreateDocument, this)); + this._register(this.workspaceContents.onDidDeleteMarkdownDocument(this.onDidDeleteDocument, this)); + } + + private async onDidCreateDocument(document: SkinnyTextDocument) { + const toc = await TableOfContents.create(this.engine, document); + this._files.set(document.uri, { toc }); + } + + private async onDidChangeDocument(document: SkinnyTextDocument) { + const existing = this._files.get(document.uri); + const newToc = await TableOfContents.create(this.engine, document); + + if (!existing || hasTableOfContentsChanged(existing.toc, newToc)) { + this._onTocChanged.fire({ uri: document.uri }); + } + + this._files.set(document.uri, { toc: newToc }); + } + + private onDidDeleteDocument(resource: vscode.Uri) { + this._files.delete(resource); + } +} diff --git a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts index a059159f613..1302a29ab6f 100644 --- a/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts +++ b/extensions/markdown-language-features/src/test/workspaceSymbolProvider.test.ts @@ -8,29 +8,31 @@ import 'mocha'; import * as vscode from 'vscode'; import { MdDocumentSymbolProvider } from '../languageFeatures/documentSymbolProvider'; import { MdWorkspaceSymbolProvider } from '../languageFeatures/workspaceSymbolProvider'; -import { SkinnyTextDocument } from '../workspaceContents'; -import { createNewMarkdownEngine } from './engine'; +import { MdTableOfContentsProvider } from '../tableOfContents'; import { InMemoryDocument } from '../util/inMemoryDocument'; +import { MdWorkspaceContents, SkinnyTextDocument } from '../workspaceContents'; +import { createNewMarkdownEngine } from './engine'; import { InMemoryWorkspaceMarkdownDocuments } from './inMemoryWorkspace'; +import { workspacePath } from './util'; - -const symbolProvider = new MdDocumentSymbolProvider(createNewMarkdownEngine()); +function getWorkspaceSymbols(workspace: MdWorkspaceContents, query = ''): Promise { + const engine = createNewMarkdownEngine(); + const symbolProvider = new MdDocumentSymbolProvider(new MdTableOfContentsProvider(engine, workspace)); + return new MdWorkspaceSymbolProvider(symbolProvider, workspace).provideWorkspaceSymbols(query); +} suite('markdown.WorkspaceSymbolProvider', () => { test('Should not return anything for empty workspace', async () => { - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([])); - - assert.deepStrictEqual(await provider.provideWorkspaceSymbols(''), []); + const workspace = new InMemoryWorkspaceMarkdownDocuments([]); + assert.deepStrictEqual(await getWorkspaceSymbols(workspace, ''), []); }); test('Should return symbols from workspace with one markdown file', async () => { - const testFileName = vscode.Uri.file('test.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ + new InMemoryDocument(workspacePath('test.md'), `# header1\nabc\n## header2`) + ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments([ - new InMemoryDocument(testFileName, `# header1\nabc\n## header2`) - ])); - - const symbols = await provider.provideWorkspaceSymbols(''); + const symbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(symbols.length, 2); assert.strictEqual(symbols[0].name, '# header1'); assert.strictEqual(symbols[1].name, '## header2'); @@ -40,64 +42,59 @@ suite('markdown.WorkspaceSymbolProvider', () => { const fileNameCount = 10; const files: SkinnyTextDocument[] = []; for (let i = 0; i < fileNameCount; ++i) { - const testFileName = vscode.Uri.file(`test${i}.md`); + const testFileName = workspacePath(`test${i}.md`); files.push(new InMemoryDocument(testFileName, `# common\nabc\n## header${i}`)); } - const provider = new MdWorkspaceSymbolProvider(symbolProvider, new InMemoryWorkspaceMarkdownDocuments(files)); + const workspace = new InMemoryWorkspaceMarkdownDocuments(files); - const symbols = await provider.provideWorkspaceSymbols(''); + const symbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(symbols.length, fileNameCount * 2); }); test('Should update results when markdown file changes symbols', async () => { - const testFileName = vscode.Uri.file('test.md'); - - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const testFileName = workspacePath('test.md'); + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`, 1 /* version */) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); // Update file - workspaceFileProvider.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); - const newSymbols = await provider.provideWorkspaceSymbols(''); + workspace.updateDocument(new InMemoryDocument(testFileName, `# new header\nabc\n## header2`, 2 /* version */)); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 2); assert.strictEqual(newSymbols[0].name, '# new header'); assert.strictEqual(newSymbols[1].name, '## header2'); }); test('Should remove results when file is deleted', async () => { - const testFileName = vscode.Uri.file('test.md'); + const testFileName = workspacePath('test.md'); - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); // delete file - workspaceFileProvider.deleteDocument(testFileName); - const newSymbols = await provider.provideWorkspaceSymbols(''); + workspace.deleteDocument(testFileName); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 0); }); test('Should update results when markdown file is created', async () => { - const testFileName = vscode.Uri.file('test.md'); + const testFileName = workspacePath('test.md'); - const workspaceFileProvider = new InMemoryWorkspaceMarkdownDocuments([ + const workspace = new InMemoryWorkspaceMarkdownDocuments([ new InMemoryDocument(testFileName, `# header1`) ]); - const provider = new MdWorkspaceSymbolProvider(symbolProvider, workspaceFileProvider); - assert.strictEqual((await provider.provideWorkspaceSymbols('')).length, 1); + assert.strictEqual((await getWorkspaceSymbols(workspace, '')).length, 1); - // Creat file - workspaceFileProvider.createDocument(new InMemoryDocument(vscode.Uri.file('test2.md'), `# new header\nabc\n## header2`)); - const newSymbols = await provider.provideWorkspaceSymbols(''); + // Create file + workspace.createDocument(new InMemoryDocument(workspacePath('test2.md'), `# new header\nabc\n## header2`)); + const newSymbols = await getWorkspaceSymbols(workspace, ''); assert.strictEqual(newSymbols.length, 3); }); }); diff --git a/extensions/markdown-language-features/src/util/file.ts b/extensions/markdown-language-features/src/util/file.ts index d61029aaf99..6d5f22e95e0 100644 --- a/extensions/markdown-language-features/src/util/file.ts +++ b/extensions/markdown-language-features/src/util/file.ts @@ -4,7 +4,24 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; +import * as URI from 'vscode-uri'; + +const markdownFileExtensions = Object.freeze([ + '.md', + '.mkd', + '.mdwn', + '.mdown', + '.markdown', + '.markdn', + '.mdtxt', + '.mdtext', + '.workbook', +]); export function isMarkdownFile(document: vscode.TextDocument) { return document.languageId === 'markdown'; -} \ No newline at end of file +} + +export function looksLikeMarkdownPath(resolvedHrefPath: vscode.Uri) { + return markdownFileExtensions.includes(URI.Utils.extname(resolvedHrefPath).toLowerCase()); +} diff --git a/extensions/markdown-language-features/src/util/schemes.ts b/extensions/markdown-language-features/src/util/schemes.ts index 02e5aba6590..7e87ef539a2 100644 --- a/extensions/markdown-language-features/src/util/schemes.ts +++ b/extensions/markdown-language-features/src/util/schemes.ts @@ -32,15 +32,3 @@ export function getUriForLinkWithKnownExternalScheme(link: string): vscode.Uri | export function isOfScheme(scheme: string, link: string): boolean { return link.toLowerCase().startsWith(scheme); } - -export const MarkdownFileExtensions: readonly string[] = [ - '.md', - '.mkd', - '.mdwn', - '.mdown', - '.markdown', - '.markdn', - '.mdtxt', - '.mdtext', - '.workbook', -]; diff --git a/extensions/markdown-language-features/src/workspaceContents.ts b/extensions/markdown-language-features/src/workspaceContents.ts index 02477c8c9d7..a5e771f2d06 100644 --- a/extensions/markdown-language-features/src/workspaceContents.ts +++ b/extensions/markdown-language-features/src/workspaceContents.ts @@ -6,9 +6,10 @@ import * as vscode from 'vscode'; import { coalesce } from './util/arrays'; import { Disposable } from './util/dispose'; -import { isMarkdownFile } from './util/file'; +import { isMarkdownFile, looksLikeMarkdownPath } from './util/file'; import { InMemoryDocument } from './util/inMemoryDocument'; import { Limiter } from './util/limiter'; +import { ResourceMap } from './util/resourceMap'; /** * Minimal version of {@link vscode.TextLine}. Used for mocking out in testing. @@ -40,7 +41,12 @@ export interface MdWorkspaceContents { */ getAllMarkdownDocuments(): Promise>; - getMarkdownDocument(resource: vscode.Uri): Promise; + /** + * Check if a document already exists in the workspace contents. + */ + hasMarkdownDocument(resource: vscode.Uri): boolean; + + getOrLoadMarkdownDocument(resource: vscode.Uri): Promise; pathExists(resource: vscode.Uri): Promise; @@ -62,6 +68,8 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace private _watcher: vscode.FileSystemWatcher | undefined; + private readonly _documentCache = new ResourceMap(); + private readonly utf8Decoder = new TextDecoder('utf-8'); /** @@ -80,7 +88,7 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); const onDiskResults = await Promise.all(resources.map(resource => { return limiter.queue(async () => { - const doc = await this.getMarkdownDocument(resource); + const doc = await this.getOrLoadMarkdownDocument(resource); if (doc) { foundFiles.add(doc.uri.toString()); } @@ -118,51 +126,77 @@ export class VsCodeMdWorkspaceContents extends Disposable implements MdWorkspace this._watcher = this._register(vscode.workspace.createFileSystemWatcher('**/*.md')); this._register(this._watcher.onDidChange(async resource => { - const document = await this.getMarkdownDocument(resource); + this._documentCache.delete(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidChangeMarkdownDocumentEmitter.fire(document); } })); this._register(this._watcher.onDidCreate(async resource => { - const document = await this.getMarkdownDocument(resource); + const document = await this.getOrLoadMarkdownDocument(resource); if (document) { this._onDidCreateMarkdownDocumentEmitter.fire(document); } })); this._register(this._watcher.onDidDelete(resource => { + this._documentCache.delete(resource); this._onDidDeleteMarkdownDocumentEmitter.fire(resource); })); + this._register(vscode.workspace.onDidOpenTextDocument(e => { + this._documentCache.delete(e.uri); + })); + this._register(vscode.workspace.onDidChangeTextDocument(e => { if (this.isRelevantMarkdownDocument(e.document)) { this._onDidChangeMarkdownDocumentEmitter.fire(e.document); } })); + + this._register(vscode.workspace.onDidCloseTextDocument(e => { + this._documentCache.delete(e.uri); + })); } private isRelevantMarkdownDocument(doc: vscode.TextDocument) { return isMarkdownFile(doc) && doc.uri.scheme !== 'vscode-bulkeditpreview'; } - public async getMarkdownDocument(resource: vscode.Uri): Promise { + public async getOrLoadMarkdownDocument(resource: vscode.Uri): Promise { + const existing = this._documentCache.get(resource); + if (existing) { + return existing; + } + const matchingDocument = vscode.workspace.textDocuments.find((doc) => this.isRelevantMarkdownDocument(doc) && doc.uri.toString() === resource.toString()); if (matchingDocument) { + this._documentCache.set(resource, matchingDocument); return matchingDocument; } + if (!looksLikeMarkdownPath(resource)) { + return undefined; + } + try { const bytes = await vscode.workspace.fs.readFile(resource); // We assume that markdown is in UTF-8 const text = this.utf8Decoder.decode(bytes); - return new InMemoryDocument(resource, text, 0); + const doc = new InMemoryDocument(resource, text, 0); + this._documentCache.set(resource, doc); + return doc; } catch { return undefined; } } + public hasMarkdownDocument(resolvedHrefPath: vscode.Uri): boolean { + return this._documentCache.has(resolvedHrefPath); + } + public async pathExists(target: vscode.Uri): Promise { let targetResourceStat: vscode.FileStat | undefined; try { diff --git a/extensions/markdown-language-features/tsconfig.json b/extensions/markdown-language-features/tsconfig.json index 7c1d4a7fca8..5b2636221ff 100644 --- a/extensions/markdown-language-features/tsconfig.json +++ b/extensions/markdown-language-features/tsconfig.json @@ -7,7 +7,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.textEditorDrop.d.ts", - "../../src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts", "../../src/vscode-dts/vscode.proposed.documentPaste.d.ts" ] } diff --git a/extensions/merge-conflict/src/documentTracker.ts b/extensions/merge-conflict/src/documentTracker.ts index d6247b20671..f52644ab9df 100644 --- a/extensions/merge-conflict/src/documentTracker.ts +++ b/extensions/merge-conflict/src/documentTracker.ts @@ -69,9 +69,7 @@ export default class DocumentMergeConflictTracker implements vscode.Disposable, return cacheItem.delayTask.trigger(() => { const conflicts = this.getConflictsOrEmpty(document, Array.from(cacheItem!.origins)); - if (this.cache) { - this.cache.delete(key!); - } + this.cache?.delete(key!); return conflicts; }); diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index 50d084e2751..f5c0a910f1e 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -4,7 +4,7 @@ "description": "%description%", "icon": "media/icon.png", "version": "1.0.0", - "publisher": "ms-vscode", + "publisher": "vscode", "license": "MIT", "engines": { "vscode": "^1.67.0" diff --git a/extensions/theme-seti/cgmanifest.json b/extensions/theme-seti/cgmanifest.json index 00e2991fb96..8bcfb2d2f40 100644 --- a/extensions/theme-seti/cgmanifest.json +++ b/extensions/theme-seti/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "seti-ui", "repositoryUrl": "https://github.com/jesseweed/seti-ui", - "commitHash": "8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0" + "commitHash": "6b83574de165123583d6d8d5b3b6c91f04b7153d" } }, "version": "0.1.0" diff --git a/extensions/theme-seti/icons/seti.woff b/extensions/theme-seti/icons/seti.woff index aeb87b845af..70e9074c0fb 100644 Binary files a/extensions/theme-seti/icons/seti.woff and b/extensions/theme-seti/icons/seti.woff differ diff --git a/extensions/theme-seti/icons/vs-seti-icon-theme.json b/extensions/theme-seti/icons/vs-seti-icon-theme.json index 41d2556aaa9..6f7f20bd8c3 100644 --- a/extensions/theme-seti/icons/vs-seti-icon-theme.json +++ b/extensions/theme-seti/icons/vs-seti-icon-theme.json @@ -750,6 +750,14 @@ "fontCharacter": "\\E050", "fontColor": "#cc3e44" }, + "_java_1_light": { + "fontCharacter": "\\E050", + "fontColor": "#498ba7" + }, + "_java_1": { + "fontCharacter": "\\E050", + "fontColor": "#519aba" + }, "_javascript_light": { "fontCharacter": "\\E051", "fontColor": "#b7b73b" @@ -1614,7 +1622,7 @@ "hxp": "_haxe_2", "hxml": "_haxe_3", "jade": "_jade", - "class": "_java", + "class": "_java_1", "classpath": "_java", "js.map": "_javascript", "spec.js": "_javascript_1", @@ -1863,6 +1871,7 @@ "clojure": "_clojure", "coffeescript": "_coffee", "jsonc": "_json", + "json": "_json", "c": "_c", "cpp": "_cpp", "cuda-cpp": "_cu", @@ -1880,7 +1889,6 @@ "java": "_java", "javascriptreact": "_react", "javascript": "_javascript", - "json": "_json", "julia": "_julia", "tex": "_tex_1", "latex": "_tex", @@ -2012,7 +2020,7 @@ "hxp": "_haxe_2_light", "hxml": "_haxe_3_light", "jade": "_jade_light", - "class": "_java_light", + "class": "_java_1_light", "classpath": "_java_light", "js.map": "_javascript_light", "spec.js": "_javascript_1_light", @@ -2179,6 +2187,7 @@ "clojure": "_clojure_light", "coffeescript": "_coffee_light", "jsonc": "_json_light", + "json": "_json_light", "c": "_c_light", "cpp": "_cpp_light", "cuda-cpp": "_cu_light", @@ -2196,7 +2205,6 @@ "java": "_java_light", "javascriptreact": "_react_light", "javascript": "_javascript_light", - "json": "_json_light", "julia": "_julia_light", "tex": "_tex_1_light", "latex": "_tex_light", @@ -2330,5 +2338,5 @@ "npm-debug.log": "_npm_ignored_light" } }, - "version": "https://github.com/jesseweed/seti-ui/commit/8dba1bc311dad1b9bc23c4779149f3bf9baa8cb0" + "version": "https://github.com/jesseweed/seti-ui/commit/6b83574de165123583d6d8d5b3b6c91f04b7153d" } \ No newline at end of file diff --git a/extensions/typescript-language-features/.eslintrc.json b/extensions/typescript-language-features/.eslintrc.json index ae8cc48c050..f7c6c1b495b 100644 --- a/extensions/typescript-language-features/.eslintrc.json +++ b/extensions/typescript-language-features/.eslintrc.json @@ -1,5 +1,5 @@ { "rules": { - "prefer-const": "error" + "@typescript-eslint/prefer-optional-chain": "warn" } } diff --git a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts index 890486d7902..214e6b24f7b 100644 --- a/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts +++ b/extensions/typescript-language-features/src/languageFeatures/codeLens/baseCodeLensProvider.ts @@ -88,7 +88,7 @@ export function getSymbolRange( } // In older versions, we have to calculate this manually. See #23924 - const span = item.spans && item.spans[0]; + const span = item.spans?.[0]; if (!span) { return undefined; } diff --git a/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts index 1d9c66caa09..8bde8b45df7 100644 --- a/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts +++ b/extensions/typescript-language-features/src/languageFeatures/signatureHelp.ts @@ -64,7 +64,7 @@ class TypeScriptSignatureHelpProvider implements vscode.SignatureHelpProvider { private getActiveParameter(info: Proto.SignatureHelpItems): number { const activeSignature = info.items[info.selectedItemIndex]; - if (activeSignature && activeSignature.isVariadic) { + if (activeSignature?.isVariadic) { return Math.min(info.argumentIndex, activeSignature.parameters.length - 1); } return info.argumentIndex; diff --git a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts index d020d265995..527a25ef8b2 100644 --- a/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts +++ b/extensions/typescript-language-features/src/languageFeatures/sourceDefinition.ts @@ -6,41 +6,13 @@ import * as vscode from 'vscode'; import * as nls from 'vscode-nls'; import { Command, CommandManager } from '../commands/commandManager'; -import * as Proto from '../protocol'; -import { ExecConfig, ITypeScriptServiceClient, ServerResponse } from '../typescriptService'; +import { ITypeScriptServiceClient } from '../typescriptService'; import API from '../utils/api'; import { isSupportedLanguageMode } from '../utils/languageIds'; import * as typeConverters from '../utils/typeConverters'; const localize = nls.loadMessageBundle(); -namespace ExperimentalProto { - export const enum CommandTypes { - FindSourceDefinition = 'findSourceDefinition' - } - - export interface SourceDefinitionRequestArgs extends Proto.FileLocationRequestArgs { } - - export interface SourceDefinitionRequest extends Proto.Request { - command: CommandTypes.FindSourceDefinition; - arguments: SourceDefinitionRequestArgs; - } - - export interface InlayHintsResponse extends Proto.DefinitionResponse { } - - export interface IExtendedTypeScriptServiceClient { - execute( - command: K, - args: ExtendedTsServerRequests[K][0], - token: vscode.CancellationToken, - config?: ExecConfig - ): Promise>; - } - - export interface ExtendedTsServerRequests { - 'findSourceDefinition': [SourceDefinitionRequestArgs, InlayHintsResponse]; - } -} class SourceDefinitionCommand implements Command { @@ -85,7 +57,7 @@ class SourceDefinitionCommand implements Command { const position = activeEditor.selection.anchor; const args = typeConverters.Position.toFileLocationRequestArgs(openedFiledPath, position); - const response = await (this.client as ExperimentalProto.IExtendedTypeScriptServiceClient).execute('findSourceDefinition', args, token); + const response = await this.client.execute('findSourceDefinition', args, token); if (response.type === 'response' && response.body) { const locations: vscode.Location[] = response.body.map(reference => typeConverters.Location.fromTextSpan(this.client.toResource(reference.file), reference)); diff --git a/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts index aeaa1bbcaae..c2219b7b4ac 100644 --- a/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts +++ b/extensions/typescript-language-features/src/languageFeatures/tagClosing.ts @@ -53,7 +53,7 @@ class TagClosing extends Disposable { return; } - const activeDocument = vscode.window.activeTextEditor && vscode.window.activeTextEditor.document; + const activeDocument = vscode.window.activeTextEditor?.document; if (document !== activeDocument) { return; } diff --git a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts index 760f82d7b09..6355e5b60c4 100644 --- a/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts +++ b/extensions/typescript-language-features/src/tsServer/versionProvider.electron.ts @@ -28,7 +28,7 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider public get globalVersion(): TypeScriptVersion | undefined { if (this.configuration?.globalTsdk) { const globals = this.loadVersionsFromSetting(TypeScriptVersionSource.UserSetting, this.configuration.globalTsdk); - if (globals && globals.length) { + if (globals?.length) { return globals[0]; } } @@ -37,7 +37,7 @@ export class DiskTypeScriptVersionProvider implements ITypeScriptVersionProvider public get localVersion(): TypeScriptVersion | undefined { const tsdkVersions = this.localTsdkVersions; - if (tsdkVersions && tsdkVersions.length) { + if (tsdkVersions?.length) { return tsdkVersions[0]; } diff --git a/extensions/typescript-language-features/src/typescriptService.ts b/extensions/typescript-language-features/src/typescriptService.ts index 616131b26d8..681cee3a4b5 100644 --- a/extensions/typescript-language-features/src/typescriptService.ts +++ b/extensions/typescript-language-features/src/typescriptService.ts @@ -73,6 +73,7 @@ interface StandardTsServerRequests { 'fileReferences': [Proto.FileRequestArgs, Proto.FileReferencesResponse]; 'provideInlayHints': [Proto.InlayHintsRequestArgs, Proto.InlayHintsResponse]; 'encodedSemanticClassifications-full': [Proto.EncodedSemanticClassificationsRequestArgs, Proto.EncodedSemanticClassificationsResponse]; + 'findSourceDefinition': [Proto.FileLocationRequestArgs, Proto.DefinitionResponse]; } interface NoResponseTsServerRequests { diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index 0fdecc5c952..0585285b6d7 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -883,7 +883,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.loadingIndicator.reset(); const diagnosticEvent = event as Proto.DiagnosticEvent; - if (diagnosticEvent.body && diagnosticEvent.body.diagnostics) { + if (diagnosticEvent.body?.diagnostics) { this._onDiagnosticsReceived.fire({ kind: getDignosticsKind(event), resource: this.toResource(diagnosticEvent.body.file), diff --git a/extensions/typescript-language-features/src/utils/async.ts b/extensions/typescript-language-features/src/utils/async.ts index 75ccf258f28..db92754fd2e 100644 --- a/extensions/typescript-language-features/src/utils/async.ts +++ b/extensions/typescript-language-features/src/utils/async.ts @@ -13,7 +13,7 @@ export class Delayer { public defaultDelay: number; private timeout: any; // Timer - private completionPromise: Promise | null; + private completionPromise: Promise | null; private onSuccess: ((value: T | PromiseLike | undefined) => void) | null; private task: ITask | null; @@ -25,7 +25,7 @@ export class Delayer { this.task = null; } - public trigger(task: ITask, delay: number = this.defaultDelay): Promise { + public trigger(task: ITask, delay: number = this.defaultDelay): Promise { this.task = task; if (delay >= 0) { this.cancelTimeout(); @@ -37,7 +37,7 @@ export class Delayer { }).then(() => { this.completionPromise = null; this.onSuccess = null; - const result = this.task && this.task(); + const result = this.task?.(); this.task = null; return result; }); diff --git a/extensions/typescript-language-features/src/utils/codeAction.ts b/extensions/typescript-language-features/src/utils/codeAction.ts index df345e33517..6a7ad144482 100644 --- a/extensions/typescript-language-features/src/utils/codeAction.ts +++ b/extensions/typescript-language-features/src/utils/codeAction.ts @@ -12,7 +12,7 @@ export function getEditForCodeAction( client: ITypeScriptServiceClient, action: Proto.CodeAction ): vscode.WorkspaceEdit | undefined { - return action.changes && action.changes.length + return action.changes?.length ? typeConverters.WorkspaceEdit.fromFileCodeEdits(client, action.changes) : undefined; } @@ -36,7 +36,7 @@ export async function applyCodeActionCommands( commands: ReadonlyArray<{}> | undefined, token: vscode.CancellationToken, ): Promise { - if (commands && commands.length) { + if (commands?.length) { for (const command of commands) { await client.execute('applyCodeActionCommand', { command }, token); } diff --git a/extensions/typescript-language-features/src/utils/configuration.ts b/extensions/typescript-language-features/src/utils/configuration.ts index 2690f743848..efa442a5d0d 100644 --- a/extensions/typescript-language-features/src/utils/configuration.ts +++ b/extensions/typescript-language-features/src/utils/configuration.ts @@ -15,7 +15,7 @@ export enum TsServerLogLevel { export namespace TsServerLogLevel { export function fromString(value: string): TsServerLogLevel { - switch (value && value.toLowerCase()) { + switch (value?.toLowerCase()) { case 'normal': return TsServerLogLevel.Normal; case 'terse': diff --git a/extensions/typescript-language-features/src/utils/telemetry.ts b/extensions/typescript-language-features/src/utils/telemetry.ts index 2ef271de107..da7ee73cad0 100644 --- a/extensions/typescript-language-features/src/utils/telemetry.ts +++ b/extensions/typescript-language-features/src/utils/telemetry.ts @@ -55,7 +55,7 @@ export class VSCodeTelemetryReporter implements TelemetryReporter { @memoize private get reporter(): VsCodeTelemetryReporter | null { - if (this.packageInfo && this.packageInfo.aiKey) { + if (this.packageInfo?.aiKey) { this._reporter = new VsCodeTelemetryReporter( this.packageInfo.name, this.packageInfo.version, diff --git a/extensions/typescript-language-features/src/utils/tsconfig.ts b/extensions/typescript-language-features/src/utils/tsconfig.ts index b92a26916d1..baf1d4fd59b 100644 --- a/extensions/typescript-language-features/src/utils/tsconfig.ts +++ b/extensions/typescript-language-features/src/utils/tsconfig.ts @@ -26,7 +26,7 @@ const defaultProjectConfig = Object.freeze module: 'ESNext' as Proto.ModuleKind, moduleResolution: 'Node' as Proto.ModuleResolutionKind, target: 'ES2020' as Proto.ScriptTarget, - jsx: 'preserve' as Proto.JsxEmit, + jsx: 'react' as Proto.JsxEmit, }); export function inferredProjectCompilerOptions( diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 7d1b1daca43..610dd2590e7 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -18,7 +18,6 @@ "fileSearchProvider", "findTextInFiles", "fsChunks", - "inlineCompletions", "notebookCellExecutionState", "notebookContentProvider", "notebookControllerKind", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts index 270d9ea9aa2..88eb9d8a2bb 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/notebook.test.ts @@ -197,7 +197,7 @@ const apiTestContentProvider: vscode.NotebookContentProvider = { assert.strictEqual(selectionRedo[0].end, 2); }); - test('editor editing event', async function () { + test.skip('editor editing event', async function () { // TODO@rebornix https://github.com/microsoft/vscode/issues/152145 const notebook = await openRandomNotebookDocument(); const editor = await vscode.window.showNotebookDocument(notebook); diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-78769_cpp.json b/extensions/vscode-colorize-tests/test/colorize-results/test-78769_cpp.json index 2d184cc6f8b..e226b96334c 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-78769_cpp.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-78769_cpp.json @@ -61,7 +61,7 @@ }, { "c": "der", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -73,7 +73,7 @@ }, { "c": ",", - "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.parameters.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp punctuation.separator.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -85,7 +85,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -97,7 +97,7 @@ }, { "c": "base", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -109,7 +109,7 @@ }, { "c": ",", - "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.parameters.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp punctuation.separator.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -121,7 +121,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -133,7 +133,7 @@ }, { "c": "func", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -145,7 +145,7 @@ }, { "c": ",", - "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.parameters.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp punctuation.separator.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -157,7 +157,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -169,7 +169,7 @@ }, { "c": "decorators", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -217,19 +217,19 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "namespace", - "t": "source.cpp meta.block.namespace.cpp meta.head.namespace.cpp keyword.other.namespace.definition.cpp storage.type.namespace.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.head.namespace.cpp keyword.other.namespace.definition.cpp storage.type.namespace.definition.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -241,67 +241,67 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.head.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.head.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "{", - "t": "source.cpp meta.block.namespace.cpp meta.head.namespace.cpp punctuation.section.block.begin.bracket.curly.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.head.namespace.cpp punctuation.section.block.begin.bracket.curly.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "struct", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp storage.type.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp storage.type.struct.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -313,67 +313,67 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "der", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp entity.name.type.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp entity.name.type.struct.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.type: #4EC9B0", "hc_light": "entity.name.type: #185E73" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ":", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp punctuation.separator.colon.inheritance.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp punctuation.separator.colon.inheritance.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "public", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp storage.type.modifier.access.public.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp storage.type.modifier.access.public.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -385,115 +385,115 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "base", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp meta.qualified_type.cpp entity.name.type.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp meta.qualified_type.cpp entity.name.type.cpp", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.type: #4EC9B0", "hc_light": "entity.name.type: #185E73" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "{", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp punctuation.section.block.begin.bracket.curly.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.head.struct.cpp punctuation.section.block.begin.bracket.curly.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "void", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -505,163 +505,163 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "f", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA", "hc_light": "entity.name.function: #5E2CBC" } }, { "c": "(", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ")", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ";", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp punctuation.terminator.statement.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "}", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp punctuation.section.block.end.bracket.curly.struct.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp meta.body.struct.cpp punctuation.section.block.end.bracket.curly.struct.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ";", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.block.struct.cpp punctuation.terminator.statement.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "static", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp storage.modifier.static.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp storage.modifier.static.cpp", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -673,19 +673,19 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "void", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.qualified_type.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -697,499 +697,499 @@ }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "func", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp entity.name.function.definition.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA", "hc_light": "entity.name.function: #5E2CBC" } }, { "c": "(", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.begin.bracket.round.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ")", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.parameters.end.bracket.round.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "{", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.head.function.definition.cpp punctuation.section.block.begin.bracket.curly.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " der v", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ";", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "v", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.object.access.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp variable.other.object.access.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "variable: #9CDCFE", "hc_light": "variable: #001080" } }, { "c": ".", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.dot-access.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.separator.dot-access.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "f", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.member.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp entity.name.function.member.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA", "hc_light": "entity.name.function: #5E2CBC" } }, { "c": "(", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.begin.bracket.round.function.member.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ")", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.arguments.end.bracket.round.function.member.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ";", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.terminator.statement.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "}", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp meta.function.definition.cpp meta.body.function.definition.cpp punctuation.section.block.end.bracket.curly.function.definition.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "DOCTEST_REGISTER_FUNCTION", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp entity.name.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA", "hc_light": "entity.name.function: #5E2CBC" } }, { "c": "(", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "DOCTEST_EMPTY", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ",", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.separator.delimiter.comma.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " func", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ",", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.separator.delimiter.comma.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.separator.delimiter.comma.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " decorators", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ")", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "}", - "t": "source.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.block.end.bracket.curly.namespace.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.block.namespace.cpp meta.body.namespace.cpp punctuation.section.block.end.bracket.curly.namespace.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "\\", - "t": "source.cpp constant.character.escape.line-continuation.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp constant.character.escape.line-continuation.cpp", "r": { "dark_plus": "constant.character.escape: #D7BA7D", "light_plus": "constant.character.escape: #EE0000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "constant.character: #569CD6", "hc_light": "constant.character.escape: #EE0000" } }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "inline", - "t": "source.cpp storage.modifier.specifier.functional.pre-parameters.inline.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp storage.modifier.specifier.functional.pre-parameters.inline.cpp", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1201,19 +1201,19 @@ }, { "c": " DOCTEST_NOINLINE ", - "t": "source.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "void", - "t": "source.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp storage.type.primitive.cpp storage.type.built-in.primitive.cpp", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1225,74 +1225,74 @@ }, { "c": " ", - "t": "source.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "der", - "t": "source.cpp entity.name.scope-resolution.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.scope-resolution.cpp", "r": { "dark_plus": "entity.name.scope-resolution: #4EC9B0", "light_plus": "entity.name.scope-resolution: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.scope-resolution: #4EC9B0", "hc_light": "entity.name.scope-resolution: #185E73" } }, { "c": "::", - "t": "source.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": "f", - "t": "source.cpp entity.name.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp entity.name.function.call.cpp", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", "hc_black": "entity.name.function: #DCDCAA", "hc_light": "entity.name.function: #5E2CBC" } }, { "c": "(", - "t": "source.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.section.arguments.begin.bracket.round.function.call.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } }, { "c": ")", - "t": "source.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp punctuation.section.arguments.end.bracket.round.function.call.cpp", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "hc_light": "default: #292929" + "dark_plus": "meta.preprocessor: #569CD6", + "light_plus": "meta.preprocessor: #0000FF", + "dark_vs": "meta.preprocessor: #569CD6", + "light_vs": "meta.preprocessor: #0000FF", + "hc_black": "meta.preprocessor: #569CD6", + "hc_light": "meta.preprocessor: #0F4A85" } } ] \ No newline at end of file diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_cpp.json b/extensions/vscode-colorize-tests/test/colorize-results/test_cpp.json index f413a589570..c2bcf1d8c5f 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_cpp.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_cpp.json @@ -1345,7 +1345,7 @@ }, { "c": "a", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1357,7 +1357,7 @@ }, { "c": ",", - "t": "source.cpp meta.preprocessor.macro.cpp punctuation.separator.parameters.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp punctuation.separator.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -1369,7 +1369,7 @@ }, { "c": " ", - "t": "source.cpp meta.preprocessor.macro.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp", "r": { "dark_plus": "meta.preprocessor: #569CD6", "light_plus": "meta.preprocessor: #0000FF", @@ -1381,7 +1381,7 @@ }, { "c": "b", - "t": "source.cpp meta.preprocessor.macro.cpp variable.parameter.preprocessor.cpp", + "t": "source.cpp meta.preprocessor.macro.cpp meta.function.preprocessor.parameters.cpp variable.parameter.preprocessor.cpp", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", diff --git a/package.json b/package.json index 96316bdfb26..68b84062f86 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.69.0", - "distro": "c58f78790c58ab690d170012ad340f84cff8b438", + "distro": "67db0559282b6e725248321639550845abfbe291", "author": { "name": "Microsoft Corporation" }, @@ -89,9 +89,9 @@ "vscode-textmate": "7.0.1", "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm-addon-serialize": "0.7.0-beta.13", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38", + "xterm-addon-webgl": "0.12.0-beta.41", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" @@ -138,7 +138,7 @@ "cssnano": "^4.1.11", "debounce": "^1.0.0", "deemon": "^1.4.0", - "electron": "18.3.2", + "electron": "18.3.4", "eslint": "8.7.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^39.3.2", diff --git a/remote/package.json b/remote/package.json index c42700d6e3c..e6fdc7a871b 100644 --- a/remote/package.json +++ b/remote/package.json @@ -28,9 +28,9 @@ "vscode-textmate": "7.0.1", "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", - "xterm-addon-serialize": "0.7.0-beta.12", + "xterm-addon-serialize": "0.7.0-beta.13", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38", + "xterm-addon-webgl": "0.12.0-beta.41", "xterm-headless": "4.19.0-beta.60", "yauzl": "^2.9.2", "yazl": "^2.4.3" diff --git a/remote/web/package.json b/remote/web/package.json index 85329a33d0b..fcc462b5733 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -15,6 +15,6 @@ "xterm": "4.19.0-beta.60", "xterm-addon-search": "0.9.0-beta.39", "xterm-addon-unicode11": "0.4.0-beta.3", - "xterm-addon-webgl": "0.12.0-beta.38" + "xterm-addon-webgl": "0.12.0-beta.41" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 7bd6d53622e..643265cb38c 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -141,10 +141,10 @@ xterm-addon-unicode11@0.4.0-beta.3: resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/remote/yarn.lock b/remote/yarn.lock index c56db3d8e0c..6ee84100308 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -937,20 +937,20 @@ xterm-addon-search@0.9.0-beta.39: resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.39.tgz#e8376e1485ee7d763c07d1a8f1354114f65b3e3e" integrity sha512-h45wkecgfqXXoAUqgNytAfSd6g0xNT6rZy/enVaEU0aes7QoL9pxHUKkCry8PP6hs03Slk0VxQ4AGsbSZGvK/w== -xterm-addon-serialize@0.7.0-beta.12: - version "0.7.0-beta.12" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.12.tgz#4f845d8b1a9f9b7ae3f910455ce8c58b041babc7" - integrity sha512-b4Ug0B/RSJMux+KAcp+PXVqubVyXjN1yCQw1FOkgVYTpmd9AH/X+EcxKml5Lz8DsKmsXqfD9AlV3WpEeT+OtMw== +xterm-addon-serialize@0.7.0-beta.13: + version "0.7.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.13.tgz#5c859c8657cab7f28405aab1a0715daf54bc7714" + integrity sha512-TYFlm/gds0pOmpzXw7ZWx8Cy48lMaOZZqZgfm5pWU37HPvzfKxXSVdYL1biWpRCH2zCH+3cWmOma8W1pBRr+Eg== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60" diff --git a/src/buildfile.js b/src/buildfile.js index 6b49aa30083..2ea007cc376 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -32,12 +32,17 @@ exports.base = [ { name: 'vs/editor/common/services/editorSimpleWorker', include: ['vs/base/common/worker/simpleWorker'], - prepend: ['vs/loader.js', 'vs/nls.js'], - append: ['vs/base/worker/workerMain'], + exclude: ['vs/nls'], + prepend: [ + { path: 'vs/loader.js' }, + { path: 'vs/nls.js', amdModuleId: 'vs/nls' } + ], + append: [{ path: 'vs/base/worker/workerMain' }], dest: 'vs/base/worker/workerMain.js' }, { name: 'vs/base/common/worker/simpleWorker', + exclude: ['vs/nls'], } ]; diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 7d18928f6b4..3d942854f5d 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -18,9 +18,8 @@ "include": [ "typings/require.d.ts", "typings/thenable.d.ts", - "vs/css.d.ts", + "vs/loader.d.ts", "vs/monaco.d.ts", - "vs/nls.d.ts", "vs/editor/*", "vs/base/common/*", "vs/base/browser/*", @@ -31,6 +30,7 @@ "node_modules/*", "vs/platform/files/browser/htmlFileSystemProvider.ts", "vs/platform/files/browser/webFileSystemAccess.ts", + "vs/platform/telemetry/browser/*", "vs/platform/assignment/*" ] } diff --git a/src/tsconfig.vscode-dts.json b/src/tsconfig.vscode-dts.json index 4ae9bc7643a..b8607658396 100644 --- a/src/tsconfig.vscode-dts.json +++ b/src/tsconfig.vscode-dts.json @@ -14,6 +14,7 @@ "types": [], "lib": [ "es5", + "ES2015.Iterable" ], }, "include": [ diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index d41cafcdb77..8191f523595 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -71,9 +71,7 @@ class DevicePixelRatioMonitor extends Disposable { } private _handleChange(fireEvent: boolean): void { - if (this._mediaQueryList) { - this._mediaQueryList.removeEventListener('change', this._listener); - } + this._mediaQueryList?.removeEventListener('change', this._listener); this._mediaQueryList = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`); this._mediaQueryList.addEventListener('change', this._listener); diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 93b87e6a563..c57e67f3698 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -86,15 +86,11 @@ class WebWorker implements IWorker { } public postMessage(message: any, transfer: Transferable[]): void { - if (this.worker) { - this.worker.then(w => w.postMessage(message, transfer)); - } + this.worker?.then(w => w.postMessage(message, transfer)); } public dispose(): void { - if (this.worker) { - this.worker.then(w => w.terminate()); - } + this.worker?.then(w => w.terminate()); this.worker = null; } } diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index d8a7ac484c4..c323e74ddfc 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1685,18 +1685,6 @@ export function getCookieValue(name: string): string | undefined { return match ? match.pop() : undefined; } -export const enum ZIndex { - SASH = 35, - SuggestWidget = 40, - Hover = 50, - DragImage = 1000, - MenubarMenuItemsHolder = 2000, // quick-input-widget - ContextView = 2500, - ModalDialog = 2600, - PaneDropOverlay = 10000 -} - - export interface IDragAndDropObserverCallbacks { readonly onDragEnter: (e: DragEvent) => void; readonly onDragLeave: (e: DragEvent) => void; diff --git a/src/vs/base/browser/indexedDB.ts b/src/vs/base/browser/indexedDB.ts index 5fba9c0ab4c..25651932124 100644 --- a/src/vs/base/browser/indexedDB.ts +++ b/src/vs/base/browser/indexedDB.ts @@ -28,7 +28,7 @@ export class IndexedDB { return new IndexedDB(database, name); } - static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { + private static async openDatabase(name: string, version: number | undefined, stores: string[]): Promise { mark(`code/willOpenDatabase/${name}`); try { return await IndexedDB.doOpenDatabase(name, version, stores); diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index a34f4924ba2..2e809e2ad48 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -265,7 +265,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende || /^command:(\/\/\/)?_workbench\.downloadResource/i.test(href) ) { // drop the link - a.replaceWith(a.textContent ?? ''); + a.replaceWith(...a.childNodes); } else { let resolvedHref = _href(href, false); if (markdown.baseUri) { diff --git a/src/vs/base/browser/ui/actionbar/actionViewItems.ts b/src/vs/base/browser/ui/actionbar/actionViewItems.ts index 61340ea1ba2..a41daab42af 100644 --- a/src/vs/base/browser/ui/actionbar/actionViewItems.ts +++ b/src/vs/base/browser/ui/actionbar/actionViewItems.ts @@ -367,9 +367,7 @@ export class ActionViewItem extends BaseActionViewItem { this.updateEnabled(); } else { - if (this.label) { - this.label.classList.remove('codicon'); - } + this.label?.classList.remove('codicon'); } } @@ -380,18 +378,14 @@ export class ActionViewItem extends BaseActionViewItem { this.label.classList.remove('disabled'); } - if (this.element) { - this.element.classList.remove('disabled'); - } + this.element?.classList.remove('disabled'); } else { if (this.label) { this.label.setAttribute('aria-disabled', 'true'); this.label.classList.add('disabled'); } - if (this.element) { - this.element.classList.add('disabled'); - } + this.element?.classList.add('disabled'); } } diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 94cbe346ed7..20ffef74fa8 100644 Binary files a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf and b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 0643cf674a6..b8f106ef7af 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -427,13 +427,9 @@ export class Dialog extends Disposable { this.element.style.backgroundColor = bgColor?.toString() ?? ''; this.element.style.border = border; - if (this.buttonBar) { - this.buttonBar.buttons.forEach(button => button.style(style)); - } + this.buttonBar?.buttons.forEach(button => button.style(style)); - if (this.checkbox) { - this.checkbox.style(style); - } + this.checkbox?.style(style); if (fgColor && bgColor) { const messageDetailColor = fgColor.transparent(.9); diff --git a/src/vs/base/browser/ui/findinput/findInput.ts b/src/vs/base/browser/ui/findinput/findInput.ts index 4c40cba159f..b9e218ce03b 100644 --- a/src/vs/base/browser/ui/findinput/findInput.ts +++ b/src/vs/base/browser/ui/findinput/findInput.ts @@ -252,9 +252,7 @@ export class FindInput extends Widget { this.domNode.appendChild(this.controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this._register(dom.addDisposableListener(this.inputBox.inputElement, 'compositionstart', (e: CompositionEvent) => { this.imeSessionInProgress = true; diff --git a/src/vs/base/browser/ui/findinput/replaceInput.ts b/src/vs/base/browser/ui/findinput/replaceInput.ts index 46b4014bb3b..a80c5a1faaf 100644 --- a/src/vs/base/browser/ui/findinput/replaceInput.ts +++ b/src/vs/base/browser/ui/findinput/replaceInput.ts @@ -225,9 +225,7 @@ export class ReplaceInput extends Widget { this.domNode.appendChild(controls); - if (parent) { - parent.appendChild(this.domNode); - } + parent?.appendChild(this.domNode); this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e)); this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e)); @@ -361,9 +359,7 @@ export class ReplaceInput extends Widget { } public showMessage(message: InputBoxMessage): void { - if (this.inputBox) { - this.inputBox.showMessage(message); - } + this.inputBox?.showMessage(message); } public clearMessage(): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 0f5d1aaa0ea..a396809facb 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -15,7 +15,6 @@ import { Delayer, disposableTimeout } from 'vs/base/common/async'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { getOrDefault } from 'vs/base/common/objects'; import { IRange, Range } from 'vs/base/common/range'; import { INewScrollDimensions, Scrollable, ScrollbarVisibility, ScrollEvent } from 'vs/base/common/scrollable'; import { ISpliceable } from 'vs/base/common/sequence'; @@ -325,7 +324,7 @@ export class ListView implements ISpliceable, IDisposable { this.domNode.classList.toggle('mouse-support', typeof options.mouseSupport === 'boolean' ? options.mouseSupport : true); - this._horizontalScrolling = getOrDefault(options, o => o.horizontalScrolling, DefaultOptions.horizontalScrolling); + this._horizontalScrolling = options.horizontalScrolling ?? DefaultOptions.horizontalScrolling; this.domNode.classList.toggle('horizontal-scrolling', this._horizontalScrolling); this.additionalScrollHeight = typeof options.additionalScrollHeight === 'undefined' ? 0 : options.additionalScrollHeight; @@ -335,7 +334,7 @@ export class ListView implements ISpliceable, IDisposable { this.rowsContainer = document.createElement('div'); this.rowsContainer.className = 'monaco-list-rows'; - const transformOptimization = getOrDefault(options, o => o.transformOptimization, DefaultOptions.transformOptimization); + const transformOptimization = options.transformOptimization ?? DefaultOptions.transformOptimization; if (transformOptimization) { this.rowsContainer.style.transform = 'translate3d(0px, 0px, 0px)'; } @@ -344,14 +343,14 @@ export class ListView implements ISpliceable, IDisposable { this.scrollable = new Scrollable({ forceIntegerValues: true, - smoothScrollDuration: getOrDefault(options, o => o.smoothScrolling, false) ? 125 : 0, + smoothScrollDuration: (options.smoothScrolling ?? false) ? 125 : 0, scheduleAtNextAnimationFrame: cb => scheduleAtNextAnimationFrame(cb) }); this.scrollableElement = this.disposables.add(new SmoothScrollableElement(this.rowsContainer, { - alwaysConsumeMouseWheel: getOrDefault(options, o => o.alwaysConsumeMouseWheel, DefaultOptions.alwaysConsumeMouseWheel), + alwaysConsumeMouseWheel: options.alwaysConsumeMouseWheel ?? DefaultOptions.alwaysConsumeMouseWheel, horizontal: ScrollbarVisibility.Auto, - vertical: getOrDefault(options, o => o.verticalScrollMode, DefaultOptions.verticalScrollMode), - useShadows: getOrDefault(options, o => o.useShadows, DefaultOptions.useShadows), + vertical: options.verticalScrollMode ?? DefaultOptions.verticalScrollMode, + useShadows: options.useShadows ?? DefaultOptions.useShadows, mouseWheelScrollSensitivity: options.mouseWheelScrollSensitivity, fastScrollSensitivity: options.fastScrollSensitivity }, this.scrollable)); @@ -371,10 +370,10 @@ export class ListView implements ISpliceable, IDisposable { this.disposables.add(addDisposableListener(this.domNode, 'dragleave', e => this.onDragLeave(this.toDragEvent(e)))); this.disposables.add(addDisposableListener(this.domNode, 'dragend', e => this.onDragEnd(e))); - this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight); - this.setRowHeight = getOrDefault(options, o => o.setRowHeight, DefaultOptions.setRowHeight); - this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights); - this.dnd = getOrDefault, IListViewDragAndDrop>(options, o => o.dnd, DefaultOptions.dnd); + this.setRowLineHeight = options.setRowLineHeight ?? DefaultOptions.setRowLineHeight; + this.setRowHeight = options.setRowHeight ?? DefaultOptions.setRowHeight; + this.supportDynamicHeights = options.supportDynamicHeights ?? DefaultOptions.supportDynamicHeights; + this.dnd = options.dnd ?? DefaultOptions.dnd; this.layout(); } @@ -813,9 +812,7 @@ export class ListView implements ISpliceable, IDisposable { throw new Error(`No renderer found for template id ${item.templateId}`); } - if (renderer) { - renderer.renderElement(item.element, index, item.row.templateData, item.size); - } + renderer?.renderElement(item.element, index, item.row.templateData, item.size); const uri = this.dnd.getDragURI(item.element); item.dragStartDisposable.dispose(); @@ -1112,9 +1109,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = true; - if (item.row) { - item.row.domNode.classList.add('drop-target'); - } + item.row?.domNode.classList.add('drop-target'); } this.currentDragFeedbackDisposable = toDisposable(() => { @@ -1122,9 +1117,7 @@ export class ListView implements ISpliceable, IDisposable { const item = this.items[index]!; item.dropTarget = false; - if (item.row) { - item.row.domNode.classList.remove('drop-target'); - } + item.row?.domNode.classList.remove('drop-target'); } }); } diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 8d509cb4da5..d477a7d3f88 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -1390,9 +1390,7 @@ export class List implements ISpliceable, IThemable, IDisposable { updateOptions(optionsUpdate: IListOptionsUpdate = {}): void { this._options = { ...this._options, ...optionsUpdate }; - if (this.typeLabelController) { - this.typeLabelController.updateOptions(this._options); - } + this.typeLabelController?.updateOptions(this._options); if (this._options.multipleSelectionController !== undefined) { if (this._options.multipleSelectionSupport) { diff --git a/src/vs/base/browser/ui/list/rowCache.ts b/src/vs/base/browser/ui/list/rowCache.ts index 319d839e7d7..a5c4b7e07c2 100644 --- a/src/vs/base/browser/ui/list/rowCache.ts +++ b/src/vs/base/browser/ui/list/rowCache.ts @@ -15,9 +15,7 @@ export interface IRow { function removeFromParent(element: HTMLElement): void { try { - if (element.parentElement) { - element.parentElement.removeChild(element); - } + element.parentElement?.removeChild(element); } catch (e) { // this will throw if this happens due to a blur event, nasty business } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 224b0cdcb73..7a22cb35038 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -609,9 +609,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { this.label.innerText = replaceDoubleEscapes(label).trim(); } - if (this.item) { - this.item.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); - } + this.item?.setAttribute('aria-keyshortcuts', (!!matches[1] ? matches[1] : matches[3]).toLocaleLowerCase()); } else { this.label.innerText = label.replace(/&&/g, '&').trim(); } @@ -966,9 +964,7 @@ class SubmenuMenuActionViewItem extends BaseMenuActionViewItem { this.submenuIndicator.style.color = fgColor ? `${fgColor}` : ''; } - if (this.parentData.submenu) { - this.parentData.submenu.style(this.menuStyle); - } + this.parentData.submenu?.style(this.menuStyle); } override dispose(): void { diff --git a/src/vs/base/browser/ui/menu/menubar.css b/src/vs/base/browser/ui/menu/menubar.css index c4a57a11c0f..80ef6a39a57 100644 --- a/src/vs/base/browser/ui/menu/menubar.css +++ b/src/vs/base/browser/ui/menu/menubar.css @@ -10,9 +10,7 @@ flex-shrink: 1; box-sizing: border-box; height: 100%; - padding: 4px 0; overflow: hidden; - flex-wrap: wrap; } .fullscreen .menubar:not(.compact) { @@ -21,15 +19,22 @@ } .menubar > .menubar-menu-button { + display: flex; align-items: center; box-sizing: border-box; - padding: 0px 8px; - border-radius: 5px; cursor: default; -webkit-app-region: no-drag; zoom: 1; white-space: nowrap; - outline: 0; + outline: 0 !important; + align-self: center; +} + +.monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title { + outline-width: 1px; + outline-style: solid; + outline-offset: -1px; + outline-color: var(--vscode-focusBorder); } .menubar.compact { @@ -43,6 +48,11 @@ padding: 0px; } +.menubar-menu-title { + padding: 0px 8px; + border-radius: 5px; +} + .menubar .menubar-menu-items-holder { position: fixed; left: 0px; @@ -64,8 +74,13 @@ } .menubar .toolbar-toggle-more { - width: 20px; - height: 100%; + width: 22px; + height: 22px; + padding: 0 8px; + display: flex; + align-items: center; + justify-content: center; + vertical-align: sub; } .menubar.compact .toolbar-toggle-more { @@ -79,19 +94,14 @@ justify-content: center; } -.menubar .toolbar-toggle-more { - padding: 0; - vertical-align: sub; -} - .menubar.compact .toolbar-toggle-more::before { content: "\eb94" !important; } /* Match behavior of outline for activity bar icons */ -.menubar.compact > .menubar-menu-button.open, -.menubar.compact > .menubar-menu-button:focus, -.menubar.compact > .menubar-menu-button:hover { +.menubar.compact > .menubar-menu-button.open .menubar-menu-title, +.menubar.compact > .menubar-menu-button:focus .menubar-menu-title, +.menubar.compact > .menubar-menu-button:hover .menubar-menu-title{ outline-width: 1px !important; outline-offset: -8px !important; } diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index 521a65e212e..907684665ed 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -956,9 +956,7 @@ export class MenuBar extends Disposable { } if (this.focusedMenu.holder) { - if (this.focusedMenu.holder.parentElement) { - this.focusedMenu.holder.parentElement.classList.remove('open'); - } + this.focusedMenu.holder.parentElement?.classList.remove('open'); this.focusedMenu.holder.remove(); } diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts index 3c4a40b824f..791c7332ffb 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarVisibilityController.ts @@ -103,9 +103,7 @@ export class ScrollbarVisibilityController extends Disposable { // The CSS animation doesn't play otherwise this._revealTimer.setIfNotSet(() => { - if (this._domNode) { - this._domNode.setClassName(this._visibleClassName); - } + this._domNode?.setClassName(this._visibleClassName); }, 0); } @@ -115,8 +113,6 @@ export class ScrollbarVisibilityController extends Disposable { return; } this._isVisible = false; - if (this._domNode) { - this._domNode.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); - } + this._domNode?.setClassName(this._invisibleClassName + (withFadeAway ? ' fade' : '')); } } diff --git a/src/vs/base/browser/ui/selectBox/selectBox.ts b/src/vs/base/browser/ui/selectBox/selectBox.ts index 55e23987440..fbf46c262fe 100644 --- a/src/vs/base/browser/ui/selectBox/selectBox.ts +++ b/src/vs/base/browser/ui/selectBox/selectBox.ts @@ -40,6 +40,7 @@ export interface ISelectBoxDelegate extends IDisposable { export interface ISelectBoxOptions { useCustomDrawn?: boolean; ariaLabel?: string; + ariaDescription?: string; minBottomMargin?: number; optionsAsChildren?: boolean; } diff --git a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts index 903700ac0a8..3f571712fdd 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxCustom.ts @@ -126,6 +126,10 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } + if (typeof this.selectBoxOptions.ariaDescription === 'string') { + this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); + } + this._onDidSelect = new Emitter(); this._register(this._onDidSelect); @@ -286,9 +290,7 @@ export class SelectBoxList extends Disposable implements ISelectBoxDelegate, ILi // Mirror options in drop-down // Populate select list for non-native select mode - if (this.selectList) { - this.selectList.splice(0, this.selectList.length, this.options); - } + this.selectList?.splice(0, this.selectList.length, this.options); } public select(index: number): void { diff --git a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts index 7248cef367e..7a43184de2b 100644 --- a/src/vs/base/browser/ui/selectBox/selectBoxNative.ts +++ b/src/vs/base/browser/ui/selectBox/selectBoxNative.ts @@ -35,6 +35,10 @@ export class SelectBoxNative extends Disposable implements ISelectBoxDelegate { this.selectElement.setAttribute('aria-label', this.selectBoxOptions.ariaLabel); } + if (typeof this.selectBoxOptions.ariaDescription === 'string') { + this.selectElement.setAttribute('aria-description', this.selectBoxOptions.ariaDescription); + } + this._onDidSelect = this._register(new Emitter()); this.styles = styles; diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index fc646081f2a..659eb9bd8fd 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -143,9 +143,7 @@ export abstract class Pane extends Disposable implements IView { return false; } - if (this.element) { - this.element.classList.toggle('expanded', expanded); - } + this.element?.classList.toggle('expanded', expanded); this._expanded = !!expanded; this.updateHeader(); diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 63941c5452e..e3c3a56066e 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -130,9 +130,7 @@ export class ToolBar extends Disposable { set context(context: unknown) { this.actionBar.context = context; - if (this.toggleMenuActionViewItem) { - this.toggleMenuActionViewItem.setActionContext(context); - } + this.toggleMenuActionViewItem?.setActionContext(context); for (const actionViewItem of this.submenuActionViewItems) { actionViewItem.setActionContext(context); } diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 7f96f25f3f2..c03f5c5e1af 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1453,9 +1453,7 @@ export abstract class AbstractTree implements IDisposable enableKeyboardNavigation: this._options.simpleKeyboardNavigation, }); - if (this.typeFilterController) { - this.typeFilterController.updateOptions(this._options); - } + this.typeFilterController?.updateOptions(this._options); this._onDidUpdateOptions.fire(this._options); diff --git a/src/vs/base/browser/usb.ts b/src/vs/base/browser/usb.ts new file mode 100644 index 00000000000..63b5d40f3e0 --- /dev/null +++ b/src/vs/base/browser/usb.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// https://wicg.github.io/webusb/ + +export interface UsbDeviceData { + readonly deviceClass: number; + readonly deviceProtocol: number; + readonly deviceSubclass: number; + readonly deviceVersionMajor: number; + readonly deviceVersionMinor: number; + readonly deviceVersionSubminor: number; + readonly manufacturerName?: string; + readonly productId: number; + readonly productName?: string; + readonly serialNumber?: string; + readonly usbVersionMajor: number; + readonly usbVersionMinor: number; + readonly usbVersionSubminor: number; + readonly vendorId: number; +} + +export async function requestUsb(options?: { filters?: unknown[] }): Promise { + const usb = (navigator as any).usb; + if (!usb) { + return undefined; + } + + const device = await usb.requestDevice({ filters: options?.filters ?? [] }); + if (!device) { + return undefined; + } + + return { + deviceClass: device.deviceClass, + deviceProtocol: device.deviceProtocol, + deviceSubclass: device.deviceSubclass, + deviceVersionMajor: device.deviceVersionMajor, + deviceVersionMinor: device.deviceVersionMinor, + deviceVersionSubminor: device.deviceVersionSubminor, + manufacturerName: device.manufacturerName, + productId: device.productId, + productName: device.productName, + serialNumber: device.serialNumber, + usbVersionMajor: device.usbVersionMajor, + usbVersionMinor: device.usbVersionMinor, + usbVersionSubminor: device.usbVersionSubminor, + vendorId: device.vendorId, + }; +} diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index cc567305a59..b567c8fe977 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -677,6 +677,18 @@ export function compareBy(selector: (item: TItem) => TCompare return (a, b) => comparator(selector(a), selector(b)); } +export function tieBreakComparators(...comparators: Comparator[]): Comparator { + return (item1, item2) => { + for (const comparator of comparators) { + const result = comparator(item1, item2); + if (!CompareResult.isNeitherLessOrGreaterThan(result)) { + return result; + } + } + return CompareResult.neitherLessOrGreaterThan; + }; +} + /** * The natural order on numbers. */ diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 7021acc29ef..b452a203600 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -428,7 +428,8 @@ export class Codicon implements CSSIcon { public static readonly debugBreakpointFunction = new Codicon('debug-breakpoint-function', { fontCharacter: '\\eb88' }); public static readonly debugBreakpointFunctionDisabled = new Codicon('debug-breakpoint-function-disabled', { fontCharacter: '\\eb88' }); public static readonly debugStackframeActive = new Codicon('debug-stackframe-active', { fontCharacter: '\\eb89' }); - public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', { fontCharacter: '\\eb8a' }); + public static readonly circleSmallFilled = new Codicon('circle-small-filled', { fontCharacter: '\\eb8a' }); + public static readonly debugStackframeDot = new Codicon('debug-stackframe-dot', Codicon.circleSmallFilled.definition); public static readonly debugStackframe = new Codicon('debug-stackframe', { fontCharacter: '\\eb8b' }); public static readonly debugStackframeFocused = new Codicon('debug-stackframe-focused', { fontCharacter: '\\eb8b' }); public static readonly debugBreakpointUnsupported = new Codicon('debug-breakpoint-unsupported', { fontCharacter: '\\eb8c' }); @@ -553,6 +554,10 @@ export class Codicon implements CSSIcon { public static readonly arrowCircleRight = new Codicon('arrow-circle-right', { fontCharacter: '\\ebfe' }); public static readonly arrowCircleUp = new Codicon('arrow-circle-up', { fontCharacter: '\\ebff' }); public static readonly heartFilled = new Codicon('heart-filled', { fontCharacter: '\\ec04' }); + public static readonly map = new Codicon('map', { fontCharacter: '\\ec05' }); + public static readonly mapFilled = new Codicon('map-filled', { fontCharacter: '\\ec06' }); + public static readonly circleSmall = new Codicon('circle-small', { fontCharacter: '\\ec07' }); + public static readonly bellSlash = new Codicon('bell-slash', { fontCharacter: '\\ec08' }); // derived icons, that could become separate icons diff --git a/src/vs/base/common/collections.ts b/src/vs/base/common/collections.ts index b3435048211..1f16cd438ea 100644 --- a/src/vs/base/common/collections.ts +++ b/src/vs/base/common/collections.ts @@ -9,42 +9,22 @@ */ export type IStringDictionary = Record; - /** * An interface for a JavaScript object that * acts a dictionary. The keys are numbers. */ export type INumberDictionary = Record; -const hasOwnProperty = Object.prototype.hasOwnProperty; - /** - * Returns an array which contains all values that reside - * in the given dictionary. + * Iterates over each entry in the provided dictionary. The iterator will stop when the callback returns `false`. + * + * @deprecated Use `Object.entries(x)` with a `for...of` loop. */ -export function values(from: IStringDictionary | INumberDictionary): T[] { - const result: T[] = []; - for (const key in from) { - if (hasOwnProperty.call(from, key)) { - result.push((from as any)[key]); - } - } - return result; -} - -/** - * Iterates over each entry in the provided dictionary. The iterator allows - * to remove elements and will stop when the callback returns {{false}}. - */ -export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T }, remove: () => void) => any): void { - for (const key in from) { - if (hasOwnProperty.call(from, key)) { - const result = callback({ key: key, value: (from as any)[key] }, function () { - delete (from as any)[key]; - }); - if (result === false) { - return; - } +export function forEach(from: IStringDictionary | INumberDictionary, callback: (entry: { key: any; value: T }) => any): void { + for (const [key, value] of Object.entries(from)) { + const result = callback({ key, value }); + if (result === false) { + return; } } } @@ -66,16 +46,6 @@ export function groupBy(data: V[], groupF return result; } -export function fromMap(original: Map): IStringDictionary { - const result: IStringDictionary = Object.create(null); - if (original) { - original.forEach((value, key) => { - result[key] = value; - }); - } - return result; -} - export function diffSets(before: Set, after: Set): { removed: T[]; added: T[] } { const removed: T[] = []; const added: T[] = []; diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index bd8ef3bbedb..647b5f1beed 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -160,9 +160,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -223,9 +221,7 @@ export namespace Event { const emitter = new Emitter(options); - if (disposable) { - disposable.add(emitter); - } + disposable?.add(emitter); return emitter.event; } @@ -276,9 +272,7 @@ export namespace Event { }); const flush = () => { - if (buffer) { - buffer.forEach(e => emitter.fire(e)); - } + buffer?.forEach(e => emitter.fire(e)); buffer = null; }; diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 62e4aeac226..7c7c4483bd9 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -186,11 +186,6 @@ export function safeStringify(obj: any): string { }); } -export function getOrDefault(obj: T, fn: (obj: T) => R | undefined, defaultValue: R): R { - const result = fn(obj); - return typeof result === 'undefined' ? defaultValue : result; -} - type obj = { [key: string]: any }; /** * Returns an object that has keys for each value that is different in the base object. Keys diff --git a/src/vs/base/common/scrollable.ts b/src/vs/base/common/scrollable.ts index 7da28a8555d..89c832edcd5 100644 --- a/src/vs/base/common/scrollable.ts +++ b/src/vs/base/common/scrollable.ts @@ -268,9 +268,7 @@ export class Scrollable extends Disposable { this._setState(newState, Boolean(this._smoothScrolling)); // Validate outstanding animated scroll position target - if (this._smoothScrolling) { - this._smoothScrolling.acceptScrollDimensions(this._state); - } + this._smoothScrolling?.acceptScrollDimensions(this._state); } /** diff --git a/src/vs/base/parts/ipc/common/ipc.ts b/src/vs/base/parts/ipc/common/ipc.ts index 42033ee91aa..93f944934f3 100644 --- a/src/vs/base/parts/ipc/common/ipc.ts +++ b/src/vs/base/parts/ipc/common/ipc.ts @@ -640,18 +640,14 @@ export class ChannelClient implements IChannelClient, IDisposable { case RequestType.Promise: case RequestType.EventListen: { const msgLength = this.send([request.type, request.id, request.channelName, request.name], request.arg); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, `${requestTypeToStr(request.type)}: ${request.channelName}.${request.name}`, request.arg); return; } case RequestType.PromiseCancel: case RequestType.EventDispose: { const msgLength = this.send([request.type, request.id]); - if (this.logger) { - this.logger.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); - } + this.logger?.logOutgoing(msgLength, request.id, RequestInitiator.LocalSide, requestTypeToStr(request.type)); return; } } @@ -682,18 +678,14 @@ export class ChannelClient implements IChannelClient, IDisposable { switch (type) { case ResponseType.Initialize: - if (this.logger) { - this.logger.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); - } + this.logger?.logIncoming(message.byteLength, 0, RequestInitiator.LocalSide, responseTypeToStr(type)); return this.onResponse({ type: header[0] }); case ResponseType.PromiseSuccess: case ResponseType.PromiseError: case ResponseType.EventFire: case ResponseType.PromiseErrorObj: - if (this.logger) { - this.logger.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); - } + this.logger?.logIncoming(message.byteLength, header[1], RequestInitiator.LocalSide, responseTypeToStr(type), body); return this.onResponse({ type: header[0], id: header[1], data: body }); } } diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index a6bfeaefbd9..8e42b693a93 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -357,13 +357,9 @@ export class InMemoryStorageDatabase implements IStorageDatabase { } async updateItems(request: IUpdateRequest): Promise { - if (request.insert) { - request.insert.forEach((value, key) => this.items.set(key, value)); - } + request.insert?.forEach((value, key) => this.items.set(key, value)); - if (request.delete) { - request.delete.forEach(key => this.items.delete(key)); - } + request.delete?.forEach(key => this.items.delete(key)); } async close(): Promise { } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 789481987b9..f4cf5ac78e1 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -301,9 +301,9 @@ export class SQLiteStorageDatabase implements IStorageDatabase { return new Promise((resolve, reject) => { import('@vscode/sqlite3').then(sqlite3 => { const connection: IDatabaseConnection = { - db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, error => { + db: new (this.logger.isTracing ? sqlite3.verbose().Database : sqlite3.Database)(path, (error: (Error & { code?: string }) | null) => { if (error) { - return connection.db ? connection.db.close(() => reject(error)) : reject(error); + return (connection.db && error.code !== 'SQLITE_CANTOPEN' /* https://github.com/TryGhost/node-sqlite3/issues/1617 */) ? connection.db.close(() => reject(error)) : reject(error); } // The following exec() statement serves two purposes: diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index c7652a95df9..f53fdb2c387 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -773,4 +773,18 @@ flakySuite('SQLite Storage Library', function () { await storage.close(); }); + + test('invalid path does not hang', async () => { + const storage = new SQLiteStorageDatabase(join(testdir, 'nonexist', 'storage.db')); + + let error; + try { + await storage.getItems(); + await storage.close(); + } catch (e) { + error = e; + } + + ok(error); + }); }); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 7503a8cd510..3852b15797e 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -173,6 +173,14 @@ suite('MarkdownRenderer', () => { `); }); + + test('render icon in without href (#152170)', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true, supportHtml: true }); + mds.appendMarkdown(`$(sync)`); + + const result: HTMLElement = renderMarkdown(mds).element; + assert.strictEqual(result.innerHTML, `

`); + }); }); suite('ThemeIcons Support Off', () => { diff --git a/src/vs/base/test/common/collections.test.ts b/src/vs/base/test/common/collections.test.ts index 2cae56c40ef..138d7486390 100644 --- a/src/vs/base/test/common/collections.test.ts +++ b/src/vs/base/test/common/collections.test.ts @@ -24,9 +24,6 @@ suite('Collections', () => { collections.forEach(dict, () => false); - collections.forEach(dict, (x, remove) => remove()); - assert.strictEqual(dict['toString'], undefined); - // don't iterate over properties that are not on the object itself const test = Object.create({ 'derived': true }); collections.forEach(test, () => assert(false)); diff --git a/src/vs/base/test/node/css.build.test.ts b/src/vs/base/test/node/css.build.test.ts new file mode 100644 index 00000000000..9e7323b7f7a --- /dev/null +++ b/src/vs/base/test/node/css.build.test.ts @@ -0,0 +1,313 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { CSSPluginUtilities, rewriteUrls } from 'vs/css.build'; + +suite('CSSPlugin', () => { + + test('Utilities.pathOf', () => { + assert.strictEqual(CSSPluginUtilities.pathOf(''), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('/a'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a/b/c.css'), 'a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('a'), ''); + assert.strictEqual(CSSPluginUtilities.pathOf('a.com/a.css'), 'a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a.css'), 'http://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a.css'), 'https://a.com/'); + assert.strictEqual(CSSPluginUtilities.pathOf('http://a.com/a/b/c.css'), 'http://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('https://a.com/a/b/c.css'), 'https://a.com/a/b/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a.css'), '/'); + assert.strictEqual(CSSPluginUtilities.pathOf('/a/b/c.css'), '/a/b/'); + }); + + test('Utilities.joinPaths', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.joinPaths(a, b), expected, '<' + a + '> + <' + b + '> = <' + expected + '>'); + } + mytest('', 'a.css', 'a.css'); + mytest('', './a.css', 'a.css'); + mytest('', '././././a.css', 'a.css'); + mytest('', './../a.css', '../a.css'); + mytest('', '../../a.css', '../../a.css'); + mytest('', '../../a/b/c.css', '../../a/b/c.css'); + mytest('/', 'a.css', '/a.css'); + mytest('/', './a.css', '/a.css'); + mytest('/', '././././a.css', '/a.css'); + mytest('/', './../a.css', '/a.css'); + mytest('/', '../../a.css', '/a.css'); + mytest('/', '../../a/b/c.css', '/a/b/c.css'); + mytest('x/y/z/', 'a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './a.css', 'x/y/z/a.css'); + mytest('x/y/z/', '././././a.css', 'x/y/z/a.css'); + mytest('x/y/z/', './../a.css', 'x/y/a.css'); + mytest('x/y/z/', '../../a.css', 'x/a.css'); + mytest('x/y/z/', '../../a/b/c.css', 'x/a/b/c.css'); + + mytest('//a.com/', 'a.css', '//a.com/a.css'); + mytest('//a.com/', './a.css', '//a.com/a.css'); + mytest('//a.com/', '././././a.css', '//a.com/a.css'); + mytest('//a.com/', './../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a.css', '//a.com/a.css'); + mytest('//a.com/', '../../a/b/c.css', '//a.com/a/b/c.css'); + mytest('//a.com/x/y/z/', 'a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', '././././a.css', '//a.com/x/y/z/a.css'); + mytest('//a.com/x/y/z/', './../a.css', '//a.com/x/y/a.css'); + mytest('//a.com/x/y/z/', '../../a.css', '//a.com/x/a.css'); + mytest('//a.com/x/y/z/', '../../a/b/c.css', '//a.com/x/a/b/c.css'); + + mytest('http://a.com/', 'a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '././././a.css', 'http://a.com/a.css'); + mytest('http://a.com/', './../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a.css', 'http://a.com/a.css'); + mytest('http://a.com/', '../../a/b/c.css', 'http://a.com/a/b/c.css'); + mytest('http://a.com/x/y/z/', 'a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', '././././a.css', 'http://a.com/x/y/z/a.css'); + mytest('http://a.com/x/y/z/', './../a.css', 'http://a.com/x/y/a.css'); + mytest('http://a.com/x/y/z/', '../../a.css', 'http://a.com/x/a.css'); + mytest('http://a.com/x/y/z/', '../../a/b/c.css', 'http://a.com/x/a/b/c.css'); + + mytest('https://a.com/', 'a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '././././a.css', 'https://a.com/a.css'); + mytest('https://a.com/', './../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a.css', 'https://a.com/a.css'); + mytest('https://a.com/', '../../a/b/c.css', 'https://a.com/a/b/c.css'); + mytest('https://a.com/x/y/z/', 'a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', '././././a.css', 'https://a.com/x/y/z/a.css'); + mytest('https://a.com/x/y/z/', './../a.css', 'https://a.com/x/y/a.css'); + mytest('https://a.com/x/y/z/', '../../a.css', 'https://a.com/x/a.css'); + mytest('https://a.com/x/y/z/', '../../a/b/c.css', 'https://a.com/x/a/b/c.css'); + }); + + test('Utilities.commonPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonPrefix(a, b), expected, 'prefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonPrefix(b, a), expected, 'prefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaa'); + }); + + test('Utilities.commonFolderPrefix', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(a, b), expected, 'folderPrefix(<' + a + '>, <' + b + '>) = <' + expected + '>'); + assert.strictEqual(CSSPluginUtilities.commonFolderPrefix(b, a), expected, 'folderPrefix(<' + b + '>, <' + a + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', ''); + mytest('aaaa', 'aaaa', ''); + mytest('aaaaxyz', 'aaaa', ''); + mytest('aaaaxyz', 'aaaatuv', ''); + mytest('/', '/', '/'); + mytest('x/', '', ''); + mytest('x/', 'x/', 'x/'); + mytest('aaaa/', 'aaaa/', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/a', 'aaaa/'); + mytest('aaaa/axyz', 'aaaa/atuv', 'aaaa/'); + }); + + test('Utilities.relativePath', () => { + function mytest(a: string, b: string, expected: string) { + assert.strictEqual(CSSPluginUtilities.relativePath(a, b), expected, 'relativePath(<' + a + '>, <' + b + '>) = <' + expected + '>'); + } + mytest('', '', ''); + mytest('x', '', ''); + mytest('x', 'x', 'x'); + mytest('aaaa', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaa', 'aaaa'); + mytest('aaaaxyz', 'aaaatuv', 'aaaatuv'); + + mytest('x/y/aaaaxyz', 'x/aaaatuv', '../aaaatuv'); + mytest('x/y/aaaaxyz', 'x/y/aaaatuv', 'aaaatuv'); + mytest('z/t/aaaaxyz', 'x/y/aaaatuv', '../../x/y/aaaatuv'); + mytest('aaaaxyz', 'x/y/aaaatuv', 'x/y/aaaatuv'); + + mytest('a', '/a', '/a'); + mytest('/', '/a', '/a'); + mytest('/a/b/c', '/a/b/c', '/a/b/c'); + mytest('/a/b', '/a/b/c/d', '/a/b/c/d'); + + mytest('a', 'http://a', 'http://a'); + mytest('/', 'http://a', 'http://a'); + mytest('/a/b/c', 'http://a/b/c', 'http://a/b/c'); + mytest('/a/b', 'http://a/b/c/d', 'http://a/b/c/d'); + + mytest('a', 'https://a', 'https://a'); + mytest('/', 'https://a', 'https://a'); + mytest('/a/b/c', 'https://a/b/c', 'https://a/b/c'); + mytest('/a/b', 'https://a/b/c/d', 'https://a/b/c/d'); + + mytest('x/', '', '../'); + mytest('x/', '', '../'); + mytest('x/', 'x/', ''); + mytest('x/a', 'x/a', 'a'); + }); + + test('Utilities.rewriteUrls', () => { + function mytest(originalFile: string, newFile: string, url: string, expected: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\'' + url + '\'); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(\"' + url + '\"); }'), 'sel { background:url(' + expected + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + url + '); }'), 'sel { background:url(' + expected + '); }'); + } + + // img/img.png + mytest('a.css', 'b.css', 'img/img.png', 'img/img.png'); + mytest('a.css', 't/b.css', 'img/img.png', '../img/img.png'); + mytest('a.css', 'x/y/b.css', 'img/img.png', '../../img/img.png'); + mytest('x/a.css', 'b.css', 'img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 'b.css', 'img/img.png', 'x/y/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'img/img.png', '../../x/y/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'img/img.png', '../y/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'img/img.png', 'img/img.png'); + mytest('/a.css', 'b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'img/img.png', '/x/y/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', 'img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', 'img/img.png', '/x/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'img/img.png', '/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', 'img/img.png', 'http://www.example.com/x/y/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'img/img.png', 'https://www.example.com/x/y/img/img.png'); + + // ../img/img.png + mytest('a.css', 'b.css', '../img/img.png', '../img/img.png'); + mytest('a.css', 't/b.css', '../img/img.png', '../../img/img.png'); + mytest('a.css', 'x/y/b.css', '../img/img.png', '../../../img/img.png'); + mytest('x/a.css', 'b.css', '../img/img.png', 'img/img.png'); + mytest('x/y/a.css', 'b.css', '../img/img.png', 'x/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '../img/img.png', '../../x/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '../img/img.png', '../img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '../img/img.png', '../img/img.png'); + mytest('/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '../img/img.png', '/x/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '../img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '../img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example2.com/b.css', '../img/img.png', 'http://www.example.com/x/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '../img/img.png', 'https://www.example.com/x/img/img.png'); + + // /img/img.png + mytest('a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 't/b.css', '/img/img.png', '/img/img.png'); + mytest('a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', '/img/img.png', '/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/b.css', '/img/img.png', '/img/img.png'); + mytest('/x/a.css', '/x/b.css', '/img/img.png', '/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', '/img/img.png', 'http://www.example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', '/img/img.png', 'https://www.example.com/img/img.png'); + + // http://example.com/img/img.png + mytest('a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 't/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 't/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/u/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/y/a.css', 'x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('/x/a.css', '/x/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + mytest('https://www.example.com/x/y/a.css', 'b.css', 'http://example.com/img/img.png', 'http://example.com/img/img.png'); + + + }); + + test('Utilities.rewriteUrls - quotes and spaces', () => { + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\t\'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \'../img/img.png\'); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\'\t); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url(\'../img/img.png\' ); }'), 'sel { background:url(../../x/img/img.png); }'); + assert.strictEqual(rewriteUrls('x/y/a.css', 't/u/b.css', 'sel { background:url( \t \'../img/img.png\' \t); }'), 'sel { background:url(../../x/img/img.png); }'); + }); + + test('Bug 9601 - css should ignore data urls', () => { + const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAACHmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAiPgogICA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iPgogICAgICAgICA8ZGM6c3ViamVjdD4KICAgICAgICAgICAgPHJkZjpCYWcvPgogICAgICAgICA8L2RjOnN1YmplY3Q+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8eG1wOkNyZWF0b3JUb29sPkFkb2JlIEltYWdlUmVhZHk8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+ClC8oVQAAAGnSURBVDiNrZMxTxNxGMZ///9dZWns9a4dTHSABFiuCU5dGt2d9BsQls6GD2LCd2AiQQfixKIJE0ObdKIUSvDa5uLZihP0Sh+HOw3ipOUZ3zzvL2+e932NJBaRe7/Q8Uw5eMRrzXllDU8A5mJkLB+/TflQ+67JXb+5O0FUNS9deLckns/tn2A7hxtDawZvn37Vp78AX8rmxZLDewf89HGJ+fgKCrkrBeuXKPy44hbGN7e8eTbRZwALcFE2nuOy48j6zmaTYP8Qtxaia9A1uLWQYP8QZ7OJI+s7LjsXZeMBIIlLn61xgEbLnqadtiQp7Z0orq8rrq8r7Z1IkqadtkbLnsYBuvTZkpQBhgF7SRVFJRQ3QqW9bgY5P1V6fpoDu4oboaISSqpoGLD3GzAIOEqqaFBBURHF9TWlZxlEktKzruL6mqJi5kmqaBBwJIl7Wf+7LICBIYBSKGyE+LsHuCurzPo9Zv0e7soq/u4BhY0Qpfn68p6HCbHv4Q0qtBPfarLd1LR1nAVWzDNphJq2jjXZbirxrQYV2n0PT9Lih/Rwp/xLCz3T/+gnd2VVRJs/vngAAAAASUVORK5CYII='; + + function mytest(originalFile: string, newFile: string) { + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url(' + dataUrl + '); }'), 'sel { background:url(' + dataUrl + '); }'); + assert.strictEqual(rewriteUrls(originalFile, newFile, 'sel { background:url( \t' + dataUrl + '\t ); }'), 'sel { background:url(' + dataUrl + '); }'); + } + + mytest('a.css', 'b.css'); + mytest('a.css', 't/b.css'); + mytest('a.css', 'x/y/b.css'); + mytest('x/a.css', 'b.css'); + mytest('x/y/a.css', 'b.css'); + mytest('x/y/a.css', 't/u/b.css'); + mytest('x/y/a.css', 'x/u/b.css'); + mytest('x/y/a.css', 'x/y/b.css'); + mytest('/a.css', 'b.css'); + mytest('/a.css', 'x/b.css'); + mytest('/a.css', 'x/y/b.css'); + mytest('/x/a.css', 'b.css'); + mytest('/x/a.css', 'x/b.css'); + mytest('/x/a.css', 'x/y/b.css'); + mytest('/x/y/a.css', 'b.css'); + mytest('/x/y/a.css', 'x/b.css'); + mytest('/x/y/a.css', 'x/y/b.css'); + mytest('/a.css', '/b.css'); + mytest('/a.css', '/b.css'); + mytest('/x/a.css', '/b.css'); + mytest('/x/a.css', '/x/b.css'); + mytest('http://www.example.com/x/y/a.css', 'b.css'); + mytest('http://www.example.com/x/y/a.css', 'http://www.example.com/x/y/b.css'); + mytest('https://www.example.com/x/y/a.css', 'b.css'); + }); +}); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index bd7c26a8616..19d51b95853 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -102,7 +102,6 @@ import { IExtensionsScannerService } from 'vs/platform/extensionManagement/commo import { ExtensionsScannerService } from 'vs/platform/extensionManagement/node/extensionsScannerService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ExtensionsProfileScannerService, IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -import { revive } from 'vs/base/common/marshalling'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { OneDataSystemAppender } from 'vs/platform/telemetry/node/1dsAppender'; @@ -233,7 +232,7 @@ class SharedProcessMain extends Disposable { fileService.registerProvider(Schemas.vscodeUserData, userDataFileSystemProvider); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(revive(this.configuration.profiles.default), revive(this.configuration.profiles.current), environmentService, fileService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(this.configuration.defaultProfile, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration @@ -241,7 +240,7 @@ class SharedProcessMain extends Disposable { services.set(IConfigurationService, configurationService); // Storage (global access only) - const storageService = new NativeStorageService(undefined, mainProcessService, userDataProfilesService, environmentService); + const storageService = new NativeStorageService(undefined, { defaultProfile: userDataProfilesService.defaultProfile, currentProfile: userDataProfilesService.defaultProfile }, mainProcessService, environmentService); services.set(IStorageService, storageService); this._register(toDisposable(() => storageService.flush())); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 218d0c30314..4f0f279867b 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -70,7 +70,7 @@ import { SharedProcess } from 'vs/platform/sharedProcess/electron-main/sharedPro import { ISignService } from 'vs/platform/sign/common/sign'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StorageDatabaseChannel } from 'vs/platform/storage/electron-main/storageIpc'; -import { GlobalStorageMainService, IGlobalStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { ApplicationStorageMainService, IApplicationStorageMainService, IStorageMainService, StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { resolveCommonProperties } from 'vs/platform/telemetry/common/commonProperties'; import { ITelemetryService, machineIdKey, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/common/telemetryIpc'; @@ -99,9 +99,9 @@ import { IWorkspacesHistoryMainService, WorkspacesHistoryMainService } from 'vs/ import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IWorkspacesManagementMainService, WorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; import { CredentialsNativeMainService } from 'vs/platform/credentials/electron-main/credentialsMainService'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IPolicyService } from 'vs/platform/policy/common/policy'; import { PolicyChannel } from 'vs/platform/policy/common/policyIpc'; +import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; /** * The main VS Code application. There will only ever be one instance, @@ -648,7 +648,7 @@ export class CodeApplication extends Disposable { // Storage services.set(IStorageMainService, new SyncDescriptor(StorageMainService)); - services.set(IGlobalStorageMainService, new SyncDescriptor(GlobalStorageMainService)); + services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService)); // External terminal if (isWindows) { @@ -711,8 +711,7 @@ export class CodeApplication extends Disposable { sharedProcessClient.then(client => client.registerChannel(LOCAL_FILE_SYSTEM_CHANNEL_NAME, fileSystemProviderChannel)); // Profiles - const userDataProfilesService = ProxyChannel.fromService(accessor.get(IUserDataProfilesService)); - mainProcessElectronServer.registerChannel('userDataProfiles', userDataProfilesService); + mainProcessElectronServer.registerChannel('userDataProfiles', ProxyChannel.fromService(accessor.get(IUserDataProfilesMainService))); // Update const updateChannel = new UpdateChannel(accessor.get(IUpdateService)); @@ -876,6 +875,13 @@ export class CodeApplication extends Disposable { // or if no window is open (macOS only) shouldOpenInNewWindow ||= isMacintosh && windowsMainService.getWindowCount() === 0; + // Pass along edit session id + if (params.get('edit-session-id') !== null) { + environmentService.editSessionId = params.get('edit-session-id') ?? undefined; + params.delete('edit-session-id'); + uri = uri.with({ query: params.toString() }); + } + // Check for URIs to open in window const windowOpenableFromProtocolLink = app.getWindowOpenableFromProtocolLink(uri); logService.trace('app#handleURL: windowOpenableFromProtocolLink = ', windowOpenableFromProtocolLink); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 28db082ca0d..333b118eee3 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -62,12 +62,13 @@ import { IStateMainService } from 'vs/platform/state/electron-main/state'; import { StateMainService } from 'vs/platform/state/electron-main/stateMainService'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IThemeMainService, ThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesMainService, UserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; import { NativePolicyService } from 'vs/platform/policy/node/nativePolicyService'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; /** * The main VS Code entry point. @@ -170,6 +171,10 @@ class CodeMain { const diskFileSystemProvider = new DiskFileSystemProvider(logService); fileService.registerProvider(Schemas.file, diskFileSystemProvider); + // URI Identity + const uriIdentityService = new UriIdentityService(fileService); + services.set(IUriIdentityService, uriIdentityService); + // Logger services.set(ILoggerService, new LoggerService(logService, fileService)); @@ -178,8 +183,8 @@ class CodeMain { services.set(IStateMainService, stateMainService); // User Data Profiles - const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, environmentMainService, fileService, logService); - services.set(IUserDataProfilesService, userDataProfilesMainService); + const userDataProfilesMainService = new UserDataProfilesMainService(stateMainService, uriIdentityService, environmentMainService, fileService, logService); + services.set(IUserDataProfilesMainService, userDataProfilesMainService); // Policy const policyService = isWindows && productService.win32RegValueName ? disposables.add(new NativePolicyService(productService.win32RegValueName)) @@ -244,10 +249,7 @@ class CodeMain { ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), // State service - stateMainService.init(), - - // User Data Profiles Service - userDataProfilesMainService.init(), + stateMainService.init().then(() => userDataProfilesMainService.init()), // Configuration service configurationService.initialize() diff --git a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts index ff591f90b72..4037fbb3441 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterMain.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterMain.ts @@ -1197,21 +1197,15 @@ export class IssueReporter extends Disposable { private addEventListener(elementId: string, eventType: string, handler: (event: Event) => void): void { const element = this.getElementById(elementId); - if (element) { - element.addEventListener(eventType, handler); - } + element?.addEventListener(eventType, handler); } } // helper functions function hide(el: Element | undefined | null) { - if (el) { - el.classList.add('hidden'); - } + el?.classList.add('hidden'); } function show(el: Element | undefined | null) { - if (el) { - el.classList.remove('hidden'); - } + el?.classList.remove('hidden'); } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 19a7e059ea9..3cda56cf0e5 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -134,7 +134,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, diskFileSystemProvider); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); services.set(IUserDataProfilesService, userDataProfilesService); // Policy diff --git a/src/vs/css.build.js b/src/vs/css.build.js deleted file mode 100644 index c8875859684..00000000000 --- a/src/vs/css.build.js +++ /dev/null @@ -1,359 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var _cssPluginGlobal = this; -var CSSBuildLoaderPlugin; -(function (CSSBuildLoaderPlugin) { - var global = (_cssPluginGlobal || {}); - var BrowserCSSLoader = /** @class */ (function () { - function BrowserCSSLoader() { - this._pendingLoads = 0; - } - BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { - var unbind = function () { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - var loadEventListener = function (e) { - unbind(); - callback(); - }; - var errorEventListener = function (e) { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); - }; - BrowserCSSLoader.prototype._onLoad = function (name, callback) { - this._pendingLoads--; - callback(); - }; - BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { - this._pendingLoads--; - errorback(err); - }; - BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { - this._pendingLoads++; - var head = document.head || document.getElementsByTagName('head')[0]; - var other = head.getElementsByTagName('link') || head.getElementsByTagName('script'); - if (other.length > 0) { - head.insertBefore(linkNode, other[other.length - 1]); - } - else { - head.appendChild(linkNode); - } - }; - BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { - var _this = this; - var linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - var callback = function () { return _this._onLoad(name, externalCallback); }; - var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; - this.attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - return linkNode; - }; - BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { - var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); - for (i = 0, len = links.length; i < len; i++) { - nameAttr = links[i].getAttribute('data-name'); - hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; - }; - BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - if (this._linkTagExists(name, cssUrl)) { - externalCallback(); - return; - } - var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); - this._insertLinkNode(linkNode); - }; - return BrowserCSSLoader; - }()); - var NodeCSSLoader = /** @class */ (function () { - function NodeCSSLoader() { - this.fs = require.nodeRequire('fs'); - } - NodeCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - var contents = this.fs.readFileSync(cssUrl, 'utf8'); - // Remove BOM - if (contents.charCodeAt(0) === NodeCSSLoader.BOM_CHAR_CODE) { - contents = contents.substring(1); - } - externalCallback(contents); - }; - NodeCSSLoader.BOM_CHAR_CODE = 65279; - return NodeCSSLoader; - }()); - // ------------------------------ Finally, the plugin - var CSSPlugin = /** @class */ (function () { - function CSSPlugin(cssLoader) { - this.cssLoader = cssLoader; - } - CSSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - var myConfig = config['vs/css'] || {}; - global.inlineResources = myConfig.inlineResources; - global.inlineResourcesLimit = myConfig.inlineResourcesLimit || 5000; - var cssUrl = req.toUrl(name + '.css'); - this.cssLoader.load(name, cssUrl, function (contents) { - // Contents has the CSS file contents if we are in a build - if (config.isBuild) { - CSSPlugin.BUILD_MAP[name] = contents; - CSSPlugin.BUILD_PATH_MAP[name] = cssUrl; - } - load({}); - }, function (err) { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + ' or it was empty'); - } - }); - }; - CSSPlugin.prototype.write = function (pluginName, moduleName, write) { - // getEntryPoint is a Monaco extension to r.js - var entryPoint = write.getEntryPoint(); - // r.js destroys the context of this plugin between calling 'write' and 'writeFile' - // so the only option at this point is to leak the data to a global - global.cssPluginEntryPoints = global.cssPluginEntryPoints || {}; - global.cssPluginEntryPoints[entryPoint] = global.cssPluginEntryPoints[entryPoint] || []; - global.cssPluginEntryPoints[entryPoint].push({ - moduleName: moduleName, - contents: CSSPlugin.BUILD_MAP[moduleName], - fsPath: CSSPlugin.BUILD_PATH_MAP[moduleName], - }); - write.asModule(pluginName + '!' + moduleName, 'define([\'vs/css!' + entryPoint + '\'], {});'); - }; - CSSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) { - if (global.cssPluginEntryPoints && global.cssPluginEntryPoints.hasOwnProperty(moduleName)) { - var fileName = req.toUrl(moduleName + '.css'); - var contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], entries = global.cssPluginEntryPoints[moduleName]; - for (var i = 0; i < entries.length; i++) { - if (global.inlineResources) { - contents.push(Utilities.rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, global.inlineResources === 'base64', global.inlineResourcesLimit)); - } - else { - contents.push(Utilities.rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); - } - } - write(fileName, contents.join('\r\n')); - } - }; - CSSPlugin.prototype.getInlinedResources = function () { - return global.cssInlinedResources || []; - }; - CSSPlugin.BUILD_MAP = {}; - CSSPlugin.BUILD_PATH_MAP = {}; - return CSSPlugin; - }()); - CSSBuildLoaderPlugin.CSSPlugin = CSSPlugin; - var Utilities = /** @class */ (function () { - function Utilities() { - } - Utilities.startsWith = function (haystack, needle) { - return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; - }; - /** - * Find the path of a file. - */ - Utilities.pathOf = function (filename) { - var lastSlash = filename.lastIndexOf('/'); - if (lastSlash !== -1) { - return filename.substr(0, lastSlash + 1); - } - else { - return ''; - } - }; - /** - * A conceptual a + b for paths. - * Takes into account if `a` contains a protocol. - * Also normalizes the result: e.g.: a/b/ + ../c => a/c - */ - Utilities.joinPaths = function (a, b) { - function findSlashIndexAfterPrefix(haystack, prefix) { - if (Utilities.startsWith(haystack, prefix)) { - return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); - } - return 0; - } - var aPathStartIndex = 0; - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); - aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); - function pushPiece(pieces, piece) { - if (piece === './') { - // Ignore - return; - } - if (piece === '../') { - var prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); - if (prevPiece && prevPiece === '/') { - // Ignore - return; - } - if (prevPiece && prevPiece !== '../') { - // Pop - pieces.pop(); - return; - } - } - // Push - pieces.push(piece); - } - function push(pieces, path) { - while (path.length > 0) { - var slashIndex = path.indexOf('/'); - var piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); - path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); - pushPiece(pieces, piece); - } - } - var pieces = []; - push(pieces, a.substr(aPathStartIndex)); - if (b.length > 0 && b.charAt(0) === '/') { - pieces = []; - } - push(pieces, b); - return a.substring(0, aPathStartIndex) + pieces.join(''); - }; - Utilities.commonPrefix = function (str1, str2) { - var len = Math.min(str1.length, str2.length); - for (var i = 0; i < len; i++) { - if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { - break; - } - } - return str1.substring(0, i); - }; - Utilities.commonFolderPrefix = function (fromPath, toPath) { - var prefix = Utilities.commonPrefix(fromPath, toPath); - var slashIndex = prefix.lastIndexOf('/'); - if (slashIndex === -1) { - return ''; - } - return prefix.substring(0, slashIndex + 1); - }; - Utilities.relativePath = function (fromPath, toPath) { - if (Utilities.startsWith(toPath, '/') || Utilities.startsWith(toPath, 'http://') || Utilities.startsWith(toPath, 'https://')) { - return toPath; - } - // Ignore common folder prefix - var prefix = Utilities.commonFolderPrefix(fromPath, toPath); - fromPath = fromPath.substr(prefix.length); - toPath = toPath.substr(prefix.length); - var upCount = fromPath.split('/').length; - var result = ''; - for (var i = 1; i < upCount; i++) { - result += '../'; - } - return result + toPath; - }; - Utilities._replaceURL = function (contents, replacer) { - // Use ")" as the terminator as quotes are oftentimes not used at all - return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, function (_) { - var matches = []; - for (var _i = 1; _i < arguments.length; _i++) { - matches[_i - 1] = arguments[_i]; - } - var url = matches[0]; - // Eliminate starting quotes (the initial whitespace is not captured) - if (url.charAt(0) === '"' || url.charAt(0) === '\'') { - url = url.substring(1); - } - // The ending whitespace is captured - while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { - url = url.substring(0, url.length - 1); - } - // Eliminate ending quotes - if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { - url = url.substring(0, url.length - 1); - } - if (!Utilities.startsWith(url, 'data:') && !Utilities.startsWith(url, 'http://') && !Utilities.startsWith(url, 'https://')) { - url = replacer(url); - } - return 'url(' + url + ')'; - }); - }; - Utilities.rewriteUrls = function (originalFile, newFile, contents) { - return this._replaceURL(contents, function (url) { - var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - }; - Utilities.rewriteOrInlineUrls = function (originalFileFSPath, originalFile, newFile, contents, forceBase64, inlineByteLimit) { - var fs = require.nodeRequire('fs'); - var path = require.nodeRequire('path'); - return this._replaceURL(contents, function (url) { - if (/\.(svg|png)$/.test(url)) { - var fsPath = path.join(path.dirname(originalFileFSPath), url); - var fileContents = fs.readFileSync(fsPath); - if (fileContents.length < inlineByteLimit) { - global.cssInlinedResources = global.cssInlinedResources || []; - var normalizedFSPath = fsPath.replace(/\\/g, '/'); - if (global.cssInlinedResources.indexOf(normalizedFSPath) >= 0) { - // console.warn('CSS INLINING IMAGE AT ' + fsPath + ' MORE THAN ONCE. CONSIDER CONSOLIDATING CSS RULES'); - } - global.cssInlinedResources.push(normalizedFSPath); - var MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; - var DATA = ';base64,' + fileContents.toString('base64'); - if (!forceBase64 && /\.svg$/.test(url)) { - // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris - var newText = fileContents.toString() - .replace(/"/g, '\'') - .replace(/%/g, '%25') - .replace(//g, '%3E') - .replace(/&/g, '%26') - .replace(/#/g, '%23') - .replace(/\s+/g, ' '); - var encodedData = ',' + newText; - if (encodedData.length < DATA.length) { - DATA = encodedData; - } - } - return '"data:' + MIME + DATA + '"'; - } - } - var absoluteUrl = Utilities.joinPaths(Utilities.pathOf(originalFile), url); - return Utilities.relativePath(newFile, absoluteUrl); - }); - }; - return Utilities; - }()); - CSSBuildLoaderPlugin.Utilities = Utilities; - (function () { - var cssLoader = null; - var isElectron = (typeof process !== 'undefined' && typeof process.versions !== 'undefined' && typeof process.versions['electron'] !== 'undefined'); - if (typeof process !== 'undefined' && process.versions && !!process.versions.node && !isElectron) { - cssLoader = new NodeCSSLoader(); - } - else { - cssLoader = new BrowserCSSLoader(); - } - define('vs/css', new CSSPlugin(cssLoader)); - })(); -})(CSSBuildLoaderPlugin || (CSSBuildLoaderPlugin = {})); diff --git a/src/vs/css.build.ts b/src/vs/css.build.ts new file mode 100644 index 00000000000..d4b3fcc96b2 --- /dev/null +++ b/src/vs/css.build.ts @@ -0,0 +1,304 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface ICSSPluginConfig { + inlineResources?: boolean | 'base64'; + inlineResourcesLimit?: number; +} + +interface ICSSEntryPointData { + moduleName: string; + contents: string; + fsPath: string; +} + +// This file gets compiled also with the standalone editor, +// so we cannot depend on types from node.d.ts +interface INodeFS { + readFileSync(path: string, encoding: 'utf8'): string; + readFileSync(path: string): INodeBuffer; +} +interface INodeBuffer { + length: number; + toString(encoding?: 'base64'): string; +} +interface INodePath { + dirname(p: string): string; + join(...paths: string[]): string; +} + +const nodeReq = (module: string): T | undefined => { + if (typeof (require).__$__nodeRequire === 'function') { + return (require).__$__nodeRequire(module); + } + return undefined; +}; + +const fs = nodeReq('fs'); +const path = nodeReq('path'); + +let inlineResources: boolean | 'base64' = false; +let inlineResourcesLimit: number = 5000; + +const contentsMap: { [moduleName: string]: string } = {}; +const pathMap: { [moduleName: string]: string } = {}; +const entryPoints: { [entryPoint: string]: ICSSEntryPointData[] } = {}; +const inlinedResources: string[] = []; + +/** + * Invoked by the loader at build-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + if (!fs) { + throw new Error(`Cannot load files without 'fs'!`); + } + config = config || {}; + const myConfig = (config['vs/css'] || {}); + inlineResources = (typeof myConfig.inlineResources === 'undefined' ? false : myConfig.inlineResources); + inlineResourcesLimit = (myConfig.inlineResourcesLimit || 5000); + const cssUrl = req.toUrl(name + '.css'); + let contents = fs.readFileSync(cssUrl, 'utf8'); + if (contents.charCodeAt(0) === 65279 /* BOM */) { + // Remove BOM + contents = contents.substring(1); + } + if (config.isBuild) { + contentsMap[name] = contents; + pathMap[name] = cssUrl; + } + load({}); +} + +/** + * Invoked by the loader at build-time + */ +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push({ + moduleName: moduleName, + contents: contentsMap[moduleName], + fsPath: pathMap[moduleName], + }); + + write.asModule(pluginName + '!' + moduleName, + 'define([\'vs/css!' + entryPoint + '\'], {});' + ); +} + +/** + * Invoked by the loader at build-time + */ +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints && entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.css'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + for (let i = 0; i < entries.length; i++) { + if (inlineResources) { + contents.push(rewriteOrInlineUrls(entries[i].fsPath, entries[i].moduleName, moduleName, entries[i].contents, inlineResources === 'base64', inlineResourcesLimit)); + } else { + contents.push(rewriteUrls(entries[i].moduleName, moduleName, entries[i].contents)); + } + } + write(fileName, contents.join('\r\n')); + } +} + +export function getInlinedResources(): string[] { + return inlinedResources || []; +} + +function rewriteOrInlineUrls(originalFileFSPath: string, originalFile: string, newFile: string, contents: string, forceBase64: boolean, inlineByteLimit: number): string { + if (!fs || !path) { + throw new Error(`Cannot rewrite or inline urls without 'fs' or 'path'!`); + } + return CSSPluginUtilities.replaceURL(contents, (url) => { + if (/\.(svg|png)$/.test(url)) { + const fsPath = path.join(path.dirname(originalFileFSPath), url); + const fileContents = fs.readFileSync(fsPath); + + if (fileContents.length < inlineByteLimit) { + const normalizedFSPath = fsPath.replace(/\\/g, '/'); + inlinedResources.push(normalizedFSPath); + + const MIME = /\.svg$/.test(url) ? 'image/svg+xml' : 'image/png'; + let DATA = ';base64,' + fileContents.toString('base64'); + + if (!forceBase64 && /\.svg$/.test(url)) { + // .svg => url encode as explained at https://codepen.io/tigt/post/optimizing-svgs-in-data-uris + const newText = fileContents.toString() + .replace(/"/g, '\'') + .replace(/%/g, '%25') + .replace(//g, '%3E') + .replace(/&/g, '%26') + .replace(/#/g, '%23') + .replace(/\s+/g, ' '); + const encodedData = ',' + newText; + if (encodedData.length < DATA.length) { + DATA = encodedData; + } + } + return '"data:' + MIME + DATA + '"'; + } + } + + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} + +export function rewriteUrls(originalFile: string, newFile: string, contents: string): string { + return CSSPluginUtilities.replaceURL(contents, (url) => { + const absoluteUrl = CSSPluginUtilities.joinPaths(CSSPluginUtilities.pathOf(originalFile), url); + return CSSPluginUtilities.relativePath(newFile, absoluteUrl); + }); +} + +export class CSSPluginUtilities { + + public static startsWith(haystack: string, needle: string): boolean { + return haystack.length >= needle.length && haystack.substr(0, needle.length) === needle; + } + + /** + * Find the path of a file. + */ + public static pathOf(filename: string): string { + const lastSlash = filename.lastIndexOf('/'); + if (lastSlash !== -1) { + return filename.substr(0, lastSlash + 1); + } else { + return ''; + } + } + + /** + * A conceptual a + b for paths. + * Takes into account if `a` contains a protocol. + * Also normalizes the result: e.g.: a/b/ + ../c => a/c + */ + public static joinPaths(a: string, b: string): string { + + function findSlashIndexAfterPrefix(haystack: string, prefix: string): number { + if (CSSPluginUtilities.startsWith(haystack, prefix)) { + return Math.max(prefix.length, haystack.indexOf('/', prefix.length)); + } + return 0; + } + + let aPathStartIndex = 0; + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, '//'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'http://'); + aPathStartIndex = aPathStartIndex || findSlashIndexAfterPrefix(a, 'https://'); + + function pushPiece(pieces: string[], piece: string): void { + if (piece === './') { + // Ignore + return; + } + if (piece === '../') { + const prevPiece = (pieces.length > 0 ? pieces[pieces.length - 1] : null); + if (prevPiece && prevPiece === '/') { + // Ignore + return; + } + if (prevPiece && prevPiece !== '../') { + // Pop + pieces.pop(); + return; + } + } + // Push + pieces.push(piece); + } + + function push(pieces: string[], path: string): void { + while (path.length > 0) { + const slashIndex = path.indexOf('/'); + const piece = (slashIndex >= 0 ? path.substring(0, slashIndex + 1) : path); + path = (slashIndex >= 0 ? path.substring(slashIndex + 1) : ''); + pushPiece(pieces, piece); + } + } + + let pieces: string[] = []; + push(pieces, a.substr(aPathStartIndex)); + if (b.length > 0 && b.charAt(0) === '/') { + pieces = []; + } + push(pieces, b); + + return a.substring(0, aPathStartIndex) + pieces.join(''); + } + + public static commonPrefix(str1: string, str2: string): string { + const len = Math.min(str1.length, str2.length); + for (let i = 0; i < len; i++) { + if (str1.charCodeAt(i) !== str2.charCodeAt(i)) { + return str1.substring(0, i); + } + } + return str1.substring(0, len); + } + + public static commonFolderPrefix(fromPath: string, toPath: string): string { + const prefix = CSSPluginUtilities.commonPrefix(fromPath, toPath); + const slashIndex = prefix.lastIndexOf('/'); + if (slashIndex === -1) { + return ''; + } + return prefix.substring(0, slashIndex + 1); + } + + public static relativePath(fromPath: string, toPath: string): string { + if (CSSPluginUtilities.startsWith(toPath, '/') || CSSPluginUtilities.startsWith(toPath, 'http://') || CSSPluginUtilities.startsWith(toPath, 'https://')) { + return toPath; + } + + // Ignore common folder prefix + const prefix = CSSPluginUtilities.commonFolderPrefix(fromPath, toPath); + fromPath = fromPath.substr(prefix.length); + toPath = toPath.substr(prefix.length); + + const upCount = fromPath.split('/').length; + let result = ''; + for (let i = 1; i < upCount; i++) { + result += '../'; + } + return result + toPath; + } + + public static replaceURL(contents: string, replacer: (url: string) => string): string { + // Use ")" as the terminator as quotes are oftentimes not used at all + return contents.replace(/url\(\s*([^\)]+)\s*\)?/g, (_: string, ...matches: string[]) => { + let url = matches[0]; + // Eliminate starting quotes (the initial whitespace is not captured) + if (url.charAt(0) === '"' || url.charAt(0) === '\'') { + url = url.substring(1); + } + // The ending whitespace is captured + while (url.length > 0 && (url.charAt(url.length - 1) === ' ' || url.charAt(url.length - 1) === '\t')) { + url = url.substring(0, url.length - 1); + } + // Eliminate ending quotes + if (url.charAt(url.length - 1) === '"' || url.charAt(url.length - 1) === '\'') { + url = url.substring(0, url.length - 1); + } + + if (!CSSPluginUtilities.startsWith(url, 'data:') && !CSSPluginUtilities.startsWith(url, 'http://') && !CSSPluginUtilities.startsWith(url, 'https://')) { + url = replacer(url); + } + + return 'url(' + url + ')'; + }); + } +} diff --git a/src/vs/css.d.ts b/src/vs/css.d.ts deleted file mode 100644 index 11a10ed5950..00000000000 --- a/src/vs/css.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - diff --git a/src/vs/css.js b/src/vs/css.js deleted file mode 100644 index 8a0f9912902..00000000000 --- a/src/vs/css.js +++ /dev/null @@ -1,111 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var CSSLoaderPlugin; -(function (CSSLoaderPlugin) { - var BrowserCSSLoader = /** @class */ (function () { - function BrowserCSSLoader() { - this._pendingLoads = 0; - } - BrowserCSSLoader.prototype.attachListeners = function (name, linkNode, callback, errorback) { - var unbind = function () { - linkNode.removeEventListener('load', loadEventListener); - linkNode.removeEventListener('error', errorEventListener); - }; - var loadEventListener = function (e) { - unbind(); - callback(); - }; - var errorEventListener = function (e) { - unbind(); - errorback(e); - }; - linkNode.addEventListener('load', loadEventListener); - linkNode.addEventListener('error', errorEventListener); - }; - BrowserCSSLoader.prototype._onLoad = function (name, callback) { - this._pendingLoads--; - callback(); - }; - BrowserCSSLoader.prototype._onLoadError = function (name, errorback, err) { - this._pendingLoads--; - errorback(err); - }; - BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) { - this._pendingLoads++; - var head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(linkNode); - }; - BrowserCSSLoader.prototype.createLinkTag = function (name, cssUrl, externalCallback, externalErrorback) { - var _this = this; - var linkNode = document.createElement('link'); - linkNode.setAttribute('rel', 'stylesheet'); - linkNode.setAttribute('type', 'text/css'); - linkNode.setAttribute('data-name', name); - var callback = function () { return _this._onLoad(name, externalCallback); }; - var errorback = function (err) { return _this._onLoadError(name, externalErrorback, err); }; - this.attachListeners(name, linkNode, callback, errorback); - linkNode.setAttribute('href', cssUrl); - return linkNode; - }; - BrowserCSSLoader.prototype._linkTagExists = function (name, cssUrl) { - var i, len, nameAttr, hrefAttr, links = document.getElementsByTagName('link'); - for (i = 0, len = links.length; i < len; i++) { - nameAttr = links[i].getAttribute('data-name'); - hrefAttr = links[i].getAttribute('href'); - if (nameAttr === name || hrefAttr === cssUrl) { - return true; - } - } - return false; - }; - BrowserCSSLoader.prototype.load = function (name, cssUrl, externalCallback, externalErrorback) { - if (this._linkTagExists(name, cssUrl)) { - externalCallback(); - return; - } - var linkNode = this.createLinkTag(name, cssUrl, externalCallback, externalErrorback); - this._insertLinkNode(linkNode); - }; - return BrowserCSSLoader; - }()); - // ------------------------------ Finally, the plugin - var CSSPlugin = /** @class */ (function () { - function CSSPlugin() { - this._cssLoader = new BrowserCSSLoader(); - } - CSSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - var cssConfig = config['vs/css'] || {}; - if (cssConfig.disabled) { - // the plugin is asked to not create any style sheets - load({}); - return; - } - var cssUrl = req.toUrl(name + '.css'); - this._cssLoader.load(name, cssUrl, function (contents) { - load({}); - }, function (err) { - if (typeof load.error === 'function') { - load.error('Could not find ' + cssUrl + ' or it was empty'); - } - }); - }; - return CSSPlugin; - }()); - CSSLoaderPlugin.CSSPlugin = CSSPlugin; - define('vs/css', new CSSPlugin()); -})(CSSLoaderPlugin || (CSSLoaderPlugin = {})); diff --git a/src/vs/css.ts b/src/vs/css.ts new file mode 100644 index 00000000000..4a5ea48d174 --- /dev/null +++ b/src/vs/css.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +interface ICSSPluginConfig { + disabled?: boolean; +} + +/** + * Invoked by the loader at run-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + const cssConfig = (config['vs/css'] || {}); + + if (cssConfig.disabled) { + // the plugin is asked to not create any style sheets + load({}); + return; + } + + const cssUrl = req.toUrl(name + '.css'); + loadCSS(name, cssUrl, () => { + load({}); + }, (err: any) => { + if (typeof load.error === 'function') { + load.error('Could not find ' + cssUrl + '.'); + } + }); +} + +function loadCSS(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + if (linkTagExists(name, cssUrl)) { + callback(); + return; + } + createLinkTag(name, cssUrl, callback, errorback); +} + +function linkTagExists(name: string, cssUrl: string): boolean { + const links = document.getElementsByTagName('link'); + for (let i = 0, len = links.length; i < len; i++) { + const nameAttr = links[i].getAttribute('data-name'); + const hrefAttr = links[i].getAttribute('href'); + if (nameAttr === name || hrefAttr === cssUrl) { + return true; + } + } + return false; +} + +function createLinkTag(name: string, cssUrl: string, callback: () => void, errorback: (err: any) => void): void { + const linkNode = document.createElement('link'); + linkNode.setAttribute('rel', 'stylesheet'); + linkNode.setAttribute('type', 'text/css'); + linkNode.setAttribute('data-name', name); + + attachListeners(name, linkNode, callback, errorback); + linkNode.setAttribute('href', cssUrl); + + const head = document.head || document.getElementsByTagName('head')[0]; + head.appendChild(linkNode); +} + +function attachListeners(name: string, linkNode: HTMLLinkElement, callback: () => void, errorback: (err: any) => void): void { + const unbind = () => { + linkNode.removeEventListener('load', loadEventListener); + linkNode.removeEventListener('error', errorEventListener); + }; + const loadEventListener = (e: any) => { + unbind(); + callback(); + }; + const errorEventListener = (e: any) => { + unbind(); + errorback(e); + }; + linkNode.addEventListener('load', loadEventListener); + linkNode.addEventListener('error', errorEventListener); +} diff --git a/src/vs/editor/browser/config/fontMeasurements.ts b/src/vs/editor/browser/config/fontMeasurements.ts index 39e1737cd33..00dde46dcc9 100644 --- a/src/vs/editor/browser/config/fontMeasurements.ts +++ b/src/vs/editor/browser/config/fontMeasurements.ts @@ -149,9 +149,7 @@ class FontMeasurementsImpl extends Disposable { private _createRequest(chr: string, type: CharWidthRequestType, all: CharWidthRequest[], monospace: CharWidthRequest[] | null): CharWidthRequest { const result = new CharWidthRequest(chr, type); all.push(result); - if (monospace) { - monospace.push(result); - } + monospace?.push(result); return result; } diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index ffbe3b55882..45970c2a2b6 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -634,9 +634,7 @@ export class TextAreaHandler extends ViewPart { public prepareRender(ctx: RenderingContext): void { this._primaryCursorPosition = new Position(this._selections[0].positionLineNumber, this._selections[0].positionColumn); this._primaryCursorVisibleRange = ctx.visibleRangeForPosition(this._primaryCursorPosition); - if (this._visibleTextArea) { - this._visibleTextArea.prepareRender(ctx); - } + this._visibleTextArea?.prepareRender(ctx); } public render(ctx: RestrictedRenderingContext): void { diff --git a/src/vs/editor/browser/dnd.ts b/src/vs/editor/browser/dnd.ts index 80a5958504d..b90bf922695 100644 --- a/src/vs/editor/browser/dnd.ts +++ b/src/vs/editor/browser/dnd.ts @@ -3,9 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { DataTransfers } from 'vs/base/browser/dnd'; +import { distinct } from 'vs/base/common/arrays'; import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { Mimes } from 'vs/base/common/mime'; import { URI } from 'vs/base/common/uri'; -import { FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd'; export function toVSDataTransfer(dataTransfer: DataTransfer) { @@ -31,3 +34,34 @@ export function createFileDataTransferItemFromFile(file: File): IDataTransferIte return new Uint8Array(await file.arrayBuffer()); }); } + +const INTERNAL_DND_MIME_TYPES = Object.freeze([ + CodeDataTransfers.EDITORS, + CodeDataTransfers.FILES, + DataTransfers.RESOURCES, +]); + +export function addExternalEditorsDropData(dataTransfer: VSDataTransfer, dragEvent: DragEvent) { + if (dragEvent.dataTransfer && !dataTransfer.has(Mimes.uriList)) { + const editorData = extractEditorsDropData(dragEvent) + .filter(input => input.resource) + .map(input => input.resource!.toString()); + + // Also add in the files + for (const item of dragEvent.dataTransfer?.items) { + const file = item.getAsFile(); + if (file) { + editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name); + } + } + + if (editorData.length) { + const str = distinct(editorData).join('\n'); + dataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); + } + } + + for (const internal of INTERNAL_DND_MIME_TYPES) { + dataTransfer.delete(internal); + } +} diff --git a/src/vs/editor/browser/services/bulkEditService.ts b/src/vs/editor/browser/services/bulkEditService.ts index 558376fae33..1fa168aedb1 100644 --- a/src/vs/editor/browser/services/bulkEditService.ts +++ b/src/vs/editor/browser/services/bulkEditService.ts @@ -45,9 +45,9 @@ export class ResourceEdit { export class ResourceTextEdit extends ResourceEdit { constructor( readonly resource: URI, - readonly textEdit: TextEdit, + readonly textEdit: TextEdit & { insertAsSnippet?: boolean }, readonly versionId?: number, - metadata?: WorkspaceEditMetadata + metadata?: WorkspaceEditMetadata, ) { super(metadata); } diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index 2edb031b356..2b1ac590d2b 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -1325,15 +1325,11 @@ class InnerMinimap extends Disposable { return false; } public onLinesDeleted(deleteFromLineNumber: number, deleteToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); - } + this._lastRenderData?.onLinesDeleted(deleteFromLineNumber, deleteToLineNumber); return true; } public onLinesInserted(insertFromLineNumber: number, insertToLineNumber: number): boolean { - if (this._lastRenderData) { - this._lastRenderData.onLinesInserted(insertFromLineNumber, insertToLineNumber); - } + this._lastRenderData?.onLinesInserted(insertFromLineNumber, insertToLineNumber); return true; } public onScrollChanged(): boolean { diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index 1a4601d7434..75810f5c95e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -588,9 +588,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public setHiddenAreas(ranges: IRange[]): void { - if (this._modelData) { - this._modelData.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); - } + this._modelData?.viewModel.setHiddenAreas(ranges.map(r => Range.lift(r))); } public getVisibleColumnFromPosition(rawPosition: IPosition): number { @@ -1786,9 +1784,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } protected _postDetachModelCleanup(detachedModel: ITextModel | null): void { - if (detachedModel) { - detachedModel.removeAllDecorationsWithOwnerId(this._id); - } + detachedModel?.removeAllDecorationsWithOwnerId(this._id); } private _detachModel(): ITextModel | null { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index ab0ec218a28..59cd4e9918a 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -6,6 +6,7 @@ import 'vs/css!./media/diffEditor'; import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import * as assert from 'vs/base/common/assert'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ISashEvent, IVerticalSashLayoutProvider, Sash, SashState, Orientation } from 'vs/base/browser/ui/sash/sash'; import { RunOnceScheduler } from 'vs/base/common/async'; @@ -149,17 +150,13 @@ class VisualEditorState { } }); - if (scrollState) { - scrollState.restore(editor); - } + scrollState?.restore(editor); // decorations this._decorations = editor.deltaDecorations(this._decorations, newDecorations.decorations); // overview ruler - if (overviewRuler) { - overviewRuler.setZones(newDecorations.overviewZones); - } + overviewRuler?.setZones(newDecorations.overviewZones); } } @@ -429,24 +426,30 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE return result; } - private _recreateOverviewRulers(): void { + private _disposeOverviewRulers(): void { + if (this._originalOverviewRuler) { + this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); + this._originalOverviewRuler.dispose(); + this._originalOverviewRuler = null; + } + if (this._modifiedOverviewRuler) { + this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); + this._modifiedOverviewRuler.dispose(); + this._modifiedOverviewRuler = null; + } + } + + private _createOverviewRulers(): void { if (!this._options.renderOverviewRuler) { return; } - if (this._originalOverviewRuler) { - this._overviewDomElement.removeChild(this._originalOverviewRuler.getDomNode()); - this._originalOverviewRuler.dispose(); - } + assert.ok(!this._originalOverviewRuler && !this._modifiedOverviewRuler); + if (this._originalEditor.hasModel()) { this._originalOverviewRuler = this._originalEditor.createOverviewRuler('original diffOverviewRuler')!; this._overviewDomElement.appendChild(this._originalOverviewRuler.getDomNode()); } - - if (this._modifiedOverviewRuler) { - this._overviewDomElement.removeChild(this._modifiedOverviewRuler.getDomNode()); - this._modifiedOverviewRuler.dispose(); - } if (this._modifiedEditor.hasModel()) { this._modifiedOverviewRuler = this._modifiedEditor.createOverviewRuler('modified diffOverviewRuler')!; this._overviewDomElement.appendChild(this._modifiedOverviewRuler.getDomNode()); @@ -794,6 +797,8 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE // Remove all view zones & decorations this._cleanViewZonesAndDecorations(); + this._disposeOverviewRulers(); + // Update code editor models this._originalEditor.setModel(model ? model.original : null); this._modifiedEditor.setModel(model ? model.modified : null); @@ -812,7 +817,7 @@ export class DiffEditorWidget extends Disposable implements editorBrowser.IDiffE this._setState(editorBrowser.DiffEditorState.Idle); if (model) { - this._recreateOverviewRulers(); + this._createOverviewRulers(); // Begin comparing this._beginUpdateDecorations(); diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index bb5e3134ec5..97394a88515 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -1549,7 +1549,7 @@ export interface CommentThread { onDidChangeInput: Event; onDidChangeRange: Event; onDidChangeLabel: Event; - onDidChangeCollasibleState: Event; + onDidChangeCollapsibleState: Event; onDidChangeState: Event; onDidChangeCanReply: Event; isDisposed: boolean; diff --git a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts index 39c24d84072..d4485d47cc4 100644 --- a/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts +++ b/src/vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts @@ -304,9 +304,7 @@ function collectBrackets( nodeOffsetStart = nodeOffsetEnd; } - if (levelPerBracketType) { - levelPerBracketType.set(node.openingBracket.text, levelPerBracket); - } + levelPerBracketType?.set(node.openingBracket.text, levelPerBracket); } else if (node.kind === AstNodeKind.UnexpectedClosingBracket) { const range = lengthsToRange(nodeOffsetStart, nodeOffsetEnd); result.push(new BracketInfo(range, level - 1, 0, true)); @@ -394,9 +392,7 @@ function collectBracketPairs( } } - if (levelPerBracketType) { - levelPerBracketType.set(node.openingBracket.text, levelPerBracket); - } + levelPerBracketType?.set(node.openingBracket.text, levelPerBracket); } else { let curOffset = nodeOffsetStart; for (const child of node.children) { diff --git a/src/vs/editor/common/services/languageService.ts b/src/vs/editor/common/services/languageService.ts index 9dbbcda9b42..431317288bc 100644 --- a/src/vs/editor/common/services/languageService.ts +++ b/src/vs/editor/common/services/languageService.ts @@ -180,8 +180,6 @@ class LanguageSelection implements ILanguageSelection { return; } this.languageId = languageId; - if (this._emitter) { - this._emitter.fire(this.languageId); - } + this._emitter?.fire(this.languageId); } } diff --git a/src/vs/editor/common/services/markerDecorationsService.ts b/src/vs/editor/common/services/markerDecorationsService.ts index 95ade426458..15893914318 100644 --- a/src/vs/editor/common/services/markerDecorationsService.ts +++ b/src/vs/editor/common/services/markerDecorationsService.ts @@ -121,9 +121,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor if (model.uri.scheme === Schemas.inMemory || model.uri.scheme === Schemas.internal || model.uri.scheme === Schemas.vscode) { - if (this._markerService) { - this._markerService.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); - } + this._markerService?.read({ resource: model.uri }).map(marker => marker.owner).forEach(owner => this._markerService.remove(owner, [model.uri])); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts index 7be66230fcc..7a4920f5dd6 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionCommands.ts @@ -244,9 +244,7 @@ function triggerCodeActionsForEditorSelection( ): void { if (editor.hasModel()) { const controller = QuickFixController.get(editor); - if (controller) { - controller.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); - } + controller?.manualTriggerAtCurrentPosition(notAvailableMessage, filter, autoApply, preview); } } diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index f0a781143ae..6eb46ebab45 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -263,9 +263,7 @@ export class CodeActionModel extends Disposable { } public trigger(trigger: CodeActionTrigger) { - if (this._codeActionOracle.value) { - this._codeActionOracle.value.trigger(trigger); - } + this._codeActionOracle.value?.trigger(trigger); } private setState(newState: CodeActionsState.State, skipNotify?: boolean) { diff --git a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts index bca6f3aa72a..d282350bfe6 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensWidget.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensWidget.ts @@ -254,9 +254,7 @@ export class CodeLensWidget { dispose(helper: CodeLensHelper, viewZoneChangeAccessor?: IViewZoneChangeAccessor): void { this._decorationIds.forEach(helper.removeDecoration, helper); this._decorationIds = []; - if (viewZoneChangeAccessor) { - viewZoneChangeAccessor.removeZone(this._viewZoneId); - } + viewZoneChangeAccessor?.removeZone(this._viewZoneId); if (this._contentWidget) { this._editor.removeContentWidget(this._contentWidget); this._contentWidget = undefined; diff --git a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts index a2959557316..773a83b80b9 100644 --- a/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts +++ b/src/vs/editor/contrib/documentSymbols/browser/outlineModel.ts @@ -30,9 +30,7 @@ export abstract class TreeElement { abstract parent: TreeElement | undefined; remove(): void { - if (this.parent) { - this.parent.children.delete(this.id); - } + this.parent?.children.delete(this.id); } static findId(candidate: DocumentSymbol | string, container: TreeElement): string { diff --git a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts index 8254c5faf12..0c050ea25f6 100644 --- a/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts +++ b/src/vs/editor/contrib/dropIntoEditor/browser/dropIntoEditorContribution.ts @@ -3,14 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Disposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { relativePath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { toVSDataTransfer } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IBulkEditService, ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; @@ -25,8 +24,6 @@ import { CodeEditorStateFlag, EditorStateCancellationTokenSource } from 'vs/edit import { performSnippetEdit } from 'vs/editor/contrib/snippet/browser/snippetController2'; import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -37,7 +34,6 @@ export class DropIntoEditorController extends Disposable implements IEditorContr constructor( editor: ICodeEditor, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IBulkEditService private readonly _bulkEditService: IBulkEditService, @@ -111,15 +107,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr } const textEditorDataTransfer = toVSDataTransfer(dragEvent.dataTransfer); - const editorData = (await this._instantiationService.invokeFunction(extractEditorsDropData, dragEvent)) - .filter(input => input.resource) - .map(input => input.resource!.toString()); - - if (editorData.length) { - const str = distinct(editorData).join('\n'); - textEditorDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(str)); - } - + addExternalEditorsDropData(textEditorDataTransfer, dragEvent); return textEditorDataTransfer; } } 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 7bcba36df5c..e3c97f93cd2 100644 --- a/src/vs/editor/contrib/find/test/browser/findController.test.ts +++ b/src/vs/editor/contrib/find/test/browser/findController.test.ts @@ -80,8 +80,8 @@ suite('FindController', async () => { isNew: () => false, flush: () => { return Promise.resolve(); }, keys: () => [], - logStorage: () => { }, - migrate: () => { throw new Error(); } + log: () => { }, + switch: () => { throw new Error(); } } as IStorageService); if (platform.isMacintosh) { @@ -511,8 +511,8 @@ suite('FindController query options persistence', async () => { isNew: () => false, flush: () => { return Promise.resolve(); }, keys: () => [], - logStorage: () => { }, - migrate: () => { throw new Error(); } + log: () => { }, + switch: () => { throw new Error(); } } as IStorageService); test('matchCase', async () => { diff --git a/src/vs/editor/contrib/folding/browser/folding.ts b/src/vs/editor/contrib/folding/browser/folding.ts index aa85b0fa29b..391249b2436 100644 --- a/src/vs/editor/contrib/folding/browser/folding.ts +++ b/src/vs/editor/contrib/folding/browser/folding.ts @@ -355,9 +355,7 @@ export class FoldingController extends Disposable implements IEditorContribution const selectionLineNumbers = selections ? selections.map(s => s.startLineNumber) : []; foldingModel.update(foldingRanges, selectionLineNumbers); - if (scrollState) { - scrollState.restore(this.editor); - } + scrollState?.restore(this.editor); // update debounce info const newValue = this.updateDebounceInfo.update(foldingModel.textModel, sw.elapsed()); diff --git a/src/vs/editor/contrib/hover/browser/contentHover.ts b/src/vs/editor/contrib/hover/browser/contentHover.ts index bf93d3b9d94..586e15584c5 100644 --- a/src/vs/editor/contrib/hover/browser/contentHover.ts +++ b/src/vs/editor/contrib/hover/browser/contentHover.ts @@ -9,7 +9,6 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; -import { Constants } from 'vs/base/common/uint'; import { ContentWidgetPositionPreference, IActiveCodeEditor, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; @@ -199,17 +198,7 @@ export class ContentHoverController extends Disposable { } private _renderMessages(anchor: HoverAnchor, messages: IHoverPart[]): void { - // update column from which to show - let renderColumn = Constants.MAX_SAFE_SMALL_INTEGER; - let highlightRange: Range = messages[0].range; - let forceShowAtRange: Range | null = null; - for (const msg of messages) { - renderColumn = Math.min(renderColumn, msg.range.startColumn); - highlightRange = Range.plusRange(highlightRange, msg.range); - if (msg.forceShowAtRange) { - forceShowAtRange = msg.range; - } - } + const { showAtPosition, showAtRange, highlightRange } = ContentHoverController.computeHoverRanges(anchor.range, messages); const disposables = new DisposableStore(); const statusBar = disposables.add(new EditorHoverStatusBar(this._keybindingService)); @@ -247,8 +236,8 @@ export class ContentHoverController extends Disposable { this._widget.showAt(fragment, new ContentHoverVisibleData( colorPicker, - forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchor.range.startLineNumber, renderColumn), - forceShowAtRange ? forceShowAtRange : highlightRange, + showAtPosition, + showAtRange, this._editor.getOption(EditorOption.hover).above, this._computer.shouldFocus, disposables @@ -262,6 +251,33 @@ export class ContentHoverController extends Disposable { description: 'content-hover-highlight', className: 'hoverHighlight' }); + + public static computeHoverRanges(anchorRange: Range, messages: IHoverPart[]) { + // The anchor range is always on a single line + const anchorLineNumber = anchorRange.startLineNumber; + let renderStartColumn = anchorRange.startColumn; + let renderEndColumn = anchorRange.endColumn; + let highlightRange: Range = messages[0].range; + let forceShowAtRange: Range | null = null; + + for (const msg of messages) { + highlightRange = Range.plusRange(highlightRange, msg.range); + if (msg.range.startLineNumber === anchorLineNumber && msg.range.endLineNumber === anchorLineNumber) { + // this message has a range that is completely sitting on the line of the anchor + renderStartColumn = Math.min(renderStartColumn, msg.range.startColumn); + renderEndColumn = Math.max(renderEndColumn, msg.range.endColumn); + } + if (msg.forceShowAtRange) { + forceShowAtRange = msg.range; + } + } + + return { + showAtPosition: forceShowAtRange ? forceShowAtRange.getStartPosition() : new Position(anchorRange.startLineNumber, renderStartColumn), + showAtRange: forceShowAtRange ? forceShowAtRange : new Range(anchorLineNumber, renderStartColumn, anchorLineNumber, renderEndColumn), + highlightRange + }; + } } class ContentHoverVisibleData { diff --git a/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts new file mode 100644 index 00000000000..7e35cea943b --- /dev/null +++ b/src/vs/editor/contrib/hover/test/browser/contentHover.test.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHover'; +import { Range } from 'vs/editor/common/core/range'; +import { Position } from 'vs/editor/common/core/position'; +import { IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; + +suite('Content Hover', () => { + test('issue #151235: Gitlens hover shows up in the wrong place', () => { + const actual = ContentHoverController.computeHoverRanges( + new Range(5, 5, 5, 5), + [{ range: new Range(4, 1, 5, 6) }] + ); + assert.deepStrictEqual( + actual, + { + showAtPosition: new Position(5, 5), + showAtRange: new Range(5, 5, 5, 5), + highlightRange: new Range(4, 1, 5, 6) + } + ); + }); +}); 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 909e62f727c..6d93e308c95 100644 --- a/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts +++ b/src/vs/editor/contrib/inlineCompletions/test/browser/suggestWidgetModel.test.ts @@ -27,7 +27,7 @@ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; -import assert = require('assert'); +import * as assert from 'assert'; import { createTextModel } from 'vs/editor/test/common/testTextModel'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; 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 224ce76ce3a..70f5e5a648d 100644 --- a/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts +++ b/src/vs/editor/contrib/multicursor/test/browser/multicursor.test.ts @@ -94,8 +94,8 @@ suite('Multicursor selection', () => { getNumber: (key: string) => undefined!, store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); }, remove: (key) => undefined, - logStorage: () => undefined, - migrate: (toWorkspace) => Promise.resolve(undefined), + log: () => undefined, + switch: () => Promise.resolve(undefined), flush: () => Promise.resolve(undefined), isNew: () => true, keys: () => [] diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 43aeb4c6509..3396636e3be 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -159,9 +159,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.keyVisible.set(true); this.visible = true; setTimeout(() => { - if (this.domNodes) { - this.domNodes.element.classList.add('visible'); - } + this.domNodes?.element.classList.add('visible'); }, 100); this.editor.layoutContentWidget(this); } @@ -176,9 +174,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { this.keyVisible.reset(); this.visible = false; this.announcedLabel = null; - if (this.domNodes) { - this.domNodes.element.classList.remove('visible'); - } + this.domNodes?.element.classList.remove('visible'); this.editor.layoutContentWidget(this); } diff --git a/src/vs/editor/contrib/snippet/browser/snippetController2.ts b/src/vs/editor/contrib/snippet/browser/snippetController2.ts index bb8282d9ee5..43e72f6ce57 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetController2.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetController2.ts @@ -16,7 +16,7 @@ import { CompletionItem, CompletionItemKind, CompletionItemProvider } from 'vs/e import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; import { ITextModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { Choice } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { Choice, SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; import { showSimpleSuggestions } from 'vs/editor/contrib/suggest/browser/suggest'; import { OvertypingCapturer } from 'vs/editor/contrib/suggest/browser/suggestOvertypingCapturer'; import { localize } from 'vs/nls'; @@ -346,3 +346,46 @@ export function performSnippetEdit(editor: ICodeEditor, snippet: string, selecti controller.insert(snippet); return controller.isInSnippet(); } + + +export type ISnippetEdit = { + range: Range; + snippet: string; +}; + +// --- + +export function performSnippetEdits(editor: ICodeEditor, edits: ISnippetEdit[]) { + + if (!editor.hasModel()) { + return false; + } + if (edits.length === 0) { + return false; + } + + const model = editor.getModel(); + let newText = ''; + let last: ISnippetEdit | undefined; + edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + + for (const item of edits) { + if (last) { + const between = Range.fromPositions(last.range.getEndPosition(), item.range.getStartPosition()); + const text = model.getValueInRange(between); + newText += SnippetParser.escape(text); + } + newText += item.snippet; + last = item; + } + + const controller = SnippetController2.get(editor); + if (!controller) { + return false; + } + model.pushStackElement(); + const range = Range.plusRange(edits[0].range, edits[edits.length - 1].range); + editor.setSelection(range); + controller.insert(newText, { undoStopBefore: false }); + return controller.isInSnippet(); +} diff --git a/src/vs/editor/contrib/snippet/browser/snippetSession.ts b/src/vs/editor/contrib/snippet/browser/snippetSession.ts index d1629256724..3ad72329e91 100644 --- a/src/vs/editor/contrib/snippet/browser/snippetSession.ts +++ b/src/vs/editor/contrib/snippet/browser/snippetSession.ts @@ -49,7 +49,7 @@ export class OneSnippet { this._placeholderGroupsIdx = -1; } - public initialize(textChange: TextChange): void { + initialize(textChange: TextChange): void { this._offset = textChange.newPosition; } diff --git a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts index 96810968961..b36e7f8b570 100644 --- a/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts +++ b/src/vs/editor/contrib/snippet/test/browser/snippetParser.test.ts @@ -97,12 +97,12 @@ suite('SnippetParser', () => { function assertMarker(input: TextmateSnippet | Marker[] | string, ...ctors: Function[]) { let marker: Marker[]; if (input instanceof TextmateSnippet) { - marker = input.children; + marker = [...input.children]; } else if (typeof input === 'string') { const p = new SnippetParser(); marker = p.parse(input).children; } else { - marker = input; + marker = [...input]; } while (marker.length > 0) { const m = marker.pop(); @@ -273,12 +273,13 @@ suite('SnippetParser', () => { assertTextAndMarker('${1|one,two,three,|}', '${1|one,two,three,|}', Text); assertTextAndMarker('${1|one,', '${1|one,', Text); - const p = new SnippetParser(); - const snippet = p.parse('${1|one,two,three|}'); - assertMarker(snippet, Placeholder); - const expected = [Placeholder, Text, Text, Text]; + const snippet = new SnippetParser().parse('${1|one,two,three|}'); + const expected: ((m: Marker) => boolean)[] = [ + m => m instanceof Placeholder, + m => m instanceof Choice && m.options.length === 3 && m.options.every(x => x instanceof Text), + ]; snippet.walk(marker => { - assert.strictEqual(marker, expected.shift()); + assert.ok(expected.shift()!(marker)); return true; }); }); 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 27fe72154f8..2fc81e3e176 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/beforeEditPositionMapper.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { splitLines } from 'vs/base/common/strings'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; 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 16c59a7bfe2..abf6200a8fa 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/brackets.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { SmallImmutableSet, DenseKeyProvider } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; 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 f0b19c8a90f..c0c81ff2096 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/concat23Trees.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { AstNode, AstNodeKind, ListAstNode, TextAstNode } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/ast'; import { toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; import { concat23Trees } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/concat23Trees'; 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 4ee07976cb0..e74219407a6 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/getBracketPairsInRange.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore, disposeOnReturn } from 'vs/base/common/lifecycle'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; 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 20e317d0843..b1932043ddd 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/length.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { Length, lengthAdd, lengthDiffNonNegative, lengthToObj, toLength } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; 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 f36fe4db187..de3168139c5 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/smallImmutableSet.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DenseKeyProvider, SmallImmutableSet } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/smallImmutableSet'; 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 2615475d9f0..d1800b09f0d 100644 --- a/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts +++ b/src/vs/editor/test/common/model/bracketPairColorizer/tokenizer.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { LanguageAgnosticBracketTokens } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/brackets'; import { Length, lengthAdd, lengthsToRange, lengthZero } from 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/length'; diff --git a/src/vs/editor/test/common/model/textModelWithTokens.test.ts b/src/vs/editor/test/common/model/textModelWithTokens.test.ts index 30a4da8ee9f..4d4bfcafcc4 100644 --- a/src/vs/editor/test/common/model/textModelWithTokens.test.ts +++ b/src/vs/editor/test/common/model/textModelWithTokens.test.ts @@ -163,9 +163,7 @@ function assertIsNotBracket(model: TextModel, lineNumber: number, column: number function assertIsBracket(model: TextModel, testPosition: Position, expected: [Range, Range]): void { expected.sort(Range.compareRangesUsingStarts); const actual = model.bracketPairs.matchBracket(testPosition); - if (actual) { - actual.sort(Range.compareRangesUsingStarts); - } + actual?.sort(Range.compareRangesUsingStarts); assert.deepStrictEqual(actual, expected, 'matches brackets at ' + testPosition); } diff --git a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts index 9b2715c1c23..9de24985297 100644 --- a/src/vs/editor/test/common/viewModel/lineBreakData.test.ts +++ b/src/vs/editor/test/common/viewModel/lineBreakData.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert = require('assert'); +import * as assert from 'assert'; import { PositionAffinity } from 'vs/editor/common/model'; import { ModelDecorationInjectedTextOptions } from 'vs/editor/common/model/textModel'; import { ModelLineProjectionData } from 'vs/editor/common/modelLineProjectionData'; diff --git a/src/vs/loader.d.ts b/src/vs/loader.d.ts new file mode 100644 index 00000000000..5495321da78 --- /dev/null +++ b/src/vs/loader.d.ts @@ -0,0 +1,35 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare namespace AMDLoader { + interface ILoaderPlugin { + load: (pluginParam: string, parentRequire: IRelativeRequire, loadCallback: IPluginLoadCallback, options: IConfigurationOptions) => void; + write?: (pluginName: string, moduleName: string, write: IPluginWriteCallback) => void; + writeFile?: (pluginName: string, moduleName: string, req: IRelativeRequire, write: IPluginWriteFileCallback, config: IConfigurationOptions) => void; + finishBuild?: (write: (filename: string, contents: string) => void) => void; + } + interface IRelativeRequire { + (dependencies: string[], callback: Function, errorback?: (error: Error) => void): void; + toUrl(id: string): string; + } + interface IPluginLoadCallback { + (value: any): void; + error(err: any): void; + } + interface IConfigurationOptions { + isBuild: boolean | undefined; + [key: string]: any; + } + interface IPluginWriteCallback { + (contents: string): void; + getEntryPoint(): string; + asModule(moduleId: string, contents: string): void; + } + interface IPluginWriteFileCallback { + (filename: string, contents: string): void; + getEntryPoint(): string; + asModule(moduleId: string, contents: string): void; + } +} diff --git a/src/vs/loader.js b/src/vs/loader.js index 69554c5fac4..38eea8a3ca5 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -291,6 +291,9 @@ var AMDLoader; if (typeof options.isBuild !== 'boolean') { options.isBuild = false; } + if (typeof options.buildForceInvokeFactory !== 'object') { + options.buildForceInvokeFactory = {}; + } if (typeof options.paths !== 'object') { options.paths = {}; } @@ -536,6 +539,15 @@ var AMDLoader; Configuration.prototype.isBuild = function () { return this.options.isBuild; }; + Configuration.prototype.shouldInvokeFactory = function (strModuleId) { + if (!this.options.isBuild) { + // outside of a build, all factories should be invoked + return true; + } + // during a build, only explicitly marked or anonymous modules get their factories invoked + return (this.options.buildForceInvokeFactory[strModuleId] + || AMDLoader.Utilities.isAnonymousModule(strModuleId)); + }; /** * Test if module `moduleId` is expected to be defined multiple times */ @@ -1171,7 +1183,7 @@ var AMDLoader; } }; Module._invokeFactory = function (config, strModuleId, callback, dependenciesValues) { - if (config.isBuild() && !AMDLoader.Utilities.isAnonymousModule(strModuleId)) { + if (!config.shouldInvokeFactory(strModuleId)) { return { returnedValue: null, producedError: null diff --git a/src/vs/nls.build.js b/src/vs/nls.build.js deleted file mode 100644 index 0d2929661aa..00000000000 --- a/src/vs/nls.build.js +++ /dev/null @@ -1,182 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var _nlsPluginGlobal = this; -var NLSBuildLoaderPlugin; -(function (NLSBuildLoaderPlugin) { - var global = (_nlsPluginGlobal || {}); - var Resources = global.Plugin && global.Plugin.Resources ? global.Plugin.Resources : undefined; - var IS_PSEUDO = (global && global.document && global.document.location && global.document.location.hash.indexOf('pseudo=true') >= 0); - function _format(message, args) { - var result; - if (args.length === 0) { - result = message; - } - else { - result = message.replace(/\{(\d+)\}/g, function (match, rest) { - var index = rest[0]; - return typeof args[index] !== 'undefined' ? args[index] : match; - }); - } - if (IS_PSEUDO) { - // FF3B and FF3D is the Unicode zenkaku representation for [ and ] - result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; - } - return result; - } - function findLanguageForModule(config, name) { - var result = config[name]; - if (result) - return result; - result = config['*']; - if (result) - return result; - return null; - } - function localize(data, message) { - var args = []; - for (var _i = 0; _i < (arguments.length - 2); _i++) { - args[_i] = arguments[_i + 2]; - } - return _format(message, args); - } - function createScopedLocalize(scope) { - return function (idx, defaultValue) { - var restArgs = Array.prototype.slice.call(arguments, 2); - return _format(scope[idx], restArgs); - }; - } - var NLSPlugin = /** @class */ (function () { - function NLSPlugin() { - this.localize = localize; - } - NLSPlugin.prototype.setPseudoTranslation = function (value) { - IS_PSEUDO = value; - }; - NLSPlugin.prototype.create = function (key, data) { - return { - localize: createScopedLocalize(data[key]) - }; - }; - NLSPlugin.prototype.load = function (name, req, load, config) { - config = config || {}; - if (!name || name.length === 0) { - load({ - localize: localize - }); - } - else { - var suffix = void 0; - if (Resources && Resources.getString) { - suffix = '.nls.keys'; - req([name + suffix], function (keyMap) { - load({ - localize: function (moduleKey, index) { - if (!keyMap[moduleKey]) - return 'NLS error: unknown key ' + moduleKey; - var mk = keyMap[moduleKey].keys; - if (index >= mk.length) - return 'NLS error unknown index ' + index; - var subKey = mk[index]; - var args = []; - args[0] = moduleKey + '_' + subKey; - for (var _i = 0; _i < (arguments.length - 2); _i++) { - args[_i + 1] = arguments[_i + 2]; - } - return Resources.getString.apply(Resources, args); - } - }); - }); - } - else { - if (config.isBuild) { - req([name + '.nls', name + '.nls.keys'], function (messages, keys) { - NLSPlugin.BUILD_MAP[name] = messages; - NLSPlugin.BUILD_MAP_KEYS[name] = keys; - load(messages); - }); - } - else { - var pluginConfig = config['vs/nls'] || {}; - var language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; - suffix = '.nls'; - if (language !== null && language !== NLSPlugin.DEFAULT_TAG) { - suffix = suffix + '.' + language; - } - req([name + suffix], function (messages) { - if (Array.isArray(messages)) { - messages.localize = createScopedLocalize(messages); - } - else { - messages.localize = createScopedLocalize(messages[name]); - } - load(messages); - }); - } - } - } - }; - NLSPlugin.prototype._getEntryPointsMap = function () { - global.nlsPluginEntryPoints = global.nlsPluginEntryPoints || {}; - return global.nlsPluginEntryPoints; - }; - NLSPlugin.prototype.write = function (pluginName, moduleName, write) { - // getEntryPoint is a Monaco extension to r.js - var entryPoint = write.getEntryPoint(); - // r.js destroys the context of this plugin between calling 'write' and 'writeFile' - // so the only option at this point is to leak the data to a global - var entryPointsMap = this._getEntryPointsMap(); - entryPointsMap[entryPoint] = entryPointsMap[entryPoint] || []; - entryPointsMap[entryPoint].push(moduleName); - if (moduleName !== entryPoint) { - write.asModule(pluginName + '!' + moduleName, 'define([\'vs/nls\', \'vs/nls!' + entryPoint + '\'], function(nls, data) { return nls.create("' + moduleName + '", data); });'); - } - }; - NLSPlugin.prototype.writeFile = function (pluginName, moduleName, req, write, config) { - var entryPointsMap = this._getEntryPointsMap(); - if (entryPointsMap.hasOwnProperty(moduleName)) { - var fileName = req.toUrl(moduleName + '.nls.js'); - var contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], entries = entryPointsMap[moduleName]; - var data = {}; - for (var i = 0; i < entries.length; i++) { - data[entries[i]] = NLSPlugin.BUILD_MAP[entries[i]]; - } - contents.push('define("' + moduleName + '.nls", ' + JSON.stringify(data, null, '\t') + ');'); - write(fileName, contents.join('\r\n')); - } - }; - NLSPlugin.prototype.finishBuild = function (write) { - write('nls.metadata.json', JSON.stringify({ - keys: NLSPlugin.BUILD_MAP_KEYS, - messages: NLSPlugin.BUILD_MAP, - bundles: this._getEntryPointsMap() - }, null, '\t')); - }; - ; - NLSPlugin.DEFAULT_TAG = 'i-default'; - NLSPlugin.BUILD_MAP = {}; - NLSPlugin.BUILD_MAP_KEYS = {}; - return NLSPlugin; - }()); - NLSBuildLoaderPlugin.NLSPlugin = NLSPlugin; - (function () { - define('vs/nls', new NLSPlugin()); - })(); -})(NLSBuildLoaderPlugin || (NLSBuildLoaderPlugin = {})); diff --git a/src/vs/nls.build.ts b/src/vs/nls.build.ts new file mode 100644 index 00000000000..f73bdc2d1b4 --- /dev/null +++ b/src/vs/nls.build.ts @@ -0,0 +1,80 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const buildMap: { [name: string]: string[] } = {}; +const buildMapKeys: { [name: string]: string[] } = {}; +const entryPoints: { [entryPoint: string]: string[] } = {}; + +export interface ILocalizeInfo { + key: string; + comment: string[]; +} + +export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { + throw new Error(`Not supported at build time!`); +} + +/** + * Invoked by the loader at build-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + if (!name || name.length === 0) { + load({ localize }); + } else { + req([name + '.nls', name + '.nls.keys'], function (messages: string[], keys: string[]) { + buildMap[name] = messages; + buildMapKeys[name] = keys; + load(messages); + }); + } +} + +/** + * Invoked by the loader at build-time + */ +export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { + const entryPoint = write.getEntryPoint(); + + entryPoints[entryPoint] = entryPoints[entryPoint] || []; + entryPoints[entryPoint].push(moduleName); + + if (moduleName !== entryPoint) { + write.asModule(pluginName + '!' + moduleName, 'define([\'vs/nls\', \'vs/nls!' + entryPoint + '\'], function(nls, data) { return nls.create("' + moduleName + '", data); });'); + } +} + +/** + * Invoked by the loader at build-time + */ +export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { + if (entryPoints.hasOwnProperty(moduleName)) { + const fileName = req.toUrl(moduleName + '.nls.js'); + const contents = [ + '/*---------------------------------------------------------', + ' * Copyright (c) Microsoft Corporation. All rights reserved.', + ' *--------------------------------------------------------*/' + ], + entries = entryPoints[moduleName]; + + const data: { [moduleName: string]: string[] } = {}; + for (let i = 0; i < entries.length; i++) { + data[entries[i]] = buildMap[entries[i]]; + } + + contents.push('define("' + moduleName + '.nls", ' + JSON.stringify(data, null, '\t') + ');'); + write(fileName, contents.join('\r\n')); + } +} + +/** + * Invoked by the loader at build-time + */ +export function finishBuild(write: AMDLoader.IPluginWriteFileCallback): void { + write('nls.metadata.json', JSON.stringify({ + keys: buildMapKeys, + messages: buildMap, + bundles: entryPoints + }, null, '\t')); +} diff --git a/src/vs/nls.d.ts b/src/vs/nls.d.ts deleted file mode 100644 index c17c85b0191..00000000000 --- a/src/vs/nls.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface ILocalizeInfo { - key: string; - comment: string[]; -} - -/** - * Localize a message. - * - * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` - * For example, `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)` - */ -export declare function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; - -/** - * Localize a message. - * - * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` - * For example, `localize('sayHello', 'hello {0}', name)` - */ -export declare function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; diff --git a/src/vs/nls.js b/src/vs/nls.js deleted file mode 100644 index 793f0d6eecb..00000000000 --- a/src/vs/nls.js +++ /dev/null @@ -1,167 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -/*--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - * Please make sure to make edits in the .ts file at https://github.com/microsoft/vscode-loader/ - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------- - *--------------------------------------------------------------------------------------------*/ -'use strict'; -var __spreadArrays = (this && this.__spreadArrays) || function () { - for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; - for (var r = Array(s), k = 0, i = 0; i < il; i++) - for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) - r[k] = a[j]; - return r; -}; -var NLSLoaderPlugin; -(function (NLSLoaderPlugin) { - var Environment = /** @class */ (function () { - function Environment() { - this._detected = false; - this._isPseudo = false; - } - Object.defineProperty(Environment.prototype, "isPseudo", { - get: function () { - this._detect(); - return this._isPseudo; - }, - enumerable: false, - configurable: true - }); - Environment.prototype._detect = function () { - if (this._detected) { - return; - } - this._detected = true; - this._isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); - }; - return Environment; - }()); - function _format(message, args, env) { - var result; - if (args.length === 0) { - result = message; - } - else { - result = message.replace(/\{(\d+)\}/g, function (match, rest) { - var index = rest[0]; - var arg = args[index]; - var result = match; - if (typeof arg === 'string') { - result = arg; - } - else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { - result = String(arg); - } - return result; - }); - } - if (env.isPseudo) { - // FF3B and FF3D is the Unicode zenkaku representation for [ and ] - result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; - } - return result; - } - function findLanguageForModule(config, name) { - var result = config[name]; - if (result) - return result; - result = config['*']; - if (result) - return result; - return null; - } - function localize(env, data, message) { - var args = []; - for (var _i = 3; _i < arguments.length; _i++) { - args[_i - 3] = arguments[_i]; - } - return _format(message, args, env); - } - function createScopedLocalize(scope, env) { - return function (idx, defaultValue) { - var restArgs = Array.prototype.slice.call(arguments, 2); - return _format(scope[idx], restArgs, env); - }; - } - var NLSPlugin = /** @class */ (function () { - function NLSPlugin(env) { - var _this = this; - this._env = env; - this.localize = function (data, message) { - var args = []; - for (var _i = 2; _i < arguments.length; _i++) { - args[_i - 2] = arguments[_i]; - } - return localize.apply(void 0, __spreadArrays([_this._env, data, message], args)); - }; - } - NLSPlugin.prototype.setPseudoTranslation = function (value) { - this._env._isPseudo = value; - }; - NLSPlugin.prototype.create = function (key, data) { - return { - localize: createScopedLocalize(data[key], this._env) - }; - }; - NLSPlugin.prototype.load = function (name, req, load, config) { - var _this = this; - var _a; - config = config || {}; - if (!name || name.length === 0) { - load({ - localize: this.localize - }); - } - else { - var pluginConfig = config['vs/nls'] || {}; - var language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; - var suffix = '.nls'; - if (language !== null && language !== NLSPlugin.DEFAULT_TAG) { - suffix = suffix + '.' + language; - } - var messagesLoaded_1 = function (messages) { - if (Array.isArray(messages)) { - messages.localize = createScopedLocalize(messages, _this._env); - } - else { - messages.localize = createScopedLocalize(messages[name], _this._env); - } - load(messages); - }; - if (typeof pluginConfig.loadBundle === 'function') { - pluginConfig.loadBundle(name, language, function (err, messages) { - // We have an error. Load the English default strings to not fail - if (err) { - req([name + '.nls'], messagesLoaded_1); - } - else { - messagesLoaded_1(messages); - } - }); - } - else { - var base = (_a = pluginConfig.baseUrl) !== null && _a !== void 0 ? _a : ''; - req([base + name + suffix], messagesLoaded_1, function (err) { - var _a; - // We have an error. Load the English default strings instead. - console.warn("Falling back to default strings. Unable to load translations because of: " + ((_a = err.message) !== null && _a !== void 0 ? _a : err)); - req([name + '.nls'], messagesLoaded_1); - }); - } - } - }; - NLSPlugin.DEFAULT_TAG = 'i-default'; - return NLSPlugin; - }()); - NLSLoaderPlugin.NLSPlugin = NLSPlugin; - define('vs/nls', new NLSPlugin(new Environment())); -})(NLSLoaderPlugin || (NLSLoaderPlugin = {})); diff --git a/src/vs/nls.ts b/src/vs/nls.ts new file mode 100644 index 00000000000..2a257b368d4 --- /dev/null +++ b/src/vs/nls.ts @@ -0,0 +1,167 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); +const DEFAULT_TAG = 'i-default'; + +interface INLSPluginConfig { + availableLanguages?: INLSPluginConfigAvailableLanguages; + loadBundle?: BundleLoader; + baseUrl?: string; +} + +interface INLSPluginConfigAvailableLanguages { + '*'?: string; + [module: string]: string | undefined; +} + +interface BundleLoader { + (bundle: string, locale: string | null, cb: (err: Error, messages: string[] | IBundledStrings) => void): void; +} + +interface IBundledStrings { + [moduleId: string]: string[]; +} + +export interface ILocalizeInfo { + key: string; + comment: string[]; +} + +interface ILocalizeFunc { + (info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + (key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; +} + +interface IBoundLocalizeFunc { + (idx: number, defaultValue: null): string; +} + +interface IConsumerAPI { + localize: ILocalizeFunc | IBoundLocalizeFunc; +} + +function _format(message: string, args: (string | number | boolean | undefined | null)[]): string { + let result: string; + + if (args.length === 0) { + result = message; + } else { + result = message.replace(/\{(\d+)\}/g, (match, rest) => { + const index = rest[0]; + const arg = args[index]; + let result = match; + if (typeof arg === 'string') { + result = arg; + } else if (typeof arg === 'number' || typeof arg === 'boolean' || arg === void 0 || arg === null) { + result = String(arg); + } + return result; + }); + } + + if (isPseudo) { + // FF3B and FF3D is the Unicode zenkaku representation for [ and ] + result = '\uFF3B' + result.replace(/[aouei]/g, '$&$&') + '\uFF3D'; + } + + return result; +} + +function findLanguageForModule(config: INLSPluginConfigAvailableLanguages, name: string) { + let result = config[name]; + if (result) { + return result; + } + result = config['*']; + if (result) { + return result; + } + return null; +} + +function createScopedLocalize(scope: string[]): IBoundLocalizeFunc { + return function (idx: number, defaultValue: null) { + const restArgs = Array.prototype.slice.call(arguments, 2); + return _format(scope[idx], restArgs); + }; +} + +/** + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize({ key: 'sayHello', comment: ['Welcomes user'] }, 'hello {0}', name)` + */ +export function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +/** + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize('sayHello', 'hello {0}', name)` + */ +export function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; + +export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { + return _format(message, args); +} + +export function setPseudoTranslation(value: boolean) { + isPseudo = value; +} + +/** + * Invoked in a built product at run-time + */ +export function create(key: string, data: IBundledStrings): IConsumerAPI { + return { + localize: createScopedLocalize(data[key]) + }; +} + +/** + * Invoked by the loader at run-time + */ +export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { + config = config || {}; + if (!name || name.length === 0) { + load({ + localize: localize + }); + } else { + const pluginConfig = (config['vs/nls'] || {}); + const language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; + let suffix = '.nls'; + if (language !== null && language !== DEFAULT_TAG) { + suffix = suffix + '.' + language; + } + const messagesLoaded = (messages: string[] | IBundledStrings) => { + if (Array.isArray(messages)) { + (messages as any as IConsumerAPI).localize = createScopedLocalize(messages); + } else { + (messages as any as IConsumerAPI).localize = createScopedLocalize(messages[name]); + } + load(messages); + }; + if (typeof pluginConfig.loadBundle === 'function') { + (pluginConfig.loadBundle as BundleLoader)(name, language, (err: Error, messages) => { + // We have an error. Load the English default strings to not fail + if (err) { + req([name + '.nls'], messagesLoaded); + } else { + messagesLoaded(messages); + } + }); + } else { + const base = pluginConfig.baseUrl ?? ''; + req([base + name + suffix], messagesLoaded, (err: Error) => { + // We have an error. Load the English default strings instead. + console.warn(`Falling back to default strings. Unable to load translations because of: ${err.message ?? err}`); + req([name + '.nls'], messagesLoaded); + }); + } + } +} diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index f945cf1b18c..63347d8f2a7 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -425,9 +425,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.removeFromSchema(key, configuration.properties[key]); } } - if (configuration.allOf) { - configuration.allOf.forEach(node => deregisterConfiguration(node)); - } + configuration.allOf?.forEach(node => deregisterConfiguration(node)); }; for (const configuration of configurations) { deregisterConfiguration(configuration); @@ -525,9 +523,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } } const subNodes = configuration.allOf; - if (subNodes) { - subNodes.forEach(register); - } + subNodes?.forEach(register); }; register(configuration); } diff --git a/src/vs/platform/contextview/browser/contextMenuHandler.ts b/src/vs/platform/contextview/browser/contextMenuHandler.ts index 8458bdcb22f..aa6612d89ff 100644 --- a/src/vs/platform/contextview/browser/contextMenuHandler.ts +++ b/src/vs/platform/contextview/browser/contextMenuHandler.ts @@ -123,9 +123,7 @@ export class ContextMenuHandler { }, focus: () => { - if (menu) { - menu.focus(!!delegate.autoSelectFirstItem); - } + menu?.focus(!!delegate.autoSelectFirstItem); }, onHide: (didCancel?: boolean) => { diff --git a/src/vs/platform/dnd/browser/dnd.ts b/src/vs/platform/dnd/browser/dnd.ts index 14c6c8d11d3..4fafc45e2ec 100644 --- a/src/vs/platform/dnd/browser/dnd.ts +++ b/src/vs/platform/dnd/browser/dnd.ts @@ -56,7 +56,7 @@ export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInpu allowWorkspaceOpen?: boolean; } -export async function extractEditorsDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { +export function extractEditorsDropData(e: DragEvent): Array { const editors: IDraggedResourceEditorInput[] = []; if (e.dataTransfer && e.dataTransfer.types.length > 0) { @@ -107,18 +107,6 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag } } - // Web: Check for file transfer - if (isWeb && containsDragType(e, DataTransfers.FILES)) { - const files = e.dataTransfer.items; - if (files) { - const instantiationService = accessor.get(IInstantiationService); - const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); - for (const fileData of filesData) { - editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); - } - } - } - // Workbench contributions const contributions = Registry.as(Extensions.DragAndDropContribution).getAll(); for (const contribution of contributions) { @@ -136,6 +124,24 @@ export async function extractEditorsDropData(accessor: ServicesAccessor, e: Drag return editors; } +export async function extractEditorsAndFilesDropData(accessor: ServicesAccessor, e: DragEvent): Promise> { + const editors = extractEditorsDropData(e); + + // Web: Check for file transfer + if (e.dataTransfer && isWeb && containsDragType(e, DataTransfers.FILES)) { + const files = e.dataTransfer.items; + if (files) { + const instantiationService = accessor.get(IInstantiationService); + const filesData = await instantiationService.invokeFunction(accessor => extractFilesDropData(accessor, e)); + for (const fileData of filesData) { + editors.push({ resource: fileData.resource, contents: fileData.contents?.toString(), isExternal: true, allowWorkspaceOpen: fileData.isDirectory }); + } + } + } + + return editors; +} + export function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string | undefined): IDraggedResourceEditorInput[] { const editors: IDraggedResourceEditorInput[] = []; diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 54d4836668a..5ed69a092ef 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -64,6 +64,9 @@ export interface IEnvironmentService { userDataSyncLogResource: URI; sync: 'on' | 'off' | undefined; + // --- continue edit session + editSessionId?: string; + // --- extension development debugExtensionHost: IExtensionHostDebugParams; isExtensionDevelopment: boolean; diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index b392af1f2b9..6f04a76edb6 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -7,7 +7,6 @@ import { distinct } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IStringDictionary } from 'vs/base/common/collections'; import { CancellationError, getErrorMessage, isCancellationError } from 'vs/base/common/errors'; -import { getOrDefault } from 'vs/base/common/objects'; import { IPager } from 'vs/base/common/paging'; import { isWeb, platform } from 'vs/base/common/platform'; import { arch } from 'vs/base/common/process'; @@ -709,7 +708,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } let text = options.text || ''; - const pageSize = getOrDefault(options, o => o.pageSize, 50); + const pageSize = options.pageSize ?? 50; let query = new Query() .withPage(1, pageSize); diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index c55491edf17..894841a4a4b 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -10,9 +10,8 @@ import { cloneAndChange } from 'vs/base/common/objects'; import { URI, UriComponents } from 'vs/base/common/uri'; import { DefaultURITransformer, IURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, InstallOptions, InstallVSIXOptions, IExtensionsControlManifest, isTargetPlatformCompatible, UninstallOptions, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { DidUninstallExtensionEvent, IExtensionIdentifier, IExtensionManagementService, IExtensionTipsService, IGalleryExtension, IGalleryMetadata, ILocalExtension, InstallExtensionEvent, InstallExtensionResult, IExtensionsControlManifest, isTargetPlatformCompatible, IServerExtensionManagementService, ServerInstallOptions, ServerInstallVSIXOptions, ServerUninstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; function transformIncomingURI(uri: UriComponents, transformer: IURITransformer | null): URI { return URI.revive(transformer ? transformer.transformIncoming(uri) : uri); @@ -98,10 +97,7 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt private readonly _onDidUninstallExtension = this._register(new Emitter()); readonly onDidUninstallExtension = this._onDidUninstallExtension.event; - constructor( - private readonly channel: IChannel, - private readonly userDataProfilesService: IUserDataProfilesService | undefined - ) { + constructor(private readonly channel: IChannel) { super(); this._register(this.channel.listen('onInstallExtension')(e => this._onInstallExtension.fire({ identifier: e.identifier, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source }))); this._register(this.channel.listen('onDidInstallExtensions')(results => this._onDidInstallExtensions.fire(results.map(e => ({ ...e, local: e.local ? transformIncomingExtension(e.local, null) : e.local, source: this.isUriComponents(e.source) ? URI.revive(e.source) : e.source }))))); @@ -138,31 +134,28 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt return Promise.resolve(this.channel.call('unzip', [zipLocation])); } - install(vsix: URI, options?: InstallVSIXOptions): Promise { - const serverInstallOptions: ServerInstallVSIXOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; - return Promise.resolve(this.channel.call('install', [vsix, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); + install(vsix: URI, options?: ServerInstallVSIXOptions): Promise { + return Promise.resolve(this.channel.call('install', [vsix, options])).then(local => transformIncomingExtension(local, null)); } getManifest(vsix: URI): Promise { return Promise.resolve(this.channel.call('getManifest', [vsix])); } - installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { - const serverInstallOptions: ServerInstallOptions = { ...installOptions, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; - return Promise.resolve(this.channel.call('installFromGallery', [extension, serverInstallOptions])).then(local => transformIncomingExtension(local, null)); + installFromGallery(extension: IGalleryExtension, installOptions?: ServerInstallOptions): Promise { + return Promise.resolve(this.channel.call('installFromGallery', [extension, installOptions])).then(local => transformIncomingExtension(local, null)); } - uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { - const serverUninstallOptions: ServerUninstallOptions = { ...options, profileLocation: this.userDataProfilesService?.currentProfile.extensionsResource }; - return Promise.resolve(this.channel.call('uninstall', [extension!, serverUninstallOptions])); + uninstall(extension: ILocalExtension, options?: ServerUninstallOptions): Promise { + return Promise.resolve(this.channel.call('uninstall', [extension!, options])); } reinstallFromGallery(extension: ILocalExtension): Promise { return Promise.resolve(this.channel.call('reinstallFromGallery', [extension])); } - getInstalled(type: ExtensionType | null = null): Promise { - return Promise.resolve(this.channel.call('getInstalled', [type, this.userDataProfilesService?.currentProfile.extensionsResource])) + getInstalled(type: ExtensionType | null = null, extensionsProfileResource?: URI): Promise { + return Promise.resolve(this.channel.call('getInstalled', [type, extensionsProfileResource])) .then(extensions => extensions.map(extension => transformIncomingExtension(extension, null))); } diff --git a/src/vs/platform/extensionManagement/common/extensionStorage.ts b/src/vs/platform/extensionManagement/common/extensionStorage.ts index 13ddc8bd4f5..3e9a3059f98 100644 --- a/src/vs/platform/extensionManagement/common/extensionStorage.ts +++ b/src/vs/platform/extensionManagement/common/extensionStorage.ts @@ -55,6 +55,7 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto return undefined; } + /* TODO @sandy081: This has to be done across all profiles */ static async removeOutdatedExtensionVersions(extensionManagementService: IExtensionManagementService, storageService: IStorageService): Promise { const extensions = await extensionManagementService.getInstalled(); const extensionVersionsToRemove: string[] = []; @@ -193,7 +194,7 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto } private get migrationList(): [string, string][] { - const value = this.storageService.get('extensionStorage.migrationList', StorageScope.GLOBAL, '[]'); + const value = this.storageService.get('extensionStorage.migrationList', StorageScope.APPLICATION, '[]'); try { const migrationList = JSON.parse(value); if (isArray(migrationList)) { @@ -205,9 +206,9 @@ export class ExtensionStorageService extends Disposable implements IExtensionSto private set migrationList(migrationList: [string, string][]) { if (migrationList.length) { - this.storageService.store('extensionStorage.migrationList', JSON.stringify(migrationList), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('extensionStorage.migrationList', JSON.stringify(migrationList), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove('extensionStorage.migrationList', StorageScope.GLOBAL); + this.storageService.remove('extensionStorage.migrationList', StorageScope.APPLICATION); } } diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index c54fe1b2a34..2c453505e19 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -173,7 +173,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem this.scanUserExtensions(userScanOptions), ]); const development = await this.scanExtensionsUnderDevelopment(systemScanOptions, [...system, ...user]); - return this.dedupExtensions([...system, ...user, ...development], await this.getTargetPlatform(), true); + return this.dedupExtensions(system, user, development, await this.getTargetPlatform(), true); } async scanSystemExtensions(scanOptions: ScanOptions): Promise { @@ -181,7 +181,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem promises.push(this.scanDefaultSystemExtensions(!!scanOptions.useCache, scanOptions.language)); promises.push(this.scanDevSystemExtensions(scanOptions.language, !!scanOptions.checkControlFile)); const [defaultSystemExtensions, devSystemExtensions] = await Promise.all(promises); - return this.applyScanOptions([...defaultSystemExtensions, ...devSystemExtensions], scanOptions, false); + return this.applyScanOptions([...defaultSystemExtensions, ...devSystemExtensions], ExtensionType.System, scanOptions, false); } async scanUserExtensions(scanOptions: ScanOptions): Promise { @@ -189,7 +189,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem const extensionsScannerInput = await this.createExtensionScannerInput(scanOptions.profileLocation ?? this.userExtensionsLocation, !!scanOptions.profileLocation, ExtensionType.User, !scanOptions.includeUninstalled, scanOptions.language); const extensionsScanner = scanOptions.useCache && !extensionsScannerInput.devMode && extensionsScannerInput.excludeObsolete ? this.userExtensionsCachedScanner : this.extensionsScanner; let extensions = await extensionsScanner.scanExtensions(extensionsScannerInput); - extensions = await this.applyScanOptions(extensions, scanOptions, true); + extensions = await this.applyScanOptions(extensions, ExtensionType.User, scanOptions, true); this.logService.trace('Scanned user extensions:', extensions.length); return extensions; } @@ -208,7 +208,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem }); }))) .flat(); - return this.applyScanOptions(extensions, scanOptions, true); + return this.applyScanOptions(extensions, 'development', scanOptions, true); } return []; } @@ -228,7 +228,7 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem async scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise { const extensionsScannerInput = await this.createExtensionScannerInput(extensionLocation, false, extensionType, true, scanOptions.language); const extensions = await this.extensionsScanner.scanOneOrMultipleExtensions(extensionsScannerInput); - return this.applyScanOptions(extensions, scanOptions, true); + return this.applyScanOptions(extensions, extensionType, scanOptions, true); } async scanMetadata(extensionLocation: URI): Promise { @@ -252,9 +252,9 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem await this.fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest, null, '\t'))); } - private async applyScanOptions(extensions: IRelaxedScannedExtension[], scanOptions: ScanOptions, pickLatest: boolean): Promise { + private async applyScanOptions(extensions: IRelaxedScannedExtension[], type: ExtensionType | 'development', scanOptions: ScanOptions, pickLatest: boolean): Promise { if (!scanOptions.includeAllVersions) { - extensions = this.dedupExtensions(extensions, await this.getTargetPlatform(), pickLatest); + extensions = this.dedupExtensions(type === ExtensionType.System ? extensions : undefined, type === ExtensionType.User ? extensions : undefined, type === 'development' ? extensions : undefined, await this.getTargetPlatform(), pickLatest); } if (!scanOptions.includeInvalid) { extensions = extensions.filter(extension => extension.isValid); @@ -272,33 +272,61 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem }); } - private dedupExtensions(extensions: IRelaxedScannedExtension[], targetPlatform: TargetPlatform, pickLatest: boolean): IRelaxedScannedExtension[] { - const result = new Map(); - for (const extension of extensions) { - const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); - const existing = result.get(extensionKey); - if (existing) { - if (existing.isValid && !extension.isValid) { - continue; + private dedupExtensions(system: IScannedExtension[] | undefined, user: IScannedExtension[] | undefined, development: IScannedExtension[] | undefined, targetPlatform: TargetPlatform, pickLatest: boolean): IScannedExtension[] { + const pick = (existing: IScannedExtension, extension: IScannedExtension): boolean => { + if (existing.isValid && !extension.isValid) { + return false; + } + if (existing.isValid === extension.isValid) { + if (pickLatest && semver.gt(existing.manifest.version, extension.manifest.version)) { + this.logService.debug(`Skipping extension ${extension.location.path} with lower version ${extension.manifest.version} in favour of ${existing.location.path} with version ${existing.manifest.version}`); + return false; } - if (existing.isValid === extension.isValid) { - if (pickLatest && semver.gt(existing.manifest.version, extension.manifest.version)) { - this.logService.debug(`Skipping extension ${extension.location.path} with lower version ${extension.manifest.version}.`); - continue; + if (semver.eq(existing.manifest.version, extension.manifest.version)) { + if (existing.type === ExtensionType.System) { + this.logService.debug(`Skipping extension ${extension.location.path} in favour of system extension ${existing.location.path} with same version`); + return false; } - if (semver.eq(existing.manifest.version, extension.manifest.version) && existing.targetPlatform === targetPlatform) { + if (existing.targetPlatform === targetPlatform) { this.logService.debug(`Skipping extension ${extension.location.path} from different target platform ${extension.targetPlatform}`); - continue; + return false; } } - if (existing.type === ExtensionType.System) { - this.logService.debug(`Overwriting system extension ${existing.location.path} with ${extension.location.path}.`); - } else { - this.logService.warn(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`); - } + } + if (existing.type === ExtensionType.System) { + this.logService.debug(`Overwriting system extension ${existing.location.path} with ${extension.location.path}.`); + } else { + this.logService.warn(`Overwriting user extension ${existing.location.path} with ${extension.location.path}.`); + } + return true; + }; + const result = new Map(); + system?.forEach((extension) => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); + } + }); + user?.forEach((extension) => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing && system && extension.type === ExtensionType.System) { + this.logService.debug(`Skipping obsolete system extension ${extension.location.path}.`); + return; + } + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); + } + }); + development?.forEach(extension => { + const extensionKey = ExtensionIdentifier.toKey(extension.identifier.id); + const existing = result.get(extensionKey); + if (!existing || pick(existing, extension)) { + result.set(extensionKey, extension); } result.set(extensionKey, extension); - } + }); return [...result.values()]; } diff --git a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts index a8d9a94f50f..c6e03e0bf5f 100644 --- a/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts +++ b/src/vs/platform/extensionManagement/electron-sandbox/extensionTipsService.ts @@ -256,7 +256,7 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private getLastPromptedMediumExeTime(): number { - let value = this.storageService.getNumber(lastPromptedMediumImpExeTimeStorageKey, StorageScope.GLOBAL); + let value = this.storageService.getNumber(lastPromptedMediumImpExeTimeStorageKey, StorageScope.APPLICATION); if (!value) { value = Date.now(); this.updateLastPromptedMediumExeTime(value); @@ -265,17 +265,17 @@ export class ExtensionTipsService extends BaseExtensionTipsService { } private updateLastPromptedMediumExeTime(value: number): void { - this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(lastPromptedMediumImpExeTimeStorageKey, value, StorageScope.APPLICATION, StorageTarget.MACHINE); } private getPromptedExecutableTips(): IStringDictionary { - return JSON.parse(this.storageService.get(promptedExecutableTipsStorageKey, StorageScope.GLOBAL, '{}')); + return JSON.parse(this.storageService.get(promptedExecutableTipsStorageKey, StorageScope.APPLICATION, '{}')); } private addToRecommendedExecutables(exeName: string, tips: IExecutableBasedExtensionTip[]) { const promptedExecutableTips = this.getPromptedExecutableTips(); promptedExecutableTips[exeName] = tips.map(({ extensionId }) => extensionId.toLowerCase()); - this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.GLOBAL, StorageTarget.USER); + this.storageService.store(promptedExecutableTipsStorageKey, JSON.stringify(promptedExecutableTips), StorageScope.APPLICATION, StorageTarget.USER); } private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[]; uninstalled: string[] } { diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 1006bd05439..a15de91f394 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -432,23 +432,33 @@ class ExtensionsScanner extends Disposable { } private async removeOutdatedExtensions(): Promise { + const systemExtensions = await this.extensionsScannerService.scanSystemExtensions({}); // System extensions const extensions = await this.extensionsScannerService.scanUserExtensions({ includeAllVersions: true, includeUninstalled: true, includeInvalid: true }); // All user extensions const toRemove: IScannedExtension[] = []; // Outdated extensions const targetPlatform = await this.extensionsScannerService.getTargetPlatform(); const byExtension = groupByExtension(extensions, e => e.identifier); - toRemove.push(...byExtension.map(p => p.sort((a, b) => { - const vcompare = semver.rcompare(a.manifest.version, b.manifest.version); - if (vcompare !== 0) { - return vcompare; + for (const extensions of byExtension) { + if (extensions.length > 1) { + toRemove.push(...extensions.sort((a, b) => { + const vcompare = semver.rcompare(a.manifest.version, b.manifest.version); + if (vcompare !== 0) { + return vcompare; + } + if (a.targetPlatform === targetPlatform) { + return -1; + } + return 1; + }).slice(1)); } - if (a.targetPlatform === targetPlatform) { - return -1; + if (extensions[0].type === ExtensionType.System) { + const systemExtension = systemExtensions.find(e => areSameExtensions(e.identifier, extensions[0].identifier)); + if (!systemExtension || semver.gte(systemExtension.manifest.version, extensions[0].manifest.version)) { + toRemove.push(extensions[0]); + } } - return 1; - }).slice(1)).flat()); - + } await Promises.settled(toRemove.map(extension => this.removeExtension(extension, 'outdated'))); } diff --git a/src/vs/platform/externalServices/common/serviceMachineId.ts b/src/vs/platform/externalServices/common/serviceMachineId.ts index e2e17317b5e..6767e7d7225 100644 --- a/src/vs/platform/externalServices/common/serviceMachineId.ts +++ b/src/vs/platform/externalServices/common/serviceMachineId.ts @@ -10,7 +10,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: IStorageService | undefined): Promise { - let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null : null; + let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.APPLICATION) || null : null; if (uuid) { return uuid; } @@ -30,8 +30,8 @@ export async function getServiceMachineId(environmentService: IEnvironmentServic //noop } } - if (storageService) { - storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL, StorageTarget.MACHINE); - } + + storageService?.store('storage.serviceMachineId', uuid, StorageScope.APPLICATION, StorageTarget.MACHINE); + return uuid; } diff --git a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts index dfb0eda9b1f..36b2f65cdfc 100644 --- a/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts +++ b/src/vs/platform/files/browser/indexedDBFileSystemProvider.ts @@ -459,9 +459,7 @@ export class IndexedDBFileSystemProvider extends Disposable implements IFileSyst if (changes.length) { this._onDidChangeFile.fire(changes); - if (this.changesBroadcastChannel) { - this.changesBroadcastChannel.postChanges(changes); - } + this.changesBroadcastChannel?.postChanges(changes); } } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 31884bfdbaa..24cc5db2fab 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -168,9 +168,7 @@ export class IssueMainService implements ICommonIssueService { throw new Error(`Unexpected command source: ${from}`); } - if (parentWindow) { - parentWindow.webContents.send('vscode:runAction', { id, from, args }); - } + parentWindow?.webContents.send('vscode:runAction', { id, from, args }); }); validatedIpcMain.on('vscode:openExternal', (_: unknown, arg: string) => { diff --git a/src/vs/platform/log/common/bufferLog.ts b/src/vs/platform/log/common/bufferLog.ts index df1bdc72c0c..58b9e41e60c 100644 --- a/src/vs/platform/log/common/bufferLog.ts +++ b/src/vs/platform/log/common/bufferLog.ts @@ -32,9 +32,7 @@ export class BufferLogService extends AbstractLogger implements ILogService { super(); this.setLevel(logLevel); this._register(this.onDidChangeLogLevel(level => { - if (this._logger) { - this._logger.setLevel(level); - } + this._logger?.setLevel(level); })); } diff --git a/src/vs/platform/log/node/spdlogLog.ts b/src/vs/platform/log/node/spdlogLog.ts index e72d4db4a36..af7a576df1c 100644 --- a/src/vs/platform/log/node/spdlogLog.ts +++ b/src/vs/platform/log/node/spdlogLog.ts @@ -57,9 +57,7 @@ export class SpdLogLogger extends AbstractMessageLogger implements ILogger { this.setLevel(level); this._loggerCreationPromise = this._createSpdLogLogger(name, filepath, rotating, donotUseFormatters); this._register(this.onDidChangeLogLevel(level => { - if (this._logger) { - this._logger.setLevel(level); - } + this._logger?.setLevel(level); })); } diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index c5cfd92a41c..5f6937e8655 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -71,7 +71,7 @@ export interface ICommonNativeHostService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; - updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise; + updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise; setMinimumSize(width: number | undefined, height: number | undefined): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index f7bd477150d..639c6cfa063 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -212,12 +212,13 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } } - async updateTitleBarOverlay(windowId: number | undefined, backgroundColor: string, foregroundColor: string): Promise { + async updateTitleBarOverlay(windowId: number | undefined, options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { const window = this.windowById(windowId); if (window?.win) { window.win.setTitleBarOverlay({ - color: backgroundColor, - symbolColor: foregroundColor + color: options.backgroundColor, + symbolColor: options.foregroundColor, + height: options.height ? options.height - 1 : undefined // account for window border }); } } @@ -228,9 +229,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } const window = this.windowById(windowId); - if (window) { - window.focus({ force: options?.force ?? false }); - } + window?.focus({ force: options?.force ?? false }); } async setMinimumSize(windowId: number | undefined, width: number | undefined, height: number | undefined): Promise { @@ -436,16 +435,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async setRepresentedFilename(windowId: number | undefined, path: string): Promise { const window = this.windowById(windowId); - if (window) { - window.setRepresentedFilename(path); - } + window?.setRepresentedFilename(path); } async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise { const window = this.windowById(windowId); - if (window) { - window.setDocumentEdited(edited); - } + window?.setDocumentEdited(edited); } async openExternal(windowId: number | undefined, url: string): Promise { @@ -657,9 +652,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain async updateTouchBar(windowId: number | undefined, items: ISerializableCommandAction[][]): Promise { const window = this.windowById(windowId); - if (window) { - window.updateTouchBar(items); - } + window?.updateTouchBar(items); } //#endregion diff --git a/src/vs/platform/notification/common/notification.ts b/src/vs/platform/notification/common/notification.ts index 99e8716173c..612aa179426 100644 --- a/src/vs/platform/notification/common/notification.ts +++ b/src/vs/platform/notification/common/notification.ts @@ -45,9 +45,16 @@ export enum NeverShowAgainScope { WORKSPACE, /** - * Will never show this notification on any workspace again. + * Will never show this notification on any workspace of the same + * profile again. */ - GLOBAL + GLOBAL, + + /** + * Will never show this notification on any workspace across all + * profiles again. + */ + APPLICATION } export interface INeverShowAgainOptions { @@ -65,7 +72,8 @@ export interface INeverShowAgainOptions { /** * Whether to persist the choice in the current workspace or for all workspaces. By - * default it will be persisted for all workspaces (= `NeverShowAgainScope.GLOBAL`). + * default it will be persisted for all workspaces across all profiles + * (= `NeverShowAgainScope.APPLICATION`). */ readonly scope?: NeverShowAgainScope; } diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index a8f94e4f107..e235c7aeb9a 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -113,9 +113,7 @@ export class QuickInputService extends Themable implements IQuickInputService { this.resetContextKeys(); - if (key) { - key.set(true); - } + key?.set(true); } private resetContextKeys() { diff --git a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts index 81bb26375b2..7da986b840b 100644 --- a/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/electron-main/sharedProcess.ts @@ -242,7 +242,7 @@ export class SharedProcess extends Disposable implements ISharedProcess { appRoot: this.environmentMainService.appRoot, codeCachePath: this.environmentMainService.codeCachePath, backupWorkspacesPath: this.environmentMainService.backupWorkspacesPath, - profiles: this.userDataProfilesService.serialize(), + defaultProfile: this.userDataProfilesService.defaultProfile, userEnv: this.userEnv, args: this.environmentMainService.args, logLevel: this.logService.getLevel(), diff --git a/src/vs/platform/sharedProcess/node/sharedProcess.ts b/src/vs/platform/sharedProcess/node/sharedProcess.ts index b5656e2d080..e445bc596d0 100644 --- a/src/vs/platform/sharedProcess/node/sharedProcess.ts +++ b/src/vs/platform/sharedProcess/node/sharedProcess.ts @@ -7,8 +7,9 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { LogLevel } from 'vs/platform/log/common/log'; -import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; +import { UriDto } from 'vs/base/common/types'; export interface ISharedProcess { @@ -28,7 +29,7 @@ export interface ISharedProcessConfiguration extends ISandboxConfiguration { readonly backupWorkspacesPath: string; - readonly profiles: IUserDataProfilesDto; + readonly defaultProfile: UriDto; readonly policiesData?: IStringDictionary<{ definition: PolicyDefinition; value: PolicyValue }>; } diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index 645049cb7c2..3989b25ab47 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -5,90 +5,179 @@ import { isSafari } from 'vs/base/browser/browser'; import { IndexedDB } from 'vs/base/browser/indexedDB'; -import { Promises } from 'vs/base/common/async'; +import { DeferredPromise, Promises } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { assertIsDefined } from 'vs/base/common/types'; import { InMemoryStorageDatabase, isStorageItemsChangeEvent, IStorage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest, Storage } from 'vs/base/parts/storage/common/storage'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class BrowserStorageService extends AbstractStorageService { private static BROWSER_DEFAULT_FLUSH_INTERVAL = 5 * 1000; // every 5s because async operations are not permitted on shutdown - private globalStorage: IStorage | undefined; - private workspaceStorage: IStorage | undefined; + private applicationStorage: IStorage | undefined; + private applicationStorageDatabase: IIndexedDBStorageDatabase | undefined; + private readonly applicationStoragePromise = new DeferredPromise<{ indededDb: IIndexedDBStorageDatabase; storage: IStorage }>(); + private globalStorage: IStorage | undefined; private globalStorageDatabase: IIndexedDBStorageDatabase | undefined; + private globalStorageProfile: IUserDataProfile; + private readonly globalStorageDisposables = this._register(new DisposableStore()); + + private workspaceStorage: IStorage | undefined; private workspaceStorageDatabase: IIndexedDBStorageDatabase | undefined; get hasPendingUpdate(): boolean { - return Boolean(this.globalStorageDatabase?.hasPendingUpdate || this.workspaceStorageDatabase?.hasPendingUpdate); + return Boolean( + this.applicationStorageDatabase?.hasPendingUpdate || + this.globalStorageDatabase?.hasPendingUpdate || + this.workspaceStorageDatabase?.hasPendingUpdate + ); } constructor( private readonly payload: IAnyWorkspaceIdentifier, - @ILogService private readonly logService: ILogService + currentProfile: IUserDataProfile, + @ILogService private readonly logService: ILogService, ) { super({ flushInterval: BrowserStorageService.BROWSER_DEFAULT_FLUSH_INTERVAL }); + + this.globalStorageProfile = currentProfile; } private getId(scope: StorageScope): string { - return scope === StorageScope.GLOBAL ? 'global' : this.payload.id; + switch (scope) { + case StorageScope.APPLICATION: + return 'global'; // use the default profile global DB for application scope + case StorageScope.GLOBAL: + if (this.globalStorageProfile.isDefault) { + return 'global'; // default profile DB has a fixed name for backwards compatibility + } else { + return `global-${this.globalStorageProfile.id}`; + } + case StorageScope.WORKSPACE: + return this.payload.id; + } } protected async doInitialize(): Promise { - // Create Storage in Parallel - const [workspaceStorageDatabase, globalStorageDatabase] = await Promises.settled([ - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService), - IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true /* only for global storage */ }, this.logService) - ]); - - // Workspace Storage - this.workspaceStorageDatabase = this._register(workspaceStorageDatabase); - this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); - this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); - - // Global Storage - this.globalStorageDatabase = this._register(globalStorageDatabase); - this.globalStorage = this._register(new Storage(this.globalStorageDatabase)); - this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); - - // Init both + // Init storages await Promises.settled([ - this.workspaceStorage.init(), - this.globalStorage.init() + this.createApplicationStorage(), + this.createGlobalStorage(this.globalStorageProfile), + this.createWorkspaceStorage() ]); + } - // Check to see if this is the first time we are "opening" the application - const firstOpen = this.globalStorage.getBoolean(IS_NEW_KEY); - if (firstOpen === undefined) { - this.globalStorage.set(IS_NEW_KEY, true); - } else if (firstOpen) { - this.globalStorage.set(IS_NEW_KEY, false); + private async createApplicationStorage(): Promise { + const applicationStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.APPLICATION), broadcastChanges: true }, this.logService); + + this.applicationStorageDatabase = this._register(applicationStorageIndexedDB); + this.applicationStorage = this._register(new Storage(this.applicationStorageDatabase)); + + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); + + await this.applicationStorage.init(); + + this.updateIsNew(this.applicationStorage); + + this.applicationStoragePromise.complete({ indededDb: applicationStorageIndexedDB, storage: this.applicationStorage }); + } + + private async createGlobalStorage(profile: IUserDataProfile): Promise { + + // First clear any previously associated disposables + this.globalStorageDisposables.clear(); + + // Remember profile associated to global storage + this.globalStorageProfile = profile; + + if (this.globalStorageProfile.isDefault) { + + // If we are in default profile, the global storage is + // actually the same as application storage. As such we + // avoid creating the storage library a second time on + // the same DB. + + const { indededDb: applicationStorageIndexedDB, storage: applicationStorage } = await this.applicationStoragePromise.p; + + this.globalStorageDatabase = applicationStorageIndexedDB; + this.globalStorage = applicationStorage; + } else { + const globalStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.GLOBAL), broadcastChanges: true }, this.logService); + + this.globalStorageDatabase = this.globalStorageDisposables.add(globalStorageIndexedDB); + this.globalStorage = this.globalStorageDisposables.add(new Storage(this.globalStorageDatabase)); } - // Check to see if this is the first time we are "opening" this workspace - const firstWorkspaceOpen = this.workspaceStorage.getBoolean(IS_NEW_KEY); - if (firstWorkspaceOpen === undefined) { - this.workspaceStorage.set(IS_NEW_KEY, true); - } else if (firstWorkspaceOpen) { - this.workspaceStorage.set(IS_NEW_KEY, false); + this.globalStorageDisposables.add(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + + await this.globalStorage.init(); + + this.updateIsNew(this.globalStorage); + } + + private async createWorkspaceStorage(): Promise { + const workspaceStorageIndexedDB = await IndexedDBStorageDatabase.create({ id: this.getId(StorageScope.WORKSPACE) }, this.logService); + + this.workspaceStorageDatabase = this._register(workspaceStorageIndexedDB); + this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase)); + + this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); + + await this.workspaceStorage.init(); + + this.updateIsNew(this.workspaceStorage); + } + + private updateIsNew(storage: IStorage): void { + const firstOpen = storage.getBoolean(IS_NEW_KEY); + if (firstOpen === undefined) { + storage.set(IS_NEW_KEY, true); + } else if (firstOpen) { + storage.set(IS_NEW_KEY, false); } } protected getStorage(scope: StorageScope): IStorage | undefined { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { return this.getId(scope); } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { + protected async switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise { + const oldGlobalStorage = assertIsDefined(this.globalStorage); + const oldItems = oldGlobalStorage.items; + + // Close old global storage but only if this is + // different from application storage! + if (oldGlobalStorage !== this.applicationStorage) { + await oldGlobalStorage.close(); + } + + // Create new global storage & init + await this.createGlobalStorage(toProfile); + + // Handle data switch and eventing + this.switchData(oldItems, assertIsDefined(this.globalStorage), StorageScope.GLOBAL, preserveData); + } + + protected async switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier, preserveData: boolean): Promise { throw new Error('Migrating storage is currently unsupported in Web'); } @@ -115,6 +204,7 @@ export class BrowserStorageService extends AbstractStorageService { // On all other browsers, we keep the databases opened because // we expect data to be written when the unload happens. if (isSafari) { + this.applicationStorage?.close(); this.globalStorageDatabase?.close(); this.workspaceStorageDatabase?.close(); } @@ -127,7 +217,7 @@ export class BrowserStorageService extends AbstractStorageService { async clear(): Promise { // Clear key/values - for (const scope of [StorageScope.GLOBAL, StorageScope.WORKSPACE]) { + for (const scope of [StorageScope.APPLICATION, StorageScope.GLOBAL, StorageScope.WORKSPACE]) { for (const target of [StorageTarget.USER, StorageTarget.MACHINE]) { for (const key of this.keys(scope, target)) { this.remove(key, scope); @@ -139,6 +229,7 @@ export class BrowserStorageService extends AbstractStorageService { // Clear databases await Promises.settled([ + this.applicationStorageDatabase?.clear() ?? Promise.resolve(), this.globalStorageDatabase?.clear() ?? Promise.resolve(), this.workspaceStorageDatabase?.clear() ?? Promise.resolve() ]); diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index 7d4ce4f359a..a3d8fd7e3b1 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -10,6 +10,7 @@ import { mark } from 'vs/base/common/performance'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { InMemoryStorageDatabase, IStorage, Storage } from 'vs/base/parts/storage/common/storage'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { isUserDataProfile, IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const IS_NEW_KEY = '__$__isNewStorageMarker'; @@ -31,7 +32,7 @@ export enum WillSaveStateReason { } export interface IWillSaveStateEvent { - reason: WillSaveStateReason; + readonly reason: WillSaveStateReason; } export interface IStorageService { @@ -68,7 +69,7 @@ export interface IStorageService { * the provided `defaultValue` if the element is `null` or `undefined`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ get(key: string, scope: StorageScope, fallbackValue: string): string; get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined; @@ -79,7 +80,7 @@ export interface IStorageService { * The element will be converted to a `boolean`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean; getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined; @@ -91,7 +92,7 @@ export interface IStorageService { * base of `10`. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. */ getNumber(key: string, scope: StorageScope, fallbackValue: number): number; getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined; @@ -102,7 +103,7 @@ export interface IStorageService { * remove the entry under the key. * * @param scope allows to define the scope of the storage operation - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. * * @param target allows to define the target of the storage operation * to either the current machine or user. @@ -113,7 +114,8 @@ export interface IStorageService { * Delete an element stored under the provided key from storage. * * The scope argument allows to define the scope of the storage - * operation to either the current workspace only or all workspaces. + * operation to either the current workspace only, all workspaces + * or all profiles. */ remove(key: string, scope: StorageScope): void; @@ -126,7 +128,7 @@ export interface IStorageService { * will be excluded from the results. * * @param scope allows to define the scope for the keys - * to either the current workspace only or all workspaces. + * to either the current workspace only, all workspaces or all profiles. * * @param target allows to define the target for the keys * to either the current machine or user. @@ -136,12 +138,13 @@ export interface IStorageService { /** * Log the contents of the storage to the console. */ - logStorage(): void; + log(): void; /** - * Migrate the storage contents to another workspace. + * Switch storage to another workspace or profile. Optionally preserve the + * current data to the new storage. */ - migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise; + switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise; /** * Whether the storage for the given scope was created during this session or @@ -163,14 +166,19 @@ export interface IStorageService { export const enum StorageScope { /** - * The stored data will be scoped to all workspaces. + * The stored data will be scoped to all workspaces across all profiles. */ - GLOBAL, + APPLICATION = -1, + + /** + * The stored data will be scoped to all workspaces of the same profile. + */ + GLOBAL = 0, /** * The stored data will be scoped to the current workspace. */ - WORKSPACE + WORKSPACE = 1 } export const enum StorageTarget { @@ -302,10 +310,16 @@ export abstract class AbstractStorageService extends Disposable implements IStor if (key === TARGET_KEY) { // Clear our cached version which is now out of date - if (scope === StorageScope.GLOBAL) { - this._globalKeyTargets = undefined; - } else if (scope === StorageScope.WORKSPACE) { - this._workspaceKeyTargets = undefined; + switch (scope) { + case StorageScope.APPLICATION: + this._applicationKeyTargets = undefined; + break; + case StorageScope.GLOBAL: + this._globalKeyTargets = undefined; + break; + case StorageScope.WORKSPACE: + this._workspaceKeyTargets = undefined; + break; } // Emit as `didChangeTarget` event @@ -440,8 +454,24 @@ export abstract class AbstractStorageService extends Disposable implements IStor return this._globalKeyTargets; } + private _applicationKeyTargets: IKeyTargets | undefined = undefined; + private get applicationKeyTargets(): IKeyTargets { + if (!this._applicationKeyTargets) { + this._applicationKeyTargets = this.loadKeyTargets(StorageScope.APPLICATION); + } + + return this._applicationKeyTargets; + } + private getKeyTargets(scope: StorageScope): IKeyTargets { - return scope === StorageScope.GLOBAL ? this.globalKeyTargets : this.workspaceKeyTargets; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationKeyTargets; + case StorageScope.GLOBAL: + return this.globalKeyTargets; + default: + return this.workspaceKeyTargets; + } } private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } { @@ -466,6 +496,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // Signal event to collect changes this._onWillSaveState.fire({ reason }); + const applicationStorage = this.getStorage(StorageScope.APPLICATION); const globalStorage = this.getStorage(StorageScope.GLOBAL); const workspaceStorage = this.getStorage(StorageScope.WORKSPACE); @@ -474,6 +505,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // Unspecific reason: just wait when data is flushed case WillSaveStateReason.NONE: await Promises.settled([ + applicationStorage?.whenFlushed() ?? Promise.resolve(), globalStorage?.whenFlushed() ?? Promise.resolve(), workspaceStorage?.whenFlushed() ?? Promise.resolve() ]); @@ -483,6 +515,7 @@ export abstract class AbstractStorageService extends Disposable implements IStor // and not hit any delays that might be there case WillSaveStateReason.SHUTDOWN: await Promises.settled([ + applicationStorage?.flush(0) ?? Promise.resolve(), globalStorage?.flush(0) ?? Promise.resolve(), workspaceStorage?.flush(0) ?? Promise.resolve() ]); @@ -490,18 +523,64 @@ export abstract class AbstractStorageService extends Disposable implements IStor } } - async logStorage(): Promise { + async log(): Promise { + const applicationItems = this.getStorage(StorageScope.APPLICATION)?.items ?? new Map(); const globalItems = this.getStorage(StorageScope.GLOBAL)?.items ?? new Map(); const workspaceItems = this.getStorage(StorageScope.WORKSPACE)?.items ?? new Map(); return logStorage( + applicationItems, globalItems, workspaceItems, + this.getLogDetails(StorageScope.APPLICATION) ?? '', this.getLogDetails(StorageScope.GLOBAL) ?? '', this.getLogDetails(StorageScope.WORKSPACE) ?? '' ); } + async switch(to: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise { + + // Signal as event so that clients can store data before we switch + this.emitWillSaveState(WillSaveStateReason.NONE); + + if (isUserDataProfile(to)) { + return this.switchToProfile(to, preserveData); + } + + return this.switchToWorkspace(to, preserveData); + } + + protected switchData(oldStorage: Map, newStorage: IStorage, scope: StorageScope, preserveData: boolean): void { + this.withPausedEmitters(() => { + + // Copy over previous keys if `preserveData` + if (preserveData) { + for (const [key, value] of oldStorage) { + newStorage.set(key, value); + } + } + + // Otherwise signal storage keys that have changed + else { + const handledkeys = new Set(); + for (const [key, oldValue] of oldStorage) { + handledkeys.add(key); + + const newValue = newStorage.get(key); + if (newValue !== oldValue) { + this.emitDidChangeValue(scope, key); + } + } + + for (const [key] of newStorage.items) { + if (!handledkeys.has(key)) { + this.emitDidChangeValue(scope, key); + } + } + } + }); + } + // --- abstract protected abstract doInitialize(): Promise; @@ -510,11 +589,13 @@ export abstract class AbstractStorageService extends Disposable implements IStor protected abstract getLogDetails(scope: StorageScope): string | undefined; - abstract migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise; + protected abstract switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise; + protected abstract switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier | IUserDataProfile, preserveData: boolean): Promise; } export class InMemoryStorageService extends AbstractStorageService { + private readonly applicationStorage = this._register(new Storage(new InMemoryStorageDatabase())); private readonly globalStorage = this._register(new Storage(new InMemoryStorageDatabase())); private readonly workspaceStorage = this._register(new Storage(new InMemoryStorageDatabase())); @@ -523,24 +604,43 @@ export class InMemoryStorageService extends AbstractStorageService { this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + this._register(this.applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); } protected getStorage(scope: StorageScope): IStorage { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? 'inMemory (global)' : 'inMemory (workspace)'; + switch (scope) { + case StorageScope.APPLICATION: + return 'inMemory (application)'; + case StorageScope.GLOBAL: + return 'inMemory (global)'; + default: + return 'inMemory (workspace)'; + } } protected async doInitialize(): Promise { } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { - // not supported + protected async switchToProfile(): Promise { + // no-op when in-memory + } + + protected async switchToWorkspace(): Promise { + // no-op when in-memory } } -export async function logStorage(global: Map, workspace: Map, globalPath: string, workspacePath: string): Promise { +export async function logStorage(application: Map, global: Map, workspace: Map, applicationPath: string, globalPath: string, workspacePath: string): Promise { const safeParse = (value: string) => { try { return JSON.parse(value); @@ -549,6 +649,13 @@ export async function logStorage(global: Map, workspace: Map(); + const applicationItemsParsed = new Map(); + application.forEach((value, key) => { + applicationItems.set(key, value); + applicationItemsParsed.set(key, safeParse(value)); + }); + const globalItems = new Map(); const globalItemsParsed = new Map(); global.forEach((value, key) => { @@ -563,15 +670,31 @@ export async function logStorage(global: Map, workspace: Map { - globalValues.push({ key, value }); + if (applicationPath !== globalPath) { + console.group(`Storage: Application (path: ${applicationPath})`); + } else { + console.group(`Storage: Application & Global (path: ${applicationPath}, default profile)`); + } + const applicationValues: { key: string; value: string }[] = []; + applicationItems.forEach((value, key) => { + applicationValues.push({ key, value }); }); - console.table(globalValues); + console.table(applicationValues); console.groupEnd(); - console.log(globalItemsParsed); + console.log(applicationItemsParsed); + + if (applicationPath !== globalPath) { + console.group(`Storage: Global (path: ${globalPath}, profile specific)`); + const globalValues: { key: string; value: string }[] = []; + globalItems.forEach((value, key) => { + globalValues.push({ key, value }); + }); + console.table(globalValues); + console.groupEnd(); + + console.log(globalItemsParsed); + } console.group(`Storage: Workspace (path: ${workspacePath})`); const workspaceValues: { key: string; value: string }[] = []; diff --git a/src/vs/platform/storage/common/storageIpc.ts b/src/vs/platform/storage/common/storageIpc.ts index 5774cb82085..240824ad37b 100644 --- a/src/vs/platform/storage/common/storageIpc.ts +++ b/src/vs/platform/storage/common/storageIpc.ts @@ -5,8 +5,10 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { UriDto } from 'vs/base/common/types'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISerializedSingleFolderWorkspaceIdentifier, ISerializedWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export type Key = string; @@ -14,6 +16,18 @@ export type Value = string; export type Item = [Key, Value]; export interface IBaseSerializableStorageRequest { + + /** + * Profile to correlate storage. Only used when no + * workspace is provided. Can be undefined to denote + * application scope. + */ + readonly profile: UriDto | undefined; + + /** + * Workspace to correlate storage. Can be undefined to + * denote application or global scope depending on profile. + */ readonly workspace: ISerializedWorkspaceIdentifier | ISerializedSingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined; } @@ -31,19 +45,23 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD abstract readonly onDidChangeItemsExternal: Event; - constructor(protected channel: IChannel, protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined) { + constructor( + protected channel: IChannel, + protected profile: UriDto | undefined, + protected workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined + ) { super(); } async getItems(): Promise> { - const serializableRequest: IBaseSerializableStorageRequest = { workspace: this.workspace }; + const serializableRequest: IBaseSerializableStorageRequest = { profile: this.profile, workspace: this.workspace }; const items: Item[] = await this.channel.call('getItems', serializableRequest); return new Map(items); } updateItems(request: IUpdateRequest): Promise { - const serializableRequest: ISerializableUpdateRequest = { workspace: this.workspace }; + const serializableRequest: ISerializableUpdateRequest = { profile: this.profile, workspace: this.workspace }; if (request.insert) { serializableRequest.insert = Array.from(request.insert.entries()); @@ -59,22 +77,22 @@ abstract class BaseStorageDatabaseClient extends Disposable implements IStorageD abstract close(): Promise; } -class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { +abstract class BaseProfileAwareStorageDatabaseClient extends BaseStorageDatabaseClient { private readonly _onDidChangeItemsExternal = this._register(new Emitter()); readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event; - constructor(channel: IChannel) { - super(channel, undefined); + constructor(channel: IChannel, profile: UriDto | undefined) { + super(channel, profile, undefined); this.registerListeners(); } private registerListeners(): void { - this._register(this.channel.listen('onDidChangeGlobalStorage')((e: ISerializableItemsChangeEvent) => this.onDidChangeGlobalStorage(e))); + this._register(this.channel.listen('onDidChangeStorage', { profile: this.profile })((e: ISerializableItemsChangeEvent) => this.onDidChangeStorage(e))); } - private onDidChangeGlobalStorage(e: ISerializableItemsChangeEvent): void { + private onDidChangeStorage(e: ISerializableItemsChangeEvent): void { if (Array.isArray(e.changed) || Array.isArray(e.deleted)) { this._onDidChangeItemsExternal.fire({ changed: e.changed ? new Map(e.changed) : undefined, @@ -82,10 +100,17 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I }); } } +} + +export class ApplicationStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { + + constructor(channel: IChannel) { + super(channel, undefined); + } async close(): Promise { - // The global storage database is shared across all instances so + // The application storage database is shared across all instances so // we do not close it from the window. However we dispose the // listener for external changes because we no longer interested in it. @@ -93,12 +118,29 @@ class GlobalStorageDatabaseClient extends BaseStorageDatabaseClient implements I } } -class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { +export class GlobalStorageDatabaseClient extends BaseProfileAwareStorageDatabaseClient { + + constructor(channel: IChannel, profile: UriDto) { + super(channel, profile); + } + + async close(): Promise { + + // The global storage database is shared across all instances of + // the same profile so we do not close it from the window. + // However we dispose the listener for external changes because + // we no longer interested in it. + + this.dispose(); + } +} + +export class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implements IStorageDatabase { readonly onDidChangeItemsExternal = Event.None; // unsupported for workspace storage because we only ever write from one window constructor(channel: IChannel, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier) { - super(channel, workspace); + super(channel, undefined, workspace); } async close(): Promise { @@ -110,31 +152,3 @@ class WorkspaceStorageDatabaseClient extends BaseStorageDatabaseClient implement this.dispose(); } } - -export class StorageDatabaseChannelClient extends Disposable { - - private _globalStorage: GlobalStorageDatabaseClient | undefined = undefined; - get globalStorage() { - if (!this._globalStorage) { - this._globalStorage = new GlobalStorageDatabaseClient(this.channel); - } - - return this._globalStorage; - } - - private _workspaceStorage: WorkspaceStorageDatabaseClient | undefined = undefined; - get workspaceStorage() { - if (!this._workspaceStorage && this.workspace) { - this._workspaceStorage = new WorkspaceStorageDatabaseClient(this.channel, this.workspace); - } - - return this._workspaceStorage; - } - - constructor( - private channel: IChannel, - private workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined - ) { - super(); - } -} diff --git a/src/vs/platform/storage/electron-main/storageIpc.ts b/src/vs/platform/storage/electron-main/storageIpc.ts index 9802b767d91..5cfb0aa2d96 100644 --- a/src/vs/platform/storage/electron-main/storageIpc.ts +++ b/src/vs/platform/storage/electron-main/storageIpc.ts @@ -5,19 +5,22 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { ILogService } from 'vs/platform/log/common/log'; import { IBaseSerializableStorageRequest, ISerializableItemsChangeEvent, ISerializableUpdateRequest, Key, Value } from 'vs/platform/storage/common/storageIpc'; import { IStorageChangeEvent, IStorageMain } from 'vs/platform/storage/electron-main/storageMain'; import { IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { reviveIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class StorageDatabaseChannel extends Disposable implements IServerChannel { private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100; - private readonly _onDidChangeGlobalStorage = this._register(new Emitter()); - private readonly onDidChangeGlobalStorage = this._onDidChangeGlobalStorage.event; + private readonly onDidChangeApplicationStorageEmitter = this._register(new Emitter()); + + private readonly mapProfileToOnDidChangeGlobalStorageEmitter = new Map>(); constructor( private logService: ILogService, @@ -25,16 +28,17 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel ) { super(); - this.registerGlobalStorageListeners(); + this.registerStorageChangeListeners(storageMainService.applicationStorage, this.onDidChangeApplicationStorageEmitter); } - //#region Global Storage Change Events + //#region Storage Change Events - private registerGlobalStorageListeners(): void { + private registerStorageChangeListeners(storage: IStorageMain, emitter: Emitter): void { - // Listen for changes in global storage to send to listeners + // Listen for changes in provided storage to send to listeners // that are listening. Use a debouncer to reduce IPC traffic. - this._register(Event.debounce(this.storageMainService.globalStorage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { + + this._register(Event.debounce(storage.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => { if (!prev) { prev = [cur]; } else { @@ -44,16 +48,16 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel return prev; }, StorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => { if (events.length) { - this._onDidChangeGlobalStorage.fire(this.serializeGlobalStorageEvents(events)); + emitter.fire(this.serializeStorageChangeEvents(events, storage)); } })); } - private serializeGlobalStorageEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent { + private serializeStorageChangeEvents(events: IStorageChangeEvent[], storage: IStorageMain): ISerializableItemsChangeEvent { const changed = new Map(); const deleted = new Set(); events.forEach(event => { - const existing = this.storageMainService.globalStorage.get(event.key); + const existing = storage.get(event.key); if (typeof existing === 'string') { changed.set(event.key, existing); } else { @@ -67,9 +71,26 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel }; } - listen(_: unknown, event: string): Event { + listen(_: unknown, event: string, arg: IBaseSerializableStorageRequest): Event { switch (event) { - case 'onDidChangeGlobalStorage': return this.onDidChangeGlobalStorage; + case 'onDidChangeStorage': { + const profile = arg.profile ? revive(arg.profile) : undefined; + + // Without profile: application scope + if (!profile) { + return this.onDidChangeApplicationStorageEmitter.event; + } + + // With profile: global scope for the profile + let globalStorageChangeEmitter = this.mapProfileToOnDidChangeGlobalStorageEmitter.get(profile.id); + if (!globalStorageChangeEmitter) { + globalStorageChangeEmitter = this._register(new Emitter()); + this.registerStorageChangeListeners(this.storageMainService.globalStorage(profile), globalStorageChangeEmitter); + this.mapProfileToOnDidChangeGlobalStorageEmitter.set(profile.id, globalStorageChangeEmitter); + } + + return globalStorageChangeEmitter.event; + } } throw new Error(`Event not found: ${event}`); @@ -78,10 +99,11 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel //#endregion async call(_: unknown, command: string, arg: IBaseSerializableStorageRequest): Promise { + const profile = arg.profile ? revive(arg.profile) : undefined; const workspace = reviveIdentifier(arg.workspace); // Get storage to be ready - const storage = await this.withStorageInitialized(workspace); + const storage = await this.withStorageInitialized(profile, workspace); // handle call switch (command) { @@ -98,9 +120,7 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } } - if (items.delete) { - items.delete.forEach(key => storage.delete(key)); - } + items.delete?.forEach(key => storage.delete(key)); break; } @@ -110,13 +130,20 @@ export class StorageDatabaseChannel extends Disposable implements IServerChannel } } - private async withStorageInitialized(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { - const storage = workspace ? this.storageMainService.workspaceStorage(workspace) : this.storageMainService.globalStorage; + private async withStorageInitialized(profile: IUserDataProfile | undefined, workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): Promise { + let storage: IStorageMain; + if (workspace) { + storage = this.storageMainService.workspaceStorage(workspace); + } else if (profile) { + storage = this.storageMainService.globalStorage(profile); + } else { + storage = this.storageMainService.applicationStorage; + } try { await storage.init(); } catch (error) { - this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : 'global'} storage due to ${error}`); + this.logService.error(`StorageIPC#init: Unable to init ${workspace ? 'workspace' : profile ? 'global' : 'application'} storage due to ${error}`); } return storage; diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index 7faf5ee69cd..b4d9ab4256e 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -17,7 +17,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IFileService } from 'vs/platform/files/common/files'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { currentSessionDateStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; @@ -31,8 +31,8 @@ export interface IStorageMainOptions { } /** - * Provides access to global and workspace storage from the - * electron-main side that is the owner of all storage connections. + * Provides access to application, global and workspace storage from + * the electron-main side that is the owner of all storage connections. */ export interface IStorageMain extends IDisposable { @@ -255,22 +255,22 @@ abstract class BaseStorageMain extends Disposable implements IStorageMain { } } -export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { +class BaseProfileAwareStorageMain extends BaseStorageMain { private static readonly STORAGE_NAME = 'state.vscdb'; get path(): string | undefined { if (!this.options.useInMemoryStorage) { - return join(this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath, GlobalStorageMain.STORAGE_NAME); + return join(this.profile.globalStorageHome.fsPath, BaseProfileAwareStorageMain.STORAGE_NAME); } return undefined; } constructor( + private readonly profile: IUserDataProfile, private readonly options: IStorageMainOptions, logService: ILogService, - private readonly userDataProfilesService: IUserDataProfilesService, fileService: IFileService ) { super(logService, fileService); @@ -281,11 +281,35 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { logging: this.createLoggingOptions() })); } +} + +export class GlobalStorageMain extends BaseProfileAwareStorageMain { + + constructor( + profile: IUserDataProfile, + options: IStorageMainOptions, + logService: ILogService, + fileService: IFileService + ) { + super(profile, options, logService, fileService); + } +} + +export class ApplicationStorageMain extends BaseProfileAwareStorageMain { + + constructor( + options: IStorageMainOptions, + userDataProfileService: IUserDataProfilesService, + logService: ILogService, + fileService: IFileService + ) { + super(userDataProfileService.defaultProfile, options, logService, fileService); + } protected override async doInit(storage: IStorage): Promise { await super.doInit(storage); - // Apply global telemetry values as part of the initialization + // Apply telemetry values as part of the application storage initialization this.updateTelemetryState(storage); } @@ -307,7 +331,7 @@ export class GlobalStorageMain extends BaseStorageMain implements IStorageMain { } } -export class WorkspaceStorageMain extends BaseStorageMain implements IStorageMain { +export class WorkspaceStorageMain extends BaseStorageMain { private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb'; private static readonly WORKSPACE_META_NAME = 'workspace.json'; diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 3c5e483fcb0..0fa9144e1cd 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -12,11 +12,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractStorageService, IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; +import { ApplicationStorageMain, GlobalStorageMain, InMemoryStorageMain, IStorageMain, IStorageMainOptions, WorkspaceStorageMain } from 'vs/platform/storage/electron-main/storageMain'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -//#region Storage Main Service (intent: make global and workspace storage accessible to windows from main process) +//#region Storage Main Service (intent: make application, global and workspace storage accessible to windows from main process) export const IStorageMainService = createDecorator('storageMainService'); @@ -25,12 +25,22 @@ export interface IStorageMainService { readonly _serviceBrand: undefined; /** - * Provides access to the global storage shared across all windows. + * Provides access to the application storage shared across all + * windows and all profiles. * * Note: DO NOT use this for reading/writing from the main process! - * Rather use `IGlobalStorageMainService` for that purpose. + * Rather use `IApplicationStorageMainService` for that purpose. */ - readonly globalStorage: IStorageMain; + applicationStorage: IStorageMain; + + /** + * Provides access to the global storage shared across all windows + * for the provided profile. + * + * Note: DO NOT use this for reading/writing from the main process! + * This is currently not supported. + */ + globalStorage(profile: IUserDataProfile): IStorageMain; /** * Provides access to the workspace storage specific to a single window. @@ -67,15 +77,21 @@ export class StorageMainService extends Disposable implements IStorageMainServic private registerListeners(): void { - // Global Storage: Warmup when any window opens + // Application Storage: Warmup when any window opens (async () => { await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); - this.globalStorage.init(); + this.applicationStorage.init(); })(); - // Workspace Storage: Warmup when related window with workspace loads this._register(this.lifecycleMainService.onWillLoadWindow(e => { + + // Global Storage: Warmup when related window with profile loads + if (e.window.profile) { + this.globalStorage(e.window.profile).init(); + } + + // Workspace Storage: Warmup when related window with workspace loads if (e.workspace) { this.workspaceStorage(e.workspace).init(); } @@ -88,38 +104,84 @@ export class StorageMainService extends Disposable implements IStorageMainServic // Remember shutdown reason this.shutdownReason = e.reason; - // Global Storage - e.join(this.globalStorage.close()); + // Application Storage + e.join(this.applicationStorage.close()); + + // Global Storage(s) + for (const [, globalStorage] of this.mapProfileToStorage) { + e.join(globalStorage.close()); + } // Workspace Storage(s) - for (const [, storage] of this.mapWorkspaceToStorage) { - e.join(storage.close()); + for (const [, workspaceStorage] of this.mapWorkspaceToStorage) { + e.join(workspaceStorage.close()); } })); } - //#region Global Storage + //#region Application Storage - readonly globalStorage = this.createGlobalStorage(); + readonly applicationStorage = this.createApplicationStorage(); - private createGlobalStorage(): IStorageMain { - this.logService.trace(`StorageMainService: creating global storage`); + private createApplicationStorage(): IStorageMain { + this.logService.trace(`StorageMainService: creating application storage`); - const globalStorage = new GlobalStorageMain(this.getStorageOptions(), this.logService, this.userDataProfilesService, this.fileService); + const applicationStorage = new ApplicationStorageMain(this.getStorageOptions(), this.userDataProfilesService, this.logService, this.fileService); - once(globalStorage.onDidCloseStorage)(() => { - this.logService.trace(`StorageMainService: closed global storage`); + once(applicationStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed application storage`); }); + return applicationStorage; + } + + //#endregion + + //#region Global Storage + + private readonly mapProfileToStorage = new Map(); + + globalStorage(profile: IUserDataProfile): IStorageMain { + if (profile.isDefault) { + return this.applicationStorage; // for default profile, use application storage + } + + let globalStorage = this.mapProfileToStorage.get(profile.id); + if (!globalStorage) { + this.logService.trace(`StorageMainService: creating global storage (${profile.name})`); + + globalStorage = this.createGlobalStorage(profile); + this.mapProfileToStorage.set(profile.id, globalStorage); + + once(globalStorage.onDidCloseStorage)(() => { + this.logService.trace(`StorageMainService: closed global storage (${profile.name})`); + + this.mapProfileToStorage.delete(profile.id); + }); + } + return globalStorage; } + private createGlobalStorage(profile: IUserDataProfile): IStorageMain { + if (this.shutdownReason === ShutdownReason.KILL) { + + // Workaround for native crashes that we see when + // SQLite DBs are being created even after shutdown + // https://github.com/microsoft/vscode/issues/143186 + + return new InMemoryStorageMain(this.logService, this.fileService); + } + + return new GlobalStorageMain(profile, this.getStorageOptions(), this.logService, this.fileService); + } + //#endregion //#region Workspace Storage - private readonly mapWorkspaceToStorage = new Map(); + private readonly mapWorkspaceToStorage = new Map(); workspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorageMain { let workspaceStorage = this.mapWorkspaceToStorage.get(workspace.id); @@ -158,15 +220,15 @@ export class StorageMainService extends Disposable implements IStorageMainServic //#endregion -//#region Global Main Storage Service (intent: use global storage from main process) +//#region Application Main Storage Service (intent: use application storage from main process) -export const IGlobalStorageMainService = createDecorator('globalStorageMainService'); +export const IApplicationStorageMainService = createDecorator('applicationStorageMainService'); /** * A specialized `IStorageService` interface that only allows - * access to the `StorageScope.GLOBAL` scope. + * access to the `StorageScope.APPLICATION` scope. */ -export interface IGlobalStorageMainService extends IStorageService { +export interface IApplicationStorageMainService extends IStorageService { /** * Important: unlike other storage services in the renderer, the @@ -174,38 +236,38 @@ export interface IGlobalStorageMainService extends IStorageService { * storage is being initialized while a window opens to reduce * pressure on startup. * - * As such, any client wanting to access global storage from the + * As such, any client wanting to access application storage from the * main process needs to wait for `whenReady`, otherwise there is * a chance that the service operates on an in-memory store that * is not backed by any persistent DB. */ readonly whenReady: Promise; - get(key: string, scope: StorageScope.GLOBAL, fallbackValue: string): string; - get(key: string, scope: StorageScope.GLOBAL, fallbackValue?: string): string | undefined; + get(key: string, scope: StorageScope.APPLICATION, fallbackValue: string): string; + get(key: string, scope: StorageScope.APPLICATION, fallbackValue?: string): string | undefined; - getBoolean(key: string, scope: StorageScope.GLOBAL, fallbackValue: boolean): boolean; - getBoolean(key: string, scope: StorageScope.GLOBAL, fallbackValue?: boolean): boolean | undefined; + getBoolean(key: string, scope: StorageScope.APPLICATION, fallbackValue: boolean): boolean; + getBoolean(key: string, scope: StorageScope.APPLICATION, fallbackValue?: boolean): boolean | undefined; - getNumber(key: string, scope: StorageScope.GLOBAL, fallbackValue: number): number; - getNumber(key: string, scope: StorageScope.GLOBAL, fallbackValue?: number): number | undefined; + getNumber(key: string, scope: StorageScope.APPLICATION, fallbackValue: number): number; + getNumber(key: string, scope: StorageScope.APPLICATION, fallbackValue?: number): number | undefined; - store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope.GLOBAL, target: StorageTarget): void; + store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope.APPLICATION, target: StorageTarget): void; - remove(key: string, scope: StorageScope.GLOBAL): void; + remove(key: string, scope: StorageScope.APPLICATION): void; - keys(scope: StorageScope.GLOBAL, target: StorageTarget): string[]; + keys(scope: StorageScope.APPLICATION, target: StorageTarget): string[]; - migrate(toWorkspace: IAnyWorkspaceIdentifier): never; + switch(): never; - isNew(scope: StorageScope.GLOBAL): boolean; + isNew(scope: StorageScope.APPLICATION): boolean; } -export class GlobalStorageMainService extends AbstractStorageService implements IGlobalStorageMainService { +export class ApplicationStorageMainService extends AbstractStorageService implements IApplicationStorageMainService { declare readonly _serviceBrand: undefined; - readonly whenReady = this.storageMainService.globalStorage.whenInit; + readonly whenReady = this.storageMainService.applicationStorage.whenInit; constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @@ -216,30 +278,41 @@ export class GlobalStorageMainService extends AbstractStorageService implements protected doInitialize(): Promise { - // global storage is being initialized as part - // of the first window opening, so we do not - // trigger it here but can join it - return this.storageMainService.globalStorage.whenInit; + // application storage is being initialized as part + // of the first window opening, so we do not trigger + // it here but can join it + return this.storageMainService.applicationStorage.whenInit; } protected getStorage(scope: StorageScope): IStorage | undefined { - switch (scope) { - case StorageScope.GLOBAL: - return this.storageMainService.globalStorage.storage; - case StorageScope.WORKSPACE: - return undefined; // unsupported from main process + if (scope === StorageScope.APPLICATION) { + return this.storageMainService.applicationStorage.storage; } + + return undefined; // any other scope is unsupported from main process } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath : undefined; + if (scope === StorageScope.APPLICATION) { + return this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath; + } + + return undefined; // any other scope is unsupported from main process } protected override shouldFlushWhenIdle(): boolean { return false; // not needed here, will be triggered from any window that is opened } - migrate(): never { + override switch(): never { throw new Error('Migrating storage is unsupported from main process'); } + + protected switchToProfile(): never { + throw new Error('Switching storage profile is unsupported from main process'); + } + + protected switchToWorkspace(): never { + throw new Error('Switching storage workspace is unsupported from main process'); + } } diff --git a/src/vs/platform/storage/electron-sandbox/storageService.ts b/src/vs/platform/storage/electron-sandbox/storageService.ts index fd69b47c141..e606e3a1645 100644 --- a/src/vs/platform/storage/electron-sandbox/storageService.ts +++ b/src/vs/platform/storage/electron-sandbox/storageService.ts @@ -4,45 +4,76 @@ *--------------------------------------------------------------------------------------------*/ import { Promises } from 'vs/base/common/async'; -import { MutableDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; import { IStorage, Storage } from 'vs/base/parts/storage/common/storage'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services'; import { AbstractStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage'; -import { StorageDatabaseChannelClient } from 'vs/platform/storage/common/storageIpc'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ApplicationStorageDatabaseClient, GlobalStorageDatabaseClient, WorkspaceStorageDatabaseClient } from 'vs/platform/storage/common/storageIpc'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IAnyWorkspaceIdentifier, IEmptyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class NativeStorageService extends AbstractStorageService { - // Global Storage is readonly and shared across windows - private readonly globalStorage: IStorage; + private readonly applicationStorage: IStorage; + private readonly applicationStorageProfile: IUserDataProfile; + + private globalStorage: IStorage; + private globalStorageProfile: IUserDataProfile | undefined = undefined; + private readonly globalStorageDisposables = this._register(new DisposableStore()); - // Workspace Storage is scoped to a window but can change - // in the current window, when entering a workspace! private workspaceStorage: IStorage | undefined = undefined; private workspaceStorageId: string | undefined = undefined; - private workspaceStorageDisposable = this._register(new MutableDisposable()); + private readonly workspaceStorageDisposables = this._register(new DisposableStore()); constructor( workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined, + { defaultProfile, currentProfile }: { defaultProfile: IUserDataProfile; currentProfile: IUserDataProfile }, private readonly mainProcessService: IMainProcessService, - private readonly userDataProfilesService: IUserDataProfilesService, - private readonly environmentService: IEnvironmentService, + private readonly environmentService: IEnvironmentService ) { super(); - this.globalStorage = this.createGlobalStorage(); + this.applicationStorageProfile = defaultProfile; + + this.applicationStorage = this.createApplicationStorage(); + this.globalStorage = this.createGlobalStorage(currentProfile); this.workspaceStorage = this.createWorkspaceStorage(workspace); } - private createGlobalStorage(): IStorage { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), undefined); + private createApplicationStorage(): IStorage { + const storageDataBaseClient = this._register(new ApplicationStorageDatabaseClient(this.mainProcessService.getChannel('storage'))); + const applicationStorage = this._register(new Storage(storageDataBaseClient)); - const globalStorage = new Storage(storageDataBaseClient.globalStorage); + this._register(applicationStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.APPLICATION, key))); - this._register(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); + return applicationStorage; + } + + private createGlobalStorage(profile: IUserDataProfile): IStorage { + + // First clear any previously associated disposables + this.globalStorageDisposables.clear(); + + // Remember profile associated to global storage + this.globalStorageProfile = profile; + + let globalStorage: IStorage; + if (profile.isDefault) { + + // If we are in default profile, the global storage is + // actually the same as application storage. As such we + // avoid creating the storage library a second time on + // the same DB. + + globalStorage = this.applicationStorage; + } else { + const storageDataBaseClient = this.globalStorageDisposables.add(new GlobalStorageDatabaseClient(this.mainProcessService.getChannel('storage'), profile)); + globalStorage = this.globalStorageDisposables.add(new Storage(storageDataBaseClient)); + } + + this.globalStorageDisposables.add(globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key))); return globalStorage; } @@ -50,38 +81,54 @@ export class NativeStorageService extends AbstractStorageService { private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier): IStorage; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined; private createWorkspaceStorage(workspace: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IEmptyWorkspaceIdentifier | undefined): IStorage | undefined { - const storageDataBaseClient = new StorageDatabaseChannelClient(this.mainProcessService.getChannel('storage'), workspace); - if (storageDataBaseClient.workspaceStorage) { - const workspaceStorage = new Storage(storageDataBaseClient.workspaceStorage); + // First clear any previously associated disposables + this.workspaceStorageDisposables.clear(); - this.workspaceStorageDisposable.value = workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)); - this.workspaceStorageId = workspace?.id; + // Remember workspace ID for logging later + this.workspaceStorageId = workspace?.id; - return workspaceStorage; - } else { - this.workspaceStorageDisposable.clear(); - this.workspaceStorageId = undefined; + let workspaceStorage: IStorage | undefined = undefined; + if (workspace) { + const storageDataBaseClient = this.workspaceStorageDisposables.add(new WorkspaceStorageDatabaseClient(this.mainProcessService.getChannel('storage'), workspace)); + workspaceStorage = this.workspaceStorageDisposables.add(new Storage(storageDataBaseClient)); - return undefined; + this.workspaceStorageDisposables.add(workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key))); } + + return workspaceStorage; } protected async doInitialize(): Promise { // Init all storage locations await Promises.settled([ + this.applicationStorage.init(), this.globalStorage.init(), this.workspaceStorage?.init() ?? Promise.resolve() ]); } protected getStorage(scope: StorageScope): IStorage | undefined { - return scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorage; + case StorageScope.GLOBAL: + return this.globalStorage; + default: + return this.workspaceStorage; + } } protected getLogDetails(scope: StorageScope): string | undefined { - return scope === StorageScope.GLOBAL ? this.userDataProfilesService.defaultProfile.globalStorageHome.fsPath : this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + switch (scope) { + case StorageScope.APPLICATION: + return this.applicationStorageProfile.globalStorageHome.fsPath; + case StorageScope.GLOBAL: + return this.globalStorageProfile?.globalStorageHome.fsPath; + default: + return this.workspaceStorageId ? `${joinPath(this.environmentService.workspaceStorageHome, this.workspaceStorageId, 'state.vscdb').fsPath}` : undefined; + } } async close(): Promise { @@ -94,30 +141,42 @@ export class NativeStorageService extends AbstractStorageService { // Do it await Promises.settled([ + this.applicationStorage.close(), this.globalStorage.close(), this.workspaceStorage?.close() ?? Promise.resolve() ]); } - async migrate(toWorkspace: IAnyWorkspaceIdentifier): Promise { + protected async switchToProfile(toProfile: IUserDataProfile, preserveData: boolean): Promise { + const oldGlobalStorage = this.globalStorage; + const oldItems = oldGlobalStorage.items; - // Keep current workspace storage items around to restore + // Close old global storage but only if this is + // different from application storage! + if (oldGlobalStorage !== this.applicationStorage) { + await oldGlobalStorage.close(); + } + + // Create new global storage & init + this.globalStorage = this.createGlobalStorage(toProfile); + await this.globalStorage.init(); + + // Handle data switch and eventing + this.switchData(oldItems, this.globalStorage, StorageScope.GLOBAL, preserveData); + } + + protected async switchToWorkspace(toWorkspace: IAnyWorkspaceIdentifier, preserveData: boolean): Promise { const oldWorkspaceStorage = this.workspaceStorage; const oldItems = oldWorkspaceStorage?.items ?? new Map(); - // Close current which will change to new workspace storage - if (oldWorkspaceStorage) { - await oldWorkspaceStorage.close(); - oldWorkspaceStorage.dispose(); - } + // Close old workspace storage + await oldWorkspaceStorage?.close(); // Create new workspace storage & init this.workspaceStorage = this.createWorkspaceStorage(toWorkspace); await this.workspaceStorage.init(); - // Copy over previous keys - for (const [key, value] of oldItems) { - this.workspaceStorage.set(key, value); - } + // Handle data switch and eventing + this.switchData(oldItems, this.workspaceStorage, StorageScope.WORKSPACE, preserveData); } } diff --git a/src/vs/platform/storage/test/browser/storageService.test.ts b/src/vs/platform/storage/test/browser/storageService.test.ts index cf51c013ef8..1e8f38ae028 100644 --- a/src/vs/platform/storage/test/browser/storageService.test.ts +++ b/src/vs/platform/storage/test/browser/storageService.test.ts @@ -6,6 +6,8 @@ import { strictEqual } from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { Storage } from 'vs/base/parts/storage/common/storage'; import { flakySuite } from 'vs/base/test/common/testUtils'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; @@ -15,6 +17,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { BrowserStorageService, IndexedDBStorageDatabase } from 'vs/platform/storage/browser/storageService'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { createSuite } from 'vs/platform/storage/test/common/storageService.test'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; async function createStorageService(): Promise<[DisposableStore, BrowserStorageService]> { const disposables = new DisposableStore(); @@ -25,7 +28,23 @@ async function createStorageService(): Promise<[DisposableStore, BrowserStorageS const userDataProvider = disposables.add(new InMemoryFileSystemProvider()); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, userDataProvider)); - const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, logService)); + const profilesRoot = URI.file('/profiles').with({ scheme: Schemas.inMemory }); + + const inMemoryExtraProfileRoot = joinPath(profilesRoot, 'extra'); + const inMemoryExtraProfile: IUserDataProfile = { + id: 'id', + name: 'inMemory', + isDefault: false, + location: inMemoryExtraProfileRoot, + globalStorageHome: joinPath(inMemoryExtraProfileRoot, 'globalStorageHome'), + settingsResource: joinPath(inMemoryExtraProfileRoot, 'settingsResource'), + keybindingsResource: joinPath(inMemoryExtraProfileRoot, 'keybindingsResource'), + tasksResource: joinPath(inMemoryExtraProfileRoot, 'tasksResource'), + snippetsHome: joinPath(inMemoryExtraProfileRoot, 'snippetsHome'), + extensionsResource: joinPath(inMemoryExtraProfileRoot, 'extensionsResource') + }; + + const storageService = disposables.add(new BrowserStorageService({ id: 'workspace-storage-test' }, inMemoryExtraProfile, logService)); await storageService.initialize(); @@ -69,6 +88,8 @@ flakySuite('StorageService (browser specific)', () => { test('clear', () => { return runWithFakedTimers({ useFakeTimers: true }, async () => { + storageService.store('bar', 'foo', StorageScope.APPLICATION, StorageTarget.MACHINE); + storageService.store('bar', 3, StorageScope.APPLICATION, StorageTarget.USER); storageService.store('bar', 'foo', StorageScope.GLOBAL, StorageTarget.MACHINE); storageService.store('bar', 3, StorageScope.GLOBAL, StorageTarget.USER); storageService.store('bar', 'foo', StorageScope.WORKSPACE, StorageTarget.MACHINE); @@ -76,7 +97,7 @@ flakySuite('StorageService (browser specific)', () => { await storageService.clear(); - for (const scope of [StorageScope.GLOBAL, StorageScope.WORKSPACE]) { + for (const scope of [StorageScope.APPLICATION, StorageScope.GLOBAL, StorageScope.WORKSPACE]) { for (const target of [StorageTarget.USER, StorageTarget.MACHINE]) { strictEqual(storageService.get('bar', scope), undefined); strictEqual(storageService.keys(scope, target).length, 0); diff --git a/src/vs/platform/storage/test/common/storageService.test.ts b/src/vs/platform/storage/test/common/storageService.test.ts index ea3af86c960..ab5ba71962e 100644 --- a/src/vs/platform/storage/test/common/storageService.test.ts +++ b/src/vs/platform/storage/test/common/storageService.test.ts @@ -18,6 +18,10 @@ export function createSuite(params: { setup: () => Pr return params.teardown(storageService); }); + test('Get Data, Integer, Boolean (application)', () => { + storeData(StorageScope.APPLICATION); + }); + test('Get Data, Integer, Boolean (global)', () => { storeData(StorageScope.GLOBAL); }); @@ -67,6 +71,10 @@ export function createSuite(params: { setup: () => Pr strictEqual(storageService.getBoolean('test.getBooleanDefault', scope, true), true); } + test('Remove Data (application)', () => { + removeData(StorageScope.APPLICATION); + }); + test('Remove Data (global)', () => { removeData(StorageScope.GLOBAL); }); @@ -97,14 +105,14 @@ export function createSuite(params: { setup: () => Pr storageService.onDidChangeValue(e => storageValueChangeEvent = e); // Empty - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { strictEqual(storageService.keys(scope, target).length, 0); } } // Add values - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageTargetEvent = Object.create(null); storageValueChangeEvent = Object.create(null); @@ -134,7 +142,7 @@ export function createSuite(params: { setup: () => Pr } // Remove values - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { const keysLength = storageService.keys(scope, target).length; @@ -153,7 +161,7 @@ export function createSuite(params: { setup: () => Pr } // Remove all - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { const keys = storageService.keys(scope, target); @@ -166,7 +174,7 @@ export function createSuite(params: { setup: () => Pr } // Adding undefined or null removes value - for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) { + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) { storageService.store('test.target1', 'value1', scope, target); strictEqual(storageService.keys(scope, target).length, 1); @@ -186,18 +194,20 @@ export function createSuite(params: { setup: () => Pr } // Target change - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(storageTargetEvent); - storageTargetEvent = undefined; - storageService.store('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE); - ok(!storageTargetEvent); // no change in target + for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL, StorageScope.APPLICATION]) { + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.USER); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(storageTargetEvent); + storageTargetEvent = undefined; + storageService.store('test.target5', 'value1', scope, StorageTarget.MACHINE); + ok(!storageTargetEvent); // no change in target + } }); } diff --git a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts index ddd09f1eb16..4150fa1fc54 100644 --- a/src/vs/platform/storage/test/electron-main/storageMainService.test.ts +++ b/src/vs/platform/storage/test/electron-main/storageMainService.test.ts @@ -6,6 +6,9 @@ import { notStrictEqual, strictEqual } from 'assert'; import { Promises } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; +import { Schemas } from 'vs/base/common/network'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; @@ -15,17 +18,31 @@ import { ILifecycleMainService, LifecycleMainPhase, ShutdownEvent, ShutdownReaso import { NullLogService } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IS_NEW_KEY } from 'vs/platform/storage/common/storage'; +import { IS_NEW_KEY, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageChangeEvent, IStorageMain, IStorageMainOptions } from 'vs/platform/storage/electron-main/storageMain'; import { StorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { currentSessionDateStorageKey, firstSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ICodeWindow, UnloadReason } from 'vs/platform/window/electron-main/window'; suite('StorageMainService', function () { const productService: IProductService = { _serviceBrand: undefined, ...product }; + const inMemoryProfileRoot = URI.file('/location').with({ scheme: Schemas.inMemory }); + const inMemoryProfile: IUserDataProfile = { + id: 'id', + name: 'inMemory', + isDefault: false, + location: inMemoryProfileRoot, + globalStorageHome: joinPath(inMemoryProfileRoot, 'globalStorageHome'), + settingsResource: joinPath(inMemoryProfileRoot, 'settingsResource'), + keybindingsResource: joinPath(inMemoryProfileRoot, 'keybindingsResource'), + tasksResource: joinPath(inMemoryProfileRoot, 'tasksResource'), + snippetsHome: joinPath(inMemoryProfileRoot, 'snippetsHome'), + extensionsResource: joinPath(inMemoryProfileRoot, 'extensionsResource') + }; + class TestStorageMainService extends StorageMainService { protected override getStorageOptions(): IStorageMainOptions { @@ -74,10 +91,10 @@ suite('StorageMainService', function () { async when(phase: LifecycleMainPhase): Promise { } } - async function testStorage(storage: IStorageMain, isGlobal: boolean): Promise { + async function testStorage(storage: IStorageMain, scope: StorageScope): Promise { - // Telemetry: added after init - if (isGlobal) { + // Telemetry: added after init unless workspace/global scoped + if (scope === StorageScope.APPLICATION) { strictEqual(storage.items.size, 0); await storage.init(); strictEqual(typeof storage.get(firstSessionDateStorageKey), 'string'); @@ -128,57 +145,80 @@ suite('StorageMainService', function () { function createStorageService(lifecycleMainService: ILifecycleMainService = new StorageTestLifecycleMainService()): TestStorageMainService { const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); const fileService = new FileService(new NullLogService()); - return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService); + return new TestStorageMainService(new NullLogService(), environmentService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService()), lifecycleMainService, fileService); } + test('basics (application)', function () { + const storageMainService = createStorageService(); + + return testStorage(storageMainService.applicationStorage, StorageScope.APPLICATION); + }); + test('basics (global)', function () { const storageMainService = createStorageService(); + const profile = inMemoryProfile; - return testStorage(storageMainService.globalStorage, true); + return testStorage(storageMainService.globalStorage(profile), StorageScope.GLOBAL); }); test('basics (workspace)', function () { const workspace = { id: generateUuid() }; const storageMainService = createStorageService(); - return testStorage(storageMainService.workspaceStorage(workspace), false); + return testStorage(storageMainService.workspaceStorage(workspace), StorageScope.WORKSPACE); }); test('storage closed onWillShutdown', async function () { const lifecycleMainService = new StorageTestLifecycleMainService(); - const workspace = { id: generateUuid() }; const storageMainService = createStorageService(lifecycleMainService); + const profile = inMemoryProfile; + const workspace = { id: generateUuid() }; + const workspaceStorage = storageMainService.workspaceStorage(workspace); let didCloseWorkspaceStorage = false; workspaceStorage.onDidCloseStorage(() => { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + strictEqual(applicationStorage, storageMainService.applicationStorage); // same instance as long as not closed + strictEqual(globalStorage, storageMainService.globalStorage(profile)); // same instance as long as not closed strictEqual(workspaceStorage, storageMainService.workspaceStorage(workspace)); // same instance as long as not closed + await applicationStorage.init(); await globalStorage.init(); await workspaceStorage.init(); await lifecycleMainService.fireOnWillShutdown(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); - const storage2 = storageMainService.workspaceStorage(workspace); - notStrictEqual(workspaceStorage, storage2); + const globalStorage2 = storageMainService.globalStorage(profile); + notStrictEqual(globalStorage, globalStorage2); - return storage2.close(); + const workspaceStorage2 = storageMainService.workspaceStorage(workspace); + notStrictEqual(workspaceStorage, workspaceStorage2); + + return workspaceStorage2.close(); }); test('storage closed before init works', async function () { const storageMainService = createStorageService(); + const profile = inMemoryProfile; const workspace = { id: generateUuid() }; const workspaceStorage = storageMainService.workspaceStorage(workspace); @@ -187,21 +227,30 @@ suite('StorageMainService', function () { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + await applicationStorage.close(); await globalStorage.close(); await workspaceStorage.close(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); test('storage closed before init awaits works', async function () { const storageMainService = createStorageService(); + const profile = inMemoryProfile; const workspace = { id: generateUuid() }; const workspaceStorage = storageMainService.workspaceStorage(workspace); @@ -210,18 +259,27 @@ suite('StorageMainService', function () { didCloseWorkspaceStorage = true; }); - const globalStorage = storageMainService.globalStorage; + const globalStorage = storageMainService.globalStorage(profile); let didCloseGlobalStorage = false; globalStorage.onDidCloseStorage(() => { didCloseGlobalStorage = true; }); + const applicationStorage = storageMainService.applicationStorage; + let didCloseApplicationStorage = false; + applicationStorage.onDidCloseStorage(() => { + didCloseApplicationStorage = true; + }); + + applicationStorage.init(); globalStorage.init(); workspaceStorage.init(); + await applicationStorage.close(); await globalStorage.close(); await workspaceStorage.close(); + strictEqual(didCloseApplicationStorage, true); strictEqual(didCloseGlobalStorage, true); strictEqual(didCloseWorkspaceStorage, true); }); diff --git a/src/vs/platform/telemetry/browser/1dsAppender.ts b/src/vs/platform/telemetry/browser/1dsAppender.ts new file mode 100644 index 00000000000..ceee3f6414f --- /dev/null +++ b/src/vs/platform/telemetry/browser/1dsAppender.ts @@ -0,0 +1,128 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { AppInsightsCore, IExtendedConfiguration } from '@microsoft/1ds-core-js'; +import type { PostChannel } from '@microsoft/1ds-post-js'; +import { onUnexpectedError } from 'vs/base/common/errors'; +import { mixin } from 'vs/base/common/objects'; +import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; + +const endpointUrl = 'https://mobile.events.data.microsoft.com/OneCollector/1.0'; + +async function getClient(instrumentationKey: string): Promise { + const oneDs = await import('@microsoft/1ds-core-js'); + const postPlugin = await import('@microsoft/1ds-post-js'); + const appInsightsCore = new oneDs.AppInsightsCore(); + const collectorChannelPlugin: PostChannel = new postPlugin.PostChannel(); + // Configure the app insights core to send to collector++ and disable logging of debug info + const coreConfig: IExtendedConfiguration = { + instrumentationKey, + endpointUrl, + loggingLevelTelemetry: 0, + loggingLevelConsole: 0, + disableCookiesUsage: true, + disableDbgExt: true, + disableInstrumentationKeyValidation: true, + channels: [[ + collectorChannelPlugin + ]] + }; + + appInsightsCore.initialize(coreConfig, []); + + appInsightsCore.addTelemetryInitializer((envelope) => { + envelope['ext'] = envelope['ext'] ?? {}; + envelope['ext']['utc'] = envelope['ext']['utc'] ?? {}; + // Sets it to be internal only based on Windows UTC flagging + envelope['ext']['utc']['flags'] = 0x0000811ECD; + }); + + return appInsightsCore; +} + +// TODO @lramos15 maybe make more in line with src/vs/platform/telemetry/browser/appInsightsAppender.ts with caching support +export class OneDataSystemWebAppender implements ITelemetryAppender { + + private _aiCoreOrKey: AppInsightsCore | string | undefined; + private _asyncAiCore: Promise | null; + + constructor( + private _eventPrefix: string, + private _defaultData: { [key: string]: any } | null, + iKeyOrClientFactory: string | (() => AppInsightsCore), // allow factory function for testing + ) { + if (!this._defaultData) { + this._defaultData = Object.create(null); + } + + if (typeof iKeyOrClientFactory === 'function') { + this._aiCoreOrKey = iKeyOrClientFactory(); + } else { + this._aiCoreOrKey = iKeyOrClientFactory; + } + this._asyncAiCore = null; + + // If we cannot fetch the endpoint it means it is down and we should not send any telemetry. + // This is most likely due to ad blockers + fetch(endpointUrl, { method: 'POST' }).catch(err => { + this._aiCoreOrKey = undefined; + }); + } + + private _withAIClient(callback: (aiCore: AppInsightsCore) => void): void { + if (!this._aiCoreOrKey) { + return; + } + + if (typeof this._aiCoreOrKey !== 'string') { + callback(this._aiCoreOrKey); + return; + } + + if (!this._asyncAiCore) { + this._asyncAiCore = getClient(this._aiCoreOrKey); + } + + this._asyncAiCore.then( + (aiClient) => { + callback(aiClient); + }, + (err) => { + onUnexpectedError(err); + console.error(err); + } + ); + } + + log(eventName: string, data?: any): void { + if (!this._aiCoreOrKey) { + return; + } + data = mixin(data, this._defaultData); + data = validateTelemetryData(data); + + try { + this._withAIClient((aiClient) => aiClient.track({ + name: this._eventPrefix + '/' + eventName, + data, + + })); + } catch { } + } + + flush(): Promise { + if (this._aiCoreOrKey) { + return new Promise(resolve => { + this._withAIClient((aiClient) => { + aiClient.unload(true, () => { + this._aiCoreOrKey = undefined; + resolve(undefined); + }); + }); + }); + } + return Promise.resolve(undefined); + } +} diff --git a/src/vs/platform/telemetry/browser/appInsightsAppender.ts b/src/vs/platform/telemetry/browser/appInsightsAppender.ts new file mode 100644 index 00000000000..2b3f099cf83 --- /dev/null +++ b/src/vs/platform/telemetry/browser/appInsightsAppender.ts @@ -0,0 +1,77 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { ApplicationInsights } from '@microsoft/applicationinsights-web'; +import { ITelemetryAppender, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; + +export class WebAppInsightsAppender implements ITelemetryAppender { + private _aiClient: ApplicationInsights | undefined; + private _aiClientLoaded = false; + private _telemetryCache: { eventName: string; data: any }[] = []; + + constructor(private _eventPrefix: string, aiKey: string) { + const endpointUrl = 'https://mobile.events.data.microsoft.com/collect/v1'; + import('@microsoft/applicationinsights-web').then(aiLibrary => { + this._aiClient = new aiLibrary.ApplicationInsights({ + config: { + instrumentationKey: aiKey, + endpointUrl, + disableAjaxTracking: true, + disableExceptionTracking: true, + disableFetchTracking: true, + disableCorrelationHeaders: true, + disableCookiesUsage: true, + autoTrackPageVisitTime: false, + emitLineDelimitedJson: true, + }, + }); + this._aiClient.loadAppInsights(); + // Client is loaded we can now flush the cached events + this._aiClientLoaded = true; + this._telemetryCache.forEach(cacheEntry => this.log(cacheEntry.eventName, cacheEntry.data)); + this._telemetryCache = []; + + // If we cannot access the endpoint this most likely means it's being blocked + // and we should not attempt to send any telemetry. + fetch(endpointUrl, { method: 'POST' }).catch(() => (this._aiClient = undefined)); + }).catch(err => { + console.error(err); + }); + } + + /** + * Logs a telemetry event with eventName and data + * @param eventName The event name + * @param data The data associated with the events + */ + public log(eventName: string, data: any): void { + if (!this._aiClient && this._aiClientLoaded) { + return; + } else if (!this._aiClient && !this._aiClientLoaded) { + this._telemetryCache.push({ eventName, data }); + return; + } + + data = validateTelemetryData(data); + + // Web does not expect properties and measurements so we must + // spread them out. This is different from desktop which expects them + data = { ...data.properties, ...data.measurements }; + + // undefined assertion is ok since above two if statements cover both cases + this._aiClient!.trackEvent({ name: this._eventPrefix + '/' + eventName }, data); + } + + /** + * Flushes all the telemetry data still in the buffer + */ + public flush(): Promise { + if (this._aiClient) { + this._aiClient.flush(); + this._aiClient = undefined; + } + return Promise.resolve(undefined); + } +} diff --git a/src/vs/platform/telemetry/browser/errorTelemetry.ts b/src/vs/platform/telemetry/browser/errorTelemetry.ts index f539d9ca69d..ae2cea2fe2e 100644 --- a/src/vs/platform/telemetry/browser/errorTelemetry.ts +++ b/src/vs/platform/telemetry/browser/errorTelemetry.ts @@ -17,9 +17,7 @@ export default class ErrorTelemetry extends BaseErrorTelemetry { } globals.onerror = function (message: string, filename: string, line: number, column?: number, e?: any) { that._onUncaughtError(message, filename, line, column, e); - if (oldOnError) { - oldOnError.apply(this, arguments); - } + oldOnError?.apply(this, arguments); }; this._disposables.add(toDisposable(() => { if (oldOnError) { diff --git a/src/vs/platform/telemetry/node/1dsAppender.ts b/src/vs/platform/telemetry/node/1dsAppender.ts index aab96b0c1b2..35ca15f81b4 100644 --- a/src/vs/platform/telemetry/node/1dsAppender.ts +++ b/src/vs/platform/telemetry/node/1dsAppender.ts @@ -67,10 +67,10 @@ async function getClient(instrumentationKey: string): Promise { appInsightsCore.initialize(coreConfig, []); appInsightsCore.addTelemetryInitializer((envelope) => { - if (envelope.tags) { - // Sets it to be internal only based on Windows UTC flagging - envelope.tags['utc.flags'] = 0x0000811ECD; - } + envelope['ext'] = envelope['ext'] ?? {}; + envelope['ext']['utc'] = envelope['ext']['utc'] ?? {}; + // Sets it to be internal only based on Windows UTC flagging + envelope['ext']['utc']['flags'] = 0x0000811ECD; }); return appInsightsCore; @@ -79,8 +79,7 @@ async function getClient(instrumentationKey: string): Promise { export class OneDataSystemAppender implements ITelemetryAppender { - private _aiCore: AppInsightsCore | undefined; - private _iKey: string | undefined; + private _aiCoreOrKey: AppInsightsCore | string | undefined; private _asyncAiCore: Promise | null; constructor( @@ -93,29 +92,28 @@ export class OneDataSystemAppender implements ITelemetryAppender { } if (typeof iKeyOrClientFactory === 'function') { - this._aiCore = iKeyOrClientFactory(); + this._aiCoreOrKey = iKeyOrClientFactory(); } else { - this._iKey = iKeyOrClientFactory; + this._aiCoreOrKey = iKeyOrClientFactory; } this._asyncAiCore = null; } private _withAIClient(callback: (aiCore: AppInsightsCore) => void): void { - if (!this._aiCore) { + if (!this._aiCoreOrKey) { return; } - if (this._aiCore) { - callback(this._aiCore); + if (typeof this._aiCoreOrKey !== 'string') { + callback(this._aiCoreOrKey); return; } - if (this._iKey && !this._asyncAiCore) { - this._asyncAiCore = getClient(this._iKey); - this._iKey = undefined; + if (!this._asyncAiCore) { + this._asyncAiCore = getClient(this._aiCoreOrKey); } - this._asyncAiCore?.then( + this._asyncAiCore.then( (aiClient) => { callback(aiClient); }, @@ -127,7 +125,7 @@ export class OneDataSystemAppender implements ITelemetryAppender { } log(eventName: string, data?: any): void { - if (!this._aiCore) { + if (!this._aiCoreOrKey) { return; } data = mixin(data, this._defaultData); @@ -137,18 +135,18 @@ export class OneDataSystemAppender implements ITelemetryAppender { try { this._withAIClient((aiClient) => aiClient.track({ name: this._eventPrefix + '/' + eventName, - data: { ...data.properties, ...data.measurements }, + data, })); } catch { } } flush(): Promise { - if (this._aiCore) { + if (this._aiCoreOrKey) { return new Promise(resolve => { this._withAIClient((aiClient) => { aiClient.unload(true, () => { - this._aiCore = undefined; + this._aiCoreOrKey = undefined; resolve(undefined); }); }); diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index 020de098700..b847384984a 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -96,6 +96,7 @@ export interface ICommandDetectionCapability { readonly onCurrentCommandInvalidated: Event; setCwd(value: string): void; setIsWindowsPty(value: boolean): void; + setIsCommandStorageDisabled(): void; /** * Gets the working directory for a line, this will return undefined if it's unknown in which * case the terminal's initial cwd should be used. diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index f9a14291b1f..b86963763bb 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -60,7 +60,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { private _onCursorMoveListener?: IDisposable; private _commandMarkers: IMarker[] = []; private _dimensions: ITerminalDimensions; - + private __isCommandStorageDisabled: boolean = false; get commands(): readonly ITerminalCommand[] { return this._commands; } get executingCommand(): string | undefined { return this._currentCommand.command; } // TODO: as is unsafe here and it duplicates behavor of executingCommand @@ -248,6 +248,10 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { this._isWindowsPty = value; } + setIsCommandStorageDisabled(): void { + this.__isCommandStorageDisabled = true; + } + getCwdForLine(line: number): string | undefined { // Handle the current partial command first, anything below it's prompt is considered part // of the current command @@ -297,6 +301,12 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { } handleCommandStart(): void { + // Only update the column if the line has already been set + if (this._currentCommand.commandStartMarker?.line === this._terminal.buffer.active.cursorY) { + this._currentCommand.commandStartX = this._terminal.buffer.active.cursorX; + this._logService.debug('CommandDetectionCapability#handleCommandStart', this._currentCommand.commandStartX, this._currentCommand.commandStartMarker?.line); + return; + } if (this._isWindowsPty) { this._handleCommandStartWindows(); return; @@ -354,7 +364,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { } // Calculate the command - this._currentCommand.command = this._terminal.buffer.active.getLine(this._currentCommand.commandStartMarker.line)?.translateToString(true, this._currentCommand.commandStartX, this._currentCommand.commandRightPromptStartX).trim(); + this._currentCommand.command = this.__isCommandStorageDisabled ? '' : this._terminal.buffer.active.getLine(this._currentCommand.commandStartMarker.line)?.translateToString(true, this._currentCommand.commandStartX, this._currentCommand.commandRightPromptStartX).trim(); let y = this._currentCommand.commandStartMarker.line + 1; const commandExecutedLine = this._currentCommand.commandExecutedMarker.line; for (; y < commandExecutedLine; y++) { @@ -486,7 +496,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { startX: undefined, endLine: e.endMarker?.line, executedLine: e.executedMarker?.line, - command: e.command, + command: this.__isCommandStorageDisabled ? '' : e.command, cwd: e.cwd, exitCode: e.exitCode, commandStartLineContent: e.commandStartLineContent, @@ -535,7 +545,7 @@ export class CommandDetectionCapability implements ICommandDetectionCapability { const endMarker = e.endLine !== undefined ? this._terminal.registerMarker(e.endLine - (buffer.baseY + buffer.cursorY)) : undefined; const executedMarker = e.executedLine !== undefined ? this._terminal.registerMarker(e.executedLine - (buffer.baseY + buffer.cursorY)) : undefined; const newCommand = { - command: e.command, + command: this.__isCommandStorageDisabled ? '' : e.command, marker, endMarker, executedMarker, diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 4bd9644b0b1..84f04637aaf 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -113,6 +113,10 @@ export const enum TerminalSettingId { ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history' } +export const enum TerminalLogConstants { + FileName = 'ptyhost' +} + export const enum PosixShellType { PowerShell = 'pwsh', Bash = 'bash', @@ -436,7 +440,7 @@ export interface IShellLaunchConfig { ignoreConfigurationCwd?: boolean; /** Whether to wait for a key press before closing the terminal. */ - waitOnExit?: boolean | string; + waitOnExit?: boolean | string | ((exitCode: number) => string); /** * A string including ANSI escape sequences that will be written to the terminal emulator diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 3e48db34a63..6bf278467ba 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -231,15 +231,16 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati case 'Cwd': { this._createOrGetCwdDetection().updateCwd(value); const commandDetection = this.capabilities.get(TerminalCapability.CommandDetection); - if (commandDetection) { - commandDetection.setCwd(value); - } + commandDetection?.setCwd(value); return true; } case 'IsWindows': { this._createOrGetCommandDetection(this._terminal).setIsWindowsPty(value === 'True' ? true : false); return true; } + case 'Task': { + this.capabilities.get(TerminalCapability.CommandDetection)?.setIsCommandStorageDisabled(); + } } } } diff --git a/src/vs/platform/terminal/node/ptyHostMain.ts b/src/vs/platform/terminal/node/ptyHostMain.ts index 15812b04d0a..3de63e7d9e6 100644 --- a/src/vs/platform/terminal/node/ptyHostMain.ts +++ b/src/vs/platform/terminal/node/ptyHostMain.ts @@ -3,11 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { join } from 'vs/base/common/path'; import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc'; import { Server } from 'vs/base/parts/ipc/node/ipc.cp'; -import { ConsoleLogger, LogService } from 'vs/platform/log/common/log'; +import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { ConsoleLogger, getLogLevel, LogService, MultiplexLogService } from 'vs/platform/log/common/log'; import { LogLevelChannel } from 'vs/platform/log/common/logIpc'; -import { IReconnectConstants, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal'; +import { SpdLogLogger } from 'vs/platform/log/node/spdlogLog'; +import product from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IReconnectConstants, TerminalIpcChannels, TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService'; import { PtyService } from 'vs/platform/terminal/node/ptyService'; @@ -16,9 +22,15 @@ const server = new Server('ptyHost'); const lastPtyId = parseInt(process.env.VSCODE_LAST_PTY_ID || '0'); delete process.env.VSCODE_LAST_PTY_ID; -const logService = new LogService(new ConsoleLogger()); -const logChannel = new LogLevelChannel(logService); -server.registerChannel(TerminalIpcChannels.Log, logChannel); +// Logging +const productService: IProductService = { _serviceBrand: undefined, ...product }; +const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService); +const logService = new LogService(new MultiplexLogService([ + new ConsoleLogger(), + new SpdLogLogger(TerminalLogConstants.FileName, join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`), true, false, getLogLevel(environmentService)) +])); +const logLevelChannel = new LogLevelChannel(logService); +server.registerChannel(TerminalIpcChannels.Log, logLevelChannel); const heartbeatService = new HeartbeatService(); server.registerChannel(TerminalIpcChannels.Heartbeat, ProxyChannel.fromService(heartbeatService)); diff --git a/src/vs/platform/terminal/node/ptyHostService.ts b/src/vs/platform/terminal/node/ptyHostService.ts index 2f4371ddcef..3564d49efb4 100644 --- a/src/vs/platform/terminal/node/ptyHostService.ts +++ b/src/vs/platform/terminal/node/ptyHostService.ts @@ -133,7 +133,7 @@ export class PtyHostService extends Disposable implements IPtyService { private _startPtyHost(): [Client, IPtyService] { const opts: IIPCOptions = { serverName: 'Pty Host', - args: ['--type=ptyHost'], + args: ['--type=ptyHost', '--logsPath', this._environmentService.logsPath], env: { VSCODE_LAST_PTY_ID: lastPtyId, VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain', diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index 02494098900..bdecef41367 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -152,7 +152,8 @@ export class PtyService extends Disposable implements IPtyService { true, terminal.processDetails.workspaceId, terminal.processDetails.workspaceName, - true + true, + terminal.replayEvent.events[0].data ); // Don't start the process here as there's no terminal to answer CPR this._revivedPtyIdMap.set(terminal.id, { newId, state: terminal }); @@ -175,7 +176,8 @@ export class PtyService extends Disposable implements IPtyService { shouldPersist: boolean, workspaceId: string, workspaceName: string, - isReviving?: boolean + isReviving?: boolean, + rawReviveBuffer?: string ): Promise { if (shellLaunchConfig.attachPersistentProcess) { throw new Error('Attempt to create a process when attach object was provided'); @@ -188,7 +190,7 @@ export class PtyService extends Disposable implements IPtyService { executableEnv, options }; - const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, processLaunchOptions, unicodeVersion, this._reconnectConstants, this._logService, isReviving ? shellLaunchConfig.initialText : undefined, shellLaunchConfig.icon, shellLaunchConfig.color, shellLaunchConfig.name, shellLaunchConfig.fixedDimensions); + const persistentProcess = new PersistentTerminalProcess(id, process, workspaceId, workspaceName, shouldPersist, cols, rows, processLaunchOptions, unicodeVersion, this._reconnectConstants, this._logService, isReviving ? shellLaunchConfig.initialText : undefined, rawReviveBuffer, shellLaunchConfig.icon, shellLaunchConfig.color, shellLaunchConfig.name, shellLaunchConfig.fixedDimensions); process.onDidChangeProperty(property => this._onDidChangeProperty.fire({ id, property })); process.onProcessExit(event => { persistentProcess.dispose(); @@ -210,10 +212,11 @@ export class PtyService extends Disposable implements IPtyService { async attachToProcess(id: number): Promise { try { - this._throwIfNoPty(id).attach(); + await this._throwIfNoPty(id).attach(); this._logService.trace(`Persistent process reconnection "${id}"`); } catch (e) { this._logService.trace(`Persistent process reconnection "${id}" failed`, e.message); + throw e; } } @@ -418,6 +421,15 @@ export class PtyService extends Disposable implements IPtyService { } } +const enum InteractionState { + /** The terminal has not been interacted with. */ + None = 0, + /** The terminal has only been interacted with by the replay mechanism. */ + ReplayOnly = 1, + /** The terminal has been directly interacted with this session. */ + Session = 2 +} + export class PersistentTerminalProcess extends Disposable { private readonly _bufferer: TerminalDataBufferer; @@ -426,7 +438,7 @@ export class PersistentTerminalProcess extends Disposable { private readonly _pendingCommands = new Map void; reject: (err: any) => void }>(); private _isStarted: boolean = false; - private _hasWrittenData: boolean = false; + private _interactionState: InteractionState = InteractionState.None; private _orphanQuestionBarrier: AutoOpenBarrier | null; private _orphanQuestionReplyTime: number; @@ -460,7 +472,7 @@ export class PersistentTerminalProcess extends Disposable { get pid(): number { return this._pid; } get shellLaunchConfig(): IShellLaunchConfig { return this._terminalProcess.shellLaunchConfig; } - get hasWrittenData(): boolean { return this._hasWrittenData; } + get hasWrittenData(): boolean { return this._interactionState !== InteractionState.None; } get title(): string { return this._title || this._terminalProcess.currentTitle; } get titleSource(): TitleEventSource { return this._titleSource; } get icon(): TerminalIcon | undefined { return this._icon; } @@ -468,13 +480,21 @@ export class PersistentTerminalProcess extends Disposable { get fixedDimensions(): IFixedTerminalDimensions | undefined { return this._fixedDimensions; } setTitle(title: string, titleSource: TitleEventSource): void { - this._hasWrittenData = true; + if (titleSource === TitleEventSource.Api) { + this._interactionState = InteractionState.Session; + this._serializer.freeRawReviveBuffer(); + } this._title = title; this._titleSource = titleSource; } setIcon(icon: TerminalIcon, color?: string): void { - this._hasWrittenData = true; + if (!this._icon || 'id' in icon && 'id' in this._icon && icon.id !== this._icon.id || + !this.color || color !== this._color) { + + this._serializer.freeRawReviveBuffer(); + this._interactionState = InteractionState.Session; + } this._icon = icon; this._color = color; } @@ -496,15 +516,13 @@ export class PersistentTerminalProcess extends Disposable { reconnectConstants: IReconnectConstants, private readonly _logService: ILogService, reviveBuffer: string | undefined, + rawReviveBuffer: string | undefined, private _icon?: TerminalIcon, private _color?: string, name?: string, fixedDimensions?: IFixedTerminalDimensions ) { super(); - if (name) { - this.setTitle(name, TitleEventSource.Api); - } this._logService.trace('persistentTerminalProcess#ctor', _persistentProcessId, arguments); this._wasRevived = reviveBuffer !== undefined; this._serializer = new XtermSerializer( @@ -513,8 +531,12 @@ export class PersistentTerminalProcess extends Disposable { reconnectConstants.scrollback, unicodeVersion, reviveBuffer, + shouldPersistTerminal ? rawReviveBuffer : undefined, this._logService ); + if (name) { + this.setTitle(name, TitleEventSource.Api); + } this._fixedDimensions = fixedDimensions; this._orphanQuestionBarrier = null; this._orphanQuestionReplyTime = 0; @@ -552,8 +574,16 @@ export class PersistentTerminalProcess extends Disposable { })); } - attach(): void { + async attach(): Promise { this._logService.trace('persistentTerminalProcess#attach', this._persistentProcessId); + if (!this._disconnectRunner1.isScheduled() && !this._disconnectRunner2.isScheduled()) { + // Something wrong happened if the disconnect runner is not canceled, this likely means + // multiple windows attempted to attach. + if (!await this._isOrphaned()) { + throw new Error(`Cannot attach to persistent process "${this._persistentProcessId}", it is already adopted`); + } + this._logService.warn(`Persistent process "${this._persistentProcessId}": Process had no disconnect runners but was an orphan`); + } this._disconnectRunner1.cancel(); this._disconnectRunner2.cancel(); } @@ -568,7 +598,7 @@ export class PersistentTerminalProcess extends Disposable { } serializeNormalBuffer(): Promise { - return this._serializer.generateReplayEvent(true); + return this._serializer.generateReplayEvent(true, this._interactionState !== InteractionState.Session); } async refreshProperty(type: T): Promise { @@ -613,7 +643,8 @@ export class PersistentTerminalProcess extends Disposable { return this._terminalProcess.shutdown(immediate); } input(data: string): void { - this._hasWrittenData = true; + this._interactionState = InteractionState.Session; + this._serializer.freeRawReviveBuffer(); if (this._inReplay) { return; } @@ -661,7 +692,9 @@ export class PersistentTerminalProcess extends Disposable { } async triggerReplay(): Promise { - this._hasWrittenData = true; + if (this._interactionState === InteractionState.None) { + this._interactionState = InteractionState.ReplayOnly; + } const ev = await this._serializer.generateReplayEvent(); let dataLength = 0; for (const e of ev.events) { @@ -745,18 +778,24 @@ class XtermSerializer implements ITerminalSerializer { rows: number, scrollback: number, unicodeVersion: '6' | '11', - reviveBuffer: string | undefined, + reviveBufferWithRestoreMessage: string | undefined, + private _rawReviveBuffer: string | undefined, logService: ILogService ) { this._xterm = new XtermTerminal({ cols, rows, scrollback }); - if (reviveBuffer) { - this._xterm.writeln(reviveBuffer); + if (reviveBufferWithRestoreMessage) { + this._xterm.writeln(reviveBufferWithRestoreMessage); } this.setUnicodeVersion(unicodeVersion); this._shellIntegrationAddon = new ShellIntegrationAddon(true, undefined, logService); this._xterm.loadAddon(this._shellIntegrationAddon); } + freeRawReviveBuffer(): void { + // Free the memory of the terminal if it will need to be re-serialized + this._rawReviveBuffer = undefined; + } + handleData(data: string): void { this._xterm.write(data); } @@ -765,15 +804,22 @@ class XtermSerializer implements ITerminalSerializer { this._xterm.resize(cols, rows); } - async generateReplayEvent(normalBufferOnly?: boolean): Promise { + async generateReplayEvent(normalBufferOnly?: boolean, restoreToLastReviveBuffer?: boolean): Promise { const serialize = new (await this._getSerializeConstructor()); this._xterm.loadAddon(serialize); - const options: ISerializeOptions = { scrollback: this._xterm.getOption('scrollback') }; + const options: ISerializeOptions = { + scrollback: this._xterm.getOption('scrollback') + }; if (normalBufferOnly) { options.excludeAltBuffer = true; options.excludeModes = true; } - const serialized = serialize.serialize(options); + let serialized: string; + if (restoreToLastReviveBuffer && this._rawReviveBuffer) { + serialized = this._rawReviveBuffer; + } else { + serialized = serialize.serialize(options); + } return { events: [ { @@ -840,7 +886,8 @@ function printTime(ms: number): string { export interface ITerminalSerializer { handleData(data: string): void; + freeRawReviveBuffer(): void; handleResize(cols: number, rows: number): void; - generateReplayEvent(normalBufferOnly?: boolean): Promise; + generateReplayEvent(normalBufferOnly?: boolean, restoreToLastReviveBuffer?: boolean): Promise; setUnicodeVersion?(version: '6' | '11'): void; } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index c167a9b97da..6d57911f12d 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -427,7 +427,7 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', { dark: export const listFocusBackground = registerColor('list.focusBackground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusOutline = registerColor('list.focusOutline', { dark: focusBorder, light: focusBorder, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); -export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#094771', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); +export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', { dark: '#04395E', light: '#0060C0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listActiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionForeground = registerColor('list.activeSelectionForeground', { dark: Color.white, light: Color.white, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionIconForeground = registerColor('list.activeSelectionIconForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listActiveSelectionIconForeground', "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', { dark: '#37373D', light: '#E4E6F1', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); @@ -438,8 +438,8 @@ export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline export const listHoverBackground = registerColor('list.hoverBackground', { dark: '#2A2D2E', light: '#F0F0F0', hcDark: null, hcLight: Color.fromHex('#0F4A85').transparent(0.1) }, nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropBackground = registerColor('list.dropBackground', { dark: '#062F4A', light: '#D6EBFF', hcDark: null, hcLight: null }, nls.localize('listDropBackground', "List/Tree drag and drop background when moving items around using the mouse.")); -export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#18A3FF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); -export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#9DDDFF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); +export const listHighlightForeground = registerColor('list.highlightForeground', { dark: '#2AAAFF', light: '#0066BF', hcDark: focusBorder, hcLight: focusBorder }, nls.localize('highlight', 'List/Tree foreground color of the match highlights when searching inside the list/tree.')); +export const listFocusHighlightForeground = registerColor('list.focusHighlightForeground', { dark: listHighlightForeground, light: ifDefinedThenElse(listActiveSelectionBackground, listHighlightForeground, '#BBE7FF'), hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('listFocusHighlightForeground', 'List/Tree foreground color of the match highlights on actively focused items when searching inside the list/tree.')); export const listInvalidItemForeground = registerColor('list.invalidItemForeground', { dark: '#B89500', light: '#B89500', hcDark: '#B89500', hcLight: '#B5200D' }, nls.localize('invalidItemForeground', 'List/Tree foreground color for invalid items, for example an unresolved root in explorer.')); export const listErrorForeground = registerColor('list.errorForeground', { dark: '#F88070', light: '#B01011', hcDark: null, hcLight: null }, nls.localize('listErrorForeground', 'Foreground color of list items containing errors.')); export const listWarningForeground = registerColor('list.warningForeground', { dark: '#CCA700', light: '#855F00', hcDark: null, hcLight: null }, nls.localize('listWarningForeground', 'Foreground color of list items containing warnings.')); diff --git a/src/vs/platform/tunnel/common/tunnel.ts b/src/vs/platform/tunnel/common/tunnel.ts index 827ec9bd42f..29b07680481 100644 --- a/src/vs/platform/tunnel/common/tunnel.ts +++ b/src/vs/platform/tunnel/common/tunnel.ts @@ -163,6 +163,21 @@ export function isPortPrivileged(port: number, os?: OperatingSystem): boolean { } } +export class DisposableTunnel { + private _onDispose: Emitter = new Emitter(); + onDidDispose: Event = this._onDispose.event; + + constructor( + public readonly remoteAddress: { port: number; host: string }, + public readonly localAddress: { port: number; host: string } | string, + private readonly _dispose: () => Promise) { } + + dispose(): Promise { + this._onDispose.fire(); + return this._dispose(); + } +} + export abstract class AbstractTunnelService implements ITunnelService { declare readonly _serviceBrand: undefined; diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts index 5ceb95727b5..4134233def6 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -27,7 +27,11 @@ configurationRegistry.registerConfiguration({ localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."), localize('start', "Check for updates only on startup. Disable automatic background update checks."), localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically.") - ] + ], + policy: { + name: 'UpdateMode', + minimumVersion: '1.67', + } }, 'update.channel': { type: 'string', diff --git a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts index 7df2c12094a..00f18d5163b 100644 --- a/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts +++ b/src/vs/platform/userData/test/browser/fileUserDataProvider.test.ts @@ -52,7 +52,7 @@ suite('FileUserDataProvider', () => { await testObject.createFolder(backupWorkspaceHomeOnDisk); environmentService = new TestEnvironmentService(userDataHomeOnDisk); - userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, testObject, logService); + userDataProfilesService = new UserDataProfilesService(undefined, environmentService, testObject, logService); fileUserDataProvider = new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService); disposables.add(fileUserDataProvider); @@ -62,25 +62,25 @@ suite('FileUserDataProvider', () => { teardown(() => disposables.clear()); test('exists return false when file does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.currentProfile.settingsResource); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(exists, false); }); test('read file throws error if not exist', async () => { try { - await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); + await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('read existing file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.readFile(userDataProfilesService.currentProfile.settingsResource); + const result = await testObject.readFile(userDataProfilesService.defaultProfile.settingsResource); assert.strictEqual(result.value.toString(), '{}'); }); test('create file', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -88,7 +88,7 @@ suite('FileUserDataProvider', () => { }); test('write file creates the file if not exist', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'settings.json')); @@ -96,7 +96,7 @@ suite('FileUserDataProvider', () => { }); test('write to existing file', async () => { - const resource = userDataProfilesService.currentProfile.settingsResource; + const resource = userDataProfilesService.defaultProfile.settingsResource; await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('{}')); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); @@ -106,33 +106,33 @@ suite('FileUserDataProvider', () => { test('delete file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - await testObject.del(userDataProfilesService.currentProfile.settingsResource); + await testObject.del(userDataProfilesService.defaultProfile.settingsResource); const result = await testObject.exists(joinPath(userDataHomeOnDisk, 'settings.json')); assert.strictEqual(false, result); }); test('resolve file', async () => { await testObject.writeFile(joinPath(userDataHomeOnDisk, 'settings.json'), VSBuffer.fromString('')); - const result = await testObject.resolve(userDataProfilesService.currentProfile.settingsResource); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.settingsResource); assert.ok(!result.isDirectory); assert.ok(result.children === undefined); }); test('exists return false for folder that does not exist', async () => { - const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, false); }); test('exists return true for folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const exists = await testObject.exists(userDataProfilesService.currentProfile.snippetsHome); + const exists = await testObject.exists(userDataProfilesService.defaultProfile.snippetsHome); assert.strictEqual(exists, true); }); test('read file throws error for folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.readFile(userDataProfilesService.currentProfile.snippetsHome); + await testObject.readFile(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail since read file is not supported for folders'); } catch (e) { } }); @@ -140,7 +140,7 @@ suite('FileUserDataProvider', () => { test('read file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -149,7 +149,7 @@ suite('FileUserDataProvider', () => { test('read file under sub folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets', 'java')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual = await testObject.readFile(resource); assert.strictEqual(actual.resource.toString(), resource.toString()); assert.strictEqual(actual.value.toString(), '{}'); @@ -157,7 +157,7 @@ suite('FileUserDataProvider', () => { test('create file under folder that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -165,7 +165,7 @@ suite('FileUserDataProvider', () => { }); test('create file under folder that does not exist', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.createFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual2 = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -174,7 +174,7 @@ suite('FileUserDataProvider', () => { test('write to not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -182,7 +182,7 @@ suite('FileUserDataProvider', () => { }); test('write to not existing file under container that does not exists', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -192,7 +192,7 @@ suite('FileUserDataProvider', () => { test('write to existing file under container', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{a:1}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); @@ -200,7 +200,7 @@ suite('FileUserDataProvider', () => { }); test('write file under sub container', async () => { - const resource = joinPath(userDataProfilesService.currentProfile.snippetsHome, 'java/settings.json'); + const resource = joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'java/settings.json'); const actual1 = await testObject.writeFile(resource, VSBuffer.fromString('{}')); assert.strictEqual(actual1.resource.toString(), resource.toString()); const actual = await testObject.readFile(joinPath(userDataHomeOnDisk, 'snippets', 'java', 'settings.json')); @@ -209,7 +209,7 @@ suite('FileUserDataProvider', () => { test('delete throws error for folder that does not exist', async () => { try { - await testObject.del(userDataProfilesService.currentProfile.snippetsHome); + await testObject.del(userDataProfilesService.defaultProfile.snippetsHome); assert.fail('Should fail the folder does not exist'); } catch (e) { } }); @@ -217,14 +217,14 @@ suite('FileUserDataProvider', () => { test('delete not existing file under container that exists', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); try { - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); test('delete not existing file under container that does not exists', async () => { try { - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); assert.fail('Should fail since file does not exist'); } catch (e) { } }); @@ -232,7 +232,7 @@ suite('FileUserDataProvider', () => { test('delete existing file under folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - await testObject.del(joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json')); + await testObject.del(joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json')); const exists = await testObject.exists(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json')); assert.strictEqual(exists, false); }); @@ -240,11 +240,11 @@ suite('FileUserDataProvider', () => { test('resolve folder', async () => { await testObject.createFolder(joinPath(userDataHomeOnDisk, 'snippets')); await testObject.writeFile(joinPath(userDataHomeOnDisk, 'snippets', 'settings.json'), VSBuffer.fromString('{}')); - const result = await testObject.resolve(userDataProfilesService.currentProfile.snippetsHome); + const result = await testObject.resolve(userDataProfilesService.defaultProfile.snippetsHome); assert.ok(result.isDirectory); assert.ok(result.children !== undefined); assert.strictEqual(result.children!.length, 1); - assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.currentProfile.snippetsHome, 'settings.json').toString()); + assert.strictEqual(result.children![0].resource.toString(), joinPath(userDataProfilesService.defaultProfile.snippetsHome, 'settings.json').toString()); }); test('read backup file', async () => { diff --git a/src/vs/platform/userDataProfile/common/userDataProfile.ts b/src/vs/platform/userDataProfile/common/userDataProfile.ts index d10dd672e4d..c10d6c5de5c 100644 --- a/src/vs/platform/userDataProfile/common/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/common/userDataProfile.ts @@ -3,20 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce } from 'vs/base/common/arrays'; -import { Emitter, Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { joinPath } from 'vs/base/common/resources'; -import { UriDto } from 'vs/base/common/types'; +import { isUndefined, UriDto } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; +import { IFileService } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; + +export type ProfileOptions = { + settings?: boolean; + keybindings?: boolean; + tasks?: boolean; + snippets?: boolean; + extensions?: boolean; + uiState?: boolean; +}; + +export const DefaultOptions: ProfileOptions = { + settings: true, + keybindings: true, + tasks: true, + snippets: true, + extensions: true, + uiState: true +}; export interface IUserDataProfile { readonly id: string; + readonly isDefault: boolean; readonly name: string; readonly location: URI; readonly globalStorageHome: URI; @@ -27,11 +47,24 @@ export interface IUserDataProfile { readonly extensionsResource: URI | undefined; } -export type IUserDataProfileDto = UriDto; -export type IUserDataProfilesDto = { - readonly current: IUserDataProfileDto; - readonly default: IUserDataProfileDto; -}; +export type CustomUserDataProfile = IUserDataProfile & { readonly extensionsResource: URI; readonly isDefault: false }; + +export function isUserDataProfile(thing: unknown): thing is IUserDataProfile { + const candidate = thing as IUserDataProfile | undefined; + + return !!(candidate && typeof candidate === 'object' + && typeof candidate.id === 'string' + && typeof candidate.isDefault === 'boolean' + && typeof candidate.name === 'string' + && URI.isUri(candidate.location) + && URI.isUri(candidate.globalStorageHome) + && URI.isUri(candidate.settingsResource) + && URI.isUri(candidate.keybindingsResource) + && URI.isUri(candidate.tasksResource) + && URI.isUri(candidate.snippetsHome) + && (isUndefined(candidate.extensionsResource) || URI.isUri(candidate.extensionsResource)) + ); +} export const IUserDataProfilesService = createDecorator('IUserDataProfilesService'); export interface IUserDataProfilesService { @@ -40,19 +73,20 @@ export interface IUserDataProfilesService { readonly profilesHome: URI; readonly defaultProfile: IUserDataProfile; - readonly onDidChangeCurrentProfile: Event; - readonly currentProfile: IUserDataProfile; + readonly onDidChangeProfiles: Event; + readonly profiles: IUserDataProfile[]; - createProfile(name: string): IUserDataProfile; - setProfile(name: string): Promise; - getAllProfiles(): Promise; - - serialize(): IUserDataProfilesDto; + newProfile(name: string, options?: ProfileOptions): CustomUserDataProfile; + createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; + setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise; + getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile; + removeProfile(profile: IUserDataProfile): Promise; } -function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProfile { +export function reviveProfile(profile: UriDto, scheme: string): IUserDataProfile { return { id: profile.id, + isDefault: profile.isDefault, name: profile.name, location: URI.revive(profile.location).with({ scheme }), globalStorageHome: URI.revive(profile.globalStorageHome).with({ scheme }), @@ -64,75 +98,55 @@ function reviveProfile(profile: IUserDataProfile, scheme: string): IUserDataProf }; } +export const EXTENSIONS_RESOURCE_NAME = 'extensions.json'; + +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true): IUserDataProfile; +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: IUserDataProfile): CustomUserDataProfile; +export function toUserDataProfile(name: string, location: URI, options: ProfileOptions, defaultProfile: true | IUserDataProfile): IUserDataProfile { + return { + id: hash(location.toString()).toString(16), + name: name, + location: location, + isDefault: defaultProfile === true, + globalStorageHome: defaultProfile === true || options.uiState ? joinPath(location, 'globalStorage') : defaultProfile.globalStorageHome, + settingsResource: defaultProfile === true || options.settings ? joinPath(location, 'settings.json') : defaultProfile.settingsResource, + keybindingsResource: defaultProfile === true || options.keybindings ? joinPath(location, 'keybindings.json') : defaultProfile.keybindingsResource, + tasksResource: defaultProfile === true || options.tasks ? joinPath(location, 'tasks.json') : defaultProfile.tasksResource, + snippetsHome: defaultProfile === true || options.snippets ? joinPath(location, 'snippets') : defaultProfile.snippetsHome, + extensionsResource: defaultProfile === true && !options.extensions ? undefined : joinPath(location, EXTENSIONS_RESOURCE_NAME), + }; +} + export class UserDataProfilesService extends Disposable implements IUserDataProfilesService { readonly _serviceBrand: undefined; - protected static DEFAULT_PROFILE_NAME = 'default'; - - protected _currentProfile: IUserDataProfile; - get currentProfile(): IUserDataProfile { return this._currentProfile; } - readonly profilesHome: URI; + protected _defaultProfile: IUserDataProfile; get defaultProfile(): IUserDataProfile { return this._defaultProfile; } - private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); - readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; + get profiles(): IUserDataProfile[] { return []; } + + protected readonly _onDidChangeProfiles = this._register(new Emitter()); + readonly onDidChangeProfiles = this._onDidChangeProfiles.event; constructor( - defaultProfile: IUserDataProfile | undefined, - currentProfile: IUserDataProfile | undefined, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + defaultProfile: UriDto | undefined, + @IEnvironmentService protected readonly environmentService: IEnvironmentService, @IFileService protected readonly fileService: IFileService, @ILogService protected readonly logService: ILogService ) { super(); this.profilesHome = joinPath(this.environmentService.userRoamingDataHome, 'profiles'); - this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : this.createProfile(undefined); - this._currentProfile = currentProfile ? reviveProfile(currentProfile, this.profilesHome.scheme) : this._defaultProfile; + this._defaultProfile = defaultProfile ? reviveProfile(defaultProfile, this.profilesHome.scheme) : toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true); } - createProfile(name: string | undefined): IUserDataProfile { - const location = name && name !== UserDataProfilesService.DEFAULT_PROFILE_NAME ? joinPath(this.profilesHome, name) : this.environmentService.userRoamingDataHome; - return { - id: hash(location.toString()).toString(16), - name: name ?? UserDataProfilesService.DEFAULT_PROFILE_NAME, - location, - globalStorageHome: joinPath(location, 'globalStorage'), - settingsResource: joinPath(location, 'settings.json'), - keybindingsResource: joinPath(location, 'keybindings.json'), - tasksResource: joinPath(location, 'tasks.json'), - snippetsHome: joinPath(location, 'snippets'), - extensionsResource: name ? joinPath(location, 'extensions.json') : undefined - }; + newProfile(name: string, options: ProfileOptions = DefaultOptions): CustomUserDataProfile { + return toUserDataProfile(name, joinPath(this.profilesHome, hash(name).toString(16)), options, this.defaultProfile); } - async getAllProfiles(): Promise { - try { - const stat = await this.fileService.resolve(this.profilesHome); - const profiles = coalesce(stat.children?.map(stat => stat.isDirectory ? this.createProfile(stat.name) : undefined) ?? []); - if (profiles.length) { - profiles.unshift(this._defaultProfile); - } - return profiles; - } catch (error) { - if ((error).fileOperationResult !== FileOperationResult.FILE_NOT_FOUND) { - this.logService.error('Error while getting all profiles', error); - } - } - return []; - } - - protected createCurrentProfile(profile: string | undefined): IUserDataProfile { - return profile === UserDataProfilesService.DEFAULT_PROFILE_NAME ? this._defaultProfile : this.createProfile(profile); - } - - setProfile(name: string): Promise { throw new Error('Not implemented'); } - - serialize(): IUserDataProfilesDto { - return { - default: this.defaultProfile, - current: this.currentProfile - }; - } + createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } + setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { throw new Error('Not implemented'); } + getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { throw new Error('Not implemented'); } + removeProfile(profile: IUserDataProfile): Promise { throw new Error('Not implemented'); } } diff --git a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts index 1a13319dc3d..fd7b93059dc 100644 --- a/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-main/userDataProfile.ts @@ -3,42 +3,147 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ResourceMap } from 'vs/base/common/map'; +import { revive } from 'vs/base/common/marshalling'; +import { UriDto } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; +import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { IStateMainService } from 'vs/platform/state/electron-main/state'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { ProfileOptions, DefaultOptions, IUserDataProfile, IUserDataProfilesService, UserDataProfilesService, reviveProfile, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; -export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesService { +export const IUserDataProfilesMainService = refineServiceDecorator(IUserDataProfilesService); +export interface IUserDataProfilesMainService extends IUserDataProfilesService { + getAllProfiles(): Promise; +} - private static CURRENT_PROFILE_KEY = 'currentUserDataProfile'; +type UserDataProfilesObject = { + profiles: IUserDataProfile[]; + workspaces: ResourceMap; +}; + +type StoredUserDataProfile = { + name: string; + location: URI; + options: ProfileOptions; +}; + +type StoredWorkspaceInfo = { + workspace: URI; + profile: URI; +}; + +export class UserDataProfilesMainService extends UserDataProfilesService implements IUserDataProfilesMainService { + + private static readonly PROFILES_KEY = 'userDataProfiles'; + private static readonly WORKSPACE_PROFILE_INFO_KEY = 'workspaceAndProfileInfo'; constructor( @IStateMainService private readonly stateMainService: IStateMainService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ILogService logService: ILogService, ) { - super(undefined, undefined, environmentService, fileService, logService); + super(toUserDataProfile(localize('defaultProfile', "Default"), environmentService.userRoamingDataHome, { ...DefaultOptions, extensions: false }, true), environmentService, fileService, logService); } - async init(): Promise { - const profileName = this.stateMainService.getItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); - if (profileName) { - const profiles = await this.getAllProfiles(); - const profile = profiles.find(p => p.name === profileName); - if (profile || (profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME && profiles.length > 1)) { - this._defaultProfile = this.createProfile(UserDataProfilesService.DEFAULT_PROFILE_NAME); - this._currentProfile = profileName === UserDataProfilesMainService.DEFAULT_PROFILE_NAME ? this._defaultProfile : profile ?? this._defaultProfile; - } else { - this.stateMainService?.removeItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY); - } + init(): void { + if (this.storedProfiles.length) { + this._defaultProfile = toUserDataProfile(this.defaultProfile.name, this.defaultProfile.location, DefaultOptions, true); } } - override async setProfile(name: string): Promise { - this.stateMainService?.setItem(UserDataProfilesMainService.CURRENT_PROFILE_KEY, name); + private _profilesObject: UserDataProfilesObject | undefined; + private get profilesObject(): UserDataProfilesObject { + if (!this._profilesObject) { + const profiles = this.storedProfiles.map(storedProfile => toUserDataProfile(storedProfile.name, storedProfile.location, storedProfile.options, this.defaultProfile)); + profiles.unshift(this.defaultProfile); + const workspaces = this.storedWorskpaceInfos.reduce((workspaces, workspaceProfileInfo) => { + const profile = profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, workspaceProfileInfo.profile)); + if (profile) { + workspaces.set(workspaceProfileInfo.workspace, profile); + } + return workspaces; + }, new ResourceMap()); + this._profilesObject = { profiles: profiles, workspaces: workspaces }; + } + return this._profilesObject; + } + + override get profiles(): IUserDataProfile[] { return this.profilesObject.profiles; } + + async getAllProfiles(): Promise { + return this.profiles; + } + + override getProfile(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): IUserDataProfile { + return this.profilesObject.workspaces.get(this.getWorkspace(workspaceIdentifier)) ?? this.defaultProfile; + } + + override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + profile = reviveProfile(profile, this.profilesHome.scheme); + if (this.storedProfiles.some(p => p.name === profile.name)) { + throw new Error(`Profile with name ${profile.name} already exists`); + } + const storedProfile: StoredUserDataProfile = { name: profile.name, location: profile.location, options }; + const storedProfiles = [...this.storedProfiles, storedProfile]; + this.storedProfiles = storedProfiles; + if (workspaceIdentifier) { + await this.setProfileForWorkspace(profile, workspaceIdentifier); + } + return this.profilesObject.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + } + + override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + profile = reviveProfile(profile, this.profilesHome.scheme); + const workspace = this.getWorkspace(workspaceIdentifier); + const storedWorkspaceInfos = this.storedWorskpaceInfos.filter(info => !this.uriIdentityService.extUri.isEqual(info.workspace, workspace)); + if (!profile.isDefault) { + storedWorkspaceInfos.push({ workspace, profile: profile.location }); + } + this.storedWorskpaceInfos = storedWorkspaceInfos; + return this.profilesObject.profiles.find(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))!; + } + + private getWorkspace(workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier) { + return isSingleFolderWorkspaceIdentifier(workspaceIdentifier) ? workspaceIdentifier.uri : workspaceIdentifier.configPath; + } + + override async removeProfile(profile: IUserDataProfile): Promise { + if (profile.isDefault) { + throw new Error('Cannot remove default profile'); + } + profile = reviveProfile(profile, this.profilesHome.scheme); + if (!this.storedProfiles.some(p => this.uriIdentityService.extUri.isEqual(p.location, profile.location))) { + throw new Error(`Profile with name ${profile.name} does not exist`); + } + this.storedWorskpaceInfos = this.storedWorskpaceInfos.filter(p => !this.uriIdentityService.extUri.isEqual(p.profile, profile.location)); + this.storedProfiles = this.storedProfiles.filter(p => !this.uriIdentityService.extUri.isEqual(p.location, profile.location)); + } + + private get storedProfiles(): StoredUserDataProfile[] { + return revive(this.stateMainService.getItem[]>(UserDataProfilesMainService.PROFILES_KEY, [])); + } + + private set storedProfiles(storedProfiles: StoredUserDataProfile[]) { + this.stateMainService.setItem(UserDataProfilesMainService.PROFILES_KEY, storedProfiles); + this._profilesObject = undefined; + this._onDidChangeProfiles.fire(this.profiles); + } + + private get storedWorskpaceInfos(): StoredWorkspaceInfo[] { + return revive(this.stateMainService.getItem[]>(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, [])); + } + + private set storedWorskpaceInfos(storedWorkspaceInfos: StoredWorkspaceInfo[]) { + this.stateMainService.setItem(UserDataProfilesMainService.WORKSPACE_PROFILE_INFO_KEY, storedWorkspaceInfos); + this._profilesObject = undefined; } } - diff --git a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts index c28b57fe965..3c5e5e0d3ef 100644 --- a/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts +++ b/src/vs/platform/userDataProfile/electron-sandbox/userDataProfile.ts @@ -3,27 +3,51 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { UriDto } from 'vs/base/common/types'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; -import { IUserDataProfile, IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ProfileOptions, IUserDataProfile, IUserDataProfilesService, reviveProfile, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export class UserDataProfilesNativeService extends UserDataProfilesService implements IUserDataProfilesService { + private _profiles: IUserDataProfile[] = []; + override get profiles(): IUserDataProfile[] { return this._profiles; } + constructor( - defaultProfile: IUserDataProfile, - currentProfile: IUserDataProfile, + defaultProfile: UriDto, private readonly channel: IChannel, @IEnvironmentService environmentService: IEnvironmentService, @IFileService fileService: IFileService, @ILogService logService: ILogService, ) { - super(defaultProfile, currentProfile, environmentService, fileService, logService); + super(defaultProfile, environmentService, fileService, logService); + this.initializeProfiles(); } - override setProfile(name: string): Promise { - return this.channel.call('setProfile', [name]); + private async initializeProfiles(): Promise { + const result = await this.channel.call[]>('getAllProfiles'); + this._profiles = result.map(profile => reviveProfile(profile, this.profilesHome.scheme)); + this._register(this.channel.listen('onDidChangeProfiles')((profiles) => { + this._profiles = profiles.map(profile => reviveProfile(profile, this.profilesHome.scheme)); + this._onDidChangeProfiles.fire(this._profiles); + })); + } + + override async createProfile(profile: IUserDataProfile, options: ProfileOptions, workspaceIdentifier?: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + const result = await this.channel.call>('createProfile', [profile, options, workspaceIdentifier]); + return reviveProfile(result, this.profilesHome.scheme); + } + + override async setProfileForWorkspace(profile: IUserDataProfile, workspaceIdentifier: ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier): Promise { + const result = await this.channel.call>('setProfileForWorkspace', [profile, workspaceIdentifier]); + return reviveProfile(result, this.profilesHome.scheme); + } + + override removeProfile(profile: IUserDataProfile): Promise { + return this.channel.call('removeProfile', [profile]); } } diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts index 8236bb281b3..8def92906ef 100644 --- a/src/vs/platform/userDataSync/common/extensionsMerge.ts +++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts @@ -45,9 +45,7 @@ export function merge(localExtensions: ISyncExtensionWithVersion[], remoteExtens const addUUID = (identifier: IExtensionIdentifier) => { if (identifier.uuid) { uuids.set(identifier.id.toLowerCase(), identifier.uuid); } }; localExtensions.forEach(({ identifier }) => addUUID(identifier)); remoteExtensions.forEach(({ identifier }) => addUUID(identifier)); - if (lastSyncExtensions) { - lastSyncExtensions.forEach(({ identifier }) => addUUID(identifier)); - } + lastSyncExtensions?.forEach(({ identifier }) => addUUID(identifier)); const getKey = (extension: ISyncExtension): string => { const uuid = extension.identifier.uuid || uuids.get(extension.identifier.id.toLowerCase()); diff --git a/src/vs/platform/userDataSync/common/globalStateSync.ts b/src/vs/platform/userDataSync/common/globalStateSync.ts index 1592622196b..a1b6c768ed4 100644 --- a/src/vs/platform/userDataSync/common/globalStateSync.ts +++ b/src/vs/platform/userDataSync/common/globalStateSync.ts @@ -56,6 +56,8 @@ function stringify(globalState: IGlobalState, format: boolean): string { const GLOBAL_STATE_DATA_VERSION = 1; /** + * TODO: @sandy081: Sync only global state of default profile + * * Synchronises global state that includes * - Global storage with user scope * - Locale from argv properties diff --git a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts index 19db5e01a89..bf59598603b 100644 --- a/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataAutoSyncService.ts @@ -54,26 +54,26 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto private lastSyncUrl: URI | undefined; private get syncUrl(): URI | undefined { - const value = this.storageService.get(storeUrlKey, StorageScope.GLOBAL); + const value = this.storageService.get(storeUrlKey, StorageScope.APPLICATION); return value ? URI.parse(value) : undefined; } private set syncUrl(syncUrl: URI | undefined) { if (syncUrl) { - this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storeUrlKey, syncUrl.toString(), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(storeUrlKey, StorageScope.GLOBAL); + this.storageService.remove(storeUrlKey, StorageScope.APPLICATION); } } private previousProductQuality: string | undefined; private get productQuality(): string | undefined { - return this.storageService.get(productQualityKey, StorageScope.GLOBAL); + return this.storageService.get(productQualityKey, StorageScope.APPLICATION); } private set productQuality(productQuality: string | undefined) { if (productQuality) { - this.storageService.store(productQualityKey, productQuality, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(productQualityKey, productQuality, StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(productQualityKey, StorageScope.GLOBAL); + this.storageService.remove(productQualityKey, StorageScope.APPLICATION); } } @@ -194,7 +194,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto this.updateEnablement(false); // Reset Session - this.storageService.remove(sessionIdKey, StorageScope.GLOBAL); + this.storageService.remove(sessionIdKey, StorageScope.APPLICATION); // Reset if (everywhere) { @@ -315,7 +315,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private async disableMachineEventually(): Promise { - this.storageService.store(disableMachineEventuallyKey, true, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(disableMachineEventuallyKey, true, StorageScope.APPLICATION, StorageTarget.MACHINE); await timeout(1000 * 60 * 10); // Return if got stopped meanwhile. @@ -332,11 +332,11 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto } private hasToDisableMachineEventually(): boolean { - return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.GLOBAL, false); + return this.storageService.getBoolean(disableMachineEventuallyKey, StorageScope.APPLICATION, false); } private stopDisableMachineEventually(): void { - this.storageService.remove(disableMachineEventuallyKey, StorageScope.GLOBAL); + this.storageService.remove(disableMachineEventuallyKey, StorageScope.APPLICATION); } private sources: string[] = []; @@ -481,7 +481,7 @@ class AutoSync extends Disposable { } } - const sessionId = this.storageService.get(sessionIdKey, StorageScope.GLOBAL); + const sessionId = this.storageService.get(sessionIdKey, StorageScope.APPLICATION); // Server session is different from client session if (sessionId && this.manifest && sessionId !== this.manifest.session) { if (this.hasSyncServiceChanged()) { @@ -521,7 +521,7 @@ class AutoSync extends Disposable { // Update local session id if (this.manifest && this.manifest.session !== sessionId) { - this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(sessionIdKey, this.manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE); } // Return if cancellation is requested diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts index ac12a674403..dfb2cccaea1 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts @@ -46,7 +46,7 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa case 'off': return false; } - return this.storageService.getBoolean(enablementKey, StorageScope.GLOBAL, false); + return this.storageService.getBoolean(enablementKey, StorageScope.APPLICATION, false); } canToggleEnablement(): boolean { @@ -58,11 +58,11 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa return; } this.telemetryService.publicLog2<{ enabled: boolean }, SyncEnablementClassification>(enablementKey, { enabled }); - this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(enablementKey, enabled, StorageScope.APPLICATION, StorageTarget.MACHINE); } isResourceEnabled(resource: SyncResource): boolean { - return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.GLOBAL, true); + return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.APPLICATION, true); } setResourceEnablement(resource: SyncResource, enabled: boolean): void { @@ -77,11 +77,11 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa } private storeResourceEnablement(resourceEnablementKey: string, enabled: boolean): void { - this.storageService.store(resourceEnablementKey, enabled, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + this.storageService.store(resourceEnablementKey, enabled, StorageScope.APPLICATION, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); } private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void { - if (storageChangeEvent.scope !== StorageScope.GLOBAL) { + if (storageChangeEvent.scope !== StorageScope.APPLICATION) { return; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts index 43b71b2ab05..1158727d42c 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncMachines.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncMachines.ts @@ -132,7 +132,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData await this.writeMachinesData(machineData); const currentMachineId = await this.currentMachineIdPromise; if (machineId === currentMachineId) { - this.storageService.store(currentMachineNameKey, name, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(currentMachineNameKey, name, StorageScope.APPLICATION, StorageTarget.MACHINE); } } } @@ -149,7 +149,7 @@ export class UserDataSyncMachinesService extends Disposable implements IUserData } private computeCurrentMachineName(machines: IMachineData[]): string { - const previousName = this.storageService.get(currentMachineNameKey, StorageScope.GLOBAL); + const previousName = this.storageService.get(currentMachineNameKey, StorageScope.APPLICATION); if (previousName) { return previousName; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 4845ad05da6..d5e5b74cb7f 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -85,7 +85,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ ) { super(); this.updateStatus([]); - this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined); + this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.APPLICATION, undefined); } async createSyncTask(manifest: IUserDataManifest | null, disableCache?: boolean): Promise { @@ -372,7 +372,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ async resetLocal(): Promise { this.checkEnablement(); - this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL); + this.storageService.remove(LAST_SYNC_TIME_KEY, StorageScope.APPLICATION); if (this.synchronizers.value) { for (const synchroniser of this.synchronizers.value.enabled) { try { @@ -451,7 +451,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ private updateLastSyncTime(): void { if (this.status === SyncStatus.Idle) { this._lastSyncTime = new Date().getTime(); - this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(LAST_SYNC_TIME_KEY, this._lastSyncTime, StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeLastSyncTime.fire(this._lastSyncTime); } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 8ae695baae4..5d926223d60 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -44,10 +44,10 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa get userDataSyncStore(): UserDataSyncStore | undefined { return this._userDataSyncStore; } protected get userDataSyncStoreType(): UserDataSyncStoreType | undefined { - return this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.GLOBAL) as UserDataSyncStoreType; + return this.storageService.get(SYNC_SERVICE_URL_TYPE, StorageScope.APPLICATION) as UserDataSyncStoreType; } protected set userDataSyncStoreType(type: UserDataSyncStoreType | undefined) { - this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.GLOBAL, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); + this.storageService.store(SYNC_SERVICE_URL_TYPE, type, StorageScope.APPLICATION, isWeb ? StorageTarget.USER /* sync in web */ : StorageTarget.MACHINE); } constructor( @@ -57,7 +57,7 @@ export abstract class AbstractUserDataSyncStoreManagementService extends Disposa ) { super(); this.updateUserDataSyncStore(); - this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === StorageScope.GLOBAL && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore())); + this._register(Event.filter(storageService.onDidChangeValue, e => e.key === SYNC_SERVICE_URL_TYPE && e.scope === StorageScope.APPLICATION && this.userDataSyncStoreType !== this.userDataSyncStore?.type)(() => this.updateUserDataSyncStore())); } protected updateUserDataSyncStore(): void { @@ -115,16 +115,16 @@ export class UserDataSyncStoreManagementService extends AbstractUserDataSyncStor ) { super(productService, configurationService, storageService); - const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + const previousConfigurationSyncStore = this.storageService.get(SYNC_PREVIOUS_STORE, StorageScope.APPLICATION); if (previousConfigurationSyncStore) { this.previousConfigurationSyncStore = JSON.parse(previousConfigurationSyncStore); } const syncStore = this.productService[CONFIGURATION_SYNC_STORE_KEY]; if (syncStore) { - this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(SYNC_PREVIOUS_STORE, JSON.stringify(syncStore), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.GLOBAL); + this.storageService.remove(SYNC_PREVIOUS_STORE, StorageScope.APPLICATION); } } @@ -202,7 +202,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private initDonotMakeRequestsUntil(): void { - const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + const donotMakeRequestsUntil = this.storageService.getNumber(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.APPLICATION); if (donotMakeRequestsUntil && Date.now() < donotMakeRequestsUntil) { this.setDonotMakeRequestsUntil(new Date(donotMakeRequestsUntil)); } @@ -219,11 +219,11 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } if (this._donotMakeRequestsUntil) { - this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(DONOT_MAKE_REQUESTS_UNTIL_KEY, this._donotMakeRequestsUntil.getTime(), StorageScope.APPLICATION, StorageTarget.MACHINE); this.resetDonotMakeRequestsUntilPromise = createCancelablePromise(token => timeout(this._donotMakeRequestsUntil!.getTime() - Date.now(), token).then(() => this.setDonotMakeRequestsUntil(undefined))); this.resetDonotMakeRequestsUntilPromise.then(null, e => null /* ignore error */); } else { - this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.GLOBAL); + this.storageService.remove(DONOT_MAKE_REQUESTS_UNTIL_KEY, StorageScope.APPLICATION); } this._onDidChangeDonotMakeRequestsUntil.fire(); @@ -362,7 +362,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } } - const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + const currentSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.APPLICATION); if (currentSessionId && manifest && currentSessionId !== manifest.session) { // Server session is different from client session so clear cached session. @@ -376,7 +376,7 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync if (manifest) { // update session - this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(USER_SESSION_ID_KEY, manifest.session, StorageScope.APPLICATION, StorageTarget.MACHINE); } return manifest; @@ -397,8 +397,8 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private clearSession(): void { - this.storageService.remove(USER_SESSION_ID_KEY, StorageScope.GLOBAL); - this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + this.storageService.remove(USER_SESSION_ID_KEY, StorageScope.APPLICATION); + this.storageService.remove(MACHINE_SESSION_ID_KEY, StorageScope.APPLICATION); } private async request(url: string, options: IRequestOptions, successCodes: number[], token: CancellationToken): Promise { @@ -518,14 +518,14 @@ export class UserDataSyncStoreClient extends Disposable implements IUserDataSync } private addSessionHeaders(headers: IHeaders): void { - let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.GLOBAL); + let machineSessionId = this.storageService.get(MACHINE_SESSION_ID_KEY, StorageScope.APPLICATION); if (machineSessionId === undefined) { machineSessionId = generateUuid(); - this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(MACHINE_SESSION_ID_KEY, machineSessionId, StorageScope.APPLICATION, StorageTarget.MACHINE); } headers['X-Machine-Session-Id'] = machineSessionId; - const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.GLOBAL); + const userSessionId = this.storageService.get(USER_SESSION_ID_KEY, StorageScope.APPLICATION); if (userSessionId !== undefined) { headers['X-User-Session-Id'] = userSessionId; } diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 242bf993092..cc94faefa1a 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -33,7 +33,7 @@ suite('UserDataAutoSyncService', () => { teardown(() => disposableStore.clear()); test('test auto sync with sync resource change triggers sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -57,7 +57,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with sync resource change triggers sync for every change', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -85,7 +85,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with non sync resource change triggers sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -109,7 +109,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync with non sync resource change does not trigger continuous syncs', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -135,7 +135,7 @@ suite('UserDataAutoSyncService', () => { }); test('test first auto sync requests', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -175,7 +175,7 @@ suite('UserDataAutoSyncService', () => { }); test('test further auto sync requests without changes', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -196,7 +196,7 @@ suite('UserDataAutoSyncService', () => { }); test('test further auto sync requests with changes', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -233,7 +233,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync send execution id header', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { // Setup the client const target = new UserDataSyncTestServer(); const client = disposableStore.add(new UserDataSyncClient(target)); @@ -258,7 +258,7 @@ suite('UserDataAutoSyncService', () => { }); test('test delete on one client throws turned off error on other client while syncing', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the client @@ -294,7 +294,7 @@ suite('UserDataAutoSyncService', () => { }); test('test disabling the machine turns off sync', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the test client @@ -328,7 +328,7 @@ suite('UserDataAutoSyncService', () => { }); test('test removing the machine adds machine back', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the test client @@ -353,7 +353,7 @@ suite('UserDataAutoSyncService', () => { }); test('test creating new session from one client throws session expired error on another client while syncing', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(); // Set up and sync from the client @@ -392,7 +392,7 @@ suite('UserDataAutoSyncService', () => { }); test('test rate limit on server', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5); // Set up and sync from the test client @@ -412,7 +412,7 @@ suite('UserDataAutoSyncService', () => { }); test('test auto sync is suspended when server donot accepts requests', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client @@ -432,7 +432,7 @@ suite('UserDataAutoSyncService', () => { }); test('test cache control header with no cache is sent when triggered with disable cache option', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client @@ -446,7 +446,7 @@ suite('UserDataAutoSyncService', () => { }); test('test cache control header is not sent when triggered without disable cache option', async () => { - await runWithFakedTimers({ useSetImmediate: true }, async () => { + await runWithFakedTimers({}, async () => { const target = new UserDataSyncTestServer(5, 1); // Set up and sync from the test client diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 968ba2d0e3c..1924fffc3ec 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -83,7 +83,7 @@ export class UserDataSyncClient extends Disposable { fileService.registerProvider(Schemas.inMemory, new InMemoryFileSystemProvider()); this.instantiationService.stub(IFileService, fileService); - const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const userDataProfilesService = this.instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, logService)); this.instantiationService.stub(IStorageService, this._register(new InMemoryStorageService())); diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index c675d3e5b13..875bd45f761 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -6,6 +6,7 @@ import { IStringDictionary } from 'vs/base/common/collections'; import { PerformanceMark } from 'vs/base/common/performance'; import { isLinux, isMacintosh, isNative, isWeb, isWindows } from 'vs/base/common/platform'; +import { UriDto } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { ISandboxConfiguration } from 'vs/base/parts/sandbox/common/sandboxTypes'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -16,7 +17,7 @@ import { FileType } from 'vs/platform/files/common/files'; import { LogLevel } from 'vs/platform/log/common/log'; import { PolicyDefinition, PolicyValue } from 'vs/platform/policy/common/policy'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IUserDataProfilesDto } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const WindowMinimumSize = { @@ -283,7 +284,10 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native execPath: string; backupPath?: string; - profiles: IUserDataProfilesDto; + profiles: { + default: UriDto; + current: UriDto; + }; homeDir: string; tmpDir: string; diff --git a/src/vs/platform/windows/electron-main/window.ts b/src/vs/platform/windows/electron-main/window.ts index 1f76f568201..6fcf7095136 100644 --- a/src/vs/platform/windows/electron-main/window.ts +++ b/src/vs/platform/windows/electron-main/window.ts @@ -29,7 +29,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IProtocolMainService } from 'vs/platform/protocol/electron-main/protocol'; import { resolveMarketplaceHeaders } from 'vs/platform/externalServices/common/marketplace'; -import { IGlobalStorageMainService, IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IApplicationStorageMainService, IStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; @@ -40,7 +40,7 @@ import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electro import { IWindowState, ICodeWindow, ILoadEvent, WindowMode, WindowError, LoadReason, defaultWindowState } from 'vs/platform/window/electron-main/window'; import { Color } from 'vs/base/common/color'; import { IPolicyService } from 'vs/platform/policy/common/policy'; -import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { revive } from 'vs/base/common/marshalling'; export interface IWindowCreationOptions { @@ -156,8 +156,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { @ILogService private readonly logService: ILogService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IPolicyService private readonly policyService: IPolicyService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IFileService private readonly fileService: IFileService, - @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService, + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, @IStorageMainService private readonly storageMainService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IThemeMainService private readonly themeMainService: IThemeMainService, @@ -276,6 +277,26 @@ export class CodeWindow extends Disposable implements ICodeWindow { this._id = this._win.id; + // re-position traffic light if command center is visible + if (useCustomTitleStyle && isMacintosh) { + const ccConfigKey = 'window.commandCenter'; + const trafficLightUpdater = () => { + // temporarily disabled because of https://github.com/microsoft/vscode/pull/150272#issuecomment-1152218493 + // const on = this.configurationService.getValue(ccConfigKey); + // if (on) { + // this._win.setTrafficLightPosition({ x: 7, y: 9 }); + // } else { + // this._win.setTrafficLightPosition({ x: 7, y: 6 }); + // } + }; + trafficLightUpdater(); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ccConfigKey)) { + trafficLightUpdater(); + } + }, undefined, this._store); + } + // Open devtools if instructed from command line args if (this.environmentMainService.args['open-devtools'] === true) { this._win.webContents.openDevTools(); @@ -521,7 +542,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private marketplaceHeadersPromise: Promise | undefined; private getMarketplaceHeaders(): Promise { if (!this.marketplaceHeadersPromise) { - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.globalStorageMainService); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(this.productService.version, this.productService, this.environmentMainService, this.configurationService, this.fileService, this.applicationStorageMainService); } return this.marketplaceHeadersPromise; @@ -882,6 +903,10 @@ export class CodeWindow extends Disposable implements ICodeWindow { configuration.isInitialStartup = false; // since this is a reload configuration.policiesData = this.policyService.serialize(); // set policies data again + configuration.profiles = { + default: this.userDataProfilesService.defaultProfile, + current: configuration.workspace ? this.userDataProfilesService.getProfile(configuration.workspace) : this.userDataProfilesService.defaultProfile, + }; // Load config this.load(configuration, { isReload: true, disableExtensions: cli?.['disable-extensions'] }); diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 43375f06971..92cba61a462 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1300,7 +1300,10 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // loading the window. backupPath: options.emptyWindowBackupInfo ? join(this.environmentMainService.backupHome, options.emptyWindowBackupInfo.backupFolder) : undefined, - profiles: this.userDataProfilesService.serialize(), + profiles: { + default: this.userDataProfilesService.defaultProfile, + current: options.workspace ? this.userDataProfilesService.getProfile(options.workspace) : this.userDataProfilesService.defaultProfile, + }, homeDir: this.environmentMainService.userHome.fsPath, tmpDir: this.environmentMainService.tmpDir.fsPath, @@ -1354,9 +1357,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Add as window tab if configured (macOS only) if (options.forceNewTabbedWindow) { const activeWindow = this.getLastActiveWindow(); - if (activeWindow) { - activeWindow.addTabbedWindow(createdWindow); - } + activeWindow?.addTabbedWindow(createdWindow); } // Add to our list of windows @@ -1472,9 +1473,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic sendToFocused(channel: string, ...args: any[]): void { const focusedWindow = this.getFocusedWindow() || this.getLastActiveWindow(); - if (focusedWindow) { - focusedWindow.sendWhenReady(channel, CancellationToken.None, ...args); - } + focusedWindow?.sendWhenReady(channel, CancellationToken.None, ...args); } sendToAll(channel: string, payload?: any, windowIdsToIgnore?: number[]): void { diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index c4247ff6b0e..0c9f5ee790c 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -19,7 +19,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { ILogService } from 'vs/platform/log/common/log'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IGlobalStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; +import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { ICodeWindow } from 'vs/platform/window/electron-main/window'; import { IRecent, IRecentFile, IRecentFolder, IRecentlyOpened, IRecentWorkspace, isRecentFile, isRecentFolder, isRecentWorkspace, restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspaces'; import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspace/common/workspace'; @@ -54,7 +54,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @ILogService private readonly logService: ILogService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IGlobalStorageMainService private readonly globalStorageMainService: IGlobalStorageMainService + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService ) { super(); @@ -217,13 +217,13 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa private async getRecentlyOpenedFromStorage(): Promise { - // Wait for global storage to be ready - await this.globalStorageMainService.whenReady; + // Wait for application storage to be ready + await this.applicationStorageMainService.whenReady; let storedRecentlyOpened: object | undefined = undefined; // First try with storage service - const storedRecentlyOpenedRaw = this.globalStorageMainService.get(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, StorageScope.GLOBAL); + const storedRecentlyOpenedRaw = this.applicationStorageMainService.get(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, StorageScope.APPLICATION); if (typeof storedRecentlyOpenedRaw === 'string') { try { storedRecentlyOpened = JSON.parse(storedRecentlyOpenedRaw); @@ -237,11 +237,11 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa private async saveRecentlyOpened(recent: IRecentlyOpened): Promise { - // Wait for global storage to be ready - await this.globalStorageMainService.whenReady; + // Wait for application storage to be ready + await this.applicationStorageMainService.whenReady; - // Store in global storage (but do not sync since this is mainly local paths) - this.globalStorageMainService.store(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, JSON.stringify(toStoreData(recent)), StorageScope.GLOBAL, StorageTarget.MACHINE); + // Store in application storage (but do not sync since this is mainly local paths) + this.applicationStorageMainService.store(WorkspacesHistoryMainService.RECENTLY_OPENED_STORAGE_KEY, JSON.stringify(toStoreData(recent)), StorageScope.APPLICATION, StorageTarget.MACHINE); } private location(recent: IRecent): URI { diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index e5c745648ec..b2ed76583ce 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -22,6 +22,7 @@ import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteCo import { removeDangerousEnvVariables } from 'vs/base/common/processes'; import { IExtensionHostStatusService } from 'vs/server/node/extensionHostStatusService'; import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import { IPCExtHostConnection, writeExtHostConnection, SocketExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export async function buildUserEnvironment(startParamsEnv: { [key: string]: string | null } = {}, withUserShellEnvironment: boolean, language: string, isDebug: boolean, environmentService: IServerEnvironmentService, logService: ILogService): Promise { const nlsConfig = await getNLSConfiguration(language, environmentService.userDataPath); @@ -244,11 +245,11 @@ export class ExtensionHostConnection { let extHostNamedPipeServer: net.Server | null; if (this._canSendSocket) { - env['VSCODE_EXTHOST_WILL_SEND_SOCKET'] = 'true'; + writeExtHostConnection(new SocketExtHostConnection(), env); extHostNamedPipeServer = null; } else { const { namedPipeServer, pipeName } = await this._listenOnPipe(); - env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), env); extHostNamedPipeServer = namedPipeServer; } @@ -326,9 +327,7 @@ export class ExtensionHostConnection { const namedPipeServer = net.createServer(); namedPipeServer.on('error', reject); namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } + namedPipeServer?.removeListener('error', reject); resolve({ pipeName, namedPipeServer }); }); }); diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index f862fc4a7be..520cca772bb 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -94,7 +94,7 @@ class CliMain extends Disposable { fileService.registerProvider(Schemas.file, this._register(new DiskFileSystemProvider(logService))); // User Data Profiles - const userDataProfilesService = this._register(new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const userDataProfilesService = this._register(new UserDataProfilesService(undefined, environmentService, fileService, logService)); services.set(IUserDataProfilesService, userDataProfilesService); // Configuration diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index 4f5a7eb718c..e3e267c1d94 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -111,7 +111,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken fileService.registerProvider(Schemas.file, disposables.add(new DiskFileSystemProvider(logService))); // Configuration - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); const configurationService = new ConfigurationService(environmentService.machineSettingsResource, fileService, new NullPolicyService(), logService); services.set(IConfigurationService, configurationService); await configurationService.initialize(); diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 906c30767f7..7184b593f3a 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -110,11 +110,11 @@ export class MainThreadCommentThread implements languages.CommentThread { set collapsibleState(newState: languages.CommentThreadCollapsibleState | undefined) { this._collapsibleState = newState; - this._onDidChangeCollasibleState.fire(this._collapsibleState); + this._onDidChangeCollapsibleState.fire(this._collapsibleState); } - private readonly _onDidChangeCollasibleState = new Emitter(); - public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event; + private readonly _onDidChangeCollapsibleState = new Emitter(); + public onDidChangeCollapsibleState = this._onDidChangeCollapsibleState.event; private _isDisposed: boolean; @@ -172,7 +172,7 @@ export class MainThreadCommentThread implements languages.CommentThread { dispose() { this._isDisposed = true; - this._onDidChangeCollasibleState.dispose(); + this._onDidChangeCollapsibleState.dispose(); this._onDidChangeComments.dispose(); this._onDidChangeInput.dispose(); this._onDidChangeLabel.dispose(); diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 6d2b31c86cb..c0ec9d1b550 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -242,9 +242,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb public $setDebugSessionName(sessionId: DebugSessionUUID, name: string): void { const session = this.debugService.getModel().getSession(sessionId); - if (session) { - session.setName(name); - } + session?.setName(name); } public $customDebugAdapterRequest(sessionId: DebugSessionUUID, request: string, args: any): Promise { @@ -284,9 +282,7 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb public $appendDebugConsole(value: string): void { // Use warning as severity to get the orange color for messages coming from the debug extension const session = this.debugService.getViewModel().focusedSession; - if (session) { - session.appendToRepl(value, severity.Warning); - } + session?.appendToRepl(value, severity.Warning); } public $acceptDAMessage(handle: number, message: DebugProtocol.ProtocolMessage) { diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 1e4a6f043d3..bf5de3a19a2 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -140,9 +140,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } private static _reviveCodeActionDto(data: ReadonlyArray): languages.CodeAction[] { - if (data) { - data.forEach(code => reviveWorkspaceEditDto(code.edit)); - } + data?.forEach(code => reviveWorkspaceEditDto(code.edit)); return data; } diff --git a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts index b93132cf613..4ebdadf9976 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookKernels.ts @@ -266,9 +266,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape const updates = data.value; try { const execution = this._executions.get(handle); - if (execution) { - execution.update(updates.map(NotebookDto.fromCellExecuteUpdateDto)); - } + execution?.update(updates.map(NotebookDto.fromCellExecuteUpdateDto)); } catch (e) { onUnexpectedError(e); } @@ -277,9 +275,7 @@ export class MainThreadNotebookKernels implements MainThreadNotebookKernelsShape $completeExecution(handle: number, data: SerializableObjectWithBuffers): void { try { const execution = this._executions.get(handle); - if (execution) { - execution.complete(NotebookDto.fromCellExecuteCompleteDto(data.value)); - } + execution?.complete(NotebookDto.fromCellExecuteCompleteDto(data.value)); } catch (e) { onUnexpectedError(e); } finally { diff --git a/src/vs/workbench/api/browser/mainThreadProgress.ts b/src/vs/workbench/api/browser/mainThreadProgress.ts index 7fa98113c30..beec4789ac9 100644 --- a/src/vs/workbench/api/browser/mainThreadProgress.ts +++ b/src/vs/workbench/api/browser/mainThreadProgress.ts @@ -57,9 +57,7 @@ export class MainThreadProgress implements MainThreadProgressShape { $progressReport(handle: number, message: IProgressStep): void { const entry = this._progress.get(handle); - if (entry) { - entry.progress.report(message); - } + entry?.progress.report(message); } $progressEnd(handle: number): void { diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index d2d28de0495..60cb8a79824 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -318,8 +318,6 @@ namespace TaskDTO { isBackground: task.configurationProperties.isBackground, problemMatchers: [], hasDefinedMatchers: ContributedTask.is(task) ? task.hasDefinedMatchers : false, - icon: task.configurationProperties.icon, - color: task.configurationProperties.color, runOptions: RunOptionsDTO.from(task.runOptions), }; result.group = TaskGroupDTO.from(task.configurationProperties.group); @@ -344,7 +342,7 @@ namespace TaskDTO { return result; } - export function to(task: ITaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean): ContributedTask | undefined { + export function to(task: ITaskDTO | undefined, workspace: IWorkspaceContextService, executeOnly: boolean, icon?: { id: string; color?: string }): ContributedTask | undefined { if (!task || (typeof task.name !== 'string')) { return undefined; } @@ -385,8 +383,7 @@ namespace TaskDTO { isBackground: !!task.isBackground, problemMatchers: task.problemMatchers.slice(), detail: task.detail, - color: task.color, - icon: task.icon + icon } ); return result; @@ -495,7 +492,7 @@ export class MainThreadTask implements MainThreadTaskShape { dto.name = ((dto.name === undefined) ? '' : dto.name); // Using an empty name causes the name to default to the one given by the provider. return Promise.resolve(this._proxy.$resolveTask(handle, dto)).then(resolvedTask => { if (resolvedTask) { - return TaskDTO.to(resolvedTask, this._workspaceContextServer, true); + return TaskDTO.to(resolvedTask, this._workspaceContextServer, true, task.configurationProperties.icon); } return undefined; diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index db958829ce8..b280f677abe 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -318,9 +318,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape public $sendProcessProperty(terminalId: number, property: IProcessProperty): void { if (property.type === ProcessPropertyType.Title) { const instance = this._terminalService.getInstanceFromId(terminalId); - if (instance) { - instance.refreshTabLabels(property.value, TitleEventSource.Api); - } + instance?.refreshTabLabels(property.value, TitleEventSource.Api); } this._terminalProcessProxies.get(terminalId)?.emitProcessProperty(property); } diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index 7c8aa9d2323..4753211a74b 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -37,14 +37,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews); } - async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise { + async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise { this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options); this.extensionService.whenInstalledExtensionsRegistered().then(() => { const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService); this._dataProviders.set(treeViewId, dataProvider); const dndController = (options.hasHandleDrag || options.hasHandleDrop) - ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, options.supportsFileDataTransfers, this._proxy) : undefined; + ? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined; const viewer = this.getTreeView(treeViewId); if (viewer) { // Order is important here. The internal tree isn't created until the dataProvider is set. @@ -201,7 +201,6 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { readonly dropMimeTypes: string[], readonly dragMimeTypes: string[], readonly hasWillDrop: boolean, - readonly supportsFileDataTransfers: boolean, private readonly _proxy: ExtHostTreeViewsShape) { } async handleDrop(dataTransfer: VSDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 16fafcafade..b438fb90ead 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -51,7 +51,6 @@ import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyId import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/common/extensionDescriptionRegistry'; import type * as vscode from 'vscode'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { values } from 'vs/base/common/collections'; import { ExtHostEditorInsets } from 'vs/workbench/api/common/extHostCodeInsets'; import { ExtHostLabelService } from 'vs/workbench/api/common/extHostLabelService'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; @@ -186,7 +185,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands)); // Check that no named customers are missing - const expected: ProxyIdentifier[] = values(ExtHostContext); + const expected = Object.values>(ExtHostContext); rpcProtocol.assertRegistered(expected); // Other instances @@ -856,7 +855,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostWorkspace.saveAll(includeUntitled); }, applyEdit(edit: vscode.WorkspaceEdit): Thenable { - return extHostBulkEdits.applyWorkspaceEdit(edit); + return extHostBulkEdits.applyWorkspaceEdit(edit, extension); }, createFileSystemWatcher: (pattern, ignoreCreate, ignoreChange, ignoreDelete): vscode.FileSystemWatcher => { return extHostFileSystemEvent.createFileSystemWatcher(extHostWorkspace, extension, pattern, ignoreCreate, ignoreChange, ignoreDelete); @@ -1327,7 +1326,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TextSearchCompleteMessageType: TextSearchCompleteMessageType, DataTransfer: extHostTypes.DataTransfer, DataTransferItem: extHostTypes.DataTransferItem, - DataTransferItemKind: extHostTypes.DataTransferItemKind, CoveredCount: extHostTypes.CoveredCount, FileCoverage: extHostTypes.FileCoverage, StatementCoverage: extHostTypes.StatementCoverage, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 1a0197ffd24..d38e5c3bc52 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -258,7 +258,7 @@ export interface MainThreadTextEditorsShape extends IDisposable { } export interface MainThreadTreeViewsShape extends IDisposable { - $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise; + $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise; $refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise; $reveal(treeViewId: string, itemInfo: { item: ITreeItem; parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise; $setMessage(treeViewId: string, message: string): void; @@ -1602,7 +1602,7 @@ export interface IWorkspaceFileEditDto { export interface IWorkspaceTextEditDto { _type: WorkspaceEditType.Text; resource: UriComponents; - edit: languages.TextEdit; + edit: languages.TextEdit & { insertAsSnippet?: boolean }; modelVersionId?: number; metadata?: IWorkspaceEditEntryMetadataDto; } diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 4b866a477f3..6b1771703ea 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -436,6 +436,12 @@ const newCommands: ApiCommand[] = [ 'vscode.revealTestInExplorer', '_revealTestInExplorer', 'Reveals a test instance in the explorer', [ApiCommandArgument.TestItem], ApiCommandResult.Void + ), + // --- continue edit session + new ApiCommand( + 'vscode.experimental.editSession.continue', '_workbench.experimental.sessionSync.actions.continueEditSession', 'Continue the current edit session in a different workspace', + [ApiCommandArgument.Uri.with('workspaceUri', 'The target workspace to continue the current edit session in')], + ApiCommandResult.Void ) ]; diff --git a/src/vs/workbench/api/common/extHostBulkEdits.ts b/src/vs/workbench/api/common/extHostBulkEdits.ts index 6547c861438..2126d41724e 100644 --- a/src/vs/workbench/api/common/extHostBulkEdits.ts +++ b/src/vs/workbench/api/common/extHostBulkEdits.ts @@ -3,10 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MainContext, MainThreadBulkEditsShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { WorkspaceEdit } from 'vs/workbench/api/common/extHostTypeConverters'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; export class ExtHostBulkEdits { @@ -26,8 +28,9 @@ export class ExtHostBulkEdits { }; } - applyWorkspaceEdit(edit: vscode.WorkspaceEdit): Promise { - const dto = WorkspaceEdit.from(edit, this._versionInformationProvider); + applyWorkspaceEdit(edit: vscode.WorkspaceEdit, extension: IExtensionDescription): Promise { + const allowSnippetTextEdit = isProposedApiEnabled(extension, 'snippetWorkspaceEdit'); + const dto = WorkspaceEdit.from(edit, this._versionInformationProvider, allowSnippetTextEdit); return this._proxy.$tryApplyWorkspaceEdit(dto); } } diff --git a/src/vs/workbench/api/common/extHostCodeInsets.ts b/src/vs/workbench/api/common/extHostCodeInsets.ts index e91a8eae63c..5caf786ffa0 100644 --- a/src/vs/workbench/api/common/extHostCodeInsets.ts +++ b/src/vs/workbench/api/common/extHostCodeInsets.ts @@ -134,8 +134,6 @@ export class ExtHostEditorInsets implements ExtHostEditorInsetsShape { $onDidReceiveMessage(handle: number, message: any): void { const value = this._insets.get(handle); - if (value) { - value.onDidReceiveMessage.fire(message); - } + value?.onDidReceiveMessage.fire(message); } } diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index b46d02fd634..2259201d139 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -172,9 +172,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo $deleteCommentThread(commentControllerHandle: number, commentThreadHandle: number) { const commentController = this._commentControllers.get(commentControllerHandle); - if (commentController) { - commentController.$deleteCommentThread(commentThreadHandle); - } + commentController?.$deleteCommentThread(commentThreadHandle); } $provideCommentingRanges(commentControllerHandle: number, uriComponents: UriComponents, token: CancellationToken): Promise { diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 178ab59c5ef..9e0e03f1379 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -495,9 +495,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E } const da = this._debugAdapters.get(debugAdapterHandle); - if (da) { - da.sendMessage(message); - } + da?.sendMessage(message); } public $stopDASession(debugAdapterHandle: number): Promise { @@ -663,9 +661,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E public async $acceptDebugSessionNameChanged(sessionDto: IDebugSessionDto, name: string): Promise { const session = await this.getSession(sessionDto); - if (session) { - session._acceptNameChanged(name); - } + session?._acceptNameChanged(name); } public async $acceptDebugSessionCustomEvent(sessionDto: IDebugSessionDto, event: any): Promise { diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index 9f5383dcd59..81cf1f2b756 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -41,9 +41,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { dispose(): void { if (!this._isDisposed) { this.#onDidChangeDiagnostics.fire([...this.#data.keys()]); - if (this.#proxy) { - this.#proxy.$clear(this._owner); - } + this.#proxy?.$clear(this._owner); this.#data.clear(); this._isDisposed = true; } @@ -108,9 +106,7 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { } } else { const currentDiagnostics = this.#data.get(uri); - if (currentDiagnostics) { - currentDiagnostics.push(...diagnostics); - } + currentDiagnostics?.push(...diagnostics); } } } @@ -166,24 +162,27 @@ export class DiagnosticCollection implements vscode.DiagnosticCollection { this._checkDisposed(); this.#onDidChangeDiagnostics.fire([uri]); this.#data.delete(uri); - if (this.#proxy) { - this.#proxy.$changeMany(this._owner, [[uri, undefined]]); - } + this.#proxy?.$changeMany(this._owner, [[uri, undefined]]); } clear(): void { this._checkDisposed(); this.#onDidChangeDiagnostics.fire([...this.#data.keys()]); this.#data.clear(); - if (this.#proxy) { - this.#proxy.$clear(this._owner); - } + this.#proxy?.$clear(this._owner); } forEach(callback: (uri: URI, diagnostics: ReadonlyArray, collection: DiagnosticCollection) => any, thisArg?: any): void { + this._checkDisposed(); + for (const [uri, values] of this) { + callback.call(thisArg, uri, values, this); + } + } + + *[Symbol.iterator](): IterableIterator<[uri: vscode.Uri, diagnostics: readonly vscode.Diagnostic[]]> { this._checkDisposed(); for (const uri of this.#data.keys()) { - callback.apply(thisArg, [uri, this.get(uri), this]); + yield [uri, this.get(uri)]; } } diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 7608468e560..afdb6fbb015 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -88,9 +88,7 @@ class DocumentSymbolAdapter { } const parent = parentStack[parentStack.length - 1]; if (EditorRange.containsRange(parent.range, element.range) && !EditorRange.equalsRange(parent.range, element.range)) { - if (parent.children) { - parent.children.push(element); - } + parent.children?.push(element); parentStack.push(element); break; } diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 67c56f94474..1bc50dd99e2 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -218,9 +218,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx $onDidChangeValue(sessionId: number, value: string): void { const session = this._sessions.get(sessionId); - if (session) { - session._fireDidChangeValue(value); - } + session?._fireDidChangeValue(value); } $onDidAccept(sessionId: number): void { @@ -246,9 +244,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx $onDidTriggerButton(sessionId: number, handle: number): void { const session = this._sessions.get(sessionId); - if (session) { - session._fireDidTriggerButton(handle); - } + session?._fireDidTriggerButton(handle); } $onDidTriggerItemButton(sessionId: number, itemHandle: number, buttonHandle: number): void { diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index db595e3c9a5..5b033610f37 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -252,12 +252,10 @@ export class ExtHostSCMInputBox implements vscode.SourceControlInputBox { private _enabled: boolean = true; get enabled(): boolean { - checkProposedApiEnabled(this._extension, 'scmInput'); return this._enabled; } set enabled(enabled: boolean) { - checkProposedApiEnabled(this._extension, 'scmInput'); enabled = !!enabled; if (this._enabled === enabled) { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 1aa1e678e00..f27f16dca52 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -538,9 +538,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I public async $acceptTerminalProcessId(id: number, processId: number): Promise { const terminal = this._getTerminalById(id); - if (terminal) { - terminal._setProcessId(processId); - } + terminal?._setProcessId(processId); } public async $startExtensionTerminal(id: number, initialDimensions: ITerminalDimensionsDto | undefined): Promise { @@ -693,9 +691,7 @@ export abstract class BaseExtHostTerminalService extends Disposable implements I this._terminalLinkCache.delete(terminalId); const oldToken = this._terminalLinkCancellationSource.get(terminalId); - if (oldToken) { - oldToken.dispose(true); - } + oldToken?.dispose(true); const cancellationSource = new CancellationTokenSource(); this._terminalLinkCancellationSource.set(terminalId, cancellationSource); @@ -880,6 +876,10 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable this.map.forEach((value, key) => callback.call(thisArg, key, value, this)); } + [Symbol.iterator](): IterableIterator<[variable: string, mutator: vscode.EnvironmentVariableMutator]> { + return this.map.entries(); + } + delete(variable: string): void { this.map.delete(variable); this._onDidChangeCollection.fire(); diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 8bdd508109c..fdbb535e422 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -24,7 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Command } from 'vs/editor/common/languages'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; -import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type TreeItemHandle = string; @@ -92,8 +92,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? []; const hasHandleDrag = !!options.dragAndDropController?.handleDrag; const hasHandleDrop = !!options.dragAndDropController?.handleDrop; - const supportsFileDataTransfers = isProposedApiEnabled(extension, 'dataTransferFiles'); - const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop, supportsFileDataTransfers }); + const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop }); const treeView = this.createExtHostTreeView(viewId, options, extension); return { get onDidCollapseElement() { return treeView.onDidCollapseElement; }, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6e4d8a09a5a..9b733dc8951 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -569,7 +569,7 @@ export namespace WorkspaceEdit { getNotebookDocumentVersion(uri: URI): number | undefined; } - export function from(value: vscode.WorkspaceEdit, versionInfo?: IVersionInformationProvider): extHostProtocol.IWorkspaceEditDto { + export function from(value: vscode.WorkspaceEdit, versionInfo?: IVersionInformationProvider, allowSnippetTextEdit?: boolean): extHostProtocol.IWorkspaceEditDto { const result: extHostProtocol.IWorkspaceEditDto = { edits: [] }; @@ -598,14 +598,21 @@ export namespace WorkspaceEdit { }); } else if (entry._type === types.FileEditType.Text) { + // text edits - result.edits.push({ + const dto = { _type: extHostProtocol.WorkspaceEditType.Text, resource: entry.uri, edit: TextEdit.from(entry.edit), modelVersionId: !toCreate.has(entry.uri) ? versionInfo?.getTextDocumentVersion(entry.uri) : undefined, metadata: entry.metadata - }); + }; + if (allowSnippetTextEdit && entry.edit.newText2 instanceof types.SnippetString) { + dto.edit.insertAsSnippet = true; + dto.edit.text = entry.edit.newText2.value; + } + result.edits.push(dto); + } else if (entry._type === types.FileEditType.Cell) { result.edits.push({ _type: extHostProtocol.WorkspaceEditType.Cell, @@ -1770,6 +1777,7 @@ export namespace TestItem { add: () => { }, delete: () => { }, forEach: () => { }, + *[Symbol.iterator]() { }, get: () => undefined, replace: () => { }, size: 0, @@ -1946,8 +1954,6 @@ export namespace DataTransferItem { const file = item.fileData; if (file) { return new class extends types.DataTransferItem { - override get kind() { return types.DataTransferItemKind.File; } - override asFile(): vscode.DataTransferFile { return { name: file.name, diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 3f9ca317b1c..ce30f68d26e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -549,6 +549,7 @@ export class TextEdit { protected _range: Range; protected _newText: string | null; + newText2?: string | SnippetString; protected _newEol?: EndOfLine; get range(): Range { @@ -1209,9 +1210,7 @@ export class DocumentSymbol { if (!candidate.range.contains(candidate.selectionRange)) { throw new Error('selectionRange must be contained in fullRange'); } - if (candidate.children) { - candidate.children.forEach(DocumentSymbol.validate); - } + candidate.children?.forEach(DocumentSymbol.validate); } name: string; @@ -2427,16 +2426,9 @@ export enum TreeItemCollapsibleState { Expanded = 2 } -export enum DataTransferItemKind { - String = 1, - File = 2, -} - @es5ClassCompat export class DataTransferItem { - get kind(): DataTransferItemKind { return DataTransferItemKind.String; } - async asString(): Promise { return typeof this.value === 'string' ? this.value : JSON.stringify(this.value); } @@ -2449,7 +2441,7 @@ export class DataTransferItem { } @es5ClassCompat -export class DataTransfer { +export class DataTransfer implements vscode.DataTransfer { #items = new Map(); constructor(init?: Iterable) { @@ -2480,6 +2472,14 @@ export class DataTransfer { } } } + + *[Symbol.iterator](): IterableIterator<[mimeType: string, item: vscode.DataTransferItem]> { + for (const [mime, items] of this.#items) { + for (const item of items) { + yield [mime, item]; + } + } + } } @es5ClassCompat diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 131552e725b..f7f206f0649 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -103,8 +103,6 @@ export interface ITaskDTO { problemMatchers: string[]; hasDefinedMatchers: boolean; runOptions?: IRunOptionsDTO; - color?: string; - icon?: string; } export interface ITaskSetDTO { diff --git a/src/vs/workbench/api/node/extHostSearch.ts b/src/vs/workbench/api/node/extHostSearch.ts index 9867e2a5e6b..8e1dab9ef1b 100644 --- a/src/vs/workbench/api/node/extHostSearch.ts +++ b/src/vs/workbench/api/node/extHostSearch.ts @@ -103,9 +103,7 @@ export class NativeExtHostSearch extends ExtHostSearch { } override $clearCache(cacheKey: string): Promise { - if (this._internalFileSearchProvider) { - this._internalFileSearchProvider.clearCache(cacheKey); - } + this._internalFileSearchProvider?.clearCache(cacheKey); return super.$clearCache(cacheKey); } diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts index 06cfede9d13..8348d9490b2 100644 --- a/src/vs/workbench/api/node/extHostStoragePaths.ts +++ b/src/vs/workbench/api/node/extHostStoragePaths.ts @@ -63,9 +63,7 @@ export class ExtensionStoragePaths extends CommonExtensionStoragePaths { override onWillDeactivateAll(): void { // the lock will be released soon - if (this._workspaceStorageLock) { - this._workspaceStorageLock.setWillRelease(6000); - } + this._workspaceStorageLock?.setWillRelease(6000); } } diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index 4769e6f194c..d7abe11a16d 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -16,27 +16,14 @@ import * as pfs from 'vs/base/node/pfs'; import * as types from 'vs/workbench/api/common/extHostTypes'; import { isLinux } from 'vs/base/common/platform'; import { IExtHostTunnelService, TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService'; -import { Event, Emitter } from 'vs/base/common/event'; -import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces } from 'vs/platform/tunnel/common/tunnel'; +import { Emitter } from 'vs/base/common/event'; +import { TunnelOptions, TunnelCreationOptions, ProvidedPortAttributes, ProvidedOnAutoForward, isLocalhost, isAllInterfaces, DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { MovingAverage } from 'vs/base/common/numbers'; import { CandidatePort } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ILogService } from 'vs/platform/log/common/log'; -class ExtensionTunnel implements vscode.Tunnel { - private _onDispose: Emitter = new Emitter(); - onDidDispose: Event = this._onDispose.event; - - constructor( - public readonly remoteAddress: { port: number; host: string }, - public readonly localAddress: { port: number; host: string } | string, - private readonly _dispose: () => Promise) { } - - dispose(): Promise { - this._onDispose.fire(); - return this._dispose(); - } -} +class ExtensionTunnel extends DisposableTunnel implements vscode.Tunnel { } export function getSockets(stdout: string): Record { const lines = stdout.trim().split('\n'); diff --git a/src/vs/workbench/api/node/extensionHostProcess.ts b/src/vs/workbench/api/node/extensionHostProcess.ts index 0d111d2f20a..bad55575c87 100644 --- a/src/vs/workbench/api/node/extensionHostProcess.ts +++ b/src/vs/workbench/api/node/extensionHostProcess.ts @@ -24,6 +24,7 @@ import { ProcessTimeRunOnceScheduler } from 'vs/base/common/async'; import { boolean } from 'vs/editor/common/config/editorOptions'; import { createURITransformer } from 'vs/workbench/api/node/uriTransformer'; import { MessagePortMain } from 'electron'; +import { ExtHostConnectionType, readExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import 'vs/workbench/api/common/extHost.common.services'; import 'vs/workbench/api/node/extHost.node.services'; @@ -90,6 +91,12 @@ function patchProcess(allowExit: boolean) { const err = new Error('An extension called process.crash() and this was prevented.'); console.warn(err.stack); }; + + // Set ELECTRON_RUN_AS_NODE environment variable for extensions that use + // child_process.spawn with process.execPath and expect to run as node process + // on the desktop. + // Refs https://github.com/microsoft/vscode/issues/151012#issuecomment-1156593228 + process.env['ELECTRON_RUN_AS_NODE'] = '1'; } interface IRendererConnection { @@ -104,7 +111,9 @@ let onTerminate = function (reason: string) { }; function _createExtHostProtocol(): Promise { - if (process.env.VSCODE_WILL_SEND_MESSAGE_PORT) { + const extHostConnection = readExtHostConnection(process.env); + + if (extHostConnection.type === ExtHostConnectionType.MessagePort) { return new Promise((resolve, reject) => { @@ -133,7 +142,7 @@ function _createExtHostProtocol(): Promise { }); - } else if (process.env.VSCODE_EXTHOST_WILL_SEND_SOCKET) { + } else if (extHostConnection.type === ExtHostConnectionType.Socket) { return new Promise((resolve, reject) => { @@ -202,7 +211,7 @@ function _createExtHostProtocol(): Promise { } else { - const pipeName = process.env.VSCODE_IPC_HOOK_EXTHOST!; + const pipeName = extHostConnection.pipeName; return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts index 4974c24314c..3b393b35347 100644 --- a/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts +++ b/src/vs/workbench/api/test/browser/extHostBulkEdits.test.ts @@ -12,6 +12,7 @@ import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/api/test/c import { NullLogService } from 'vs/platform/log/common/log'; import { assertType } from 'vs/base/common/types'; import { ExtHostBulkEdits } from 'vs/workbench/api/common/extHostBulkEdits'; +import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { @@ -46,7 +47,7 @@ suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { test('uses version id if document available', async () => { const edit = new extHostTypes.WorkspaceEdit(); edit.replace(resource, new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await bulkEdits.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit, nullExtensionDescription); assert.strictEqual(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); @@ -56,7 +57,7 @@ suite('ExtHostBulkEdits.applyWorkspaceEdit', () => { test('does not use version id if document is not available', async () => { const edit = new extHostTypes.WorkspaceEdit(); edit.replace(URI.parse('foo:bar2'), new extHostTypes.Range(0, 0, 0, 0), 'hello'); - await bulkEdits.applyWorkspaceEdit(edit); + await bulkEdits.applyWorkspaceEdit(edit, nullExtensionDescription); assert.strictEqual(workspaceResourceEdits.edits.length, 1); const [first] = workspaceResourceEdits.edits; assertType(first._type === WorkspaceEditType.Text); diff --git a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts index 4947ad75e2e..4c2501f4692 100644 --- a/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts +++ b/src/vs/workbench/api/test/browser/extHostEditorTabs.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as vscode from 'vscode'; -import assert = require('assert'); +import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/base/test/common/mock'; import { IEditorTabDto, IEditorTabGroupDto, MainThreadEditorTabsShape, TabInputKind, TabModelOperationKind, TextInputDto } from 'vs/workbench/api/common/extHost.protocol'; diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index c4ce107acf1..2ab14e477a1 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -36,8 +36,8 @@ const assertTreesEqual = (a: TestItemImpl | undefined, b: TestItemImpl | undefin assert.deepStrictEqual(simplify(a), simplify(b)); - const aChildren = [...a.children].map(c => c.id).sort(); - const bChildren = [...b.children].map(c => c.id).sort(); + const aChildren = [...a.children].map(([_, c]) => c.id).sort(); + const bChildren = [...b.children].map(([_, c]) => c.id).sort(); assert.strictEqual(aChildren.length, bChildren.length, `expected ${a.label}.children.length == ${b.label}.children.length`); aChildren.forEach(key => assertTreesEqual(a.children.get(key) as TestItemImpl, b.children.get(key) as TestItemImpl)); }; @@ -242,7 +242,7 @@ suite('ExtHost Testing', () => { const oldA = single.root.children.get('id-a') as TestItemImpl; const newA = new TestItemImpl('ctrlId', 'id-a', 'Hello world', undefined); - newA.children.replace([...oldA.children]); + newA.children.replace([...oldA.children].map(([_, item]) => item)); single.root.children.replace([ newA, new TestItemImpl('ctrlId', 'id-b', single.root.children.get('id-b')!.label, undefined), @@ -334,7 +334,7 @@ suite('ExtHost Testing', () => { }, ]); - assert.deepStrictEqual([...single.root.children], [single.root.children.get('id-a')]); + assert.deepStrictEqual([...single.root.children].map(([_, item]) => item), [single.root.children.get('id-a')]); assert.deepStrictEqual(b.parent, a); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index 4afba6e05e6..1386a01b234 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -75,7 +75,7 @@ suite('MainThreadHostTreeView', function () { } drain(): any { return null; } }, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService()); - mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, supportsFileDataTransfers: false }); + mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false }); await testExtensionService.whenInstalledExtensionsRegistered(); }); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index c5a04846dc0..5a0d08a8623 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -48,9 +48,7 @@ class InspectContextKeysAction extends Action2 { const stylesheet = createStyleSheet(); disposables.add(toDisposable(() => { - if (stylesheet.parentNode) { - stylesheet.parentNode.removeChild(stylesheet); - } + stylesheet.parentNode?.removeChild(stylesheet); })); createCSSRule('*', 'cursor: crosshair !important;', stylesheet); @@ -275,7 +273,7 @@ class LogStorageAction extends Action2 { } run(accessor: ServicesAccessor): void { - accessor.get(IStorageService).logStorage(); + accessor.get(IStorageService).log(); } } diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index dd3fb125fb5..16796a686b6 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -24,7 +24,6 @@ import { IOpenEmptyWindowOptions, IOpenWindowOptions, IWindowOpenable } from 'vs import { IRecent, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { ILocalizedString } from 'vs/platform/action/common/action'; -import { isWeb } from 'vs/base/common/platform'; export const ADD_ROOT_FOLDER_COMMAND_ID = 'addRootFolder'; export const ADD_ROOT_FOLDER_LABEL: ILocalizedString = { value: localize('addFolderToWorkspace', "Add Folder to Workspace..."), original: 'Add Folder to Workspace...' }; @@ -308,8 +307,3 @@ CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function return workspacesService.getRecentlyOpened(); }); -if (isWeb) { - CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor): Promise => { - await (navigator as any).usb.requestDevice({ filters: [] }); - }); -} diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 3a186e54ab4..dc877136e37 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -19,7 +19,7 @@ import { FileAccess, Schemas } from 'vs/base/common/network'; import { isWindows } from 'vs/base/common/platform'; import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; +import { CodeDataTransfers, createDraggedEditorInputFromRawResourcesData, Extensions, extractEditorsAndFilesDropData, IDragAndDropContributionRegistry, IDraggedResourceEditorInput, IResourceStat } from 'vs/platform/dnd/browser/dnd'; import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -105,7 +105,7 @@ export class ResourcesDropHandler { } async handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup | undefined, afterDrop: (targetGroup: IEditorGroup | undefined) => void, targetIndex?: number): Promise { - const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, event)); + const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, event)); if (!editors.length) { return; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index c138f37aeae..7227705fc60 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -269,7 +269,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Title Menu changes - this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this._onDidLayout.fire(this._dimension))); + this._register(this.titleService.onDidChangeCommandCenterVisibility(() => this.layout())); // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index e81c92e69b1..fa48267fa04 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -495,9 +495,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.keyboardNavigationDisposables.add(addDisposableListener(this.compositeBarContainer, EventType.KEY_DOWN, e => { const kbEvent = new StandardKeyboardEvent(e); if (kbEvent.equals(KeyCode.DownArrow) || kbEvent.equals(KeyCode.RightArrow)) { - if (this.globalActivityActionBar) { - this.globalActivityActionBar.focus(true); - } + this.globalActivityActionBar?.focus(true); } else if (kbEvent.equals(KeyCode.UpArrow) || kbEvent.equals(KeyCode.LeftArrow)) { if (this.menuBar) { this.menuBar.toggleFocus(); @@ -511,9 +509,7 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart this.keyboardNavigationDisposables.add(addDisposableListener(this.globalActivitiesContainer, EventType.KEY_DOWN, e => { const kbEvent = new StandardKeyboardEvent(e); if (kbEvent.equals(KeyCode.UpArrow) || kbEvent.equals(KeyCode.LeftArrow)) { - if (this.compositeBar) { - this.compositeBar.focus(this.getVisiblePaneCompositeIds().length - 1); - } + this.compositeBar?.focus(this.getVisiblePaneCompositeIds().length - 1); } })); } @@ -558,6 +554,9 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart } private toggleAccountsActivity() { + if (!!this.accountsActivityAction === this.accountsVisibilityPreference) { + return; + } if (this.globalActivityActionBar) { if (this.accountsActivityAction) { this.globalActivityActionBar.pull(ActivitybarPart.ACCOUNTS_ACTION_INDEX); @@ -841,7 +840,13 @@ export class ActivitybarPart extends Part implements IPaneCompositeSelectorPart for (let index = 0; index < compositeItems.length; index++) { // Add items currently exists but does not exist in new. if (!newCompositeItems.some(({ id }) => id === compositeItems[index].id)) { - newCompositeItems.splice(index, 0, compositeItems[index]); + const viewContainer = this.viewDescriptorService.getViewContainerById(compositeItems[index].id); + newCompositeItems.splice(index, 0, { + ...compositeItems[index], + pinned: true, + visible: true, + order: viewContainer?.order, + }); } } diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 9d960b0bf5c..d15c1f36d96 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -79,9 +79,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { } this.openComposite(newContainer.id, true).then(composite => { - if (composite) { - composite.openView(viewToMove.id, true); - } + composite?.openView(viewToMove.id, true); }); } } @@ -301,9 +299,7 @@ export class CompositeBar extends Widget implements ICompositeBar { } focus(index?: number): void { - if (this.compositeSwitcherBar) { - this.compositeSwitcherBar.focus(index); - } + this.compositeSwitcherBar?.focus(index); } recomputeSizes(): void { diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index b6fddb4625f..9ad13c8a659 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -232,9 +232,7 @@ export abstract class CompositePart extends Part { // Take Composite on-DOM and show const contentArea = this.getContentArea(); - if (contentArea) { - contentArea.appendChild(compositeContainer); - } + contentArea?.appendChild(compositeContainer); show(compositeContainer); // Setup action runner @@ -362,9 +360,7 @@ export abstract class CompositePart extends Part { } // Clear any running Progress - if (this.progressBar) { - this.progressBar.stop().hide(); - } + this.progressBar?.stop().hide(); // Empty Actions if (this.toolBar) { @@ -477,9 +473,7 @@ export abstract class CompositePart extends Part { this.contentAreaSize = Dimension.lift(super.layoutContents(width, height).contentSize); // Layout composite - if (this.activeComposite) { - this.activeComposite.layout(this.contentAreaSize); - } + this.activeComposite?.layout(this.contentAreaSize); } protected removeComposite(compositeId: string): boolean { diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 6343fd24e4f..18bc56414ed 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -349,7 +349,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_DIFF_SID MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: SHOW_EDITORS_IN_GROUP, title: localize('showOpenedEditors', "Show Opened Editors") }, group: '3_open', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID, title: localize('closeAll', "Close All") }, group: '5_close', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: CLOSE_SAVED_EDITORS_COMMAND_ID, title: localize('closeAllSaved', "Close Saved") }, group: '5_close', order: 20 }); -MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('toggleKeepEditors', "Keep Editors Open"), toggled: ContextKeyExpr.not('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); +MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_KEEP_EDITORS_COMMAND_ID, title: localize('togglePreviewMode', "Enable Preview Editors"), toggled: ContextKeyExpr.has('config.workbench.editor.enablePreview') }, group: '7_settings', order: 10 }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: TOGGLE_LOCK_GROUP_COMMAND_ID, title: localize('lockGroup', "Lock Group"), toggled: ActiveEditorGroupLockedContext }, group: '8_lock', order: 10, when: MultipleEditorGroupsContext }); interface IEditorToolItem { id: string; title: string; icon?: { dark?: URI; light?: URI } | ThemeIcon } diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index a07f202b076..5dae9e8d098 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -1281,9 +1281,7 @@ function registerOtherEditorCommands(): void { const editorGroupService = accessor.get(IEditorGroupsService); const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context)); - if (group) { - group.lock(locked ?? !group.isLocked); - } + group?.lock(locked ?? !group.isLocked); } registerAction2(class extends Action2 { diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 6c5f32362ac..6bc1a580226 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -72,9 +72,7 @@ class GridWidgetView implements IView { } layout(width: number, height: number, top: number, left: number): void { - if (this.gridWidget) { - this.gridWidget.layout(width, height, top, left); - } + this.gridWidget?.layout(width, height, top, left); } dispose(): void { @@ -595,9 +593,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro this.doUpdateMostRecentActive(group, true); // Mark previous one as inactive - if (previousActiveGroup) { - previousActiveGroup.setActive(false); - } + previousActiveGroup?.setActive(false); // Mark group as new active group.setActive(true); diff --git a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css index 1237c847967..378d2bfa63f 100644 --- a/src/vs/workbench/browser/parts/editor/media/titlecontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/titlecontrol.css @@ -5,6 +5,10 @@ /* Editor Label */ +.monaco-workbench .part.editor > .content .editor-group-container > .title { + cursor: pointer; +} + .monaco-workbench .part.editor > .content .editor-group-container > .title .title-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab .tab-label { white-space: nowrap; @@ -36,10 +40,6 @@ /* Drag and Drop */ -.monaco-workbench .part.editor > .content .editor-group-container > .title { - cursor: grab; -} - .monaco-editor-group-drag-image { display: inline-block; padding: 1px 7px; diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index 0215c0f5aa4..ab589ba87e0 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -367,9 +367,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } private doHide(): void { - if (this.notificationsToastsContainer) { - this.notificationsToastsContainer.classList.remove('visible'); - } + this.notificationsToastsContainer?.classList.remove('visible'); // Context Key this.notificationsToastsVisibleContextKey.set(false); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 1f2594bc9f0..0409199c6d2 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -717,9 +717,7 @@ export abstract class BasePanelPart extends CompositePart impleme private emptyPanelMessageElement: HTMLElement | undefined; private layoutEmptyMessage(): void { - if (this.emptyPanelMessageElement) { - this.emptyPanelMessageElement.classList.toggle('visible', this.compositeBar.getVisibleComposites().length === 0); - } + this.emptyPanelMessageElement?.classList.toggle('visible', this.compositeBar.getVisibleComposites().length === 0); } private getViewContainer(id: string): ViewContainer | undefined { @@ -731,9 +729,7 @@ export abstract class BasePanelPart extends CompositePart impleme const primaryActions = this.globalActions.getPrimaryActions(); const secondaryActions = this.globalActions.getSecondaryActions(); - if (this.globalToolBar) { - this.globalToolBar.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); - } + this.globalToolBar?.setActions(prepareActions(primaryActions), prepareActions(secondaryActions)); } private getCompositeActions(compositeId: string): { activityAction: PanelActivityAction; pinnedAction: ToggleCompositePinnedAction } { diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index 4d6c56e5b32..600d79dc989 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -406,13 +406,9 @@ export class StatusbarViewModel extends Disposable { } // Mark: first visible item - if (firstVisibleItem) { - firstVisibleItem.container.classList.add('first-visible-item'); - } + firstVisibleItem?.container.classList.add('first-visible-item'); // Mark: last visible item - if (lastVisibleItem) { - lastVisibleItem.container.classList.add('last-visible-item'); - } + lastVisibleItem?.container.classList.add('last-visible-item'); } } diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index fc9f14b7ea8..dd0207b43a2 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -24,7 +24,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import * as colors from 'vs/platform/theme/common/colorRegistry'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; -import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; +import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND, PANEL_BORDER, TITLE_BAR_ACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; export class CommandCenterControl { @@ -186,7 +186,7 @@ colors.registerColor( localize('commandCenter-background', "Background color of the command center"), false ); -const activeBackground = colors.registerColor( +colors.registerColor( 'commandCenter.activeBackground', { dark: MENUBAR_SELECTION_BACKGROUND, hcDark: MENUBAR_SELECTION_BACKGROUND, light: MENUBAR_SELECTION_BACKGROUND, hcLight: MENUBAR_SELECTION_BACKGROUND }, localize('commandCenter-activeBackground', "Active background color of the command center"), @@ -194,8 +194,7 @@ const activeBackground = colors.registerColor( ); // border: defaults to active background colors.registerColor( - 'commandCenter.border', - { dark: activeBackground, hcDark: colors.inputBorder, light: activeBackground, hcLight: colors.inputBorder }, + 'commandCenter.border', { dark: PANEL_BORDER, hcDark: PANEL_BORDER, light: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('commandCenter-border', "Border color of the command center"), false ); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 18739cd4e2b..d68e9da7536 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -41,7 +41,6 @@ .monaco-workbench.web .part.titlebar>.titlebar-container, .monaco-workbench.windows .part.titlebar>.titlebar-container, .monaco-workbench.linux .part.titlebar>.titlebar-container { - height: 30px; line-height: 22px; justify-content: left; } @@ -162,6 +161,7 @@ /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ z-index: 2500; min-width: 36px; + flex-wrap: nowrap; } /* Resizer */ @@ -228,7 +228,7 @@ text-align: center; z-index: 3000; -webkit-app-region: no-drag; - height: 30px; + height: 100%; width: 138px; zoom: calc(1 / var(--zoom-factor)); } @@ -246,13 +246,19 @@ /* Window Control Icons */ .monaco-workbench .part.titlebar>.window-controls-container>.window-icon { - display: inline-block; - line-height: 30px; + display: flex; + justify-content: center; + align-items: center; height: 100%; width: 46px; font-size: 16px; } +.monaco-workbench .part.titlebar>.window-controls-container>.window-icon::before { + height: 16px; + line-height: 16px; +} + .monaco-workbench .part.titlebar>.window-controls-container>.window-icon:hover { background-color: rgba(255, 255, 255, 0.1); } diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index a22ba7b284a..27bd3301696 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -481,9 +481,9 @@ export class CustomMenubarControl extends MenubarControl { const menubarSelectedBgColor = theme.getColor(MENUBAR_SELECTION_BACKGROUND); if (menubarSelectedBgColor) { collector.addRule(` - .monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open, - .monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus, - .monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover { + .monaco-workbench .menubar:not(.compact) > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar:not(.compact) > .menubar-menu-button:focus .menubar-menu-title, + .monaco-workbench .menubar:not(:focus-within):not(.compact) > .menubar-menu-button:hover .menubar-menu-title { background-color: ${menubarSelectedBgColor}; } `); @@ -492,19 +492,20 @@ export class CustomMenubarControl extends MenubarControl { const menubarSelectedBorderColor = theme.getColor(MENUBAR_SELECTION_BORDER); if (menubarSelectedBorderColor) { collector.addRule(` - .monaco-workbench .menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button:hover .menubar-menu-title { outline: dashed 1px; } - .monaco-workbench .menubar > .menubar-menu-button.open, - .monaco-workbench .menubar > .menubar-menu-button:focus { + .monaco-workbench .menubar > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:focus .menubar-menu-title { outline: solid 1px; } - .monaco-workbench .menubar > .menubar-menu-button.open, - .monaco-workbench .menubar > .menubar-menu-button:focus, - .monaco-workbench .menubar > .menubar-menu-button:hover { + .monaco-workbench .menubar > .menubar-menu-button.open .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:focus .menubar-menu-title, + .monaco-workbench .menubar > .menubar-menu-button:hover .menubar-menu-title { outline-color: ${menubarSelectedBorderColor}; + outline-offset: -1px; } `); } @@ -751,9 +752,7 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, title); - if (this.menubar) { - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); - } + this.menubar?.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); } })); @@ -763,9 +762,7 @@ export class CustomMenubarControl extends MenubarControl { if (!this.focusInsideMenubar) { const actions: IAction[] = []; updateActions(menu, actions, title); - if (this.menubar) { - this.menubar.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); - } + this.menubar?.updateMenu({ actions: actions, label: mnemonicMenuLabel(this.topLevelTitles[title]) }); } })); } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 927dad9e236..388be333154 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -45,7 +45,10 @@ export class TitlebarPart extends Part implements ITitleService { readonly minimumWidth: number = 0; readonly maximumWidth: number = Number.POSITIVE_INFINITY; - get minimumHeight(): number { return 30 / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); } + get minimumHeight(): number { + const value = this.isCommandCenterVisible ? 35 : 30; + return value / (this.currentMenubarVisibility === 'hidden' || getZoomFactor() < 1 ? getZoomFactor() : 1); + } get maximumHeight(): number { return this.minimumHeight; } //#endregion @@ -53,8 +56,8 @@ export class TitlebarPart extends Part implements ITitleService { private _onMenubarVisibilityChange = this._register(new Emitter()); readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event; - private readonly _onDidChangeTitleMenuVisibility = new Emitter(); - readonly onDidChangeTitleMenuVisibility: Event = this._onDidChangeTitleMenuVisibility.event; + private readonly _onDidChangeCommandCenterVisibility = new Emitter(); + readonly onDidChangeCommandCenterVisibility: Event = this._onDidChangeCommandCenterVisibility.event; protected rootContainer!: HTMLElement; protected windowControls: HTMLElement | undefined; @@ -140,7 +143,7 @@ export class TitlebarPart extends Part implements ITitleService { if (event.affectsConfiguration(TitlebarPart.configCommandCenter)) { this.updateTitle(); this.adjustTitleMarginToCenter(); - this._onDidChangeTitleMenuVisibility.fire(); + this._onDidChangeCommandCenterVisibility.fire(); } } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 3250245d937..fefe8a7eade 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -31,7 +31,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import 'vs/css!./media/views'; -import { createStringDataTransferItem, VSDataTransfer } from 'vs/base/common/dataTransfer'; +import { VSDataTransfer } from 'vs/base/common/dataTransfer'; import { Command } from 'vs/editor/common/languages'; import { localize } from 'vs/nls'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -54,7 +54,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder, textCodeBlockBackground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { ColorScheme } from 'vs/platform/theme/common/theme'; import { FileThemeIcon, FolderThemeIcon, IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; -import { convertResourceUrlsToUriList, DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; +import { DraggedTreeItemsIdentifier, fillEditorsDragData, LocalSelectionTransfer } from 'vs/workbench/browser/dnd'; import { IResourceLabel, ResourceLabels } from 'vs/workbench/browser/labels'; import { API_OPEN_DIFF_EDITOR_COMMAND_ID, API_OPEN_EDITOR_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; @@ -66,7 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; -import { createFileDataTransferItemFromFile } from 'vs/editor/browser/dnd'; +import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; export class TreeViewPane extends ViewPane { @@ -722,9 +722,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this._width = width; const treeHeight = height - DOM.getTotalHeight(this.messageElement); this.treeContainer.style.height = treeHeight + 'px'; - if (this.tree) { - this.tree.layout(treeHeight, width); - } + this.tree?.layout(treeHeight, width); } } @@ -780,9 +778,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { } setSelection(items: ITreeItem[]): void { - if (this.tree) { - this.tree.setSelection(items); - } + this.tree?.setSelection(items); } setFocus(item: ITreeItem): void { @@ -1336,8 +1332,6 @@ interface TreeDragSourceInfo { itemHandles: string[]; } -const INTERNAL_MIME_TYPES = [CodeDataTransfers.EDITORS.toLowerCase(), CodeDataTransfers.FILES.toLowerCase()]; - export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { private readonly treeMimeType: string; private readonly treeItemsTransfer = LocalSelectionTransfer.getInstance(); @@ -1436,14 +1430,23 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { } onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction { - const types: Set = new Set(); - originalEvent.dataTransfer?.types.forEach((value, index) => { - if (INTERNAL_MIME_TYPES.indexOf(value) < 0) { - types.add(this.convertKnownMimes(value).type); + const dataTransfer = toVSDataTransfer(originalEvent.dataTransfer!); + addExternalEditorsDropData(dataTransfer, originalEvent); + + const types = new Set(Array.from(dataTransfer.entries()).map(x => x[0])); + + if (originalEvent.dataTransfer) { + // Also add uri-list if we have any files. At this stage we can't actually access the file itself though. + for (const item of originalEvent.dataTransfer.items) { + if (item.kind === 'file' || item.type === DataTransfers.RESOURCES.toLowerCase()) { + types.add(Mimes.uriList); + break; + } } - }); + } this.debugLog(types); + const dndController = this.dndController; if (!dndController || !originalEvent.dataTransfer || (dndController.dropMimeTypes.length === 0)) { return false; @@ -1479,26 +1482,11 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { return element.label ? element.label.label : (element.resourceUri ? this.labelService.getUriLabel(URI.revive(element.resourceUri)) : undefined); } - private convertKnownMimes(type: string, kind?: string, value?: string | FileSystemHandle): { type: string; value?: string } { - let convertedValue = undefined; - let convertedType = type; - if (type === DataTransfers.RESOURCES.toLowerCase()) { - convertedValue = value ? convertResourceUrlsToUriList(value as string) : undefined; - convertedType = Mimes.uriList; - } else if ((type === 'Files') || (kind === 'file')) { - convertedType = Mimes.uriList; - convertedValue = value ? (value as FileSystemHandle).name : undefined; - } - return { type: convertedType, value: convertedValue }; - } - async drop(data: IDragAndDropData, targetNode: ITreeItem | undefined, targetIndex: number | undefined, originalEvent: DragEvent): Promise { const dndController = this.dndController; if (!originalEvent.dataTransfer || !dndController) { return; } - const treeDataTransfer = new VSDataTransfer(); - const uris: URI[] = []; let treeSourceInfo: TreeDragSourceInfo | undefined; let willDropUuid: string | undefined; @@ -1506,53 +1494,30 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop { willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier; } - await Promise.all([...originalEvent.dataTransfer.items].map(async dataItem => { - const type = dataItem.type; - const kind = dataItem.kind; - const convertedType = this.convertKnownMimes(type, kind).type; - if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0) - && (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) { - if (dataItem.kind === 'string') { - await new Promise(resolve => - dataItem.getAsString(dataValue => { - if (convertedType === this.treeMimeType) { - treeSourceInfo = JSON.parse(dataValue); - } - if (dataValue) { - const converted = this.convertKnownMimes(type, kind, dataValue); - treeDataTransfer.append(converted.type, createStringDataTransferItem(converted.value + '')); - } - resolve(); - })); - } else if (dataItem.kind === 'file') { - const file = dataItem.getAsFile(); - if (file) { - uris.push(URI.file(file.path)); - if (dndController.supportsFileDataTransfers) { - treeDataTransfer.append(type, createFileDataTransferItemFromFile(file)); - } + const originalDataTransfer = toVSDataTransfer(originalEvent.dataTransfer); + addExternalEditorsDropData(originalDataTransfer, originalEvent); + + const outDataTransfer = new VSDataTransfer(); + for (const [type, item] of originalDataTransfer.entries()) { + if (type === this.treeMimeType || dndController.dropMimeTypes.indexOf(type) >= 0) { + outDataTransfer.append(type, item); + if (type === this.treeMimeType) { + try { + treeSourceInfo = JSON.parse(await item.asString()); + } catch { + // noop } } } - })); - - // Check if there are uris to add and add them - if (uris.length) { - treeDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(uris.map(uri => uri.toString()).join('\n'))); } - const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); - if (!additionalWillDropPromise) { - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - } - return additionalWillDropPromise.then(additionalDataTransfer => { - if (additionalDataTransfer) { - for (const item of additionalDataTransfer.entries()) { - treeDataTransfer.append(item[0], item[1]); - } + const additionalDataTransfer = await this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid); + if (additionalDataTransfer) { + for (const item of additionalDataTransfer.entries()) { + outDataTransfer.append(item[0], item[1]); } - return dndController.handleDrop(treeDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); - }); + } + return dndController.handleDrop(outDataTransfer, targetNode, new CancellationTokenSource().token, willDropUuid, treeSourceInfo?.id, treeSourceInfo?.itemHandles); } onDragEnd(originalEvent: DragEvent): void { diff --git a/src/vs/workbench/browser/web.api.ts b/src/vs/workbench/browser/web.api.ts index f41ce51c118..99c7b044635 100644 --- a/src/vs/workbench/browser/web.api.ts +++ b/src/vs/workbench/browser/web.api.ts @@ -110,6 +110,16 @@ export interface IWorkbench { * has been persisted. */ shutdown: () => Promise; + + /** + * Forwards a port. If the current embedder implements a tunnelFactory then that will be used to make the tunnel. + * By default, openTunnel only support localhost; however, a tunnelFactory can be used to support other ips. + * + * @throws When run in an environment without a remote. + * + * @param tunnelOptions The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + openTunnel(tunnelOptions: ITunnelOptions): Thenable; } export interface IWorkbenchConstructionOptions { @@ -159,6 +169,11 @@ export interface IWorkbenchConstructionOptions { */ readonly codeExchangeProxyEndpoints?: { [providerId: string]: string }; + /** + * The identifier of an edit session associated with the current workspace. + */ + readonly editSessionId?: string; + /** * [TEMPORARY]: This will be removed soon. * Endpoints to be used for proxying repository tarball download calls in the browser. diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 0a1507fe9fc..bd79f5a8820 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -31,7 +31,7 @@ import { WorkspaceService } from 'vs/workbench/services/configuration/browser/co import { ConfigurationCache } from 'vs/workbench/services/configuration/common/configurationCache'; import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/browser/signService'; -import { IWorkbenchConstructionOptions, IWorkbench } from 'vs/workbench/browser/web.api'; +import { IWorkbenchConstructionOptions, IWorkbench, ITunnel } from 'vs/workbench/browser/web.api'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; @@ -75,6 +75,11 @@ import { DelayedLogChannel } from 'vs/workbench/services/output/common/delayedLo import { dirname, joinPath } from 'vs/base/common/resources'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; +import { IRemoteExplorerService, TunnelSource } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { DisposableTunnel } from 'vs/platform/tunnel/common/tunnel'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class BrowserMain extends Disposable { @@ -134,6 +139,8 @@ export class BrowserMain extends Disposable { const progessService = accessor.get(IProgressService); const environmentService = accessor.get(IBrowserWorkbenchEnvironmentService); const instantiationService = accessor.get(IInstantiationService); + const remoteExplorerService = accessor.get(IRemoteExplorerService); + const labelService = accessor.get(ILabelService); const embedderLogger = instantiationService.createInstance(DelayedLogChannel, 'webEmbedder', productService.embedderIdentifier || localize('vscode.dev', "vscode.dev"), joinPath(dirname(environmentService.logFile), `webEmbedder.log`)); @@ -163,7 +170,26 @@ export class BrowserMain extends Disposable { window: { withProgress: (options, task) => progessService.withProgress(options, task) }, - shutdown: () => lifecycleService.shutdown() + shutdown: () => lifecycleService.shutdown(), + openTunnel: async (tunnelOptions) => { + const tunnel = await remoteExplorerService.forward({ + remote: tunnelOptions.remoteAddress, + local: tunnelOptions.localAddressPort, + name: tunnelOptions.label, + source: { + source: TunnelSource.Extension, + description: labelService.getHostLabel(Schemas.vscodeRemote, this.configuration.remoteAuthority) + }, + elevateIfNeeded: false + }); + if (!tunnel) { + throw new Error('cannot open tunnel'); + } + + return new class extends DisposableTunnel implements ITunnel { + override localAddress!: string; + }({ port: tunnel.tunnelRemotePort, host: tunnel.tunnelRemoteHost }, tunnel.localAddress, () => tunnel.dispose()); + } }; }); } @@ -234,8 +260,10 @@ export class BrowserMain extends Disposable { await this.registerFileSystemProviders(environmentService, fileService, remoteAgentService, logService, logsPath); // User Data Profiles - const userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesService(undefined, environmentService, fileService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile); + serviceCollection.set(IUserDataProfileService, userDataProfileService); // URI Identity const uriIdentityService = new UriIdentityService(fileService); @@ -243,7 +271,7 @@ export class BrowserMain extends Disposable { // Long running services (workspace, config, storage) const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -254,7 +282,7 @@ export class BrowserMain extends Disposable { return service; }), - this.createStorageService(payload, logService).then(service => { + this.createStorageService(payload, logService, userDataProfileService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -426,8 +454,8 @@ export class BrowserMain extends Disposable { }); } - private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService): Promise { - const storageService = new BrowserStorageService(payload, logService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, logService: ILogService, userDataProfileService: IUserDataProfileService): Promise { + const storageService = new BrowserStorageService(payload, userDataProfileService.currentProfile, logService); try { await storageService.initialize(); @@ -444,9 +472,9 @@ export class BrowserMain extends Disposable { } } - private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { + private async createWorkspaceService(payload: IAnyWorkspaceIdentifier, environmentService: IWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, logService: ILogService): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData, Schemas.tmp] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); + const workspaceService = new WorkspaceService({ remoteAuthority: this.configuration.remoteAuthority, configurationCache }, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, new NullPolicyService()); try { await workspaceService.initialize(payload); diff --git a/src/vs/workbench/browser/window.ts b/src/vs/workbench/browser/window.ts index 7d15b8bf58f..3c991cc3972 100644 --- a/src/vs/workbench/browser/window.ts +++ b/src/vs/workbench/browser/window.ts @@ -6,6 +6,7 @@ import { isSafari, setFullscreen } from 'vs/base/browser/browser'; import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; +import { requestUsb, UsbDeviceData } from 'vs/base/browser/usb'; import { timeout } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -14,8 +15,10 @@ import { isIOS, isMacintosh } from 'vs/base/common/platform'; import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -120,6 +123,9 @@ export class BrowserWindow extends Disposable { // Label formatting this.registerLabelFormatters(); + // Commands + this.registerCommands(); + // Smoke Test Driver this.setupDriver(); } @@ -227,7 +233,7 @@ export class BrowserWindow extends Disposable { }); } - private registerLabelFormatters() { + private registerLabelFormatters(): void { this._register(this.labelService.registerFormatter({ scheme: Schemas.vscodeUserData, priority: true, @@ -237,4 +243,12 @@ export class BrowserWindow extends Disposable { } })); } + + private registerCommands(): void { + + // Allow extensions to request USB devices in Web + CommandsRegistry.registerCommand('workbench.experimental.requestUsbDevice', async (_accessor: ServicesAccessor, options?: { filters?: unknown[] }): Promise => { + return requestUsb(options); + }); + } } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index e1a960c81de..017f41f481a 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -7,15 +7,19 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; -import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { ConfigurationMigrationWorkbenchContribution, workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; import { isStandalone } from 'vs/base/browser/browser'; -import 'vs/workbench/common/configurationMigration'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; const registry = Registry.as(ConfigurationExtensions.Configuration); // Configuration (function registerConfiguration(): void { + // Migration support + Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); + // Workbench registry.registerConfiguration({ ...workbenchConfigurationNodeBase, diff --git a/src/vs/workbench/common/configuration.ts b/src/vs/workbench/common/configuration.ts index 8405ae63a5f..d25d5728cef 100644 --- a/src/vs/workbench/common/configuration.ts +++ b/src/vs/workbench/common/configuration.ts @@ -5,6 +5,12 @@ import { localize } from 'vs/nls'; import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter } from 'vs/base/common/event'; export const workbenchConfigurationNodeBase = Object.freeze({ 'id': 'workbench', @@ -12,3 +18,88 @@ export const workbenchConfigurationNodeBase = Object.freeze( 'title': localize('workbenchConfigurationTitle', "Workbench"), 'type': 'object', }); + +export const Extensions = { + ConfigurationMigration: 'base.contributions.configuration.migration' +}; + +export type ConfigurationValue = { value: any | undefined /* Remove */ }; +export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; +export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; +export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; + +export interface IConfigurationMigrationRegistry { + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; +} + +class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry { + + readonly migrations: ConfigurationMigration[] = []; + + private readonly _onDidRegisterConfigurationMigrations = new Emitter(); + readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; + + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { + this.migrations.push(...configurationMigrations); + } + +} + +const configurationMigrationRegistry = new ConfigurationMigrationRegistry(); +Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry); + +export class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IConfigurationService private readonly configurationService: IConfigurationService, + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, + ) { + super(); + this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => { + for (const folder of e.added) { + await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations); + } + })); + this.migrateConfigurations(configurationMigrationRegistry.migrations); + this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration))); + } + + private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise { + await this.migrateConfigurationsForFolder(undefined, migrations); + for (const folder of this.workspaceService.getWorkspace().folders) { + await this.migrateConfigurationsForFolder(folder, migrations); + } + } + + private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise { + await Promise.all(migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, { resource: folder?.uri }))); + } + + private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, overrides: IConfigurationOverrides): Promise { + const data = this.configurationService.inspect(migration.key, overrides); + + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userValue', ConfigurationTarget.USER); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userLocalValue', ConfigurationTarget.USER_LOCAL); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userRemoteValue', ConfigurationTarget.USER_REMOTE); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceFolderValue', ConfigurationTarget.WORKSPACE_FOLDER); + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceValue', ConfigurationTarget.WORKSPACE); + + if (typeof overrides.overrideIdentifier === 'undefined' && typeof data.overrideIdentifiers !== 'undefined') { + for (const overrideIdentifier of data.overrideIdentifiers) { + await this.migrateConfigurationsForFolderAndOverride(migration, { resource: overrides.resource, overrideIdentifier }); + } + } + } + + private async migrateConfigurationForFolderOverrideAndTarget(migration: ConfigurationMigration, overrides: IConfigurationOverrides, data: IConfigurationValue, dataKey: keyof IConfigurationValue, target: ConfigurationTarget): Promise { + const value = data[dataKey]; + if (typeof value === 'undefined') { + return; + } + + const valueAccessor = (key: string) => this.configurationService.inspect(key, overrides)[dataKey]; + const result = await migration.migrateFn(value, valueAccessor); + const keyValuePairs: ConfigurationKeyValuePairs = Array.isArray(result) ? result : [[migration.key, result]]; + await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); + } +} diff --git a/src/vs/workbench/common/configurationMigration.ts b/src/vs/workbench/common/configurationMigration.ts deleted file mode 100644 index 44bcd77a4ad..00000000000 --- a/src/vs/workbench/common/configurationMigration.ts +++ /dev/null @@ -1,99 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; -import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { Emitter } from 'vs/base/common/event'; - -export const Extensions = { - ConfigurationMigration: 'base.contributions.configuration.migration' -}; - -export type ConfigurationValue = { value: any | undefined /* Remove */ }; -export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; -export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise; -export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; - -export interface IConfigurationMigrationRegistry { - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; -} - -class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry { - - readonly migrations: ConfigurationMigration[] = []; - - private readonly _onDidRegisterConfigurationMigrations = new Emitter(); - readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; - - registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { - this.migrations.push(...configurationMigrations); - } - -} - -const configurationMigrationRegistry = new ConfigurationMigrationRegistry(); -Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry); - -class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution { - - constructor( - @IConfigurationService private readonly configurationService: IConfigurationService, - @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, - ) { - super(); - this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => { - for (const folder of e.added) { - await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations); - } - })); - this.migrateConfigurations(configurationMigrationRegistry.migrations); - this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration))); - } - - private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise { - await this.migrateConfigurationsForFolder(undefined, migrations); - for (const folder of this.workspaceService.getWorkspace().folders) { - await this.migrateConfigurationsForFolder(folder, migrations); - } - } - - private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise { - await Promise.all(migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, { resource: folder?.uri }))); - } - - private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, overrides: IConfigurationOverrides): Promise { - const data = this.configurationService.inspect(migration.key, overrides); - - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userValue', ConfigurationTarget.USER); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userLocalValue', ConfigurationTarget.USER_LOCAL); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userRemoteValue', ConfigurationTarget.USER_REMOTE); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceFolderValue', ConfigurationTarget.WORKSPACE_FOLDER); - await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceValue', ConfigurationTarget.WORKSPACE); - - if (typeof overrides.overrideIdentifier === 'undefined' && typeof data.overrideIdentifiers !== 'undefined') { - for (const overrideIdentifier of data.overrideIdentifiers) { - await this.migrateConfigurationsForFolderAndOverride(migration, { resource: overrides.resource, overrideIdentifier }); - } - } - } - - private async migrateConfigurationForFolderOverrideAndTarget(migration: ConfigurationMigration, overrides: IConfigurationOverrides, data: IConfigurationValue, dataKey: keyof IConfigurationValue, target: ConfigurationTarget): Promise { - const value = data[dataKey]; - if (typeof value === 'undefined') { - return; - } - - const valueAccessor = (key: string) => this.configurationService.inspect(key, overrides)[dataKey]; - const result = await migration.migrateFn(value, valueAccessor); - const keyValuePairs: ConfigurationKeyValuePairs = Array.isArray(result) ? result : [[migration.key, result]]; - await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); - } -} - -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(ConfigurationMigrationWorkbenchContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/common/editor/textResourceEditorInput.ts b/src/vs/workbench/common/editor/textResourceEditorInput.ts index 94236604209..ee14358d2b8 100644 --- a/src/vs/workbench/common/editor/textResourceEditorInput.ts +++ b/src/vs/workbench/common/editor/textResourceEditorInput.ts @@ -133,9 +133,7 @@ export class TextResourceEditorInput extends AbstractTextResourceEditorInput imp setLanguageId(languageId: string): void { this.setPreferredLanguageId(languageId); - if (this.cachedModel) { - this.cachedModel.setLanguageId(languageId); - } + this.cachedModel?.setLanguageId(languageId); } setPreferredLanguageId(languageId: string): void { diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index 2d52437793a..1c506756d1a 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -11,6 +11,7 @@ export type MementoObject = { [key: string]: any }; export class Memento { + private static readonly applicationMementos = new Map(); private static readonly globalMementos = new Map(); private static readonly workspaceMementos = new Map(); @@ -23,53 +24,60 @@ export class Memento { } getMemento(scope: StorageScope, target: StorageTarget): MementoObject { + switch (scope) { - // Scope by Workspace - if (scope === StorageScope.WORKSPACE) { - let workspaceMemento = Memento.workspaceMementos.get(this.id); - if (!workspaceMemento) { - workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.workspaceMementos.set(this.id, workspaceMemento); + // Scope by Workspace + case StorageScope.WORKSPACE: { + let workspaceMemento = Memento.workspaceMementos.get(this.id); + if (!workspaceMemento) { + workspaceMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.workspaceMementos.set(this.id, workspaceMemento); + } + + return workspaceMemento.getMemento(); } - return workspaceMemento.getMemento(); - } + // Scope Global + case StorageScope.GLOBAL: { + let globalMemento = Memento.globalMementos.get(this.id); + if (!globalMemento) { + globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.globalMementos.set(this.id, globalMemento); + } - // Scope Global - let globalMemento = Memento.globalMementos.get(this.id); - if (!globalMemento) { - globalMemento = new ScopedMemento(this.id, scope, target, this.storageService); - Memento.globalMementos.set(this.id, globalMemento); - } + return globalMemento.getMemento(); + } - return globalMemento.getMemento(); + // Scope Application + case StorageScope.APPLICATION: { + let applicationMemento = Memento.applicationMementos.get(this.id); + if (!applicationMemento) { + applicationMemento = new ScopedMemento(this.id, scope, target, this.storageService); + Memento.applicationMementos.set(this.id, applicationMemento); + } + + return applicationMemento.getMemento(); + } + } } saveMemento(): void { - - // Workspace - const workspaceMemento = Memento.workspaceMementos.get(this.id); - if (workspaceMemento) { - workspaceMemento.save(); - } - - // Global - const globalMemento = Memento.globalMementos.get(this.id); - if (globalMemento) { - globalMemento.save(); - } + Memento.workspaceMementos.get(this.id)?.save(); + Memento.globalMementos.get(this.id)?.save(); + Memento.applicationMementos.get(this.id)?.save(); } static clear(scope: StorageScope): void { - - // Workspace - if (scope === StorageScope.WORKSPACE) { - Memento.workspaceMementos.clear(); - } - - // Global - if (scope === StorageScope.GLOBAL) { - Memento.globalMementos.clear(); + switch (scope) { + case StorageScope.WORKSPACE: + Memento.workspaceMementos.clear(); + break; + case StorageScope.GLOBAL: + Memento.globalMementos.clear(); + break; + case StorageScope.APPLICATION: + Memento.applicationMementos.clear(); + break; } } } diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index a255a45c0e2..ac9b39c0c21 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -831,7 +831,6 @@ export interface ITreeViewDataProvider { export interface ITreeViewDragAndDropController { readonly dropMimeTypes: string[]; readonly dragMimeTypes: string[]; - readonly supportsFileDataTransfers: boolean; handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise; handleDrop(elements: VSDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise; } diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts index b4cf1ff3071..c63d63003c6 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkTextEdits.ts @@ -19,6 +19,8 @@ import { ResourceMap } from 'vs/base/common/map'; import { IModelService } from 'vs/editor/common/services/model'; import { ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { SnippetParser } from 'vs/editor/contrib/snippet/browser/snippetParser'; +import { performSnippetEdits } from 'vs/editor/contrib/snippet/browser/snippetController2'; type ValidationResult = { canApply: true } | { canApply: false; reason: URI }; @@ -27,7 +29,7 @@ class ModelEditTask implements IDisposable { readonly model: ITextModel; private _expectedModelVersionId: number | undefined; - protected _edits: ISingleEditOperation[]; + protected _edits: (ISingleEditOperation & { insertAsSnippet?: boolean })[]; protected _newEol: EndOfLineSequence | undefined; constructor(private readonly _modelReference: IReference) { @@ -75,7 +77,7 @@ class ModelEditTask implements IDisposable { } else { range = Range.lift(textEdit.range); } - this._edits.push(EditOperation.replaceMove(range, textEdit.text)); + this._edits.push({ ...EditOperation.replaceMove(range, textEdit.text), insertAsSnippet: textEdit.insertAsSnippet }); } validate(): ValidationResult { @@ -91,7 +93,9 @@ class ModelEditTask implements IDisposable { apply(): void { if (this._edits.length > 0) { - this._edits = this._edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + this._edits = this._edits + .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)) + .map(edit => ({ ...edit, text: edit.text && SnippetParser.escape(edit.text) })); this.model.pushEditOperations(null, this._edits, () => null); } if (this._newEol !== undefined) { @@ -121,10 +125,18 @@ class EditorEditTask extends ModelEditTask { super.apply(); return; } - if (this._edits.length > 0) { - this._edits = this._edits.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); - this._editor.executeEdits('', this._edits); + + const insertAsSnippet = this._edits.every(edit => edit.insertAsSnippet); + if (insertAsSnippet) { + // todo@jrieken what ABOUT EOL? + performSnippetEdits(this._editor, this._edits.map(edit => ({ range: Range.lift(edit.range!), snippet: edit.text! }))); + + } else { + this._edits = this._edits + .sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range)); + this._editor.executeEdits('', this._edits); + } } if (this._newEol !== undefined) { if (this._editor.hasModel()) { @@ -193,7 +205,7 @@ export class BulkTextEdits { let makeMinimal = false; if (this._editor?.getModel()?.uri.toString() === ref.object.textEditorModel.uri.toString()) { task = new EditorEditTask(ref, this._editor); - makeMinimal = true; + makeMinimal = true && false; // todo@jrieken HACK } else { task = new ModelEditTask(ref); } diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts index 6ea920674a5..5175aa33d6d 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyTree.ts @@ -119,6 +119,7 @@ export class CallRenderer implements ITreeRenderer, _index: number, template: CallRenderingTemplate): void { const { element, filterData } = node; const deprecated = element.item.tags?.includes(SymbolTag.Deprecated); + template.icon.className = ''; template.icon.classList.add('inline', ...CSSIcon.asClassNameArray(SymbolKinds.toIcon(element.item.kind))); template.label.setLabel( element.item.name, diff --git a/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts b/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts index 6a08839d154..6803bea8f1a 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/editorSettingsMigration.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { EditorSettingMigration, ISettingsWriter } from 'vs/editor/browser/config/migrateOptions'; -import { ConfigurationKeyValuePairs, Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configurationMigration'; +import { ConfigurationKeyValuePairs, Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; Registry.as(Extensions.ConfigurationMigration) .registerConfigurationMigrations(EditorSettingMigration.items.map(item => ({ diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 2e100dc8f09..9041f5e39de 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -204,9 +204,7 @@ export class TrimFinalNewLinesParticipant implements ITextFileSaveParticipant { model.pushEditOperations(prevSelection, [EditOperation.delete(deletionRange)], _edits => prevSelection); - if (editor) { - editor.setSelections(prevSelection); - } + editor?.setSelections(prevSelection); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 7babdf9a73d..d1d118f5c15 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -273,6 +273,12 @@ export class CommentNode extends Disposable { } } + async submitComment(): Promise { + if (this._commentEditor && this._commentFormActions) { + this._commentFormActions.triggerDefaultAction(); + } + } + private createReactionPicker(reactionGroup: languages.CommentReaction[]): ToggleReactionsAction { const toggleReactionAction = this._register(new ToggleReactionsAction(() => { if (toggleReactionActionViewItem) { @@ -361,9 +367,7 @@ export class CommentNode extends Disposable { } }, reaction.iconPath, reaction.count); - if (this._reactionsActionBar) { - this._reactionsActionBar.push(action, { label: true, icon: true }); - } + this._reactionsActionBar?.push(action, { label: true, icon: true }); }); if (hasReactionHandler) { @@ -468,9 +472,7 @@ export class CommentNode extends Disposable { this._register(menu); this._register(menu.onDidChange(() => { - if (this._commentFormActions) { - this._commentFormActions.setActions(menu); - } + this._commentFormActions?.setActions(menu); })); this._commentFormActions = new CommentFormActions(formActions, (action: IAction): void => { diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index abddde8f53c..099e848c97b 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -206,9 +206,7 @@ export class CommentService extends Disposable implements ICommentService { disposeCommentThread(owner: string, threadId: string) { const controller = this.getCommentController(owner); - if (controller) { - controller.deleteCommentThreadMain(threadId); - } + controller?.deleteCommentThreadMain(threadId); } getCommentMenus(owner: string): CommentMenus { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts index 614402762c1..973762aa995 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadRangeDecorator.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IRange } from 'vs/editor/common/core/range'; +import { CommentThread, CommentThreadCollapsibleState } from 'vs/editor/common/languages'; import { IModelDecorationOptions, IModelDeltaDecoration } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { ICommentInfo, ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; @@ -34,6 +35,8 @@ export class CommentThreadRangeDecorator extends Disposable { private decorationIds: string[] = []; private activeDecorationIds: string[] = []; private editor: ICodeEditor | undefined; + private threadCollapseStateListeners: IDisposable[] = []; + private currentThreadCollapseStateListener: IDisposable | undefined; constructor(commentService: ICommentService) { super(); @@ -55,19 +58,34 @@ export class CommentThreadRangeDecorator extends Disposable { this.activeDecorationOptions = ModelDecorationOptions.createDynamic(activeDecorationOptions); this._register(commentService.onDidChangeCurrentCommentThread(thread => { - if (!this.editor) { - return; - } - const newDecoration: CommentThreadRangeDecoration[] = []; - if (thread) { - const range = thread.range; - if (!((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn))) { + this.updateCurrent(thread); + })); + this._register(commentService.onDidUpdateCommentThreads(() => { + this.updateCurrent(undefined); + })); + } + + private updateCurrent(thread: CommentThread | undefined) { + if (!this.editor) { + return; + } + this.currentThreadCollapseStateListener?.dispose(); + const newDecoration: CommentThreadRangeDecoration[] = []; + if (thread) { + const range = thread.range; + if (!((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn))) { + if (thread.collapsibleState === CommentThreadCollapsibleState.Expanded) { + this.currentThreadCollapseStateListener = thread.onDidChangeCollapsibleState(state => { + if (state === CommentThreadCollapsibleState.Collapsed) { + this.updateCurrent(undefined); + } + }); newDecoration.push(new CommentThreadRangeDecoration(range, this.activeDecorationOptions)); } } - this.activeDecorationIds = this.editor.deltaDecorations(this.activeDecorationIds, newDecoration); - newDecoration.forEach((decoration, index) => decoration.id = this.decorationIds[index]); - })); + } + this.activeDecorationIds = this.editor.deltaDecorations(this.activeDecorationIds, newDecoration); + newDecoration.forEach((decoration, index) => decoration.id = this.decorationIds[index]); } public update(editor: ICodeEditor, commentInfos: ICommentInfo[]) { @@ -75,6 +93,7 @@ export class CommentThreadRangeDecorator extends Disposable { if (!model) { return; } + dispose(this.threadCollapseStateListeners); this.editor = editor; const commentThreadRangeDecorations: CommentThreadRangeDecoration[] = []; @@ -83,12 +102,22 @@ export class CommentThreadRangeDecorator extends Disposable { if (thread.isDisposed) { return; } + const range = thread.range; // We only want to show a range decoration when there's the range spans either multiple lines // or, when is spans multiple characters on the sample line if ((range.startLineNumber === range.endLineNumber) && (range.startColumn === range.endColumn)) { return; } + + this.threadCollapseStateListeners.push(thread.onDidChangeCollapsibleState(() => { + this.update(editor, commentInfos); + })); + + if (thread.collapsibleState === CommentThreadCollapsibleState.Collapsed) { + return; + } + commentThreadRangeDecorations.push(new CommentThreadRangeDecoration(range, this.decorationOptions)); }); } @@ -96,4 +125,10 @@ export class CommentThreadRangeDecorator extends Disposable { this.decorationIds = editor.deltaDecorations(this.decorationIds, commentThreadRangeDecorations); commentThreadRangeDecorations.forEach((decoration, index) => decoration.id = this.decorationIds[index]); } + + override dispose() { + dispose(this.threadCollapseStateListeners); + this.currentThreadCollapseStateListener?.dispose(); + super.dispose(); + } } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 1da1f2064d7..f4f972a1a00 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/review'; import * as dom from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as languages from 'vs/editor/common/languages'; import { IMarkdownRendererOptions } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; @@ -18,7 +18,6 @@ import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentSe import { CommentThreadBody } from 'vs/workbench/contrib/comments/browser/commentThreadBody'; import { CommentThreadHeader } from 'vs/workbench/contrib/comments/browser/commentThreadHeader'; import { CommentContextKeys } from 'vs/workbench/contrib/comments/common/commentContextKeys'; -import { CommentNode } from 'vs/workbench/contrib/comments/common/commentModel'; import { ICommentThreadWidget } from 'vs/workbench/contrib/comments/common/commentThreadWidget'; import { IColorTheme } from 'vs/platform/theme/common/themeService'; import { contrastBorder, focusBorder, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, textBlockQuoteBackground, textBlockQuoteBorder, textLinkActiveForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; @@ -148,7 +147,7 @@ export class CommentThreadWidget extends updateCommentThread(commentThread: languages.CommentThread) { if (this._commentThread !== commentThread) { - this._commentThreadDisposables.forEach(disposable => disposable.dispose()); + dispose(this._commentThreadDisposables); } this._commentThread = commentThread; @@ -273,7 +272,9 @@ export class CommentThreadWidget extends async submitComment() { const activeComment = this._body.activeComment; - if (activeComment && !(activeComment instanceof CommentNode)) { + if (activeComment) { + activeComment.submitComment(); + } else if ((this._commentReply?.getPendingComment()?.length ?? 0) > 0) { this._commentReply?.submitComment(); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index c9c6c215711..32b36c7f42f 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -342,7 +342,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget } })); - this._commentThreadDisposables.push(this._commentThread.onDidChangeCollasibleState(state => { + this._commentThreadDisposables.push(this._commentThread.onDidChangeCollapsibleState(state => { if (state === languages.CommentThreadCollapsibleState.Expanded && !this._isExpanded) { const lineNumber = this._commentThread.range.startLineNumber; diff --git a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts index 606f1e77fd8..9d3eb7de6bf 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsEditorContribution.ts @@ -9,7 +9,7 @@ import { coalesce, findFirstInSorted } from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/review'; import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent, isCodeEditor, isDiffEditor, IViewZone } from 'vs/editor/browser/editorBrowser'; import { EditorAction, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; @@ -310,7 +310,7 @@ export class CommentController implements IEditorContribution { private _computeCommentingRangePromise!: CancelablePromise | null; private _computeCommentingRangeScheduler!: Delayer> | null; private _pendingCommentCache: { [key: string]: { [key: string]: string } }; - private _editorDisposables: IDisposable[] | undefined; + private _editorDisposables: IDisposable[] = []; private _activeCursorHasCommentingRange: IContextKey; constructor( @@ -340,7 +340,7 @@ export class CommentController implements IEditorContribution { this.globalToDispose.add(this._commentingRangeDecorator.onDidChangeDecorationsCount(count => { if (count === 0) { this.clearEditorListeners(); - } else if (!this._editorDisposables) { + } else if (this._editorDisposables.length === 0) { this.registerEditorListeners(); } })); @@ -376,8 +376,8 @@ export class CommentController implements IEditorContribution { } private clearEditorListeners() { - this._editorDisposables?.forEach(disposable => disposable.dispose()); - this._editorDisposables = undefined; + dispose(this._editorDisposables); + this._editorDisposables = []; } private onEditorMouseMove(e: IEditorMouseEvent): void { @@ -556,7 +556,7 @@ export class CommentController implements IEditorContribution { this.localToDispose.add(this.editor.onMouseDown(e => this.onEditorMouseDown(e))); this.localToDispose.add(this.editor.onMouseUp(e => this.onEditorMouseUp(e))); - if (this._editorDisposables) { + if (this._editorDisposables.length) { this.clearEditorListeners(); this.registerEditorListeners(); } @@ -858,9 +858,7 @@ export class CommentController implements IEditorContribution { } public closeWidget(): void { - if (this._commentWidgets) { - this._commentWidgets.forEach(widget => widget.hide()); - } + this._commentWidgets?.forEach(widget => widget.hide()); this.editor.focus(); this.editor.revealRangeInCenter(this.editor.getSelection()!); diff --git a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts index 83a37ddc12e..4aed7b2d974 100644 --- a/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts +++ b/src/vs/workbench/contrib/configExporter/electron-sandbox/configurationExportHelper.ts @@ -98,9 +98,7 @@ export class DefaultConfigurationExportHelper { } } - if (config.allOf) { - config.allOf.forEach(processConfig); - } + config.allOf?.forEach(processConfig); }; configurations.forEach(processConfig); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 2e020ea8fa6..8772e62d4f9 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -242,9 +242,7 @@ export class BreakpointsView extends ViewPane { } super.layoutBody(height, width); - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); try { this.ignoreLayout = true; this.updateSize(); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 95d616fe67b..1f9a2e611c3 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -20,7 +20,7 @@ import { } from 'vs/workbench/contrib/debug/common/debug'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { DebugService } from 'vs/workbench/contrib/debug/browser/debugService'; -import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; +import { ADD_CONFIGURATION_ID, TOGGLE_INLINE_BREAKPOINT_ID, COPY_STACK_TRACE_ID, RESTART_SESSION_ID, TERMINATE_THREAD_ID, STEP_OVER_ID, STEP_INTO_ID, STEP_OUT_ID, PAUSE_ID, DISCONNECT_ID, STOP_ID, RESTART_FRAME_ID, CONTINUE_ID, FOCUS_REPL_ID, JUMP_TO_CURSOR_ID, RESTART_LABEL, STEP_INTO_LABEL, STEP_OVER_LABEL, STEP_OUT_LABEL, PAUSE_LABEL, DISCONNECT_LABEL, STOP_LABEL, CONTINUE_LABEL, DEBUG_START_LABEL, DEBUG_START_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_RUN_COMMAND_ID, EDIT_EXPRESSION_COMMAND_ID, REMOVE_EXPRESSION_COMMAND_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SET_EXPRESSION_COMMAND_ID, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL } from 'vs/workbench/contrib/debug/browser/debugCommands'; import { StatusBarColorProvider } from 'vs/workbench/contrib/debug/browser/statusbarColorProvider'; import { IViewsRegistry, Extensions as ViewExtensions, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views'; import { isMacintosh, isWeb } from 'vs/base/common/platform'; @@ -120,6 +120,8 @@ registerDebugCommandPaletteItem(TOGGLE_INLINE_BREAKPOINT_ID, nls.localize('inlin registerDebugCommandPaletteItem(DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); registerDebugCommandPaletteItem(SELECT_AND_START_ID, SELECT_AND_START_LABEL, ContextKeyExpr.and(CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE.notEqualsTo(getStateLabel(State.Initializing)))); +registerDebugCommandPaletteItem(NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL); +registerDebugCommandPaletteItem(PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL); // Debug callstack context menu diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 3042792f8e8..eddb769f3df 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -8,7 +8,7 @@ import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { List } from 'vs/base/browser/ui/list/listWidget'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; -import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, REPL_VIEW_ID, CONTEXT_DEBUGGERS_AVAILABLE, State, getStateLabel, CONTEXT_BREAKPOINT_INPUT_FOCUSED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, VIEWLET_ID, CONTEXT_DISASSEMBLY_VIEW_FOCUS, CONTEXT_IN_DEBUG_REPL } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -58,6 +58,8 @@ export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run'; export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression'; export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression'; export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression'; +export const NEXT_DEBUG_CONSOLE_ID = 'workbench.action.debug.nextConsole'; +export const PREV_DEBUG_CONSOLE_ID = 'workbench.action.debug.prevConsole'; export const RESTART_LABEL = nls.localize('restartDebug', "Restart"); export const STEP_OVER_LABEL = nls.localize('stepOverDebug', "Step Over"); @@ -73,6 +75,8 @@ export const SELECT_AND_START_LABEL = nls.localize('selectAndStartDebugging', "S export const DEBUG_CONFIGURE_LABEL = nls.localize('openLaunchJson', "Open '{0}'", 'launch.json'); export const DEBUG_START_LABEL = nls.localize('startDebug', "Start Debugging"); export const DEBUG_RUN_LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging"); +export const NEXT_DEBUG_CONSOLE_LABEL = nls.localize('nextDebugConsole', "Focus Next Debug Console"); +export const PREV_DEBUG_CONSOLE_LABEL = nls.localize('prevDebugConsole', "Focus Previous Debug Console"); interface CallStackContext { sessionId: string; @@ -136,6 +140,33 @@ function isSessionContext(obj: any): obj is CallStackContext { return obj && typeof obj.sessionId === 'string'; } +async function changeDebugConsoleFocus(accessor: ServicesAccessor, next: boolean) { + const debugService = accessor.get(IDebugService); + const viewsService = accessor.get(IViewsService); + const sessions = debugService.getModel().getSessions(true).filter(s => s.hasSeparateRepl()); + let currSession = debugService.getViewModel().focusedSession; + + let nextIndex = 0; + if (sessions.length > 0 && currSession) { + while (currSession && !currSession.hasSeparateRepl()) { + currSession = currSession.parentSession; + } + + if (currSession) { + const currIndex = sessions.indexOf(currSession); + if (next) { + nextIndex = (currIndex === (sessions.length - 1) ? 0 : (currIndex + 1)); + } else { + nextIndex = (currIndex === 0 ? (sessions.length - 1) : (currIndex - 1)); + } + } + } + await debugService.focusStackFrame(undefined, undefined, sessions[nextIndex], { explicit: true }); + + if (!viewsService.isViewVisible(REPL_VIEW_ID)) { + await viewsService.openView(REPL_VIEW_ID, true); + } +} // These commands are used in call stack context menu, call stack inline actions, command palette, debug toolbar, mac native touch bar // When the command is exectued in the context of a thread(context menu on a thread, inline call stack action) we pass the thread id @@ -229,6 +260,28 @@ MenuRegistry.appendMenuItem(MenuId.EditorContext, { order: 3 }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: NEXT_DEBUG_CONSOLE_ID, + weight: KeybindingWeight.WorkbenchContrib + 1, + when: CONTEXT_IN_DEBUG_REPL, + primary: KeyMod.CtrlCmd | KeyCode.PageDown, + mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketRight }, + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + changeDebugConsoleFocus(accessor, true); + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: PREV_DEBUG_CONSOLE_ID, + weight: KeybindingWeight.WorkbenchContrib + 1, + when: CONTEXT_IN_DEBUG_REPL, + primary: KeyMod.CtrlCmd | KeyCode.PageUp, + mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.BracketLeft }, + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { + changeDebugConsoleFocus(accessor, false); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: RESTART_SESSION_ID, weight: KeybindingWeight.WorkbenchContrib, diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 398674d57aa..b308716a8bb 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -870,8 +870,8 @@ export class DebugService implements IDebugService { const lineNumber = stackFrame.range.startLineNumber; if (lineNumber >= 1 && lineNumber <= model.getLineCount()) { const lineContent = control.getModel().getLineContent(lineNumber); - aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the stack frame name, second is the line number, third placeholder is the reason why debugging is stopped, for example "breakpoint" and the last one is the file line content.'] }, - "{0}:{1}, debugging paused {2}, {3}", stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', lineContent)); + aria.alert(nls.localize({ key: 'debuggingPaused', comment: ['First placeholder is the file line content, second placeholder is the reason why debugging is stopped, for example "breakpoint", third is the stack frame name, and last is the line number.'] }, + "{0}, debugging paused {1}, {2}:{3}", lineContent, thread && thread.stoppedDetails ? `, reason ${thread.stoppedDetails.reason}` : '', stackFrame.source ? stackFrame.source.name : '', stackFrame.range.startLineNumber)); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 3ff60d94d24..4a53a4e0c93 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -925,9 +925,7 @@ export class DebugSession implements IDebugSession { } catch (e) { // Disconnect the debug session on configuration done error #10596 this.notificationService.error(e); - if (this.raw) { - this.raw.disconnect({}); - } + this.raw?.disconnect({}); } } @@ -1030,9 +1028,7 @@ export class DebugSession implements IDebugSession { this.stoppedDetails = this.stoppedDetails.filter(sd => sd.threadId !== threadId); const tokens = this.cancellationMap.get(threadId); this.cancellationMap.delete(threadId); - if (tokens) { - tokens.forEach(t => t.cancel()); - } + tokens?.forEach(t => t.cancel()); } else { this.stoppedDetails = []; this.cancelAllRequests(); diff --git a/src/vs/workbench/contrib/debug/browser/debugStatus.ts b/src/vs/workbench/contrib/debug/browser/debugStatus.ts index 898b0329b5e..c4a8ef6be74 100644 --- a/src/vs/workbench/contrib/debug/browser/debugStatus.ts +++ b/src/vs/workbench/contrib/debug/browser/debugStatus.ts @@ -49,9 +49,7 @@ export class DebugStatusContribution implements IWorkbenchContribution { } })); this.toDispose.push(this.debugService.getConfigurationManager().onDidSelectConfiguration(e => { - if (this.entryAccessor) { - this.entryAccessor.update(this.entry); - } + this.entryAccessor?.update(this.entry); })); } diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index 682133ef7bd..2bfc78a80ae 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -284,9 +284,7 @@ export class DisassemblyView extends EditorPane { } layout(dimension: Dimension): void { - if (this._disassembledInstructions) { - this._disassembledInstructions.layout(dimension.height); - } + this._disassembledInstructions?.layout(dimension.height); } /** diff --git a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts index 772193c7f16..5cdb5c988cb 100644 --- a/src/vs/workbench/contrib/debug/common/debugContentProvider.ts +++ b/src/vs/workbench/contrib/debug/common/debugContentProvider.ts @@ -62,9 +62,7 @@ export class DebugContentProvider implements IWorkbenchContribution, ITextModelC * If there is no model for the given resource, this method does nothing. */ static refreshDebugContent(resource: uri): void { - if (DebugContentProvider.INSTANCE) { - DebugContentProvider.INSTANCE.createOrUpdateContentModel(resource, false); - } + DebugContentProvider.INSTANCE?.createOrUpdateContentModel(resource, false); } /** diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index 6b58a993556..66325b37e09 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -260,9 +260,7 @@ function convertPaths(msg: DebugProtocol.ProtocolMessage, fixSourcePath: (toDA: case 'disassemble': { const di = response; - if (di.body) { - di.body.instructions.forEach(di => fixSourcePath(false, di.location)); - } + di.body?.instructions.forEach(di => fixSourcePath(false, di.location)); } break; default: diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 4f0bf897787..b4f6c7e4daf 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -259,9 +259,7 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { // }); this.serverProcess.stderr!.on('data', (data: string) => { const channel = outputService.getChannel(ExtensionsChannelId); - if (channel) { - channel.append(sanitize(data)); - } + channel?.append(sanitize(data)); }); } else { this.serverProcess.stderr!.resume(); diff --git a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts index a850ee85522..a10722cff13 100644 --- a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts +++ b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts @@ -58,9 +58,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib this.paneCompositeService.openPaneComposite(EXTENSIONS_VIEWLET_ID, ViewContainerLocation.Sidebar, true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { - if (viewlet) { - viewlet.search('curated:' + command.curatedExtensionsKey); - } + viewlet?.search('curated:' + command.curatedExtensionsKey); }); } else if (command.codeCommand) { this.commandService.executeCommand(command.codeCommand.id, ...command.codeCommand.arguments); diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 07741ba7717..235217dbc64 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -223,9 +223,9 @@ export class ExperimentService extends Disposable implements IExperimentService public markAsCompleted(experimentId: string): void { const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); experimentState.state = ExperimentState.Complete; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); } protected async getExperiments(): Promise { @@ -255,11 +255,11 @@ export class ExperimentService extends Disposable implements IExperimentService return this.getExperiments().then(rawExperiments => { // Offline mode if (!rawExperiments) { - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.APPLICATION), []); if (Array.isArray(allExperimentIdsFromStorage)) { allExperimentIdsFromStorage.forEach(experimentId => { const storageKey = 'experiments.' + experimentId; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), null); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), null); if (experimentState) { this._experiments.push({ id: experimentId, @@ -278,19 +278,19 @@ export class ExperimentService extends Disposable implements IExperimentService rawExperiments = rawExperiments.filter(e => (e.schemaVersion || 0) <= currentSchemaVersion); // Clear disbaled/deleted experiments from storage - const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.GLOBAL), []); + const allExperimentIdsFromStorage = safeParse(this.storageService.get('allExperiments', StorageScope.APPLICATION), []); const enabledExperiments = rawExperiments.filter(experiment => !!experiment.enabled).map(experiment => experiment.id.toLowerCase()); if (Array.isArray(allExperimentIdsFromStorage)) { allExperimentIdsFromStorage.forEach(experiment => { if (enabledExperiments.indexOf(experiment) === -1) { - this.storageService.remove(`experiments.${experiment}`, StorageScope.GLOBAL); + this.storageService.remove(`experiments.${experiment}`, StorageScope.APPLICATION); } }); } if (enabledExperiments.length) { - this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('allExperiments', JSON.stringify(enabledExperiments), StorageScope.APPLICATION, StorageTarget.MACHINE); } else { - this.storageService.remove('allExperiments', StorageScope.GLOBAL); + this.storageService.remove('allExperiments', StorageScope.APPLICATION); } const activationEvents = new Set(rawExperiments.map(exp => exp.condition?.activationEvent?.event) @@ -348,7 +348,7 @@ export class ExperimentService extends Disposable implements IExperimentService } const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); if (!experimentState.hasOwnProperty('enabled')) { experimentState.enabled = processedExperiment.enabled; } @@ -360,7 +360,7 @@ export class ExperimentService extends Disposable implements IExperimentService return this.shouldRunExperiment(experiment, processedExperiment).then((state: ExperimentState) => { experimentState.state = processedExperiment.state = state; - this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(experimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); if (state === ExperimentState.Run) { this.fireRunExperiment(processedExperiment); @@ -372,7 +372,7 @@ export class ExperimentService extends Disposable implements IExperimentService private fireRunExperiment(experiment: IExperiment) { this._onExperimentEnabled.fire(experiment); - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.APPLICATION), []); if (runExperimentIdsFromStorage.indexOf(experiment.id) === -1) { runExperimentIdsFromStorage.push(experiment.id); } @@ -380,14 +380,14 @@ export class ExperimentService extends Disposable implements IExperimentService // Ensure we dont store duplicates const distinctExperiments = distinct(runExperimentIdsFromStorage); if (runExperimentIdsFromStorage.length !== distinctExperiments.length) { - this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('currentOrPreviouslyRunExperiments', JSON.stringify(distinctExperiments), StorageScope.APPLICATION, StorageTarget.MACHINE); } } private checkExperimentDependencies(experiment: IRawExperiment): boolean { const experimentsPreviouslyRun = experiment.condition?.experimentsPreviouslyRun; if (experimentsPreviouslyRun) { - const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.GLOBAL), []); + const runExperimentIdsFromStorage: string[] = safeParse(this.storageService.get('currentOrPreviouslyRunExperiments', StorageScope.APPLICATION), []); let includeCheck = true; let excludeCheck = true; const includes = experimentsPreviouslyRun.includes; @@ -407,9 +407,9 @@ export class ExperimentService extends Disposable implements IExperimentService private recordActivatedEvent(event: string) { const key = experimentEventStorageKey(event); - const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.GLOBAL), undefined)); + const record = getCurrentActivationRecord(safeParse(this.storageService.get(key, StorageScope.APPLICATION), undefined)); record.count[0]++; - this.storageService.store(key, JSON.stringify(record), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(key, JSON.stringify(record), StorageScope.APPLICATION, StorageTarget.MACHINE); this._experiments .filter(e => { @@ -434,7 +434,7 @@ export class ExperimentService extends Disposable implements IExperimentService const events = typeof setting.event === 'string' ? [setting.event] : setting.event; for (const event of events) { - const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(event), StorageScope.GLOBAL), undefined)); + const { count } = getCurrentActivationRecord(safeParse(this.storageService.get(experimentEventStorageKey(event), StorageScope.APPLICATION), undefined)); for (const entry of count) { if (entry > 0) { @@ -483,7 +483,7 @@ export class ExperimentService extends Disposable implements IExperimentService return Promise.resolve(ExperimentState.NoRun); } - const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL); + const isNewUser = !this.storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION); if ((condition.newUser === true && !isNewUser) || (condition.newUser === false && isNewUser)) { return Promise.resolve(ExperimentState.NoRun); @@ -532,7 +532,7 @@ export class ExperimentService extends Disposable implements IExperimentService } const storageKey = 'experiments.' + experiment.id; - const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const experimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); return extensionsCheckPromise.then(success => { const fileEdits = condition.fileEdits; @@ -549,7 +549,7 @@ export class ExperimentService extends Disposable implements IExperimentService // Process model-save event every 250ms to reduce load const onModelsSavedWorker = this._register(new RunOnceWorker(models => { const date = new Date().toDateString(); - const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.GLOBAL), {}); + const latestExperimentState: IExperimentStorageState = safeParse(this.storageService.get(storageKey, StorageScope.APPLICATION), {}); if (latestExperimentState.state !== ExperimentState.Evaluating) { onSaveHandler.dispose(); onModelsSavedWorker.dispose(); @@ -579,12 +579,12 @@ export class ExperimentService extends Disposable implements IExperimentService if (filePathCheck && workspaceCheck) { latestExperimentState.editCount = (latestExperimentState.editCount || 0) + 1; latestExperimentState.lastEditedDate = date; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); } }); if (typeof latestExperimentState.editCount === 'number' && latestExperimentState.editCount >= fileEdits.minEditCount) { processedExperiment.state = latestExperimentState.state = (typeof condition.userProbability === 'number' && Math.random() < condition.userProbability && this.checkExperimentDependencies(experiment)) ? ExperimentState.Run : ExperimentState.NoRun; - this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(storageKey, JSON.stringify(latestExperimentState), StorageScope.APPLICATION, StorageTarget.MACHINE); if (latestExperimentState.state === ExperimentState.Run && processedExperiment.action && ExperimentActionType[processedExperiment.action.type] === ExperimentActionType.Prompt) { this.fireRunExperiment(processedExperiment); } diff --git a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts index a97d0ace12a..2dbfdafd694 100644 --- a/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts +++ b/src/vs/workbench/contrib/experiments/test/electron-browser/experimentService.test.ts @@ -535,7 +535,7 @@ suite('Experiment Service', () => { didGetCall = true; assert.strictEqual(key, 'experimentEventRecord-my-event'); assert.deepStrictEqual(JSON.parse(value).count, [1, 0, 10, 0, 0, 0, 0]); - assert.strictEqual(scope, StorageScope.GLOBAL); + assert.strictEqual(scope, StorageScope.APPLICATION); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts index 302bdc38202..c801cfe94f6 100644 --- a/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/abstractRuntimeExtensionsEditor.ts @@ -95,9 +95,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { protected async _updateExtensions(): Promise { this._elements = await this._resolveExtensions(); - if (this._list) { - this._list.splice(0, this._list.length, this._elements); - } + this._list?.splice(0, this._list.length, this._elements); } private async _resolveExtensions(): Promise { @@ -475,9 +473,7 @@ export abstract class AbstractRuntimeExtensionsEditor extends EditorPane { } public layout(dimension: Dimension): void { - if (this._list) { - this._list.layout(dimension.height); - } + this._list?.layout(dimension.height); } protected abstract _getProfileInfo(): IExtensionHostProfile | null; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5667fe59720..381096c914f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -69,7 +69,7 @@ import { errorIcon, infoIcon, preReleaseIcon, verifiedPublisherIcon as verifiedP import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; import { IExtensionGalleryService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import semver = require('vs/base/common/semver/semver'); +import * as semver from 'vs/base/common/semver/semver'; class NavBar extends Disposable { @@ -1757,9 +1757,7 @@ registerAction2(class StartExtensionEditorFindNextAction extends Action2 { } run(accessor: ServicesAccessor): any { const extensionEditor = getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.runFindAction(false); - } + extensionEditor?.runFindAction(false); } }); @@ -1779,9 +1777,7 @@ registerAction2(class StartExtensionEditorFindPreviousAction extends Action2 { } run(accessor: ServicesAccessor): any { const extensionEditor = getExtensionEditor(accessor); - if (extensionEditor) { - extensionEditor.runFindAction(true); - } + extensionEditor?.runFindAction(true); } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index d9650884885..befa640deb2 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -224,6 +224,11 @@ Registry.as(ConfigurationExtensions.Configuration) } }] }, + 'extensions.experimental.useUtilityProcess': { + type: 'boolean', + description: localize('extensionsUseUtilityProcess', "When enabled, the extension host will be launched using the new UtilityProcess Electron API."), + default: false + }, [WORKSPACE_TRUST_EXTENSION_SUPPORT]: { type: 'object', scope: ConfigurationScope.APPLICATION, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 03dc8eaca40..710e811a4f7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -921,9 +921,7 @@ export abstract class ExtensionDropDownAction extends ExtensionAction { } public override run({ actionGroups, disposeActionsOnHide }: { actionGroups: IAction[][]; disposeActionsOnHide: boolean }): Promise { - if (this._actionViewItem) { - this._actionViewItem.showMenu(actionGroups, disposeActionsOnHide); - } + this._actionViewItem?.showMenu(actionGroups, disposeActionsOnHide); return Promise.resolve(); } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index 20d6716ae62..ae5a743a290 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -58,7 +58,7 @@ import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/act import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { coalesce } from 'vs/base/common/arrays'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { extname } from 'vs/base/common/resources'; const SearchMarketplaceExtensionsContext = new RawContextKey('searchMarketplaceExtensions', false); @@ -541,7 +541,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE if (this.isSupportedDragElement(e)) { hide(overlay); - const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, e))) + const vsixs = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, e))) .map(editor => editor.resource && extname(editor.resource) === '.vsix' ? editor.resource : undefined)); if (vsixs.length > 0) { @@ -571,9 +571,7 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE this.root.classList.toggle('narrow', dimension.width <= 250); this.root.classList.toggle('mini', dimension.width <= 200); } - if (this.searchBox) { - this.searchBox.layout(new Dimension(dimension.width - 34 - /*padding*/8, 20)); - } + this.searchBox?.layout(new Dimension(dimension.width - 34 - /*padding*/8, 20)); super.layout(new Dimension(dimension.width, dimension.height - 41)); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index d2199de3a6e..36ec5fd2d48 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -209,9 +209,7 @@ export class ExtensionsListView extends ViewPane { if (this.bodyTemplate) { this.bodyTemplate.extensionsList.style.height = height + 'px'; } - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); } async show(query: string, refresh?: boolean): Promise> { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 355bfcfeb41..2c7618d29fc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1568,7 +1568,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension }).then(undefined, error => this.onError(error)); } - + /* TODO: @sandy081 Extension version shall be moved to extensions.json file */ private _ignoredAutoUpdateExtensions: string[] | undefined; private get ignoredAutoUpdateExtensions(): string[] { if (!this._ignoredAutoUpdateExtensions) { diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts index 44295a6868b..6e07fd90be6 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensionProfileService.ts @@ -92,9 +92,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio const timeStarted = Date.now(); const handle = setInterval(() => { - if (this.profilingStatusBarIndicator) { - this.profilingStatusBarIndicator.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); - } + this.profilingStatusBarIndicator?.update({ ...indicator, text: nls.localize('profilingExtensionHostTime', "Profiling Extension Host ({0} sec)", Math.round((new Date().getTime() - timeStarted) / 1000)), }); }, 1000); this.profilingStatusBarIndicatorLabelUpdater.value = toDisposable(() => clearInterval(handle)); diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index aa4a1d19536..7209af8ccaf 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -57,11 +57,11 @@ export class RemoteExtensionsInitializerContribution implements IWorkbenchContri } const newRemoteConnectionKey = `${IS_NEW_KEY}.${connection.remoteAuthority}`; // Skip: Not a new remote connection - if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.GLOBAL, true)) { + if (!this.storageService.getBoolean(newRemoteConnectionKey, StorageScope.APPLICATION, true)) { this.logService.trace(`Skipping initializing remote extensions because the window with this remote authority was opened before.`); return; } - this.storageService.store(newRemoteConnectionKey, false, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(newRemoteConnectionKey, false, StorageScope.APPLICATION, StorageTarget.MACHINE); // Skip: Not a new workspace if (!this.storageService.isNew(StorageScope.WORKSPACE)) { this.logService.trace(`Skipping initializing remote extensions because this workspace was opened before.`); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 6cbe2bd6ebd..ad2e86b38ba 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -12,7 +12,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, getTargetPlatform, IExtensionsControlManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, ExtensionInstallLocation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, ExtensionInstallLocation, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -99,6 +99,7 @@ async function setupTest() { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { @@ -113,7 +114,7 @@ async function setupTest() { instantiationService.stub(IRemoteAgentService, RemoteAgentService); - const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' }; instantiationService.stub(IExtensionManagementServerService, >{ get localExtensionManagementServer(): IExtensionManagementServer { return localExtensionManagementServer; @@ -1543,7 +1544,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action when installing local workspace extension', async () => { // multi server setup - const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const remoteExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); remoteExtensionManagementService.onInstallExtension = onInstallExtension.event; const localWorkspaceExtension = aLocalExtension('a', { extensionKind: ['workspace'] }, { location: URI.file(`pub.a`) }); @@ -1574,7 +1575,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action when installing local workspace extension is finished', async () => { // multi server setup - const remoteExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const remoteExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); remoteExtensionManagementService.onInstallExtension = onInstallExtension.event; const onDidInstallEvent = new Emitter(); @@ -1777,7 +1778,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action is disabled for local workspace extension if it is uninstalled locally', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1921,7 +1922,7 @@ suite('RemoteInstallAction', () => { test('Test remote install action is disabled if local language pack extension is uninstalled', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1994,7 +1995,7 @@ suite('LocalInstallAction', () => { test('Test local install action when installing remote ui extension', async () => { // multi server setup - const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const localExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); localExtensionManagementService.onInstallExtension = onInstallExtension.event; const remoteUIExtension = aLocalExtension('a', { extensionKind: ['ui'] }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); @@ -2025,7 +2026,7 @@ suite('LocalInstallAction', () => { test('Test local install action when installing remote ui extension is finished', async () => { // multi server setup - const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const localExtensionManagementService = createExtensionManagementService(); const onInstallExtension = new Emitter(); localExtensionManagementService.onInstallExtension = onInstallExtension.event; const onDidInstallEvent = new Emitter(); @@ -2188,7 +2189,7 @@ suite('LocalInstallAction', () => { test('Test local install action is disabled for remoteUI extension if it is uninstalled locally', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -2311,7 +2312,7 @@ suite('LocalInstallAction', () => { test('Test local install action is disabled if remote language pack extension is uninstalled', async () => { // multi server setup - const extensionManagementService = instantiationService.get(IExtensionManagementService); + const extensionManagementService = instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService; const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -2362,7 +2363,7 @@ function aPage(...objects: T[]): IPager { return { firstPage: objects, total: objects.length, pageSize: objects.length, getPage: () => null! }; } -function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { +function aSingleRemoteExtensionManagementServerService(instantiationService: TestInstantiationService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const remoteExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-remote', label: 'remote', @@ -2386,7 +2387,7 @@ function aSingleRemoteExtensionManagementServerService(instantiationService: Tes }; } -function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { +function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const localExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-local', label: 'local', @@ -2418,12 +2419,13 @@ function aMultiExtensionManagementServerService(instantiationService: TestInstan }; } -function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService { - return { +function createExtensionManagementService(installed: ILocalExtension[] = []): IProfileAwareExtensionManagementService { + return { onInstallExtension: Event.None, onDidInstallExtensions: Event.None, onUninstallExtension: Event.None, onDidUninstallExtension: Event.None, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed), canInstall: async (extension: IGalleryExtension) => { return true; }, installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 1212a0ed3b2..f7b374b3387 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -13,7 +13,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy, InstallExtensionResult, getTargetPlatform, IExtensionInfo } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -96,6 +96,7 @@ suite('ExtensionsListView Tests', () => { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async canInstall() { return true; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, @@ -105,7 +106,7 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IContextKeyService, new MockContextKeyService()); instantiationService.stub(IMenuService, new TestMenuService()); - const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService), label: 'local', id: 'vscode-local' }; + const localExtensionManagementServer = { extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, label: 'local', id: 'vscode-local' }; instantiationService.stub(IExtensionManagementServerService, >{ get localExtensionManagementServer(): IExtensionManagementServer { return localExtensionManagementServer; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 70f286ce690..018ad78c48f 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -13,7 +13,7 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation, IExtensionTipsService, IGalleryMetadata, InstallExtensionResult, getTargetPlatform, IExtensionsControlManifest } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { anExtensionManagementServerService, TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; @@ -94,6 +94,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { onDidInstallExtensions: didInstallEvent.event, onUninstallExtension: uninstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, async getInstalled() { return []; }, async getExtensionsControlManifest() { return { malicious: [], deprecated: {} }; }, async updateMetadata(local: ILocalExtension, metadata: IGalleryMetadata) { @@ -109,7 +110,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: instantiationService.get(IExtensionManagementService), + extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, }, null, null)); instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); @@ -1458,7 +1459,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); } - function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IExtensionManagementService, remoteExtensionManagementService?: IExtensionManagementService): IExtensionManagementServerService { + function aMultiExtensionManagementServerService(instantiationService: TestInstantiationService, localExtensionManagementService?: IProfileAwareExtensionManagementService, remoteExtensionManagementService?: IProfileAwareExtensionManagementService): IExtensionManagementServerService { const localExtensionManagementServer: IExtensionManagementServer = { id: 'vscode-local', label: 'local', @@ -1472,12 +1473,13 @@ suite('ExtensionsWorkbenchServiceTest', () => { return anExtensionManagementServerService(localExtensionManagementServer, remoteExtensionManagementServer, null); } - function createExtensionManagementService(installed: ILocalExtension[] = []): IExtensionManagementService { - return { + function createExtensionManagementService(installed: ILocalExtension[] = []): IProfileAwareExtensionManagementService { + return { onInstallExtension: Event.None, onDidInstallExtensions: Event.None, onUninstallExtension: Event.None, onDidUninstallExtension: Event.None, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed), installFromGallery: (extension: IGalleryExtension) => Promise.reject(new Error('not supported')), updateMetadata: async (local: ILocalExtension, metadata: IGalleryMetadata) => { diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index f74f5df3aa4..6188a5bcb86 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -224,9 +224,7 @@ export class ExplorerViewPaneContainer extends ViewPaneContainer { } const openEditorsView = this.getOpenEditorsView(); - if (openEditorsView) { - openEditorsView.setStructuralRefreshDelay(0); - } + openEditorsView?.setStructuralRefreshDelay(0); } }); } diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index 2215ecd93ad..647334b791c 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -20,7 +20,7 @@ import { ExplorerItem } from 'vs/workbench/contrib/files/common/explorerModel'; import { URI } from 'vs/base/common/uri'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { extractEditorsDropData } from 'vs/platform/dnd/browser/dnd'; +import { extractEditorsAndFilesDropData } from 'vs/platform/dnd/browser/dnd'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { isWeb } from 'vs/base/common/platform'; import { triggerDownload } from 'vs/base/browser/dom'; @@ -427,7 +427,7 @@ export class ExternalFileImport { private async doImport(target: ExplorerItem, source: DragEvent, token: CancellationToken): Promise { // Activate all providers for the resources dropped - const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsDropData(accessor, source))).map(editor => editor.resource)); + const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, source))).map(editor => editor.resource)); await Promise.all(candidateFiles.map(resource => this.fileService.activateProvider(resource.scheme))); // Check for dropped external files to be folders diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index 9f61cd64ee5..1e2e605ec5e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -489,9 +489,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { const element = e.node.element?.element; if (element) { const navigationController = this.renderer.getCompressedNavigationController(element instanceof Array ? element[0] : element); - if (navigationController) { - navigationController.updateCollapsed(e.node.collapsed); - } + navigationController?.updateCollapsed(e.node.collapsed); } })); @@ -765,9 +763,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { itemsCopied(stats: ExplorerItem[], cut: boolean, previousCut: ExplorerItem[] | undefined): void { this.fileCopiedContextKey.set(stats.length > 0); this.resourceCutContextKey.set(cut && stats.length > 0); - if (previousCut) { - previousCut.forEach(item => this.tree.rerender(item)); - } + previousCut?.forEach(item => this.tree.rerender(item)); if (cut) { stats.forEach(s => this.tree.rerender(s)); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 3fd683669db..c42d94b385d 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -760,9 +760,8 @@ export class FilesFilter implements ITreeFilter { filter(stat: ExplorerItem, parentVisibility: TreeVisibility): boolean { // Add newly visited .gitignore files to the ignore tree - if (stat.name === '.gitignore') { + if (stat.name === '.gitignore' && this.ignoreTreesPerRoot.has(stat.resource.toString())) { this.processIgnoreFile(stat.root.resource.toString(), stat.resource, false); - // Never hide .gitignore files return true; } diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index e3e79b236a0..ebb2b295f0f 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -320,9 +320,7 @@ export class OpenEditorsView extends ViewPane { protected override layoutBody(height: number, width: number): void { super.layoutBody(height, width); - if (this.list) { - this.list.layout(height, width); - } + this.list?.layout(height, width); } private get showGroups(): boolean { diff --git a/src/vs/workbench/contrib/files/common/explorerModel.ts b/src/vs/workbench/contrib/files/common/explorerModel.ts index ac4207fb18d..61d0ab50ca7 100644 --- a/src/vs/workbench/contrib/files/common/explorerModel.ts +++ b/src/vs/workbench/contrib/files/common/explorerModel.ts @@ -178,13 +178,9 @@ export class ExplorerItem { private updateName(value: string): void { // Re-add to parent since the parent has a name map to children and the name might have changed - if (this._parent) { - this._parent.removeChild(this); - } + this._parent?.removeChild(this); this._name = value; - if (this._parent) { - this._parent.addChild(this); - } + this._parent?.addChild(this); } getId(): string { @@ -411,12 +407,8 @@ export class ExplorerItem { * Moves this element under a new parent element. */ move(newParent: ExplorerItem): void { - if (this.nestedParent) { - this.nestedParent.removeChild(this); - } - if (this._parent) { - this._parent.removeChild(this); - } + this.nestedParent?.removeChild(this); + this._parent?.removeChild(this); newParent.removeChild(this); // make sure to remove any previous version of the file if any newParent.addChild(this); this.updateResource(true); diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 7223d0cdc20..e363e609335 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -267,9 +267,7 @@ class ToggleMultilineActionViewItem extends ActionViewItem { } private updateExpandedAttribute(): void { - if (this.element) { - this.element.setAttribute('aria-expanded', `${this._action.class === ThemeIcon.asClassName(expandedIcon)}`); - } + this.element?.setAttribute('aria-expanded', `${this._action.class === ThemeIcon.asClassName(expandedIcon)}`); } } diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 18fa3e2930f..bf75c6804ca 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -227,9 +227,7 @@ export class MarkersView extends ViewPane implements IMarkersView { const wasSmallLayout = this.smallLayout; this.smallLayout = width < 600 && height > 100; if (this.smallLayout !== wasSmallLayout) { - if (this.filterActionBar) { - this.filterActionBar.getContainer().classList.toggle('hide', !this.smallLayout); - } + this.filterActionBar?.getContainer().classList.toggle('hide', !this.smallLayout); } const contentHeight = this.smallLayout ? height - 44 : height; if (this.messageBoxContainer) { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts new file mode 100644 index 00000000000..6feb020989e --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/commands.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from 'vs/base/common/codicons'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { Action2, MenuId } from 'vs/platform/actions/common/actions'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class OpenMergeEditor extends Action2 { + + constructor() { + super({ + id: '_open.mergeEditor', + title: localize('title', "Open Merge Editor"), + }); + } + run(accessor: ServicesAccessor, ...args: any[]): void { + const validatedArgs = IRelaxedOpenArgs.validate(args[0]); + + const instaService = accessor.get(IInstantiationService); + const input = instaService.createInstance( + MergeEditorInput, + validatedArgs.ancestor, + validatedArgs.input1, + validatedArgs.input2, + validatedArgs.output, + ); + accessor.get(IEditorService).openEditor(input); + } +} + +namespace IRelaxedOpenArgs { + function toUri(obj: unknown): URI { + if (typeof obj === 'string') { + return URI.parse(obj, true); + } else if (obj && typeof obj === 'object') { + return URI.revive(obj); + } + throw new TypeError('invalid argument'); + } + + function isUriComponents(obj: unknown): obj is UriComponents { + if (!obj || typeof obj !== 'object') { + return false; + } + return typeof (obj).scheme === 'string' + && typeof (obj).authority === 'string' + && typeof (obj).path === 'string' + && typeof (obj).query === 'string' + && typeof (obj).fragment === 'string'; + } + + function toInputResource(obj: unknown): MergeEditorInputData { + if (typeof obj === 'string') { + return new MergeEditorInputData(URI.parse(obj, true), undefined, undefined); + } + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + + if (isUriComponents(obj)) { + return new MergeEditorInputData(URI.revive(obj), undefined, undefined); + } + + const uri = toUri((obj).uri); + const detail = (obj).detail; + const description = (obj).description; + return new MergeEditorInputData(uri, detail, description); + } + + export function validate(obj: unknown): IOpenEditorArgs { + if (!obj || typeof obj !== 'object') { + throw new TypeError('invalid argument'); + } + const ancestor = toUri((obj).ancestor); + const output = toUri((obj).output); + const input1 = toInputResource((obj).input1); + const input2 = toInputResource((obj).input2); + return { ancestor, input1, input2, output }; + } +} + +type IRelaxedInputData = { uri: UriComponents; detail?: string; description?: string }; + +type IRelaxedOpenArgs = { + ancestor: UriComponents | string; + input1: IRelaxedInputData | string; + input2: IRelaxedInputData | string; + output: UriComponents | string; +}; + +interface IOpenEditorArgs { + ancestor: URI; + input1: MergeEditorInputData; + input2: MergeEditorInputData; + output: URI; +} + +export class ToggleLayout extends Action2 { + constructor() { + super({ + id: 'merge.toggleLayout', + title: localize('toggle.title', "Switch to column view"), + icon: Codicon.layoutCentered, + toggled: { + condition: ctxUsesColumnLayout, + icon: Codicon.layoutPanel, + title: localize('toggle.title2', "Switch to 2 by 1 view"), + }, + menu: [{ + id: MenuId.EditorTitle, + when: ctxIsMergeEditor, + group: 'navigation' + }] + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + if (activeEditorPane instanceof MergeEditor) { + activeEditorPane.toggleLayout(); + } + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.ts new file mode 100644 index 00000000000..45f0b293e7f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/commands/devCommands.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 { VSBuffer } from 'vs/base/common/buffer'; +import { Codicon } from 'vs/base/common/codicons'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { Action2 } from 'vs/platform/actions/common/actions'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; +import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { INotificationService } from 'vs/platform/notification/common/notification'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files'; +import { URI } from 'vs/base/common/uri'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; + +interface MergeEditorContents { + languageId: string; + base: string; + input1: string; + input2: string; + result: string; +} + +export class MergeEditorCopyContentsToJSON extends Action2 { + + constructor() { + super({ + id: 'merge.dev.copyContents', + title: localize('merge.dev.copyContents', "Developer Merge Editor: Copy Contents of Inputs, Base and Result as JSON"), + icon: Codicon.layoutCentered, + f1: true, + }); + } + + run(accessor: ServicesAccessor): void { + const { activeEditorPane } = accessor.get(IEditorService); + const clipboardService = accessor.get(IClipboardService); + const notificationService = accessor.get(INotificationService); + + if (!(activeEditorPane instanceof MergeEditor)) { + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") + }); + return; + } + const model = activeEditorPane.model; + if (!model) { + return; + } + const contents: MergeEditorContents = { + languageId: model.result.getLanguageId(), + base: model.base.getValue(), + input1: model.input1.getValue(), + input2: model.input2.getValue(), + result: model.result.getValue(), + }; + const jsonStr = JSON.stringify(contents, undefined, 4); + clipboardService.writeText(jsonStr); + + notificationService.info({ + name: localize('mergeEditor.name', 'Merge Editor'), + message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor contents"), + }); + } +} + +export class MergeEditorOpenContents extends Action2 { + + constructor() { + super({ + id: 'merge.dev.openContents', + title: localize('merge.dev.openContents', "Developer Merge Editor: Open Contents of Inputs, Base and Result from JSON"), + icon: Codicon.layoutCentered, + f1: true, + }); + } + + async run(accessor: ServicesAccessor): Promise { + const service = accessor.get(IWorkbenchFileService); + const instaService = accessor.get(IInstantiationService); + const editorService = accessor.get(IEditorService); + const inputService = accessor.get(IQuickInputService); + const clipboardService = accessor.get(IClipboardService); + const textModelService = accessor.get(ITextModelService); + + const result = await inputService.input({ + prompt: localize('mergeEditor.enterJSON', 'Enter JSON'), + value: await clipboardService.readText(), + }); + if (!result) { + return; + } + + const content: MergeEditorContents = JSON.parse(result); + + const scheme = 'merge-editor-dev'; + + let provider = service.getProvider(scheme) as InMemoryFileSystemProvider | undefined; + if (!provider) { + provider = new InMemoryFileSystemProvider(); + service.registerProvider(scheme, provider); + } + + const baseUri = URI.from({ scheme, path: '/ancestor' }); + const input1Uri = URI.from({ scheme, path: '/input1' }); + const input2Uri = URI.from({ scheme, path: '/input2' }); + const resultUri = URI.from({ scheme, path: '/result' }); + + function writeFile(uri: URI, content: string): Promise { + return provider!.writeFile(uri, VSBuffer.fromString(content).buffer, { create: true, overwrite: true, unlock: true }); + } + + await Promise.all([ + writeFile(baseUri, content.base), + writeFile(input1Uri, content.input1), + writeFile(input2Uri, content.input2), + writeFile(resultUri, content.result), + ]); + + async function setLanguageId(uri: URI, languageId: string): Promise { + const ref = await textModelService.createModelReference(uri); + ref.object.textEditorModel.setMode(languageId); + ref.dispose(); + } + + await Promise.all([ + setLanguageId(baseUri, content.languageId), + setLanguageId(input1Uri, content.languageId), + setLanguageId(input2Uri, content.languageId), + setLanguageId(resultUri, content.languageId), + ]); + + const input = instaService.createInstance( + MergeEditorInput, + baseUri, + { uri: input1Uri, description: 'Input 1', detail: '(from JSON)' }, + { uri: input2Uri, description: 'Input 2', detail: '(from JSON)' }, + resultUri, + ); + editorService.openEditor(input); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts index 1b3bd8141bc..be139f7608b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.contribution.ts @@ -3,28 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { localize } from 'vs/nls'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; +import { registerAction2 } from 'vs/platform/actions/common/actions'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; import { EditorExtensions, IEditorFactoryRegistry } from 'vs/workbench/common/editor'; -import { ctxIsMergeEditor, ctxUsesColumnLayout, MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditor'; -import { MergeEditorInput, MergeEditorInputData } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { OpenMergeEditor, ToggleLayout } from 'vs/workbench/contrib/mergeEditor/browser/commands/commands'; +import { MergeEditorCopyContentsToJSON, MergeEditorOpenContents } from 'vs/workbench/contrib/mergeEditor/browser/commands/devCommands'; +import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; +import { MergeEditor } from 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor'; import { MergeEditorSerializer } from './mergeEditorSerializer'; -import { Codicon } from 'vs/base/common/codicons'; -import { IWorkbenchFileService } from 'vs/workbench/services/files/common/files'; -import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; -import { VSBuffer } from 'vs/base/common/buffer'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import './colors'; Registry.as(EditorExtensions.EditorPane).registerEditorPane( EditorPaneDescriptor.create( @@ -42,250 +31,8 @@ Registry.as(EditorExtensions.EditorFactory).registerEdit MergeEditorSerializer ); -registerAction2(class ToggleLayout extends Action2 { +registerAction2(ToggleLayout); +registerAction2(OpenMergeEditor); - constructor() { - super({ - id: 'merge.toggleLayout', - title: localize('toggle.title', "Switch to column view"), - icon: Codicon.layoutCentered, - toggled: { - condition: ctxUsesColumnLayout, - icon: Codicon.layoutPanel, - title: localize('toggle.title2', "Switch to 2 by 1 view"), - }, - menu: [{ - id: MenuId.EditorTitle, - when: ctxIsMergeEditor, - group: 'navigation' - }] - }); - } - - run(accessor: ServicesAccessor): void { - const { activeEditorPane } = accessor.get(IEditorService); - if (activeEditorPane instanceof MergeEditor) { - activeEditorPane.toggleLayout(); - } - } -}); - -registerAction2(class Open extends Action2 { - - constructor() { - super({ - id: '_open.mergeEditor', - title: localize('title', "Open Merge Editor"), - }); - } - run(accessor: ServicesAccessor, ...args: any[]): void { - const validatedArgs = IRelaxedOpenArgs.validate(args[0]); - - const instaService = accessor.get(IInstantiationService); - const input = instaService.createInstance( - MergeEditorInput, - validatedArgs.ancestor, - validatedArgs.input1, - validatedArgs.input2, - validatedArgs.output, - ); - accessor.get(IEditorService).openEditor(input); - } - -}); - -namespace IRelaxedOpenArgs { - function toUri(obj: unknown): URI { - if (typeof obj === 'string') { - return URI.parse(obj, true); - } else if (obj && typeof obj === 'object') { - return URI.revive(obj); - } - throw new TypeError('invalid argument'); - } - - function isUriComponents(obj: unknown): obj is UriComponents { - if (!obj || typeof obj !== 'object') { - return false; - } - return typeof (obj).scheme === 'string' - && typeof (obj).authority === 'string' - && typeof (obj).path === 'string' - && typeof (obj).query === 'string' - && typeof (obj).fragment === 'string'; - } - - function toInputResource(obj: unknown): MergeEditorInputData { - if (typeof obj === 'string') { - return new MergeEditorInputData(URI.parse(obj, true), undefined, undefined); - } - if (!obj || typeof obj !== 'object') { - throw new TypeError('invalid argument'); - } - - if (isUriComponents(obj)) { - return new MergeEditorInputData(URI.revive(obj), undefined, undefined); - } - - const uri = toUri((obj).uri); - const detail = (obj).detail; - const description = (obj).description; - return new MergeEditorInputData(uri, detail, description); - } - - export function validate(obj: unknown): IOpenEditorArgs { - if (!obj || typeof obj !== 'object') { - throw new TypeError('invalid argument'); - } - const ancestor = toUri((obj).ancestor); - const output = toUri((obj).output); - const input1 = toInputResource((obj).input1); - const input2 = toInputResource((obj).input2); - return { ancestor, input1, input2, output }; - } -} - -type IRelaxedInputData = { uri: UriComponents; detail?: string; description?: string }; - -type IRelaxedOpenArgs = { - ancestor: UriComponents | string; - input1: IRelaxedInputData | string; - input2: IRelaxedInputData | string; - output: UriComponents | string; -}; - -interface IOpenEditorArgs { - ancestor: URI; - input1: MergeEditorInputData; - input2: MergeEditorInputData; - output: URI; -} - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'merge.dev.copyContents', - title: localize('merge.dev.copyContents', "Developer Merge Editor: Copy Contents of Inputs, Base and Result as JSON"), - icon: Codicon.layoutCentered, - f1: true, - }); - } - - run(accessor: ServicesAccessor): void { - const { activeEditorPane } = accessor.get(IEditorService); - const clipboardService = accessor.get(IClipboardService); - const notificationService = accessor.get(INotificationService); - - if (!(activeEditorPane instanceof MergeEditor)) { - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.noActiveMergeEditor', "No active merge editor") - }); - return; - } - const model = activeEditorPane.model; - if (!model) { - return; - } - const contents: MergeEditorContents = { - languageId: model.result.getLanguageId(), - base: model.base.getValue(), - input1: model.input1.getValue(), - input2: model.input2.getValue(), - result: model.result.getValue(), - }; - const jsonStr = JSON.stringify(contents, undefined, 4); - clipboardService.writeText(jsonStr); - - notificationService.info({ - name: localize('mergeEditor.name', 'Merge Editor'), - message: localize('mergeEditor.successfullyCopiedMergeEditorContents', "Successfully copied merge editor contents"), - }); - } -}); - -registerAction2(class extends Action2 { - - constructor() { - super({ - id: 'merge.dev.openContents', - title: localize('merge.dev.openContents', "Developer Merge Editor: Open Contents of Inputs, Base and Result from JSON"), - icon: Codicon.layoutCentered, - f1: true, - }); - } - - async run(accessor: ServicesAccessor): Promise { - const service = accessor.get(IWorkbenchFileService); - const instaService = accessor.get(IInstantiationService); - const editorService = accessor.get(IEditorService); - const inputService = accessor.get(IQuickInputService); - const clipboardService = accessor.get(IClipboardService); - const textModelService = accessor.get(ITextModelService); - - const result = await inputService.input({ - prompt: localize('mergeEditor.enterJSON', 'Enter JSON'), - value: await clipboardService.readText(), - }); - if (!result) { - return; - } - - const content: MergeEditorContents = JSON.parse(result); - - const scheme = 'merge-editor-dev'; - - let provider = service.getProvider(scheme) as InMemoryFileSystemProvider | undefined; - if (!provider) { - provider = new InMemoryFileSystemProvider(); - service.registerProvider(scheme, provider); - } - - const baseUri = URI.from({ scheme, path: '/ancestor' }); - const input1Uri = URI.from({ scheme, path: '/input1' }); - const input2Uri = URI.from({ scheme, path: '/input2' }); - const resultUri = URI.from({ scheme, path: '/result' }); - - function writeFile(uri: URI, content: string): Promise { - return provider!.writeFile(uri, VSBuffer.fromString(content).buffer, { create: true, overwrite: true, unlock: true }); - } - - await Promise.all([ - writeFile(baseUri, content.base), - writeFile(input1Uri, content.input1), - writeFile(input2Uri, content.input2), - writeFile(resultUri, content.result), - ]); - - async function setLanguageId(uri: URI, languageId: string): Promise { - const ref = await textModelService.createModelReference(uri); - ref.object.textEditorModel.setMode(languageId); - ref.dispose(); - } - - await Promise.all([ - setLanguageId(baseUri, content.languageId), - setLanguageId(input1Uri, content.languageId), - setLanguageId(input2Uri, content.languageId), - setLanguageId(resultUri, content.languageId), - ]); - - const input = instaService.createInstance( - MergeEditorInput, - baseUri, - { uri: input1Uri, description: 'Input 1', detail: '(from JSON)' }, - { uri: input2Uri, description: 'Input 2', detail: '(from JSON)' }, - resultUri, - ); - editorService.openEditor(input); - } -}); - -interface MergeEditorContents { - languageId: string; - base: string; - input1: string; - input2: string; - result: string; -} +registerAction2(MergeEditorCopyContentsToJSON); +registerAction2(MergeEditorOpenContents); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts index efa8b0360e0..b96047e2cfd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts @@ -14,7 +14,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IUntypedEditorInput, EditorInputCapabilities } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { AbstractTextResourceEditorInput } from 'vs/workbench/common/editor/textResourceEditorInput'; -import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model.ts b/src/vs/workbench/contrib/mergeEditor/browser/model.ts deleted file mode 100644 index 21bb807161c..00000000000 --- a/src/vs/workbench/contrib/mergeEditor/browser/model.ts +++ /dev/null @@ -1,524 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Comparator, compareBy, equals, numberComparator } from 'vs/base/common/arrays'; -import { BugIndicatingError } from 'vs/base/common/errors'; -import { Range } from 'vs/editor/common/core/range'; -import { ILineChange } from 'vs/editor/common/diff/diffComputer'; -import { ITextModel } from 'vs/editor/common/model'; - -/** - * Represents an edit, expressed in whole lines: - * At {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. -*/ -export class LineEdit { - constructor( - public readonly range: LineRange, - public readonly newLines: string[] - ) { } - - public equals(other: LineEdit): boolean { - return this.range.equals(other.range) && equals(this.newLines, other.newLines); - } - - public apply(model: ITextModel): void { - new LineEdits([this]).apply(model); - } -} - -export class LineEdits { - constructor(public readonly edits: readonly LineEdit[]) { } - - public apply(model: ITextModel): void { - model.pushEditOperations( - null, - this.edits.map((e) => { - if (e.range.endLineNumberExclusive <= model.getLineCount()) { - return { - range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), - text: e.newLines.map(s => s + '\n').join(''), - }; - } - - if (e.range.startLineNumber === 1) { - return { - range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.join('\n'), - }; - } - - return { - range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), - text: e.newLines.map(s => '\n' + s).join(''), - }; - }), - () => null - ); - } -} - -export class LineRange { - public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); - - public static join(ranges: LineRange[]): LineRange | undefined { - if (ranges.length === 0) { - return undefined; - } - - let startLineNumber = Number.MAX_SAFE_INTEGER; - let endLineNumber = 0; - for (const range of ranges) { - startLineNumber = Math.min(startLineNumber, range.startLineNumber); - endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); - } - return new LineRange(startLineNumber, endLineNumber - startLineNumber); - } - - constructor( - public readonly startLineNumber: number, - public readonly lineCount: number - ) { - if (lineCount < 0) { - throw new BugIndicatingError(); - } - } - - public join(other: LineRange): LineRange { - return new LineRange(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - this.startLineNumber); - } - - public get endLineNumberExclusive(): number { - return this.startLineNumber + this.lineCount; - } - - public get isEmpty(): boolean { - return this.lineCount === 0; - } - - /** - * Returns false if there is at least one line between `this` and `other`. - */ - public touches(other: LineRange): boolean { - return ( - this.endLineNumberExclusive >= other.startLineNumber && - other.endLineNumberExclusive >= this.startLineNumber - ); - } - - public isAfter(modifiedRange: LineRange): boolean { - return this.startLineNumber >= modifiedRange.endLineNumberExclusive; - } - - public delta(lineDelta: number): LineRange { - return new LineRange(this.startLineNumber + lineDelta, this.lineCount); - } - - public toString() { - return `[${this.startLineNumber},${this.endLineNumberExclusive})`; - } - - public equals(originalRange: LineRange) { - return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; - } - - public contains(lineNumber: number): boolean { - return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; - } - - public deltaEnd(delta: number): LineRange { - return new LineRange(this.startLineNumber, this.lineCount + delta); - } - - public getLines(model: ITextModel): string[] { - const result = new Array(this.lineCount); - for (let i = 0; i < this.lineCount; i++) { - result[i] = model.getLineContent(this.startLineNumber + i); - } - return result; - } -} - -export class LineDiff { - public static fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): LineDiff { - let originalRange: LineRange; - if (lineChange.originalEndLineNumber === 0) { - // Insertion - originalRange = new LineRange(lineChange.originalStartLineNumber + 1, 0); - } else { - originalRange = new LineRange(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1); - } - - let modifiedRange: LineRange; - if (lineChange.modifiedEndLineNumber === 0) { - // Insertion - modifiedRange = new LineRange(lineChange.modifiedStartLineNumber + 1, 0); - } else { - modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); - } - - return new LineDiff( - originalTextModel, - originalRange, - modifiedTextModel, - modifiedRange, - ); - } - - public static hull(lineDiffs: readonly LineDiff[]): LineDiff | undefined { - if (lineDiffs.length === 0) { - return undefined; - } - - return new LineDiff( - lineDiffs[0].originalTextModel, - LineRange.join(lineDiffs.map((d) => d.originalRange))!, - lineDiffs[0].modifiedTextModel, - LineRange.join(lineDiffs.map((d) => d.modifiedRange))!, - ); - } - - public static alignOriginalRange(lineDiffs: readonly LineDiff[]): LineDiff[] { - if (lineDiffs.length === 0) { - return []; - } - const originalRange = LineRange.join(lineDiffs.map((d) => d.originalRange))!; - return lineDiffs.map(l => { - const startDelta = originalRange.startLineNumber - l.originalRange.startLineNumber; - const endDelta = originalRange.endLineNumberExclusive - l.originalRange.endLineNumberExclusive; - return new LineDiff( - l.originalTextModel, - originalRange, - l.modifiedTextModel, - new LineRange( - l.modifiedRange.startLineNumber + startDelta, - l.modifiedRange.lineCount - startDelta + endDelta - ) - ); - }); - } - - constructor( - public readonly originalTextModel: ITextModel, - public readonly originalRange: LineRange, - public readonly modifiedTextModel: ITextModel, - public readonly modifiedRange: LineRange, - ) { - } - - public get resultingDeltaFromOriginalToModified(): number { - return this.modifiedRange.endLineNumberExclusive - this.originalRange.endLineNumberExclusive; - } - - private ensureSameOriginalModel(other: LineDiff): void { - if (this.originalTextModel !== other.originalTextModel) { - // Both changes must refer to the same original model - throw new BugIndicatingError(); - } - } - - public conflicts(other: LineDiff): boolean { - this.ensureSameOriginalModel(other); - return this.originalRange.touches(other.originalRange); - } - - public isStrictBefore(other: LineDiff): boolean { - this.ensureSameOriginalModel(other); - return this.originalRange.endLineNumberExclusive <= other.originalRange.startLineNumber; - } - - public getLineEdit(): LineEdit { - return new LineEdit( - this.originalRange, - this.getModifiedLines() - ); - } - - public getReverseLineEdit(): LineEdit { - return new LineEdit( - this.modifiedRange, - this.getOriginalLines() - ); - } - - private getModifiedLines(): string[] { - return this.modifiedRange.getLines(this.modifiedTextModel); - } - - private getOriginalLines(): string[] { - return this.originalRange.getLines(this.originalTextModel); - } -} - - -/** - * Describes modifications in input 1 and input 2 for a specific range in base. - * - * The UI offers a mechanism to either apply all changes from input 1 or input 2 or both. - * - * Immutable. -*/ -export class ModifiedBaseRange { - /** - * diffs1 and diffs2 together with the conflict relation form a bipartite graph. - * This method computes strongly connected components of that graph while maintaining the side of each diff. - */ - public static fromDiffs( - baseTextModel: ITextModel, - input1TextModel: ITextModel, - diffs1: readonly LineDiff[], - input2TextModel: ITextModel, - diffs2: readonly LineDiff[] - ): ModifiedBaseRange[] { - const compareByStartLineNumber = compareBy( - (d) => d.originalRange.startLineNumber, - numberComparator - ); - - const diffs = diffs1 - .map((diff) => ({ source: 0 as 0 | 1, diff })) - .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); - - diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); - - const currentDiffs = [ - new Array(), - new Array(), - ]; - const deltaFromBaseToInput = [0, 0]; - - const result = new Array(); - - function pushAndReset() { - if (currentDiffs[0].length === 0 && currentDiffs[1].length === 0) { - return; - } - result.push(new ModifiedBaseRange( - baseTextModel, - input1TextModel, - currentDiffs[0], - deltaFromBaseToInput[0], - input2TextModel, - currentDiffs[1], - deltaFromBaseToInput[1], - )); - currentDiffs[0] = []; - currentDiffs[1] = []; - } - - let currentRange: LineRange | undefined; - - for (const diff of diffs) { - const range = diff.diff.originalRange; - if (currentRange && !currentRange.touches(range)) { - pushAndReset(); - } - deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; - currentRange = currentRange ? currentRange.join(range) : range; - currentDiffs[diff.source].push(diff.diff); - } - pushAndReset(); - - return result; - } - - public readonly input1CombinedDiff = LineDiff.hull(this.input1Diffs); - public readonly input2CombinedDiff = LineDiff.hull(this.input2Diffs); - - public readonly baseRange: LineRange; - public readonly input1Range: LineRange; - public readonly input2Range: LineRange; - - constructor( - public readonly baseTextModel: ITextModel, - public readonly input1TextModel: ITextModel, - public readonly input1Diffs: readonly LineDiff[], - input1DeltaLineCount: number, - public readonly input2TextModel: ITextModel, - public readonly input2Diffs: readonly LineDiff[], - input2DeltaLineCount: number, - ) { - if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { - throw new BugIndicatingError('must have at least one diff'); - } - - const input1Diff = - this.input1CombinedDiff || - new LineDiff( - baseTextModel, - this.input2CombinedDiff!.originalRange, - input1TextModel, - this.input2CombinedDiff!.originalRange.delta(input1DeltaLineCount) - ); - - const input2Diff = - this.input2CombinedDiff || - new LineDiff( - baseTextModel, - this.input1CombinedDiff!.originalRange, - input1TextModel, - this.input1CombinedDiff!.originalRange.delta(input2DeltaLineCount) - ); - - const results = LineDiff.alignOriginalRange([input1Diff, input2Diff]); - this.baseRange = results[0].originalRange; - this.input1Range = results[0].modifiedRange; - this.input2Range = results[1].modifiedRange; - } - - public getInputRange(inputNumber: 1 | 2): LineRange { - return inputNumber === 1 ? this.input1Range : this.input2Range; - } - - public getInputDiffs(inputNumber: 1 | 2): readonly LineDiff[] { - return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; - } - - public get isConflicting(): boolean { - return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; - } -} - -export class ModifiedBaseRangeState { - public static readonly default = new ModifiedBaseRangeState(false, false, false, false); - public static readonly conflicting = new ModifiedBaseRangeState(false, false, false, true); - - private constructor( - public readonly input1: boolean, - public readonly input2: boolean, - public readonly input2First: boolean, - public readonly conflicting: boolean, - ) { } - - public getInput(inputNumber: 1 | 2): boolean | undefined { - if (this.conflicting) { - return undefined; - } - if (inputNumber === 1) { - return this.input1; - } else { - return this.input2; - } - } - - public withInputValue(inputNumber: 1 | 2, value: boolean): ModifiedBaseRangeState { - return inputNumber === 1 ? this.withInput1(value) : this.withInput2(value); - } - - public withInput1(value: boolean): ModifiedBaseRangeState { - return new ModifiedBaseRangeState( - value, - this.input2, - value !== this.input2 ? this.input2 : this.input2First, - false, - ); - } - - public withInput2(value: boolean): ModifiedBaseRangeState { - return new ModifiedBaseRangeState( - this.input1, - value, - value !== this.input1 ? value : this.input2First, - false - ); - } - - public toggleInput1(): ModifiedBaseRangeState { - return this.withInput1(!this.input1); - } - - public toggleInput2(): ModifiedBaseRangeState { - return this.withInput2(!this.input2); - } - - public get isEmpty(): boolean { - return !this.input1 && !this.input2; - } - - public toString(): string { - const arr: ('1' | '2')[] = []; - if (this.input1) { - arr.push('1'); - } - if (this.input2) { - arr.push('2'); - } - if (this.input2First) { - arr.reverse(); - } - return arr.join(','); - } -} - -/* -export class LineMappings { - public static fromDiffs( - diffs1: readonly LineDiff[], - diffs2: readonly LineDiff[], - inputLineCount: number, - ): LineMappings { - const compareByStartLineNumber = compareBy( - (d) => d.originalRange.startLineNumber, - numberComparator - ); - - const diffs = diffs1 - .map((diff) => ({ source: 0 as 0 | 1, diff })) - .concat(diffs2.map((diff) => ({ source: 1 as const, diff }))); - - diffs.sort(compareBy(d => d.diff, compareByStartLineNumber)); - - const currentDiffs = [ - new Array(), - new Array(), - ]; - let deltaFromBaseToInput = [0, 0]; - - const result = new Array(); - - function pushAndReset() { - result.push(LineMapping.create( - baseTextModel, - input1TextModel, - currentDiffs[0], - deltaFromBaseToInput[0], - input2TextModel, - currentDiffs[1], - deltaFromBaseToInput[1], - )); - currentDiffs[0] = []; - currentDiffs[1] = []; - } - - let currentRange: LineRange | undefined; - - for (const diff of diffs) { - const range = diff.diff.originalRange; - if (currentRange && !currentRange.touches(range)) { - pushAndReset(); - } - deltaFromBaseToInput[diff.source] = diff.diff.resultingDeltaFromOriginalToModified; - currentRange = currentRange ? currentRange.join(range) : range; - currentDiffs[diff.source].push(diff.diff); - } - pushAndReset(); - - return result; - } - - constructor(private readonly lineMappings: LineMapping[]) {} -} - -// A lightweight ModifiedBaseRange. Maybe they can be united? -export class LineMapping { - public static create(input: LineDiff, ): LineMapping { - - } - - constructor( - public readonly inputRange: LineRange, - public readonly resultRange: LineRange - ) { } -} -*/ diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts new file mode 100644 index 00000000000..452443bb0aa --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts @@ -0,0 +1,162 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ICharChange, IDiffComputationResult, ILineChange } from 'vs/editor/common/diff/diffComputer'; +import { ITextModel } from 'vs/editor/common/model'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; +import { DetailedLineRangeMapping, RangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; + +export interface IDiffComputer { + computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; +} + +export interface IDiffComputerResult { + diffs: DetailedLineRangeMapping[] | null; +} + +export class EditorWorkerServiceDiffComputer implements IDiffComputer { + constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { } + + async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise { + const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000); + if (!diffs || diffs.quitEarly) { + return { diffs: null }; + } + return { diffs: EditorWorkerServiceDiffComputer.fromDiffComputationResult(diffs, textModel1, textModel2) }; + } + + public static fromDiffComputationResult(result: IDiffComputationResult, textModel1: ITextModel, textModel2: ITextModel): DetailedLineRangeMapping[] { + return result.changes.map((c) => fromLineChange(c, textModel1, textModel2)); + } +} + +function fromLineChange(lineChange: ILineChange, originalTextModel: ITextModel, modifiedTextModel: ITextModel): DetailedLineRangeMapping { + let originalRange: LineRange; + if (lineChange.originalEndLineNumber === 0) { + // Insertion + originalRange = new LineRange(lineChange.originalStartLineNumber + 1, 0); + } else { + originalRange = new LineRange(lineChange.originalStartLineNumber, lineChange.originalEndLineNumber - lineChange.originalStartLineNumber + 1); + } + + let modifiedRange: LineRange; + if (lineChange.modifiedEndLineNumber === 0) { + // Deletion + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber + 1, 0); + } else { + modifiedRange = new LineRange(lineChange.modifiedStartLineNumber, lineChange.modifiedEndLineNumber - lineChange.modifiedStartLineNumber + 1); + } + + let innerDiffs = lineChange.charChanges?.map(c => rangeMappingFromCharChange(c, originalTextModel, modifiedTextModel)); + if (!innerDiffs) { + innerDiffs = [rangeMappingFromLineRanges(originalRange, modifiedRange)]; + } + + return new DetailedLineRangeMapping( + originalRange, + originalTextModel, + modifiedRange, + modifiedTextModel, + innerDiffs + ); +} + +function rangeMappingFromLineRanges(originalRange: LineRange, modifiedRange: LineRange): RangeMapping { + return new RangeMapping( + new Range( + originalRange.startLineNumber, + 1, + originalRange.endLineNumberExclusive, + 1, + ), + new Range( + modifiedRange.startLineNumber, + 1, + modifiedRange.endLineNumberExclusive, + 1, + ) + ); +} + +function rangeMappingFromCharChange(charChange: ICharChange, inputTextModel: ITextModel, modifiedTextModel: ITextModel): RangeMapping { + return normalizeRangeMapping(new RangeMapping( + new Range(charChange.originalStartLineNumber, charChange.originalStartColumn, charChange.originalEndLineNumber, charChange.originalEndColumn), + new Range(charChange.modifiedStartLineNumber, charChange.modifiedStartColumn, charChange.modifiedEndLineNumber, charChange.modifiedEndColumn) + ), inputTextModel, modifiedTextModel); +} + +function normalizeRangeMapping(rangeMapping: RangeMapping, inputTextModel: ITextModel, outputTextModel: ITextModel): RangeMapping { + const inputRangeEmpty = rangeMapping.inputRange.isEmpty(); + const outputRangeEmpty = rangeMapping.outputRange.isEmpty(); + + if (inputRangeEmpty && outputRangeEmpty) { + throw new BugIndicatingError(); // This case makes no sense, but it is an edge case we need to rule out + } + + const originalStartsAtEndOfLine = isAtEndOfLine(rangeMapping.inputRange.startLineNumber, rangeMapping.inputRange.startColumn, inputTextModel); + const modifiedStartsAtEndOfLine = isAtEndOfLine(rangeMapping.outputRange.startLineNumber, rangeMapping.outputRange.startColumn, outputTextModel); + + if (!inputRangeEmpty && !outputRangeEmpty && originalStartsAtEndOfLine && modifiedStartsAtEndOfLine) { + // a b c [\n] x y z \n + // d e f [\n a] \n + // -> + // a b c \n [] x y z \n + // d e f \n [a] \n + + return new RangeMapping( + rangeMapping.inputRange.setStartPosition(rangeMapping.inputRange.startLineNumber + 1, 1), + + rangeMapping.outputRange.setStartPosition(rangeMapping.outputRange.startLineNumber + 1, 1), + ); + } + + if ( + modifiedStartsAtEndOfLine && + originalStartsAtEndOfLine && + ((inputRangeEmpty && rangeEndsAtEndOfLine(rangeMapping.outputRange, outputTextModel)) || + (outputRangeEmpty && rangeEndsAtEndOfLine(rangeMapping.inputRange, inputTextModel))) + ) { + // o: a b c [] \n x y z \n + // m: d e f [\n a] \n + // -> + // o: a b c \n [] x y z \n + // m: d e f \n [a \n] + + // or + + // a b c [\n x y z] \n + // d e f [] \n a \n + // -> + // a b c \n [x y z \n] + // d e f \n [] a \n + + return new RangeMapping( + moveRange(rangeMapping.inputRange), + moveRange(rangeMapping.outputRange) + ); + } + + return rangeMapping; +} + +function isAtEndOfLine(lineNumber: number, column: number, model: ITextModel): boolean { + return column >= model.getLineMaxColumn(lineNumber); +} + +function rangeEndsAtEndOfLine(range: Range, model: ITextModel,): boolean { + return isAtEndOfLine(range.endLineNumber, range.endColumn, model); +} + +function moveRange(range: Range): Range { + return new Range( + range.startLineNumber + 1, + 1, + range.endLineNumber + 1, + 1, + ); +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts new file mode 100644 index 00000000000..de2e9e2c6df --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/editing.ts @@ -0,0 +1,70 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/common/arrays'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { LineRange } from './lineRange'; + +/** + * Represents an edit, expressed in whole lines: + * At (before) {@link LineRange.startLineNumber}, delete {@link LineRange.lineCount} many lines and insert {@link newLines}. +*/ +export class LineRangeEdit { + constructor( + public readonly range: LineRange, + public readonly newLines: string[] + ) { } + + public equals(other: LineRangeEdit): boolean { + return this.range.equals(other.range) && equals(this.newLines, other.newLines); + } + + public apply(model: ITextModel): void { + new LineEdits([this]).apply(model); + } +} + +export class RangeEdit { + constructor( + public readonly range: Range, + public readonly newText: string + ) { } + + public equals(other: RangeEdit): boolean { + return Range.equalsRange(this.range, other.range) && this.newText === other.newText; + } +} + +export class LineEdits { + constructor(public readonly edits: readonly LineRangeEdit[]) { } + + public apply(model: ITextModel): void { + model.pushEditOperations( + null, + this.edits.map((e) => { + if (e.range.endLineNumberExclusive <= model.getLineCount()) { + return { + range: new Range(e.range.startLineNumber, 1, e.range.endLineNumberExclusive, 1), + text: e.newLines.map(s => s + '\n').join(''), + }; + } + + if (e.range.startLineNumber === 1) { + return { + range: new Range(1, 1, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.join('\n'), + }; + } + + return { + range: new Range(e.range.startLineNumber - 1, Number.MAX_SAFE_INTEGER, model.getLineCount(), Number.MAX_SAFE_INTEGER), + text: e.newLines.map(s => '\n' + s).join(''), + }; + }), + () => null + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts new file mode 100644 index 00000000000..57730125f09 --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/lineRange.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Comparator, compareBy, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; + +export class LineRange { + public static readonly compareByStart: Comparator = compareBy(l => l.startLineNumber, numberComparator); + + public static join(ranges: LineRange[]): LineRange | undefined { + if (ranges.length === 0) { + return undefined; + } + + let startLineNumber = Number.MAX_SAFE_INTEGER; + let endLineNumber = 0; + for (const range of ranges) { + startLineNumber = Math.min(startLineNumber, range.startLineNumber); + endLineNumber = Math.max(endLineNumber, range.startLineNumber + range.lineCount); + } + return new LineRange(startLineNumber, endLineNumber - startLineNumber); + } + + static fromLineNumbers(startLineNumber: number, endExclusiveLineNumber: number): LineRange { + return new LineRange(startLineNumber, endExclusiveLineNumber - startLineNumber); + } + + constructor( + public readonly startLineNumber: number, + public readonly lineCount: number + ) { + if (lineCount < 0) { + throw new BugIndicatingError(); + } + } + + public join(other: LineRange): LineRange { + return new LineRange(Math.min(this.startLineNumber, other.startLineNumber), Math.max(this.endLineNumberExclusive, other.endLineNumberExclusive) - this.startLineNumber); + } + + public get endLineNumberExclusive(): number { + return this.startLineNumber + this.lineCount; + } + + public get isEmpty(): boolean { + return this.lineCount === 0; + } + + /** + * Returns false if there is at least one line between `this` and `other`. + */ + public touches(other: LineRange): boolean { + return ( + this.endLineNumberExclusive >= other.startLineNumber && + other.endLineNumberExclusive >= this.startLineNumber + ); + } + + public isAfter(modifiedRange: LineRange): boolean { + return this.startLineNumber >= modifiedRange.endLineNumberExclusive; + } + + public delta(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount); + } + + public toString() { + return `[${this.startLineNumber},${this.endLineNumberExclusive})`; + } + + public equals(originalRange: LineRange) { + return this.startLineNumber === originalRange.startLineNumber && this.lineCount === originalRange.lineCount; + } + + public contains(lineNumber: number): boolean { + return this.startLineNumber <= lineNumber && lineNumber < this.endLineNumberExclusive; + } + + public deltaEnd(delta: number): LineRange { + return new LineRange(this.startLineNumber, this.lineCount + delta); + } + + public deltaStart(lineDelta: number): LineRange { + return new LineRange(this.startLineNumber + lineDelta, this.lineCount - lineDelta); + } + + public getLines(model: ITextModel): string[] { + const result = new Array(this.lineCount); + for (let i = 0; i < this.lineCount; i++) { + result[i] = model.getLineContent(this.startLineNumber + i); + } + return result; + } + + public containsRange(range: LineRange): boolean { + return this.startLineNumber <= range.startLineNumber && range.endLineNumberExclusive <= this.endLineNumberExclusive; + } + + public toRange(): Range { + return new Range(this.startLineNumber, 1, this.endLineNumberExclusive, 1); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts new file mode 100644 index 00000000000..cfbd5cc48ee --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mapping.ts @@ -0,0 +1,268 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { compareBy, findLast, numberComparator } from 'vs/base/common/arrays'; +import { BugIndicatingError } from 'vs/base/common/errors'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel } from 'vs/editor/common/model'; +import { concatArrays } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { LineRange } from './lineRange'; +import { LineRangeEdit } from './editing'; + +export class LineRangeMapping { + public static join(mappings: readonly LineRangeMapping[]): LineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); + } + + constructor( + public readonly inputRange: LineRange, + public readonly outputRange: LineRange + ) { } + + public extendInputRange(extendedInputRange: LineRange): LineRangeMapping { + if (!extendedInputRange.containsRange(this.inputRange)) { + throw new BugIndicatingError(); + } + + const startDelta = extendedInputRange.startLineNumber - this.inputRange.startLineNumber; + const endDelta = extendedInputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + return new LineRangeMapping( + extendedInputRange, + new LineRange( + this.outputRange.startLineNumber + startDelta, + this.outputRange.lineCount - startDelta + endDelta + ) + ); + } + + public join(other: LineRangeMapping): LineRangeMapping { + return new LineRangeMapping( + this.inputRange.join(other.inputRange), + this.outputRange.join(other.outputRange) + ); + } + + public get resultingDeltaFromOriginalToModified(): number { + return this.outputRange.endLineNumberExclusive - this.inputRange.endLineNumberExclusive; + } + + public toString(): string { + return `${this.inputRange.toString()} -> ${this.outputRange.toString()}`; + } + + public addOutputLineDelta(delta: number): LineRangeMapping { + return new LineRangeMapping( + this.inputRange, + this.outputRange.delta(delta) + ); + } + + public getRange(direction: MappingDirection): LineRange { + return direction === MappingDirection.input ? this.inputRange : this.outputRange; + } +} + +export function getOppositeDirection(direction: MappingDirection): MappingDirection { + return direction === MappingDirection.input ? MappingDirection.output : MappingDirection.input; +} + +export const enum MappingDirection { + input = 0, + output = 1, +} + +export class MappingAlignment { + public static compute( + fromBaseToInput1: readonly T[], + fromBaseToInput2: readonly T[] + ): MappingAlignment[] { + const compareByStartLineNumber = compareBy( + (d) => d.inputRange.startLineNumber, + numberComparator + ); + + const combinedDiffs = concatArrays( + fromBaseToInput1.map((diff) => ({ source: 0 as const, diff })), + fromBaseToInput2.map((diff) => ({ source: 1 as const, diff })) + ).sort(compareBy((d) => d.diff, compareByStartLineNumber)); + + const currentDiffs = [new Array(), new Array()]; + const deltaFromBaseToInput = [0, 0]; + + const alignments = new Array>(); + + function pushAndReset(baseRange: LineRange) { + const mapping1 = LineRangeMapping.join(currentDiffs[0]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[0])); + const mapping2 = LineRangeMapping.join(currentDiffs[1]) || new LineRangeMapping(baseRange, baseRange.delta(deltaFromBaseToInput[1])); + + alignments.push( + new MappingAlignment( + currentInputRange!, + mapping1.extendInputRange(currentInputRange!).outputRange, + currentDiffs[0], + mapping2.extendInputRange(currentInputRange!).outputRange, + currentDiffs[1] + ) + ); + currentDiffs[0] = []; + currentDiffs[1] = []; + } + + let currentInputRange: LineRange | undefined; + + for (const diff of combinedDiffs) { + const range = diff.diff.inputRange; + if (currentInputRange && !currentInputRange.touches(range)) { + pushAndReset(currentInputRange); + currentInputRange = undefined; + } + deltaFromBaseToInput[diff.source] = + diff.diff.resultingDeltaFromOriginalToModified; + currentInputRange = currentInputRange ? currentInputRange.join(range) : range; + currentDiffs[diff.source].push(diff.diff); + } + if (currentInputRange) { + pushAndReset(currentInputRange); + } + + return alignments; + } + + constructor( + public readonly baseRange: LineRange, + public readonly input1Range: LineRange, + public readonly input1LineMappings: T[], + public readonly input2Range: LineRange, + public readonly input2LineMappings: T[], + ) { + } + + toString(): string { + return `${this.input1Range} <- ${this.baseRange} -> ${this.input2Range}`; + } +} + +export class DocumentMapping { + public static betweenOutputs( + inputToOutput1: readonly LineRangeMapping[], + inputToOutput2: readonly LineRangeMapping[], + inputLineCount: number + ): DocumentMapping { + const alignments = MappingAlignment.compute(inputToOutput1, inputToOutput2); + const mappings = alignments.map((m) => new LineRangeMapping(m.input1Range, m.input2Range)); + return new DocumentMapping(mappings, inputLineCount); + } + + public getMappingContaining(lineNumber: number, containingDirection: MappingDirection): LineRangeMapping { + const mapTo = getOppositeDirection(containingDirection); + const lastBefore = findLast(this.lineRangeMappings, r => r.getRange(containingDirection).startLineNumber <= lineNumber); + if (lastBefore) { + if (lastBefore.getRange(containingDirection).contains(lineNumber)) { + return lastBefore; + } + const containingRange = new LineRange(lineNumber, 1); + const mappedRange = new LineRange( + lineNumber + + lastBefore.getRange(mapTo).endLineNumberExclusive - + lastBefore.getRange(containingDirection).endLineNumberExclusive, + 1 + ); + + return containingDirection === MappingDirection.input + ? new LineRangeMapping(containingRange, mappedRange) + : new LineRangeMapping(mappedRange, containingRange); + } + return new LineRangeMapping( + new LineRange(lineNumber, 1), + new LineRange(lineNumber, 1) + ); + } + + constructor( + public readonly lineRangeMappings: LineRangeMapping[], + public readonly inputLineCount: number + ) { } +} + +export class DetailedLineRangeMapping extends LineRangeMapping { + public static override join(mappings: readonly DetailedLineRangeMapping[]): DetailedLineRangeMapping | undefined { + return mappings.reduce((acc, cur) => acc ? acc.join(cur) : cur, undefined); + } + + public readonly rangeMappings: readonly RangeMapping[]; + + constructor( + inputRange: LineRange, + public readonly inputTextModel: ITextModel, + outputRange: LineRange, + public readonly outputTextModel: ITextModel, + rangeMappings?: readonly RangeMapping[], + ) { + super(inputRange, outputRange); + + this.rangeMappings = rangeMappings || [new RangeMapping(this.inputRange.toRange(), this.outputRange.toRange())]; + } + + public override addOutputLineDelta(delta: number): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange, + this.inputTextModel, + this.outputRange.delta(delta), + this.outputTextModel, + this.rangeMappings.map(d => d.addOutputLineDelta(delta)) + ); + } + + public override join(other: DetailedLineRangeMapping): DetailedLineRangeMapping { + return new DetailedLineRangeMapping( + this.inputRange.join(other.inputRange), + this.inputTextModel, + this.outputRange.join(other.outputRange), + this.outputTextModel, + ); + } + + public getLineEdit(): LineRangeEdit { + return new LineRangeEdit(this.inputRange, this.getOutputLines()); + } + + public getReverseLineEdit(): LineRangeEdit { + return new LineRangeEdit(this.outputRange, this.getInputLines()); + } + + private getOutputLines(): string[] { + return this.outputRange.getLines(this.outputTextModel); + } + + private getInputLines(): string[] { + return this.inputRange.getLines(this.inputTextModel); + } +} + +export class RangeMapping { + constructor(public readonly inputRange: Range, public readonly outputRange: Range) { + } + + toString(): string { + function rangeToString(range: Range) { + // TODO@hediet make this the default Range.toString + return `[${range.startLineNumber}:${range.startColumn}, ${range.endLineNumber}:${range.endColumn})`; + } + + return `${rangeToString(this.inputRange)} -> ${rangeToString(this.outputRange)}`; + } + + addOutputLineDelta(deltaLines: number): RangeMapping { + return new RangeMapping( + this.inputRange, + new Range( + this.outputRange.startLineNumber + deltaLines, + this.outputRange.startColumn, + this.outputRange.endLineNumber + deltaLines, + this.outputRange.endColumn + ) + ); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts similarity index 50% rename from src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts rename to src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts index 0d1907a4eb9..65bcadcc743 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts @@ -3,15 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { compareBy, CompareResult, equals } from 'vs/base/common/arrays'; +import { compareBy, CompareResult, tieBreakComparators, equals, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; +import { splitLines } from 'vs/base/common/strings'; +import { Constants } from 'vs/base/common/uint'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { EditorModel } from 'vs/workbench/common/editor/editorModel'; import { autorunHandleChanges, derivedObservable, derivedObservableWithCache, IObservable, ITransaction, keepAlive, ObservableValue, transaction, waitForState } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineDiff, LineEdit, LineRange, ModifiedBaseRange, ModifiedBaseRangeState } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { EditorWorkerServiceDiffComputer, TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/textModelDiffs'; -import { leftJoin } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { EditorWorkerServiceDiffComputer } from 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer'; +import { DetailedLineRangeMapping, DocumentMapping, LineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRangeEdit, RangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; +import { TextModelDiffChangeReason, TextModelDiffs, TextModelDiffState } from 'vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs'; +import { concatArrays, leftJoin, elementAtOrUndefined } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { ModifiedBaseRange, ModifiedBaseRangeState } from './modifiedBaseRange'; export const enum MergeEditorModelState { initializing = 1, @@ -53,7 +61,7 @@ export class MergeEditorModel extends EditorModel { const input1Diffs = this.input1TextModelDiffs.diffs.read(reader); const input2Diffs = this.input2TextModelDiffs.diffs.read(reader); - return ModifiedBaseRange.fromDiffs(this.base, this.input1, input1Diffs, this.input2, input2Diffs); + return ModifiedBaseRange.fromDiffs(input1Diffs, input2Diffs, this.base, this.input1, this.input2); }); public readonly input1LinesDiffs = this.input1TextModelDiffs.diffs; @@ -68,6 +76,40 @@ export class MergeEditorModel extends EditorModel { return map; }); + public readonly input1ResultMapping = derivedObservable('input1ResultMapping', reader => { + const resultDiffs = this.resultDiffs.read(reader); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(this.input1LinesDiffs.read(reader), resultDiffs, this.input1.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new LineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); + }); + + public readonly input2ResultMapping = derivedObservable('input2ResultMapping', reader => { + const resultDiffs = this.resultDiffs.read(reader); + const modifiedBaseRanges = DocumentMapping.betweenOutputs(this.input2LinesDiffs.read(reader), resultDiffs, this.input2.getLineCount()); + + return new DocumentMapping( + modifiedBaseRanges.lineRangeMappings.map((m) => + m.inputRange.isEmpty || m.outputRange.isEmpty + ? new LineRangeMapping( + m.inputRange.deltaStart(-1), + m.outputRange.deltaStart(-1) + ) + : m + ), + modifiedBaseRanges.inputLineCount + ); + }); + constructor( readonly base: ITextModel, readonly input1: ITextModel, @@ -82,6 +124,8 @@ export class MergeEditorModel extends EditorModel { super(); this._register(keepAlive(this.modifiedBaseRangeStateStores)); + this._register(keepAlive(this.input1ResultMapping)); + this._register(keepAlive(this.input2ResultMapping)); this._register( autorunHandleChanges( @@ -109,22 +153,22 @@ export class MergeEditorModel extends EditorModel { }); } - private recomputeState(resultDiffs: LineDiff[], stores: Map>): void { + private recomputeState(resultDiffs: DetailedLineRangeMapping[], stores: Map>): void { transaction(tx => { const baseRangeWithStoreAndTouchingDiffs = leftJoin( stores, resultDiffs, (baseRange, diff) => - baseRange[0].baseRange.touches(diff.originalRange) + baseRange[0].baseRange.touches(diff.inputRange) ? CompareResult.neitherLessOrGreaterThan : LineRange.compareByStart( baseRange[0].baseRange, - diff.originalRange + diff.inputRange ) ); for (const row of baseRangeWithStoreAndTouchingDiffs) { - row.left[1].set(computeState(row.left[0], row.rights), tx); + row.left[1].set(this.computeState(row.left[0], row.rights), tx); } }); } @@ -193,65 +237,73 @@ export class MergeEditorModel extends EditorModel { this.resultTextModelDiffs.applyEditRelativeToOriginal(edit, transaction); } } + + private computeState(baseRange: ModifiedBaseRange, conflictingDiffs: DetailedLineRangeMapping[]): ModifiedBaseRangeState { + if (conflictingDiffs.length === 0) { + return ModifiedBaseRangeState.default; + } + const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); + + function editsAgreeWithDiffs(diffs: readonly DetailedLineRangeMapping[]): boolean { + return equals( + conflictingEdits, + diffs.map((d) => d.getLineEdit()), + (a, b) => a.equals(b) + ); + } + + if (editsAgreeWithDiffs(baseRange.input1Diffs)) { + return ModifiedBaseRangeState.default.withInput1(true); + } + if (editsAgreeWithDiffs(baseRange.input2Diffs)) { + return ModifiedBaseRangeState.default.withInput2(true); + } + + const states = [ + ModifiedBaseRangeState.default.withInput1(true).withInput2(true), + ModifiedBaseRangeState.default.withInput2(true).withInput1(true), + ]; + + for (const s of states) { + const { edit } = getEditForBase(baseRange, s); + if (edit) { + const resultRange = this.resultTextModelDiffs.getResultRange(baseRange.baseRange); + const existingLines = resultRange.getLines(this.result); + + if (equals(edit.newLines, existingLines, (a, b) => a === b)) { + return s; + } + } + } + + return ModifiedBaseRangeState.conflicting; + } + } -function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineEdit | undefined; effectiveState: ModifiedBaseRangeState } { - interface LineDiffWithInputNumber { - diff: LineDiff; - inputNumber: 1 | 2; - } +function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeState): { edit: LineRangeEdit | undefined; effectiveState: ModifiedBaseRangeState } { + const diffs = concatArrays( + state.input1 && baseRange.input1CombinedDiff ? [{ diff: baseRange.input1CombinedDiff, inputNumber: 1 as const }] : [], + state.input2 && baseRange.input2CombinedDiff ? [{ diff: baseRange.input2CombinedDiff, inputNumber: 2 as const }] : [], + ); - const diffs = new Array(); - if (state.input1) { - if (baseRange.input1CombinedDiff) { - diffs.push({ diff: baseRange.input1CombinedDiff, inputNumber: 1 }); - } - } - if (state.input2) { - if (baseRange.input2CombinedDiff) { - diffs.push({ diff: baseRange.input2CombinedDiff, inputNumber: 2 }); - } - } if (state.input2First) { diffs.reverse(); } - const firstDiff: LineDiffWithInputNumber | undefined = diffs[0]; - const secondDiff: LineDiffWithInputNumber | undefined = diffs[1]; - diffs.sort(compareBy(d => d.diff.originalRange, LineRange.compareByStart)); + + const firstDiff = elementAtOrUndefined(diffs, 0); + const secondDiff = elementAtOrUndefined(diffs, 1); if (!firstDiff) { return { edit: undefined, effectiveState: ModifiedBaseRangeState.default }; } - if (!secondDiff) { return { edit: firstDiff.diff.getLineEdit(), effectiveState: ModifiedBaseRangeState.default.withInputValue(firstDiff.inputNumber, true) }; } - // Two inserts - if ( - firstDiff.diff.originalRange.lineCount === 0 && - firstDiff.diff.originalRange.equals(secondDiff.diff.originalRange) - ) { - return { - edit: new LineEdit( - firstDiff.diff.originalRange, - firstDiff.diff - .getLineEdit() - .newLines.concat(secondDiff.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; - } - - // Technically non-conflicting diffs - if (diffs.length === 2 && diffs[0].diff.originalRange.endLineNumberExclusive === diffs[1].diff.originalRange.startLineNumber) { - return { - edit: new LineEdit( - LineRange.join(diffs.map(d => d.diff.originalRange))!, - diffs.flatMap(d => d.diff.getLineEdit().newLines) - ), - effectiveState: state, - }; + const result = combineInputs(baseRange, state.input2First ? 2 : 1); + if (result) { + return { edit: result, effectiveState: state }; } return { @@ -263,26 +315,67 @@ function getEditForBase(baseRange: ModifiedBaseRange, state: ModifiedBaseRangeSt }; } -function computeState(baseRange: ModifiedBaseRange, conflictingDiffs: LineDiff[]): ModifiedBaseRangeState { - if (conflictingDiffs.length === 0) { - return ModifiedBaseRangeState.default; - } - const conflictingEdits = conflictingDiffs.map((d) => d.getLineEdit()); +function combineInputs(baseRange: ModifiedBaseRange, firstInput: 1 | 2): LineRangeEdit | undefined { + const combinedDiffs = concatArrays( + baseRange.input1Diffs.flatMap((diffs) => + diffs.rangeMappings.map((diff) => ({ diff, input: 1 as const })) + ), + baseRange.input2Diffs.flatMap((diffs) => + diffs.rangeMappings.map((diff) => ({ diff, input: 2 as const })) + ) + ).sort( + tieBreakComparators( + compareBy((d) => d.diff.inputRange, Range.compareRangesUsingStarts), + compareBy((d) => (d.input === firstInput ? 1 : 2), numberComparator) + ) + ); - function editsAgreeWithDiffs(diffs: readonly LineDiff[]): boolean { - return equals( - conflictingEdits, - diffs.map((d) => d.getLineEdit()), - (a, b) => a.equals(b) - ); - } + const sortedEdits = combinedDiffs.map(d => { + const sourceTextModel = d.input === 1 ? baseRange.input1TextModel : baseRange.input2TextModel; + return new RangeEdit(d.diff.inputRange, sourceTextModel.getValueInRange(d.diff.outputRange)); + }); - if (editsAgreeWithDiffs(baseRange.input1Diffs)) { - return ModifiedBaseRangeState.default.withInput1(true); - } - if (editsAgreeWithDiffs(baseRange.input2Diffs)) { - return ModifiedBaseRangeState.default.withInput2(true); - } - - return ModifiedBaseRangeState.conflicting; + return editsToLineRangeEdit(baseRange.baseRange, sortedEdits, baseRange.baseTextModel); +} + +function editsToLineRangeEdit(range: LineRange, sortedEdits: RangeEdit[], textModel: ITextModel): LineRangeEdit | undefined { + let text = ''; + const startsLineBefore = range.startLineNumber > 1; + let currentPosition = startsLineBefore + ? new Position( + range.startLineNumber - 1, + Constants.MAX_SAFE_SMALL_INTEGER + ) + : new Position(range.startLineNumber, 1); + + for (const edit of sortedEdits) { + const diffStart = edit.range.getStartPosition(); + if (!currentPosition.isBeforeOrEqual(diffStart)) { + return undefined; + } + const originalText = textModel.getValueInRange(Range.fromPositions(currentPosition, diffStart)); + text += originalText; + text += edit.newText; + currentPosition = edit.range.getEndPosition(); + } + + const endsLineAfter = range.endLineNumberExclusive <= textModel.getLineCount(); + const end = endsLineAfter ? new Position( + range.endLineNumberExclusive, + 1 + ) : new Position(range.endLineNumberExclusive - 1, Constants.MAX_SAFE_SMALL_INTEGER); + + const originalText = textModel.getValueInRange( + Range.fromPositions(currentPosition, end) + ); + text += originalText; + + const lines = splitLines(text); + if (startsLineBefore) { + lines.shift(); + } + if (endsLineAfter) { + lines.pop(); + } + return new LineRangeEdit(range, lines); } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts new file mode 100644 index 00000000000..efd6b87fbec --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/common/errors'; +import { ITextModel } from 'vs/editor/common/model'; +import { DetailedLineRangeMapping, MappingAlignment } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; + +/** + * Describes modifications in input 1 and input 2 for a specific range in base. + * + * The UI offers a mechanism to either apply all changes from input 1 or input 2 or both. + * + * Immutable. +*/ +export class ModifiedBaseRange { + /** + * diffs1 and diffs2 together with the conflict relation form a bipartite graph. + * This method computes strongly connected components of that graph while maintaining the side of each diff. + */ + public static fromDiffs( + diffs1: readonly DetailedLineRangeMapping[], + diffs2: readonly DetailedLineRangeMapping[], + baseTextModel: ITextModel, + input1TextModel: ITextModel, + input2TextModel: ITextModel + ): ModifiedBaseRange[] { + const alignments = MappingAlignment.compute(diffs1, diffs2); + return alignments.map( + (a) => new ModifiedBaseRange( + a.baseRange, + baseTextModel, + a.input1Range, + input1TextModel, + a.input1LineMappings, + a.input2Range, + input2TextModel, + a.input2LineMappings + ) + ); + } + + public readonly input1CombinedDiff = DetailedLineRangeMapping.join(this.input1Diffs); + public readonly input2CombinedDiff = DetailedLineRangeMapping.join(this.input2Diffs); + + + constructor( + public readonly baseRange: LineRange, + public readonly baseTextModel: ITextModel, + public readonly input1Range: LineRange, + public readonly input1TextModel: ITextModel, + public readonly input1Diffs: readonly DetailedLineRangeMapping[], + public readonly input2Range: LineRange, + public readonly input2TextModel: ITextModel, + public readonly input2Diffs: readonly DetailedLineRangeMapping[] + ) { + if (this.input1Diffs.length === 0 && this.input2Diffs.length === 0) { + throw new BugIndicatingError('must have at least one diff'); + } + } + + public getInputRange(inputNumber: 1 | 2): LineRange { + return inputNumber === 1 ? this.input1Range : this.input2Range; + } + + public getInputDiffs(inputNumber: 1 | 2): readonly DetailedLineRangeMapping[] { + return inputNumber === 1 ? this.input1Diffs : this.input2Diffs; + } + + public get isConflicting(): boolean { + return this.input1Diffs.length > 0 && this.input2Diffs.length > 0; + } +} + +export class ModifiedBaseRangeState { + public static readonly default = new ModifiedBaseRangeState(false, false, false, false); + public static readonly conflicting = new ModifiedBaseRangeState(false, false, false, true); + + private constructor( + public readonly input1: boolean, + public readonly input2: boolean, + public readonly input2First: boolean, + public readonly conflicting: boolean + ) { } + + public getInput(inputNumber: 1 | 2): InputState { + if (this.conflicting) { + return InputState.conflicting; + } + if (inputNumber === 1) { + return !this.input1 ? InputState.excluded : this.input2First ? InputState.second : InputState.first; + } else { + return !this.input2 ? InputState.excluded : !this.input2First ? InputState.second : InputState.first; + } + } + + public withInputValue(inputNumber: 1 | 2, value: boolean): ModifiedBaseRangeState { + return inputNumber === 1 ? this.withInput1(value) : this.withInput2(value); + } + + public withInput1(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( + value, + this.input2, + value !== this.input2 ? this.input2 : this.input2First, + false + ); + } + + public withInput2(value: boolean): ModifiedBaseRangeState { + return new ModifiedBaseRangeState( + this.input1, + value, + value !== this.input1 ? value : this.input2First, + false + ); + } + + public get isEmpty(): boolean { + return !this.input1 && !this.input2; + } + + public toString(): string { + const arr: ('1' | '2')[] = []; + if (this.input1) { + arr.push('1'); + } + if (this.input2) { + arr.push('2'); + } + if (this.input2First) { + arr.reverse(); + } + return arr.join(','); + } +} + +export const enum InputState { + excluded = 0, + first = 1, + second = 2, + conflicting = 3 +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts similarity index 56% rename from src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts rename to src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts index e7c6c9967e5..d3b9e29c4df 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/textModelDiffs.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/model/textModelDiffs.ts @@ -7,15 +7,17 @@ import { compareBy, numberComparator } from 'vs/base/common/arrays'; import { BugIndicatingError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITextModel } from 'vs/editor/common/model'; -import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineDiff, LineEdit, LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { DetailedLineRangeMapping } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { LineRangeEdit } from 'vs/workbench/contrib/mergeEditor/browser/model/editing'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { IDiffComputer } from './diffComputer'; export class TextModelDiffs extends Disposable { private updateCount = 0; private readonly _state = new ObservableValue(TextModelDiffState.initializing, 'LiveDiffState'); - private readonly _diffs = new ObservableValue([], 'LiveDiffs'); + private readonly _diffs = new ObservableValue([], 'LiveDiffs'); private readonly barrier = new ReentrancyBarrier(); @@ -35,7 +37,7 @@ export class TextModelDiffs extends Disposable { return this._state; } - public get diffs(): IObservable { + public get diffs(): IObservable { return this._diffs; } @@ -63,9 +65,9 @@ export class TextModelDiffs extends Disposable { } transaction(tx => { - if (result) { + if (result.diffs) { this._state.set(TextModelDiffState.upToDate, tx, TextModelDiffChangeReason.textChange); - this._diffs.set(result, tx, TextModelDiffChangeReason.textChange); + this._diffs.set(result.diffs, tx, TextModelDiffChangeReason.textChange); } else { this._state.set(TextModelDiffState.error, tx, TextModelDiffChangeReason.textChange); } @@ -78,10 +80,10 @@ export class TextModelDiffs extends Disposable { } } - public removeDiffs(diffToRemoves: LineDiff[], transaction: ITransaction | undefined): void { + public removeDiffs(diffToRemoves: DetailedLineRangeMapping[], transaction: ITransaction | undefined): void { this.ensureUpToDate(); - diffToRemoves.sort(compareBy((d) => d.originalRange.startLineNumber, numberComparator)); + diffToRemoves.sort(compareBy((d) => d.inputRange.startLineNumber, numberComparator)); diffToRemoves.reverse(); let diffs = this._diffs.get(); @@ -99,15 +101,8 @@ export class TextModelDiffs extends Disposable { }); diffs = diffs.map((d) => - d.modifiedRange.isAfter(diffToRemove.modifiedRange) - ? new LineDiff( - d.originalTextModel, - d.originalRange, - d.modifiedTextModel, - d.modifiedRange.delta( - diffToRemove.originalRange.lineCount - diffToRemove.modifiedRange.lineCount - ) - ) + d.outputRange.isAfter(diffToRemove.outputRange) + ? d.addOutputLineDelta(diffToRemove.inputRange.lineCount - diffToRemove.outputRange.lineCount) : d ); } @@ -118,80 +113,79 @@ export class TextModelDiffs extends Disposable { /** * Edit must be conflict free. */ - public applyEditRelativeToOriginal(edit: LineEdit, transaction: ITransaction | undefined): void { + public applyEditRelativeToOriginal(edit: LineRangeEdit, transaction: ITransaction | undefined): void { this.ensureUpToDate(); + const editMapping = new DetailedLineRangeMapping( + edit.range, + this.baseTextModel, + new LineRange(edit.range.startLineNumber, edit.newLines.length), + this.textModel + ); + let firstAfter = false; let delta = 0; - const newDiffs = new Array(); + const newDiffs = new Array(); for (const diff of this.diffs.get()) { - if (diff.originalRange.touches(edit.range)) { + if (diff.inputRange.touches(edit.range)) { throw new BugIndicatingError('Edit must be conflict free.'); - } else if (diff.originalRange.isAfter(edit.range)) { + } else if (diff.inputRange.isAfter(edit.range)) { if (!firstAfter) { firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.textModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); + newDiffs.push(editMapping.addOutputLineDelta(delta)); } - newDiffs.push(new LineDiff( - diff.originalTextModel, - diff.originalRange, - diff.modifiedTextModel, - diff.modifiedRange.delta(edit.newLines.length - edit.range.lineCount) - )); + newDiffs.push(diff.addOutputLineDelta(edit.newLines.length - edit.range.lineCount)); } else { newDiffs.push(diff); } if (!firstAfter) { - delta += diff.modifiedRange.lineCount - diff.originalRange.lineCount; + delta += diff.outputRange.lineCount - diff.inputRange.lineCount; } } if (!firstAfter) { firstAfter = true; - - newDiffs.push(new LineDiff( - this.baseTextModel, - edit.range, - this.textModel, - new LineRange(edit.range.startLineNumber + delta, edit.newLines.length) - )); + newDiffs.push(editMapping.addOutputLineDelta(delta)); } this.barrier.runExclusivelyOrThrow(() => { - new LineEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel); + new LineRangeEdit(edit.range.delta(delta), edit.newLines).apply(this.textModel); }); this._diffs.set(newDiffs, transaction, TextModelDiffChangeReason.other); } - public findTouchingDiffs(baseRange: LineRange): LineDiff[] { - return this.diffs.get().filter(d => d.originalRange.touches(baseRange)); + public findTouchingDiffs(baseRange: LineRange): DetailedLineRangeMapping[] { + return this.diffs.get().filter(d => d.inputRange.touches(baseRange)); } - /* - public getResultRange(baseRange: LineRange): LineRange { - let startOffset = 0; - let lengthOffset = 0; + private getResultLine(lineNumber: number): number | DetailedLineRangeMapping { + let offset = 0; for (const diff of this.diffs.get()) { - if (diff.originalRange.endLineNumberExclusive <= baseRange.startLineNumber) { - startOffset += diff.resultingDeltaFromOriginalToModified; - } else if (diff.originalRange.startLineNumber <= baseRange.endLineNumberExclusive) { - lengthOffset += diff.resultingDeltaFromOriginalToModified; + if (diff.inputRange.contains(lineNumber) || diff.inputRange.endLineNumberExclusive === lineNumber) { + return diff; + } else if (diff.inputRange.endLineNumberExclusive < lineNumber) { + offset = diff.resultingDeltaFromOriginalToModified; } else { break; } } - - return new LineRange(baseRange.startLineNumber + startOffset, baseRange.lineCount + lengthOffset); + return lineNumber + offset; + } + + public getResultRange(baseRange: LineRange): LineRange { + let start = this.getResultLine(baseRange.startLineNumber); + if (typeof start !== 'number') { + start = start.outputRange.startLineNumber; + } + let endExclusive = this.getResultLine(baseRange.endLineNumberExclusive); + if (typeof endExclusive !== 'number') { + endExclusive = endExclusive.outputRange.endLineNumberExclusive; + } + + return LineRange.fromLineNumbers(start, endExclusive); } - */ } export const enum TextModelDiffChangeReason { @@ -208,22 +202,5 @@ export const enum TextModelDiffState { export interface ITextModelDiffsState { state: TextModelDiffState; - diffs: LineDiff[]; -} - -export interface IDiffComputer { - computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise; -} - -export class EditorWorkerServiceDiffComputer implements IDiffComputer { - constructor(@IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService) { } - - async computeDiff(textModel1: ITextModel, textModel2: ITextModel): Promise { - //await wait(1000); - const diffs = await this.editorWorkerService.computeDiff(textModel1.uri, textModel2.uri, false, 1000); - if (!diffs || diffs.quitEarly) { - return null; - } - return diffs.changes.map((c) => LineDiff.fromLineChange(c, textModel1, textModel2)); - } + diffs: DetailedLineRangeMapping[]; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index d72b2dff331..dcdc184e704 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -53,21 +53,21 @@ export class ReentrancyBarrier { } } -export function n(tag: TTag): never; -export function n)[]>( +export function h(tag: TTag): never; +export function h( + tag: TTag, + attributes: { $: TId } +): Record>; +export function h)[]>( tag: TTag, children: T ): (ArrayToObj & Record<'root', TagToElement>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; -export function n( - tag: TTag, - attributes: { $: TId } -): Record>; -export function n)[]>( +export function h)[]>( tag: TTag, attributes: { $: TId }, children: T ): (ArrayToObj & Record>) extends infer Y ? { [TKey in keyof Y]: Y[TKey] } : never; -export function n(tag: string, ...args: [] | [attributes: { $: string } | Record, children?: any[]] | [children: any[]]): Record { +export function h(tag: string, ...args: [] | [attributes: { $: string } | Record, children?: any[]] | [children: any[]]): Record { let attributes: Record; let children: (Record | HTMLElement)[] | undefined; @@ -199,3 +199,11 @@ export function* join( yield { left: leftElement, rights: equals || [] }; } } + +export function concatArrays(...arrays: TArr): TArr[number][number][] { + return ([] as any[]).concat(...arrays); +} + +export function elementAtOrUndefined(arr: T[], index: number): T | undefined { + return arr[index]; +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts new file mode 100644 index 00000000000..c977770909f --- /dev/null +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/codeEditorView.ts @@ -0,0 +1,309 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IView, IViewSize } from 'vs/base/browser/ui/grid/grid'; +import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { CompareResult } from 'vs/base/common/arrays'; +import { Codicon } from 'vs/base/common/codicons'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { noBreakWhitespace } from 'vs/base/common/strings'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { Range } from 'vs/editor/common/core/range'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; +import { autorun, derivedObservable, IObservable, ITransaction, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; +import { InputState } from 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; +import { applyObservableDecorations, join, h, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; + +export interface ICodeEditorViewOptions { + readonly: boolean; +} + + +abstract class CodeEditorView extends Disposable { + private readonly _model = new ObservableValue(undefined, 'model'); + readonly model: IObservable = this._model; + + protected readonly htmlElements = h('div.code-view', [ + h('div.title', { $: 'title' }), + h('div.container', [ + h('div.gutter', { $: 'gutterDiv' }), + h('div', { $: 'editor' }), + ]), + ]); + + private readonly _onDidViewChange = new Emitter(); + + public readonly view: IView = { + element: this.htmlElements.root, + minimumWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, + maximumWidth: DEFAULT_EDITOR_MAX_DIMENSIONS.width, + minimumHeight: DEFAULT_EDITOR_MIN_DIMENSIONS.height, + maximumHeight: DEFAULT_EDITOR_MAX_DIMENSIONS.height, + onDidChange: this._onDidViewChange.event, + layout: (width: number, height: number, top: number, left: number) => { + setStyle(this.htmlElements.root, { width, height, top, left }); + this.editor.layout({ + width: width - this.htmlElements.gutterDiv.clientWidth, + height: height - this.htmlElements.title.clientHeight, + }); + } + // preferredWidth?: number | undefined; + // preferredHeight?: number | undefined; + // priority?: LayoutPriority | undefined; + // snap?: boolean | undefined; + }; + + private readonly _title = new IconLabel(this.htmlElements.title, { supportIcons: true }); + private readonly _detail = new IconLabel(this.htmlElements.title, { supportIcons: true }); + + public readonly editor = this.instantiationService.createInstance( + CodeEditorWidget, + this.htmlElements.editor, + { + minimap: { enabled: false }, + readOnly: this._options.readonly, + glyphMargin: false, + lineNumbersMinChars: 2, + }, + { contributions: [] } + ); + + constructor( + private readonly _options: ICodeEditorViewOptions, + @IInstantiationService + private readonly instantiationService: IInstantiationService + ) { + super(); + } + + public setModel( + model: MergeEditorModel, + textModel: ITextModel, + title: string, + description: string | undefined, + detail: string | undefined + ): void { + this.editor.setModel(textModel); + this._title.setLabel(title, description); + this._detail.setLabel('', detail); + + this._model.set(model, undefined); + } +} +export class InputCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + for (const m of model.modifiedBaseRanges.read(reader)) { + const range = m.getInputRange(this.inputNumber); + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + className: `merge-editor-modified-base-range-input${this.inputNumber}`, + description: 'Base Range Projection' + } + }); + + const inputDiffs = m.getInputDiffs(this.inputNumber); + for (const diff of inputDiffs) { + if (diff.rangeMappings) { + for (const d of diff.rangeMappings) { + result.push({ + range: d.outputRange, + options: { + className: `merge-editor-diff-input${this.inputNumber}`, + description: 'Base Range Projection' + } + }); + } + } + } + } + } + return result; + }); + + constructor( + public readonly inputNumber: 1 | 2, + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(options, instantiationService); + + this._register(applyObservableDecorations(this.editor, this.decorations)); + + this._register( + new EditorGutter(this.editor, this.htmlElements.gutterDiv, { + getIntersectingGutterItems: (range, reader) => { + const model = this.model.read(reader); + if (!model) { return []; } + return model.modifiedBaseRanges.read(reader) + .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) + .map((baseRange, idx) => ({ + id: idx.toString(), + additionalHeightInPx: 0, + offsetInPx: 0, + range: baseRange.getInputRange(this.inputNumber), + enabled: model.isUpToDate, + toggleState: derivedObservable('toggle', (reader) => model + .getState(baseRange) + .read(reader) + .getInput(this.inputNumber) + ), + setState: (value, tx) => model.setState( + baseRange, + model + .getState(baseRange) + .get() + .withInputValue(this.inputNumber, value), + tx + ), + })); + }, + createView: (item, target) => new MergeConflictGutterItemView(item, target), + }) + ); + } +} + +interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { + enabled: IObservable; + toggleState: IObservable; + setState(value: boolean, tx: ITransaction): void; +} + +class MergeConflictGutterItemView extends Disposable implements IGutterItemView { + private readonly item = new ObservableValue(undefined, 'item'); + + constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { + super(); + + this.item.set(item, undefined); + + target.classList.add('merge-accept-gutter-marker'); + + const checkBox = new Toggle({ isChecked: false, title: localize('acceptMerge', "Accept Merge"), icon: Codicon.check }); + checkBox.domNode.classList.add('accept-conflict-group'); + + this._register( + autorun((reader) => { + const item = this.item.read(reader)!; + const value = item.toggleState.read(reader); + const iconMap: Record = { + [InputState.excluded]: { icon: undefined, checked: false }, + [InputState.conflicting]: { icon: Codicon.circleFilled, checked: false }, + [InputState.first]: { icon: Codicon.check, checked: true }, + [InputState.second]: { icon: Codicon.checkAll, checked: true }, + }; + checkBox.setIcon(iconMap[value].icon); + checkBox.checked = iconMap[value].checked; + + if (!item.enabled.read(reader)) { + checkBox.disable(); + } else { + checkBox.enable(); + } + }, 'Update Toggle State') + ); + + this._register(checkBox.onChange(() => { + transaction(tx => { + this.item.get()!.setState(checkBox.checked, tx); + }); + })); + + target.appendChild(h('div.background', [noBreakWhitespace]).root); + target.appendChild( + h('div.checkbox', [h('div.checkbox-background', [checkBox.domNode])]).root + ); + } + + layout(top: number, height: number, viewTop: number, viewHeight: number): void { + this.target.classList.remove('multi-line'); + this.target.classList.remove('single-line'); + this.target.classList.add(height > 30 ? 'multi-line' : 'single-line'); + } + + update(baseRange: ModifiedBaseRangeGutterItemInfo): void { + this.item.set(baseRange, undefined); + } +} + +export class ResultCodeEditorView extends CodeEditorView { + private readonly decorations = derivedObservable('decorations', reader => { + const model = this.model.read(reader); + if (!model) { + return []; + } + const result = new Array(); + + const baseRangeWithStoreAndTouchingDiffs = join( + model.modifiedBaseRanges.read(reader), + model.resultDiffs.read(reader), + (baseRange, diff) => baseRange.baseRange.touches(diff.inputRange) + ? CompareResult.neitherLessOrGreaterThan + : LineRange.compareByStart( + baseRange.baseRange, + diff.inputRange + ) + ); + + for (const m of baseRangeWithStoreAndTouchingDiffs) { + for (const r of m.rights) { + const range = r.outputRange; + + const state = m.left ? model.getState(m.left).read(reader) : undefined; + + if (!range.isEmpty) { + result.push({ + range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), + options: { + isWholeLine: true, + // TODO + className: (() => { + if (state) { + if (state.input1 && !state.input2) { + return 'merge-editor-modified-base-range-input1'; + } + if (state.input2 && !state.input1) { + return 'merge-editor-modified-base-range-input2'; + } + if (state.input1 && state.input2) { + return 'merge-editor-modified-base-range-combination'; + } + } + return 'merge-editor-modified-base-range'; + })(), + description: 'Result Diff' + } + }); + } + } + } + return result; + }); + + constructor( + options: ICodeEditorViewOptions, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(options, instantiationService); + + this._register(applyObservableDecorations(this.editor, this.decorations)); + } +} diff --git a/src/vs/workbench/contrib/mergeEditor/browser/colors.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts similarity index 79% rename from src/vs/workbench/contrib/mergeEditor/browser/colors.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts index 1eb2f15c408..9760a7f83f9 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/colors.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts @@ -62,6 +62,34 @@ export const diffInput1 = registerColor( ) ); +export const diff = registerColor( + 'mergeEditor.diff', + { + dark: '#d3d3d321', + light: '#d3d3d321', + hcDark: '#d3d3d321', + hcLight: '#d3d3d321', + }, + localize( + 'mergeEditor.diff', + 'The foreground color for changes in the result.' + ) +); + +export const diffWord = registerColor( + 'mergeEditor.diff.word', + { + dark: '#e571db21', + light: '#e571db21', + hcDark: '#e571db21', + hcLight: '#e571db21', + }, + localize( + 'mergeEditor.diff.word', + 'The foreground color for word changes in the result.' + ) +); + export const diffInput2 = registerColor( 'mergeEditor.diff.input2', { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts similarity index 99% rename from src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts index 63ec1e1c2c1..fee958633e5 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/editorGutter.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts @@ -7,7 +7,7 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { autorun, IReader, observableFromEvent, ObservableValue } from 'vs/workbench/contrib/audioCues/browser/observable'; -import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; +import { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange'; export class EditorGutter extends Disposable { private readonly scrollTop = observableFromEvent( @@ -132,10 +132,12 @@ export interface IGutterItemProvider { export interface IGutterItemInfo { id: string; range: LineRange; + /* // To accommodate view zones: offsetInPx: number; additionalHeightInPx: number; + */ } export interface IGutterItemView extends IDisposable { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css b/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css similarity index 96% rename from src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css rename to src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css index 6b09151c147..ab4189bc484 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/media/mergeEditor.css +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/media/mergeEditor.css @@ -54,6 +54,10 @@ background-color: var(--vscode-mergeEditor-modifiedBaseRange-combination); } +.merge-editor-modified-base-range { + background-color: var(--vscode-mergeEditor-diff); +} + .gutter-item { position: absolute; } diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts similarity index 55% rename from src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts rename to src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 38250239a2a..f8acdc3deaa 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -3,28 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/mergeEditor'; +import './colors'; import { $, Dimension, reset } from 'vs/base/browser/dom'; -import { Direction, Grid, IView, IViewSize, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; -import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; +import { Direction, Grid, IView, SerializableGrid } from 'vs/base/browser/ui/grid/grid'; import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { IAction } from 'vs/base/common/actions'; -import { CompareResult, findLast } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { BugIndicatingError } from 'vs/base/common/errors'; -import { Emitter } from 'vs/base/common/event'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { noBreakWhitespace } from 'vs/base/common/strings'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import 'vs/css!./media/mergeEditor'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions as ICodeEditorOptions } from 'vs/editor/common/config/editorOptions'; -import { Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfiguration'; import { localize } from 'vs/nls'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -39,20 +32,19 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { FloatingClickWidget } from 'vs/workbench/browser/codeeditor'; -import { DEFAULT_EDITOR_MAX_DIMENSIONS, DEFAULT_EDITOR_MIN_DIMENSIONS } from 'vs/workbench/browser/parts/editor/editor'; import { AbstractTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { applyTextEditorOptions } from 'vs/workbench/common/editor/editorOptions'; -import { autorun, autorunWithStore, derivedObservable, IObservable, ITransaction, keepAlive, ObservableValue, transaction } from 'vs/workbench/contrib/audioCues/browser/observable'; +import { autorunWithStore } from 'vs/workbench/contrib/audioCues/browser/observable'; import { MergeEditorInput } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput'; -import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/mergeEditorModel'; -import { LineRange, ModifiedBaseRange } from 'vs/workbench/contrib/mergeEditor/browser/model'; -import { applyObservableDecorations, join, n, ReentrancyBarrier, setStyle } from 'vs/workbench/contrib/mergeEditor/browser/utils'; +import { MergeEditorModel } from 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel'; +import { DocumentMapping, getOppositeDirection, MappingDirection } from 'vs/workbench/contrib/mergeEditor/browser/model/mapping'; +import { ReentrancyBarrier } from 'vs/workbench/contrib/mergeEditor/browser/utils'; import { settingsSashBorder } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EditorGutter, IGutterItemInfo, IGutterItemView } from './editorGutter'; +import { InputCodeEditorView, ResultCodeEditorView } from './codeEditorView'; export const ctxIsMergeEditor = new RawContextKey('isMergeEditor', false); export const ctxUsesColumnLayout = new RawContextKey('mergeEditorUsesColumnLayout', false); @@ -103,34 +95,12 @@ export class MergeEditor extends AbstractTextEditor { const reentrancyBarrier = new ReentrancyBarrier(); - const input1ResultMapping = derivedObservable('input1ResultMapping', reader => { - const model = this.input1View.model.read(reader); - if (!model) { - return undefined; - } - const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input1, model.input1LinesDiffs.read(reader), model.result, resultDiffs); - return modifiedBaseRanges; - }); - const input2ResultMapping = derivedObservable('input2ResultMapping', reader => { - const model = this.input2View.model.read(reader); - if (!model) { - return undefined; - } - const resultDiffs = model.resultDiffs.read(reader); - const modifiedBaseRanges = ModifiedBaseRange.fromDiffs(model.base, model.input2, model.input2LinesDiffs.read(reader), model.result, resultDiffs); - return modifiedBaseRanges; - }); - - this._register(keepAlive(input1ResultMapping)); - this._register(keepAlive(input2ResultMapping)); - this._store.add( this.input1View.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping = input1ResultMapping.get(); - synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, 1); + const mapping = this.model?.input1ResultMapping.get(); + synchronizeScrolling(this.input1View.editor, this.inputResultView.editor, mapping, MappingDirection.input); this.input2View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); } }) @@ -140,8 +110,8 @@ export class MergeEditor extends AbstractTextEditor { this.input2View.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping = input2ResultMapping.get(); - synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, 1); + const mapping = this.model?.input2ResultMapping.get(); + synchronizeScrolling(this.input2View.editor, this.inputResultView.editor, mapping, MappingDirection.input); this.input1View.editor.setScrollTop(c.scrollTop, ScrollType.Immediate); } }) @@ -151,10 +121,10 @@ export class MergeEditor extends AbstractTextEditor { this.inputResultView.editor.onDidScrollChange( reentrancyBarrier.makeExclusive((c) => { if (c.scrollTopChanged) { - const mapping1 = input1ResultMapping.get(); - synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1, 2); - const mapping2 = input2ResultMapping.get(); - synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, 2); + const mapping1 = this.model?.input1ResultMapping.get(); + synchronizeScrolling(this.inputResultView.editor, this.input1View.editor, mapping1, MappingDirection.output); + const mapping2 = this.model?.input2ResultMapping.get(); + synchronizeScrolling(this.inputResultView.editor, this.input2View.editor, mapping2, MappingDirection.output); } }) ) @@ -390,7 +360,7 @@ export class MergeEditor extends AbstractTextEditor { } } -function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: ModifiedBaseRange[] | undefined, sourceNumber: 1 | 2) { +function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: CodeEditorWidget, mapping: DocumentMapping | undefined, source: MappingDirection) { if (!mapping) { return; } @@ -401,28 +371,9 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C } const topLineNumber = visibleRanges[0].startLineNumber - 1; - const firstBefore = findLast(mapping, r => r.getInputRange(sourceNumber).startLineNumber <= topLineNumber); - let sourceRange: LineRange; - let targetRange: LineRange; - - const targetNumber = sourceNumber === 1 ? 2 : 1; - - const firstBeforeSourceRange = firstBefore?.getInputRange(sourceNumber); - const firstBeforeTargetRange = firstBefore?.getInputRange(targetNumber); - - if (firstBeforeSourceRange && firstBeforeSourceRange.contains(topLineNumber)) { - sourceRange = firstBeforeSourceRange; - targetRange = firstBeforeTargetRange!; - } else if (firstBeforeSourceRange && firstBeforeSourceRange.isEmpty && firstBeforeSourceRange.startLineNumber === topLineNumber) { - sourceRange = firstBeforeSourceRange.deltaEnd(1); - targetRange = firstBeforeTargetRange!.deltaEnd(1); - } else { - const delta = firstBeforeSourceRange ? firstBeforeTargetRange!.endLineNumberExclusive - firstBeforeSourceRange.endLineNumberExclusive : 0; - sourceRange = new LineRange(topLineNumber, 1); - targetRange = new LineRange(topLineNumber + delta, 1); - } - - // sourceRange contains topLineNumber! + const result = mapping.getMappingContaining(topLineNumber, source); + const sourceRange = result.getRange(source); + const targetRange = result.getRange(getOppositeDirection(source)); const resultStartTopPx = targetEditor.getTopForLineNumber(targetRange.startLineNumber); const resultEndPx = targetEditor.getTopForLineNumber(targetRange.endLineNumberExclusive); @@ -435,276 +386,3 @@ function synchronizeScrolling(scrollingEditor: CodeEditorWidget, targetEditor: C targetEditor.setScrollTop(resultScrollPosition, ScrollType.Immediate); } - -interface ICodeEditorViewOptions { - readonly: boolean; -} - - -abstract class CodeEditorView extends Disposable { - private readonly _model = new ObservableValue(undefined, 'model'); - readonly model: IObservable = this._model; - - protected readonly htmlElements = n('div.code-view', [ - n('div.title', { $: 'title' }), - n('div.container', [ - n('div.gutter', { $: 'gutterDiv' }), - n('div', { $: 'editor' }), - ]), - ]); - - private readonly _onDidViewChange = new Emitter(); - - public readonly view: IView = { - element: this.htmlElements.root, - minimumWidth: DEFAULT_EDITOR_MIN_DIMENSIONS.width, - maximumWidth: DEFAULT_EDITOR_MAX_DIMENSIONS.width, - minimumHeight: DEFAULT_EDITOR_MIN_DIMENSIONS.height, - maximumHeight: DEFAULT_EDITOR_MAX_DIMENSIONS.height, - onDidChange: this._onDidViewChange.event, - layout: (width: number, height: number, top: number, left: number) => { - setStyle(this.htmlElements.root, { width, height, top, left }); - this.editor.layout({ - width: width - this.htmlElements.gutterDiv.clientWidth, - height: height - this.htmlElements.title.clientHeight, - }); - } - - // preferredWidth?: number | undefined; - // preferredHeight?: number | undefined; - // priority?: LayoutPriority | undefined; - // snap?: boolean | undefined; - }; - - private readonly _title = new IconLabel(this.htmlElements.title, { supportIcons: true }); - private readonly _detail = new IconLabel(this.htmlElements.title, { supportIcons: true }); - - public readonly editor = this.instantiationService.createInstance( - CodeEditorWidget, - this.htmlElements.editor, - { - minimap: { enabled: false }, - readOnly: this._options.readonly, - glyphMargin: false, - lineNumbersMinChars: 2, - }, - { contributions: [] } - ); - - constructor( - private readonly _options: ICodeEditorViewOptions, - @IInstantiationService - private readonly instantiationService: IInstantiationService - ) { - super(); - } - - public setModel( - model: MergeEditorModel, - textModel: ITextModel, - title: string, - description: string | undefined, - detail: string | undefined - ): void { - this.editor.setModel(textModel); - this._title.setLabel(title, description); - this._detail.setLabel('', detail); - - this._model.set(model, undefined); - } -} - -class InputCodeEditorView extends CodeEditorView { - private readonly decorations = derivedObservable('decorations', reader => { - const model = this.model.read(reader); - if (!model) { - return []; - } - const result = new Array(); - for (const m of model.modifiedBaseRanges.read(reader)) { - const range = m.getInputRange(this.inputNumber); - if (!range.isEmpty) { - result.push({ - range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - className: `merge-editor-modified-base-range-input${this.inputNumber}`, - description: 'Base Range Projection' - } - }); - } - } - return result; - }); - - constructor( - public readonly inputNumber: 1 | 2, - options: ICodeEditorViewOptions, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(options, instantiationService); - - this._register(applyObservableDecorations(this.editor, this.decorations)); - - this._register( - new EditorGutter(this.editor, this.htmlElements.gutterDiv, { - getIntersectingGutterItems: (range, reader) => { - const model = this.model.read(reader); - if (!model) { return []; } - return model.modifiedBaseRanges.read(reader) - .filter((r) => r.getInputDiffs(this.inputNumber).length > 0) - .map((baseRange, idx) => ({ - id: idx.toString(), - additionalHeightInPx: 0, - offsetInPx: 0, - range: baseRange.getInputRange(this.inputNumber), - enabled: model.isUpToDate, - toggleState: derivedObservable('toggle', (reader) => - model - .getState(baseRange) - .read(reader) - .getInput(this.inputNumber) - ), - setState: (value, tx) => - model.setState( - baseRange, - model - .getState(baseRange) - .get() - .withInputValue(this.inputNumber, value), - tx - ), - })); - }, - createView: (item, target) => - new MergeConflictGutterItemView(item, target), - }) - ); - } -} - -interface ModifiedBaseRangeGutterItemInfo extends IGutterItemInfo { - enabled: IObservable; - toggleState: IObservable; - setState(value: boolean, tx: ITransaction): void; -} - -class MergeConflictGutterItemView extends Disposable implements IGutterItemView { - private readonly item = new ObservableValue(undefined, 'item'); - - constructor(item: ModifiedBaseRangeGutterItemInfo, private readonly target: HTMLElement) { - super(); - - this.item.set(item, undefined); - - target.classList.add('merge-accept-gutter-marker'); - - const checkBox = new Toggle({ isChecked: false, title: localize('acceptMerge', "Accept Merge"), icon: Codicon.check }); - checkBox.domNode.classList.add('accept-conflict-group'); - - this._register( - autorun((reader) => { - const item = this.item.read(reader)!; - const value = item.toggleState.read(reader); - checkBox.setIcon( - value === true - ? Codicon.check - : value === false - ? undefined - : Codicon.circleFilled - ); - checkBox.checked = value === true; - - if (!item.enabled.read(reader)) { - checkBox.disable(); - } else { - checkBox.enable(); - } - }, 'Update Toggle State') - ); - - this._register(checkBox.onChange(() => { - transaction(tx => { - this.item.get()!.setState(checkBox.checked, tx); - }); - })); - - target.appendChild(n('div.background', [noBreakWhitespace]).root); - target.appendChild( - n('div.checkbox', [n('div.checkbox-background', [checkBox.domNode])]).root - ); - } - - layout(top: number, height: number, viewTop: number, viewHeight: number): void { - this.target.classList.remove('multi-line'); - this.target.classList.remove('single-line'); - this.target.classList.add(height > 30 ? 'multi-line' : 'single-line'); - } - - update(baseRange: ModifiedBaseRangeGutterItemInfo): void { - this.item.set(baseRange, undefined); - } -} - -class ResultCodeEditorView extends CodeEditorView { - private readonly decorations = derivedObservable('decorations', reader => { - const model = this.model.read(reader); - if (!model) { - return []; - } - const result = new Array(); - - const baseRangeWithStoreAndTouchingDiffs = join( - model.modifiedBaseRanges.read(reader), - model.resultDiffs.read(reader), - (baseRange, diff) => - baseRange.baseRange.touches(diff.originalRange) - ? CompareResult.neitherLessOrGreaterThan - : LineRange.compareByStart( - baseRange.baseRange, - diff.originalRange - ) - ); - - for (const m of baseRangeWithStoreAndTouchingDiffs) { - for (const r of m.rights) { - const range = r.modifiedRange; - - const state = m.left ? model.getState(m.left).read(reader) : undefined; - - if (!range.isEmpty) { - result.push({ - range: new Range(range.startLineNumber, 1, range.endLineNumberExclusive - 1, 1), - options: { - isWholeLine: true, - // TODO - - className: (() => { - if (state) { - if (state.input1 && !state.input2) { - return 'merge-editor-modified-base-range-input1'; - } - if (state.input2 && !state.input1) { - return 'merge-editor-modified-base-range-input2'; - } - } - return 'merge-editor-modified-base-range-combination'; - })(), - description: 'Result Diff' - } - }); - } - } - } - return result; - }); - - constructor( - options: ICodeEditorViewOptions, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(options, instantiationService); - - this._register(applyObservableDecorations(this.editor, this.decorations)); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 1ae1e7ea6bf..367c1f80e41 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -97,9 +97,7 @@ export class NotebookEditor extends EditorPane implements IEditorPaneWithSelecti } private _updateReadonly(input: NotebookEditorInput): void { - if (this._widget.value) { - this._widget.value.setOptions({ isReadOnly: input.hasCapability(EditorInputCapabilities.Readonly) }); - } + this._widget.value?.setOptions({ isReadOnly: input.hasCapability(EditorInputCapabilities.Readonly) }); } get textModel(): NotebookTextModel | undefined { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts index 374f1d707a1..055e95e8a91 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookServiceImpl.ts @@ -317,8 +317,8 @@ export class NotebookOutputRendererInfoStore { const enum ReuseOrder { PreviouslySelected = 1 << 8, SameExtensionAsNotebook = 2 << 8, - BuiltIn = 3 << 8, - OtherRenderer = 4 << 8, + OtherRenderer = 3 << 8, + BuiltIn = 4 << 8, } const preferred = notebookProviderInfo && this.preferredMimetype.getValue()[notebookProviderInfo.id]?.[mimeType]; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index cd410d4a510..181777ec7d4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -92,9 +92,7 @@ export class CellOutputElement extends Disposable { } detach() { - if (this.renderedOutputContainer) { - this.renderedOutputContainer.parentElement?.removeChild(this.renderedOutputContainer); - } + this.renderedOutputContainer?.parentElement?.removeChild(this.renderedOutputContainer); let count = 0; if (this.innerContainer) { diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts index 67cdfaf7002..870d4b2d719 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/markdownCell.ts @@ -435,9 +435,7 @@ export class StatefulMarkdownCell extends Disposable { updateEditorOptions(newValue: IEditorOptions): void { this.editorOptions = newValue; - if (this.editor) { - this.editor.updateOptions(this.editorOptions); - } + this.editor?.updateOptions(this.editorOptions); } private layoutFoldingIndicator() { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 65f85230512..52dd982f0d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -776,9 +776,7 @@ async function webviewPreloads(ctx: PreloadContext) { highlightCurrentMatch(index: number) { const oldMatch = this.matches[this._findMatchIndex]; - if (oldMatch) { - oldMatch.highlightResult?.update(matchColor, oldMatch.isShadow ? undefined : 'find-match'); - } + oldMatch?.highlightResult?.update(matchColor, oldMatch.isShadow ? undefined : 'find-match'); const match = this.matches[index]; this._findMatchIndex = index; diff --git a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts index 51b61d425b1..f07e2ac2bec 100644 --- a/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts +++ b/src/vs/workbench/contrib/notebook/common/services/notebookSimpleWorker.ts @@ -199,9 +199,7 @@ export class NotebookEditorSimpleWorker implements IRequestHandler, IDisposable public acceptModelChanged(strURL: string, event: NotebookCellsChangedEventDto) { const model = this._models[strURL]; - if (model) { - model.acceptModelChanged(event); - } + model?.acceptModelChanged(event); } public acceptRemovedModel(strURL: string): void { diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index f3b78f84cca..c13ec6b5e0c 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -145,9 +145,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo this.setActiveChannel(channel); this._onActiveOutputChannel.fire(channelId); const outputView = this.viewsService.getActiveViewWithId(OUTPUT_VIEW_ID); - if (outputView) { - outputView.showChannel(channel, true); - } + outputView?.showChannel(channel, true); } } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index f754c3a78a8..a3b51602a06 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -84,9 +84,7 @@ export class OutputViewPane extends ViewPane { override focus(): void { super.focus(); - if (this.editorPromise) { - this.editorPromise.then(() => this.editor.focus()); - } + this.editorPromise?.then(() => this.editor.focus()); } override renderBody(container: HTMLElement): void { diff --git a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts index 320f1d39ed1..3d1ddf27618 100644 --- a/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/browser/perfviewEditor.ts @@ -95,9 +95,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { this._model = this._modelService.getModel(resource) || this._modelService.createModel('Loading...', langId, resource); this._modelDisposables.push(langId.onDidChange(e => { - if (this._model) { - this._model.setMode(e); - } + this._model?.setMode(e); })); this._modelDisposables.push(this._extensionService.onDidChangeExtensionsStatus(this._updateModel, this)); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts index 1f36a446b97..fd43f3b24f5 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditorContribution.ts @@ -32,7 +32,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { equals } from 'vs/base/common/arrays'; import { assertIsDefined } from 'vs/base/common/types'; import { isEqual } from 'vs/base/common/resources'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const NLS_LAUNCH_MESSAGE = nls.localize('defineKeybinding.start', "Define Keybinding"); const NLS_KB_LAYOUT_ERROR_MESSAGE = nls.localize('defineKeybinding.kbLayoutErrorMessage', "You won't be able to produce this key combination under your current keyboard layout."); @@ -51,7 +51,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon constructor( private _editor: ICodeEditor, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService ) { super(); @@ -70,7 +70,7 @@ export class DefineKeybindingController extends Disposable implements IEditorCon } private _update(): void { - if (!isInterestingEditorModel(this._editor, this._userDataProfilesService)) { + if (!isInterestingEditorModel(this._editor, this._userDataProfileService)) { this._disposeKeybindingWidgetRenderer(); this._disposeKeybindingDecorationRenderer(); return; @@ -365,7 +365,7 @@ class DefineKeybindingCommand extends EditorCommand { } runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor): void { - if (!isInterestingEditorModel(editor, accessor.get(IUserDataProfilesService)) || editor.getOption(EditorOption.readOnly)) { + if (!isInterestingEditorModel(editor, accessor.get(IUserDataProfileService)) || editor.getOption(EditorOption.readOnly)) { return; } const controller = DefineKeybindingController.get(editor); @@ -375,12 +375,12 @@ class DefineKeybindingCommand extends EditorCommand { } } -function isInterestingEditorModel(editor: ICodeEditor, userDataProfilesService: IUserDataProfilesService): boolean { +function isInterestingEditorModel(editor: ICodeEditor, userDataProfileService: IUserDataProfileService): boolean { const model = editor.getModel(); if (!model) { return false; } - return isEqual(model.uri, userDataProfilesService.currentProfile.keybindingsResource); + return isEqual(model.uri, userDataProfileService.currentProfile.keybindingsResource); } registerEditorContribution(DefineKeybindingController.ID, DefineKeybindingController); diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 20f1af21202..c32f22f0870 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -353,12 +353,12 @@ .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-ignored .codicon, .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-default-overridden .codicon { - vertical-align: text-top; + vertical-align: middle; padding-left: 1px; } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-label .codicon { - vertical-align: text-top; + vertical-align: middle; } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides a.modified-scope { diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 34c4289aca3..99dc246ef1f 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -43,7 +43,7 @@ import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const SETTINGS_EDITOR_COMMAND_SEARCH = 'settings.action.search'; @@ -140,7 +140,7 @@ class PreferencesActionsContribution extends Disposable implements IWorkbenchCon constructor( @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - @IUserDataProfilesService private readonly userDataProfileService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IPreferencesService private readonly preferencesService: IPreferencesService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILabelService private readonly labelService: ILabelService, diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts index f74ec72414c..cab53e0d6a6 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesWidgets.ts @@ -463,15 +463,11 @@ export class SearchWidget extends Widget { layout(dimension: DOM.Dimension) { if (dimension.width < 400) { - if (this.countElement) { - this.countElement.classList.add('hide'); - } + this.countElement?.classList.add('hide'); this.inputBox.inputElement.style.paddingRight = '0px'; } else { - if (this.countElement) { - this.countElement.classList.remove('hide'); - } + this.countElement?.classList.remove('hide'); this.inputBox.inputElement.style.paddingRight = this.getControlsWidth() + 'px'; } @@ -506,9 +502,7 @@ export class SearchWidget extends Widget { } override dispose(): void { - if (this.options.focusKey) { - this.options.focusKey.set(false); - } + this.options.focusKey?.set(false); super.dispose(); } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index dcf4395b126..a13ed3f1843 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -254,9 +254,7 @@ export class SettingsEditor2 extends EditorPane { })); this._register(workspaceTrustManagementService.onDidChangeTrust(() => { - if (this.searchResultModel) { - this.searchResultModel.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); - } + this.searchResultModel?.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); if (this.settingsTreeModel) { this.settingsTreeModel.updateWorkspaceTrust(workspaceTrustManagementService.isWorkspaceTrusted()); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index aac1b348f1c..9bfac0d0356 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -5,14 +5,17 @@ import * as DOM from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; +import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; +import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; -import { getDefaultIgnoredSettings } from 'vs/platform/userDataSync/common/userDataSync'; +import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; +import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; const $ = DOM.$; @@ -22,27 +25,34 @@ export interface ISettingOverrideClickEvent { } /** - * Renders the indicators next to a setting, such as Sync Ignored, Also Modified In, etc. + * Renders the indicators next to a setting, such as "Also Modified In". */ export class SettingsTreeIndicatorsLabel { - /** - * This element wraps around the other elements. - */ - private labelElement: HTMLElement; + private indicatorsContainerElement: HTMLElement; private scopeOverridesElement: HTMLElement; private syncIgnoredElement: HTMLElement; private defaultOverrideIndicatorElement: HTMLElement; - private defaultOverrideIndicatorLabel: SimpleIconLabel; + private hoverDelegate: IHoverDelegate; - constructor(container: HTMLElement) { - this.labelElement = DOM.append(container, $('.misc-label')); - this.labelElement.style.display = 'inline'; + constructor( + container: HTMLElement, + @IConfigurationService configurationService: IConfigurationService, + @IHoverService hoverService: IHoverService, + @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService) { + this.indicatorsContainerElement = DOM.append(container, $('.misc-label')); + this.indicatorsContainerElement.style.display = 'inline'; this.scopeOverridesElement = this.createScopeOverridesElement(); this.syncIgnoredElement = this.createSyncIgnoredElement(); - const { element: defaultOverrideElement, label: defaultOverrideLabel } = this.createDefaultOverrideIndicator(); - this.defaultOverrideIndicatorElement = defaultOverrideElement; - this.defaultOverrideIndicatorLabel = defaultOverrideLabel; + this.defaultOverrideIndicatorElement = this.createDefaultOverrideIndicator(); + + this.hoverDelegate = { + showHover: (options: IHoverDelegateOptions, focus?: boolean) => { + return hoverService.showHover(options, focus); + }, + delay: configurationService.getValue('workbench.hover.delay'), + placement: 'element' + }; } private createScopeOverridesElement(): HTMLElement { @@ -53,15 +63,17 @@ export class SettingsTreeIndicatorsLabel { private createSyncIgnoredElement(): HTMLElement { const syncIgnoredElement = $('span.setting-item-ignored'); const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); - syncIgnoredLabel.text = `$(sync-ignored) ${localize('extensionSyncIgnoredLabel', 'Sync: Ignored')}`; - syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); + syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Not synced'); + const syncIgnoredHoverContent = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); + setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent); return syncIgnoredElement; } - private createDefaultOverrideIndicator(): { element: HTMLElement; label: SimpleIconLabel } { + private createDefaultOverrideIndicator(): HTMLElement { const defaultOverrideIndicator = $('span.setting-item-default-overridden'); const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator); - return { element: defaultOverrideIndicator, label: defaultOverrideLabel }; + defaultOverrideLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); + return defaultOverrideIndicator; } private render() { @@ -69,22 +81,23 @@ export class SettingsTreeIndicatorsLabel { return element.style.display !== 'none'; }); - this.labelElement.innerText = ''; - this.labelElement.style.display = 'none'; + this.indicatorsContainerElement.innerText = ''; + this.indicatorsContainerElement.style.display = 'none'; if (elementsToShow.length) { - this.labelElement.style.display = 'inline'; - DOM.append(this.labelElement, $('span', undefined, '(')); + this.indicatorsContainerElement.style.display = 'inline'; + DOM.append(this.indicatorsContainerElement, $('span', undefined, '(')); for (let i = 0; i < elementsToShow.length - 1; i++) { - DOM.append(this.labelElement, elementsToShow[i]); - DOM.append(this.labelElement, $('span.comma', undefined, ', ')); + DOM.append(this.indicatorsContainerElement, elementsToShow[i]); + DOM.append(this.indicatorsContainerElement, $('span.comma', undefined, ' • ')); } - DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); - DOM.append(this.labelElement, $('span', undefined, ')')); + DOM.append(this.indicatorsContainerElement, elementsToShow[elementsToShow.length - 1]); + DOM.append(this.indicatorsContainerElement, $('span', undefined, ')')); } } updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) { - this.syncIgnoredElement.style.display = ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; + this.syncIgnoredElement.style.display = this.userDataSyncEnablementService.isEnabled() + && ignoredSettings.includes(element.setting.key) ? 'inline' : 'none'; this.render(); } @@ -122,24 +135,29 @@ export class SettingsTreeIndicatorsLabel { updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { this.defaultOverrideIndicatorElement.style.display = 'none'; - const defaultValueSource = element.defaultValueSource; - if (defaultValueSource) { + const sourceToDisplay = getDefaultValueSourceToDisplay(element); + if (sourceToDisplay !== undefined) { this.defaultOverrideIndicatorElement.style.display = 'inline'; - let sourceToDisplay = ''; - if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; - } else if (typeof defaultValueSource === 'string') { - sourceToDisplay = defaultValueSource; - } - if (sourceToDisplay) { - this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); - this.defaultOverrideIndicatorLabel.text = `$(replace) ${sourceToDisplay}`; - } + const defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); + setupCustomHover(this.hoverDelegate, this.defaultOverrideIndicatorElement, defaultOverrideHoverContent); } this.render(); } } +function getDefaultValueSourceToDisplay(element: SettingsTreeSettingElement): string | undefined { + let sourceToDisplay: string | undefined; + const defaultValueSource = element.defaultValueSource; + if (defaultValueSource) { + if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { + sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; + } else if (typeof defaultValueSource === 'string') { + sourceToDisplay = defaultValueSource; + } + } + return sourceToDisplay; +} + export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService): string { const ariaLabelSections: string[] = []; @@ -159,14 +177,9 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, } // Add default override indicator text - if (element.defaultValueSource) { - const defaultValueSource = element.defaultValueSource; - if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { - const extensionSource = defaultValueSource.displayName ?? defaultValueSource.id; - ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", extensionSource)); - } else if (typeof defaultValueSource === 'string') { - ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", defaultValueSource)); - } + const sourceToDisplay = getDefaultValueSourceToDisplay(element); + if (sourceToDisplay !== undefined) { + ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); } const ariaLabel = ariaLabelSections.join('. '); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 5e905799eef..fcf032fd281 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -785,7 +785,7 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre const categoryElement = DOM.append(labelCategoryContainer, $('span.setting-item-category')); const labelElementContainer = DOM.append(labelCategoryContainer, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); + const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement); const descriptionElement = DOM.append(container, $('.setting-item-description')); const modifiedIndicatorElement = DOM.append(container, $('.setting-item-modified-indicator')); @@ -1814,7 +1814,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const categoryElement = DOM.append(titleElement, $('span.setting-item-category')); const labelElementContainer = DOM.append(titleElement, $('span.setting-item-label')); const labelElement = new SimpleIconLabel(labelElementContainer); - const indicatorsLabel = new SettingsTreeIndicatorsLabel(titleElement); + const indicatorsLabel = this._instantiationService.createInstance(SettingsTreeIndicatorsLabel, titleElement); const descriptionAndValueElement = DOM.append(container, $('.setting-item-value-description')); const controlElement = DOM.append(descriptionAndValueElement, $('.setting-item-bool-control')); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index e03798c8af3..b7b89c030d7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -267,9 +267,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.tags.add(MODIFIED_SETTING_TAG); } - if (this.setting.tags) { - this.setting.tags.forEach(tag => this.tags!.add(tag)); - } + this.setting.tags?.forEach(tag => this.tags!.add(tag)); if (this.setting.restricted) { this.tags.add(REQUIRE_TRUSTED_WORKSPACE_SETTING_TAG); @@ -810,9 +808,7 @@ export class SearchResultModel extends SettingsTreeModel { const localMatchKeys = new Set(); const localResult = this.rawSearchResults[SearchResultIdx.Local]; - if (localResult) { - localResult.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); - } + localResult?.filterMatches.forEach(m => localMatchKeys.add(m.setting.key)); const remoteResult = this.rawSearchResults[SearchResultIdx.Remote]; if (remoteResult) { diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 1aad47f77dc..be64900c807 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -23,7 +23,7 @@ import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEdit import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -36,7 +36,7 @@ export class PreferencesContribution implements IWorkbenchContribution { @ITextModelService private readonly textModelResolverService: ITextModelService, @IPreferencesService private readonly preferencesService: IPreferencesService, @ILanguageService private readonly languageService: ILanguageService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, @@ -71,7 +71,7 @@ export class PreferencesContribution implements IWorkbenchContribution { }, ({ resource, options }): EditorInputWithOptions => { // Global User Settings File - if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { return { editor: this.preferencesService.createSplitJsonEditorInput(ConfigurationTarget.USER_LOCAL, resource), options }; } diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 7da92791ee6..b6488780711 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -32,6 +32,7 @@ import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; import { timeout } from 'vs/base/common/async'; +import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; export class LabelContribution implements IWorkbenchContribution { constructor( @@ -99,6 +100,7 @@ class RemoteLogOutputChannels implements IWorkbenchContribution { if (remoteEnv) { const outputChannelRegistry = Registry.as(OutputExt.OutputChannels); outputChannelRegistry.registerChannel({ id: 'remoteExtensionLog', label: localize('remoteExtensionLog', "Remote Server"), file: joinPath(remoteEnv.logsPath, `${RemoteExtensionLogFileName}.log`), log: true }); + outputChannelRegistry.registerChannel({ id: 'remotePtyHostLog', label: localize('remotePtyHostLog', "Remote Pty Host"), file: joinPath(remoteEnv.logsPath, `${TerminalLogConstants.FileName}.log`), log: true }); } }); } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 47468bfe3bc..8d824b6a5fd 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2380,9 +2380,7 @@ export class SCMViewPane extends ViewPane { if (e.editorOptions.pinned) { const activeEditorPane = this.editorService.activeEditorPane; - if (activeEditorPane) { - activeEditorPane.group.pinEditor(activeEditorPane.input); - } + activeEditorPane?.group.pinEditor(activeEditorPane.input); } } diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index b19a449124a..77782e09059 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -55,7 +55,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ISearchConfiguration, SearchSortOrder, SEARCH_EXCLUDE_CONFIG, VIEWLET_ID, VIEW_ID } from 'vs/workbench/services/search/common/search'; -import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configurationMigration'; +import { Extensions, IConfigurationMigrationRegistry } from 'vs/workbench/common/configuration'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index becdd37f551..e6be26f64a1 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -291,9 +291,7 @@ export function cancelSearch(accessor: ServicesAccessor) { export function refreshSearch(accessor: ServicesAccessor) { const viewsService = accessor.get(IViewsService); const searchView = getSearchView(viewsService); - if (searchView) { - searchView.triggerQueryChange({ preserveFocus: false }); - } + searchView?.triggerQueryChange({ preserveFocus: false }); } export function collapseDeepestExpandedLevel(accessor: ServicesAccessor) { diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 17baf536043..8f87fe9247d 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -395,9 +395,7 @@ export class SearchView extends ViewPane { } // Enable highlights if there are searchresults - if (this.viewModel) { - this.viewModel.searchResult.toggleHighlights(visible); - } + this.viewModel?.searchResult.toggleHighlights(visible); } get searchAndReplaceWidget(): SearchWidget { @@ -483,18 +481,14 @@ export class SearchView extends ViewPane { this._register(inputFocusTracker.onDidFocus(() => { this.lastFocusState = 'input'; this.inputBoxFocused.set(true); - if (contextKey) { - contextKey.set(true); - } + contextKey?.set(true); })); this._register(inputFocusTracker.onDidBlur(() => { this.inputBoxFocused.set(this.searchWidget.searchInputHasFocus() || this.searchWidget.replaceInputHasFocus() || this.inputPatternIncludes.inputHasFocus() || this.inputPatternExcludes.inputHasFocus()); - if (contextKey) { - contextKey.set(false); - } + contextKey?.set(false); })); } @@ -752,9 +746,7 @@ export class SearchView extends ViewPane { this._register(Event.debounce(this.tree.onDidOpen, (last, event) => event, 75, true)(options => { if (options.element instanceof Match) { const selectedMatch: Match = options.element; - if (this.currentSelectedFileMatch) { - this.currentSelectedFileMatch.setSelectedMatch(null); - } + this.currentSelectedFileMatch?.setSelectedMatch(null); this.currentSelectedFileMatch = selectedMatch.parent(); this.currentSelectedFileMatch.setSelectedMatch(selectedMatch); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index 2fd8f4ab2c8..4df55f37483 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -528,9 +528,7 @@ export class FolderMatch extends Disposable { bindModel(model: ITextModel): void { const fileMatch = this._fileMatches.get(model.uri); - if (fileMatch) { - fileMatch.bindModel(model); - } + fileMatch?.bindModel(model); } add(raw: IFileMatch[], silent: boolean): void { @@ -784,9 +782,7 @@ export class SearchResult extends Disposable { private onModelAdded(model: ITextModel): void { const folderMatch = this._folderMatchesMap.findSubstr(model.uri); - if (folderMatch) { - folderMatch.bindModel(model); - } + folderMatch?.bindModel(model); } private createFolderMatchWithResource(resource: URI, id: string, index: number, query: ITextQuery): FolderMatchWithResource { @@ -818,9 +814,7 @@ export class SearchResult extends Disposable { } const folderMatch = this.getFolderMatch(raw[0].resource); - if (folderMatch) { - folderMatch.add(raw, silent); - } + folderMatch?.add(raw, silent); }); this._otherFilesMatch?.add(other, silent); diff --git a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts index 104eb095df6..cd49e55a241 100644 --- a/src/vs/workbench/contrib/search/test/common/searchModel.test.ts +++ b/src/vs/workbench/contrib/search/test/common/searchModel.test.ts @@ -113,9 +113,7 @@ suite('SearchModel', () => { function canceleableSearchService(tokenSource: CancellationTokenSource): ISearchService { return { textSearch(query: ISearchQuery, token?: CancellationToken, onProgress?: (result: ISearchProgressItem) => void): Promise { - if (token) { - token.onCancellationRequested(() => tokenSource.cancel()); - } + token?.onCancellationRequested(() => tokenSource.cancel()); return new Promise(resolve => { queueMicrotask(() => { diff --git a/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts b/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts index 24aec32b2b6..7ea8636ceae 100644 --- a/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts +++ b/src/vs/workbench/contrib/sessionSync/browser/sessionSync.contribution.ts @@ -27,17 +27,24 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; registerSingleton(ISessionSyncWorkbenchService, SessionSyncWorkbenchService); const applyLatestCommand = { - id: 'workbench.sessionSync.actions.applyLatest', + id: 'workbench.experimental.sessionSync.actions.applyLatest', title: localize('apply latest', "{0}: Apply Latest Edit Session", EDIT_SESSION_SYNC_TITLE), }; const storeLatestCommand = { - id: 'workbench.sessionSync.actions.storeLatest', + id: 'workbench.experimental.sessionSync.actions.storeLatest', title: localize('store latest', "{0}: Store Latest Edit Session", EDIT_SESSION_SYNC_TITLE), }; +const continueEditSessionCommand = { + id: '_workbench.experimental.sessionSync.actions.continueEditSession', + title: localize('continue edit session', "{0}: Continue Edit Session", EDIT_SESSION_SYNC_TITLE), +}; +const queryParamName = 'editSessionId'; export class SessionSyncContribution extends Disposable implements IWorkbenchContribution { @@ -47,17 +54,23 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon @ISessionSyncWorkbenchService private readonly sessionSyncWorkbenchService: ISessionSyncWorkbenchService, @IFileService private readonly fileService: IFileService, @IProgressService private readonly progressService: IProgressService, + @IOpenerService private readonly openerService: IOpenerService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ISCMService private readonly scmService: ISCMService, @INotificationService private readonly notificationService: INotificationService, @IDialogService private readonly dialogService: IDialogService, @ILogService private readonly logService: ILogService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, @IProductService private readonly productService: IProductService, @IConfigurationService private configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, ) { super(); + if (this.environmentService.editSessionId !== undefined) { + void this.applyEditSession(this.environmentService.editSessionId).then(() => this.environmentService.editSessionId = undefined); + } + this.configurationService.onDidChangeConfiguration((e) => { if (e.affectsConfiguration('workbench.experimental.sessionSync.enabled')) { this.registerActions(); @@ -72,15 +85,50 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon return; } - this.registerApplyEditSessionAction(); - this.registerStoreEditSessionAction(); + this.registerContinueEditSessionAction(); + + this.registerApplyLatestEditSessionAction(); + this.registerStoreLatestEditSessionAction(); this.registered = true; } - private registerApplyEditSessionAction(): void { + private registerContinueEditSessionAction() { const that = this; - this._register(registerAction2(class ApplyEditSessionAction extends Action2 { + this._register(registerAction2(class ContinueEditSessionAction extends Action2 { + constructor() { + super({ + id: continueEditSessionCommand.id, + title: continueEditSessionCommand.title + }); + } + + async run(accessor: ServicesAccessor, workspaceUri: URI): Promise { + // Run the store action to get back a ref + const ref = await that.storeEditSession(); + + // Append the ref to the URI + if (ref !== undefined) { + const encodedRef = encodeURIComponent(ref); + workspaceUri = workspaceUri.with({ + query: workspaceUri.query.length > 0 ? (workspaceUri + `&${queryParamName}=${encodedRef}`) : `${queryParamName}=${encodedRef}` + }); + + that.environmentService.editSessionId = ref; + } else { + that.logService.warn(`Edit Sessions: Failed to store edit session when invoking ${continueEditSessionCommand.id}.`); + } + + // Open the URI + that.logService.info(`Edit Sessions: opening ${workspaceUri.toString()}`); + await that.openerService.open(workspaceUri, { openExternal: true }); + } + })); + } + + private registerApplyLatestEditSessionAction(): void { + const that = this; + this._register(registerAction2(class ApplyLatestEditSessionAction extends Action2 { constructor() { super({ id: applyLatestCommand.id, @@ -100,9 +148,9 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon })); } - private registerStoreEditSessionAction(): void { + private registerStoreLatestEditSessionAction(): void { const that = this; - this._register(registerAction2(class StoreEditSessionAction extends Action2 { + this._register(registerAction2(class StoreLatestEditSessionAction extends Action2 { constructor() { super({ id: storeLatestCommand.id, @@ -122,8 +170,12 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon })); } - async applyEditSession() { - const editSession = await this.sessionSyncWorkbenchService.read(undefined); + async applyEditSession(ref?: string): Promise { + if (ref !== undefined) { + this.logService.info(`Edit Sessions: Applying edit session with ref ${ref}.`); + } + + const editSession = await this.sessionSyncWorkbenchService.read(ref); if (!editSession) { return; } @@ -160,6 +212,7 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } if (hasLocalUncommittedChanges) { + // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents const result = await this.dialogService.confirm({ message: localize('apply edit session warning', 'Applying your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'), type: 'warning', @@ -178,12 +231,12 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } } } catch (ex) { - this.logService.error(ex); + this.logService.error('Edit Sessions:', (ex as Error).toString()); this.notificationService.error(localize('apply failed', "Failed to apply your edit session.")); } } - async storeEditSession() { + async storeEditSession(): Promise { const folders: Folder[] = []; for (const repository of this.scmService.repositories) { @@ -223,7 +276,9 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon const data: EditSession = { folders, version: 1 }; try { - await this.sessionSyncWorkbenchService.write(data); + const ref = await this.sessionSyncWorkbenchService.write(data); + this.logService.info(`Edit Sessions: Stored edit session with ref ${ref}.`); + return ref; } catch (ex) { type UploadFailedEvent = { reason: string }; type UploadFailedClassification = { @@ -245,6 +300,8 @@ export class SessionSyncContribution extends Disposable implements IWorkbenchCon } } } + + return undefined; } private getChangedResources(repository: ISCMRepository) { diff --git a/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts b/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts index b4f169a12f9..83cfaf3e64a 100644 --- a/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts +++ b/src/vs/workbench/contrib/sessionSync/test/browser/sessionSync.test.ts @@ -26,6 +26,8 @@ import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; +import { TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; const folderName = 'test-folder'; const folderUri = URI.file(`/${folderName}`); @@ -53,6 +55,7 @@ suite('Edit session sync', () => { instantiationService.stub(ISessionSyncWorkbenchService, new class extends mock() { }); instantiationService.stub(IProgressService, ProgressService); instantiationService.stub(ISCMService, SCMService); + instantiationService.stub(IEnvironmentService, TestEnvironmentService); instantiationService.stub(IConfigurationService, new TestConfigurationService({ workbench: { experimental: { sessionSync: { enabled: true } } } })); instantiationService.stub(IWorkspaceContextService, new class extends mock() { override getWorkspace() { diff --git a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts index ef7217b935b..917e0da44e4 100644 --- a/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/configureSnippets.ts @@ -18,7 +18,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { isValidBasename } from 'vs/base/common/extpath'; import { joinPath, basename } from 'vs/base/common/resources'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; namespace ISnippetPick { export function is(thing: object | undefined): thing is ISnippetPick { @@ -31,7 +31,7 @@ interface ISnippetPick extends IQuickPickItem { hint?: true; } -async function computePicks(snippetService: ISnippetsService, userDataProfilesService: IUserDataProfilesService, languageService: ILanguageService) { +async function computePicks(snippetService: ISnippetsService, userDataProfileService: IUserDataProfileService, languageService: ILanguageService) { const existing: ISnippetPick[] = []; const future: ISnippetPick[] = []; @@ -85,7 +85,7 @@ async function computePicks(snippetService: ISnippetsService, userDataProfilesSe } } - const dir = userDataProfilesService.currentProfile.snippetsHome; + const dir = userDataProfileService.currentProfile.snippetsHome; for (const languageId of languageService.getRegisteredLanguageIds()) { const label = languageService.getLanguageName(languageId); if (label && !seen.has(languageId)) { @@ -227,19 +227,19 @@ registerAction2(class ConfigureSnippets extends Action2 { const quickInputService = accessor.get(IQuickInputService); const opener = accessor.get(IOpenerService); const languageService = accessor.get(ILanguageService); - const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileService = accessor.get(IUserDataProfileService); const workspaceService = accessor.get(IWorkspaceContextService); const fileService = accessor.get(IFileService); const textFileService = accessor.get(ITextFileService); - const picks = await computePicks(snippetService, userDataProfilesService, languageService); + const picks = await computePicks(snippetService, userDataProfileService, languageService); const existing: QuickPickInput[] = picks.existing; type SnippetPick = IQuickPickItem & { uri: URI } & { scope: string }; const globalSnippetPicks: SnippetPick[] = [{ scope: nls.localize('new.global_scope', 'global'), label: nls.localize('new.global', "New Global Snippets file..."), - uri: userDataProfilesService.currentProfile.snippetsHome + uri: userDataProfileService.currentProfile.snippetsHome }]; const workspaceSnippetPicks: SnippetPick[] = []; diff --git a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts index 9946967398e..5fa9d1d244a 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetsService.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetsService.ts @@ -30,7 +30,7 @@ import { isStringArray } from 'vs/base/common/types'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ILanguageConfigurationService } from 'vs/editor/common/languages/languageConfigurationRegistry'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; namespace snippetExt { @@ -178,7 +178,7 @@ class SnippetsService implements ISnippetsService { constructor( @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @ILanguageService private readonly _languageService: ILanguageService, @ILogService private readonly _logService: ILogService, @@ -350,7 +350,7 @@ class SnippetsService implements ISnippetsService { } private async _initUserSnippets(): Promise { - const userSnippetsFolder = this._userDataProfilesService.currentProfile.snippetsHome; + const userSnippetsFolder = this._userDataProfileService.currentProfile.snippetsHome; await this._fileService.createFolder(userSnippetsFolder); return await this._initFolderSnippets(SnippetSource.User, userSnippetsFolder, this._disposables); } diff --git a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts index 0d307a071df..f7d122e7e15 100644 --- a/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts +++ b/src/vs/workbench/contrib/tasks/browser/taskQuickPick.ts @@ -9,17 +9,17 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import * as Types from 'vs/base/common/types'; import { ITaskService, IWorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService'; -import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput'; +import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Codicon } from 'vs/base/common/codicons'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { getColorClass, getColorStyleElement, getStandardColors } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; +import { getColorClass, getColorStyleElement } from 'vs/workbench/contrib/terminal/browser/terminalIcon'; import { TaskQuickPickEntryType } from 'vs/workbench/contrib/tasks/browser/abstractTaskService'; export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail'; @@ -78,25 +78,25 @@ export class TaskQuickPick extends Disposable { } public static getTaskLabelWithIcon(task: Task | ConfiguringTask): string { - return task.configurationProperties.icon ? `$(${task.configurationProperties.icon}) ${task._label}` : task.configurationProperties.color ? `$(${Codicon.tools.id}) ${task._label}` : `${task._label}`; + const icon = task.configurationProperties.icon; + if (!icon) { + return `${task._label}`; + } + return icon.id ? `$(${icon.id}) ${task._label}` : `$(${Codicon.tools.id}) ${task._label}`; } public static applyColorStyles(task: Task | ConfiguringTask, entry: TaskQuickPickEntryType | ITaskTwoLevelQuickPickEntry, themeService: IThemeService): void { - if (task.configurationProperties.color) { + if (task.configurationProperties.icon?.color) { const colorTheme = themeService.getColorTheme(); const styleElement = getColorStyleElement(colorTheme); - entry.iconClasses = [getColorClass(task.configurationProperties.color)]; + entry.iconClasses = [getColorClass(task.configurationProperties.icon.color)]; document.body.appendChild(styleElement); } } private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry { - const customizeIconButton = { iconClass: ThemeIcon.asClassName(Codicon.pencil), tooltip: nls.localize('setIconAndColor', "Choose color and icon") }; const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined }; entry.buttons = []; - if (CustomTask.is(task)) { - entry.buttons.push(customizeIconButton); - } entry.buttons.push({ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }); entry.buttons.push(...extraButtons); TaskQuickPick.applyColorStyles(task, entry, this._themeService); @@ -234,9 +234,6 @@ export class TaskQuickPick extends Disposable { if (indexToRemove >= 0) { picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)]; } - } else if (context.button.iconClass = ThemeIcon.asClassName(Codicon.pencil)) { - await this._setColor(task); - await this._setIcon(task); } else { this._quickInputService.cancel(); if (ContributedTask.is(task)) { @@ -291,72 +288,6 @@ export class TaskQuickPick extends Disposable { return; } - private async _setColor(task: Task | ConfiguringTask | null | string | undefined): Promise { - if (task === undefined || task === null || typeof task === 'string') { - return; - } - const colorTheme = this._themeService.getColorTheme(); - const standardColors: string[] = getStandardColors(colorTheme); - const styleElement = getColorStyleElement(colorTheme); - const items: (IQuickPickItem | IQuickPickSeparator)[] = []; - for (const colorKey of standardColors) { - const colorClass = getColorClass(colorKey); - items.push({ - label: `$(${Codicon.circleFilled.id}) ${colorKey.replace('terminal.ansi', '')}`, id: colorKey, description: colorKey, iconClasses: [colorClass] - }); - } - items.push({ type: 'separator' }); - const showAllColorsItem = { label: 'Reset to default' }; - items.push(showAllColorsItem); - document.body.appendChild(styleElement); - - const quickPick = this._quickInputService.createQuickPick(); - quickPick.items = items; - quickPick.matchOnDescription = true; - quickPick.show(); - const disposables: IDisposable[] = []; - const result = await new Promise(r => { - disposables.push(quickPick.onDidHide(() => r(undefined))); - disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0]))); - }); - dispose(disposables); - - if (result && task && typeof task !== 'string') { - task.configurationProperties.color = result.id; - } - document.body.removeChild(styleElement); - quickPick.hide(); - } - - private async _setIcon(task: Task | ConfiguringTask | null | string | undefined): Promise { - if (task === undefined || task === null || typeof task === 'string') { - return; - } - type Item = IQuickPickItem & { icon: ThemeIcon }; - const items: Item[] = []; - for (const icon of Codicon.getAll()) { - items.push({ label: `$(${icon.id})`, description: `${icon.id}`, id: icon.id, icon, iconClasses: task.configurationProperties.color ? [getColorClass(task.configurationProperties.color)] : undefined }); - } - - const quickPick = this._quickInputService.createQuickPick(); - quickPick.items = items; - quickPick.matchOnDescription = true; - quickPick.show(); - const disposables: IDisposable[] = []; - const result = await new Promise(r => { - disposables.push(quickPick.onDidHide(() => r(undefined))); - disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0]))); - }); - dispose(disposables); - - if (result && task && typeof task !== 'string') { - task.configurationProperties.icon = result.id; - } - if (CustomTask.is(task) && result) { - await this._taskService.customize(task, { icon: result.id, color: task.configurationProperties.color }, false); - } - quickPick.hide(); - } private async _doPickerFirstLevel(picker: IQuickPick, taskQuickPickEntries: QuickPickInput[]): Promise { diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 5946cc37dd0..1c286f2f732 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -10,7 +10,7 @@ import * as Types from 'vs/base/common/types'; import * as Platform from 'vs/base/common/platform'; import * as Async from 'vs/base/common/async'; import * as resources from 'vs/base/common/resources'; -import { IStringDictionary, values } from 'vs/base/common/collections'; +import { IStringDictionary } from 'vs/base/common/collections'; import { LinkedMap, Touch } from 'vs/base/common/map'; import Severity from 'vs/base/common/severity'; import { Event, Emitter } from 'vs/base/common/event'; @@ -52,6 +52,11 @@ import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/b import { INotificationService } from 'vs/platform/notification/common/notification'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; +import { GroupKind } from 'vs/workbench/contrib/tasks/common/taskConfiguration'; +import { Codicon } from 'vs/base/common/codicons'; + +const taskShellIntegrationStartSequence = '\x1b]633;A\x07' + '\x1b]633;P;Task=\x07' + '\x1b]633;B\x07'; +const taskShellIntegrationOutputSequence = '\x1b]633;C\x07'; interface ITerminalData { terminal: ITerminalInstance; @@ -506,6 +511,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { for (const dependency of task.configurationProperties.dependsOn) { const dependencyTask = await resolver.resolve(dependency.uri, dependency.task!); if (dependencyTask) { + dependencyTask.configurationProperties.icon = task.configurationProperties.icon; const key = dependencyTask.getMapKey(); let promise = this._activeTasks[key] ? this._getDependencyPromise(this._activeTasks[key]) : undefined; if (!promise) { @@ -1009,7 +1015,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return needsFolderQualification ? task.getQualifiedLabel() : (task.configurationProperties.name || ''); } - private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string): Promise { + private async _createShellLaunchConfig(task: CustomTask | ContributedTask, workspaceFolder: IWorkspaceFolder | undefined, variableResolver: VariableResolver, platform: Platform.Platform, options: CommandOptions, command: CommandString, args: CommandString[], waitOnExit: boolean | string | ((exitCode: number) => string)): Promise { let shellLaunchConfig: IShellLaunchConfig; const isShellCommand = task.command.runtime === RuntimeType.Shell; const needsFolderQualification = this._contextService.getWorkbenchState() === WorkbenchState.WORKSPACE; @@ -1029,14 +1035,22 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { os, remoteAuthority: this._environmentService.remoteAuthority }); + let icon: URI | ThemeIcon | { light: URI; dark: URI } | undefined; + if (task.configurationProperties.icon) { + icon = ThemeIcon.fromId(task.configurationProperties.icon.id); + } else { + const taskGroupKind = task.configurationProperties.group ? GroupKind.to(task.configurationProperties.group) : undefined; + const kindId = typeof taskGroupKind === 'string' ? taskGroupKind : taskGroupKind?.kind; + icon = kindId === 'test' ? ThemeIcon.fromId(Codicon.beaker.id) : defaultProfile.icon; + } shellLaunchConfig = { name: terminalName, type, executable: defaultProfile.path, args: defaultProfile.args, env: { ...defaultProfile.env }, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : defaultProfile.icon, - color: task.configurationProperties.color || defaultProfile.color, + icon, + color: task.configurationProperties.icon?.color || undefined, waitOnExit }; let shellSpecified: boolean = false; @@ -1112,9 +1126,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig.args = windowsShellArgs ? combinedShellArgs.join(' ') : combinedShellArgs; if (task.command.presentation && task.command.presentation.echo) { if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task in folder ${workspaceFolder.name}: ${commandLine}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ + key: 'task.executingInFolder', + comment: ['The workspace folder the task is running in', 'The task command line or label'] + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, commandLine), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } else { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task: ${commandLine}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', commandLine), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } } } else { @@ -1127,8 +1147,8 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { shellLaunchConfig = { name: terminalName, type, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : undefined, - color: task.configurationProperties.color, + icon: task.configurationProperties.icon?.id ? ThemeIcon.fromId(task.configurationProperties.icon.id) : undefined, + color: task.configurationProperties.icon?.color || undefined, executable: executable, args: args.map(a => Types.isString(a) ? a : a.value), waitOnExit @@ -1144,9 +1164,15 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return args.join(' '); }; if (needsFolderQualification && workspaceFolder) { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task in folder ${workspaceFolder.name}: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ + key: 'task.executingInFolder', + comment: ['The workspace folder the task is running in', 'The task command line or label'] + }, 'Executing task in folder {0}: {1}', workspaceFolder.name, `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } else { - shellLaunchConfig.initialText = formatMessageForTerminal(`Executing task: ${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`, { excludeLeadingNewLine: true }); + shellLaunchConfig.initialText = taskShellIntegrationStartSequence + formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', `${shellLaunchConfig.executable} ${getArgsToEcho(shellLaunchConfig.args)}`), { excludeLeadingNewLine: true }) + taskShellIntegrationOutputSequence; } } } @@ -1195,7 +1221,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (group) { // Try to find an existing terminal to split. // Even if an existing terminal is found, the split can fail if the terminal width is too small. - for (const terminal of values(this._terminals)) { + for (const terminal of Object.values(this._terminals)) { if (terminal.group === group) { this._logService.trace(`Found terminal to split for group ${group}`); const originalInstance = terminal.terminal; @@ -1218,7 +1244,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { const options = await this._resolveOptions(resolver, task.command.options); const presentationOptions = task.command.presentation; - let waitOnExit: boolean | string = false; + let waitOnExit: boolean | string | ((exitCode: number) => string) = false; if (!presentationOptions) { throw new Error('Task presentation options should not be undefined here.'); } @@ -1226,9 +1252,9 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if ((presentationOptions.close === undefined) || (presentationOptions.close === false)) { if ((presentationOptions.reveal !== RevealKind.Never) || !task.configurationProperties.isBackground || (presentationOptions.close === false)) { if (presentationOptions.panel === PanelKind.New) { - waitOnExit = nls.localize('closeTerminal', 'Press any key to close the terminal.'); + waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('closeTerminal', 'Press any key to close the terminal.')); } else if (presentationOptions.showReuseMessage) { - waitOnExit = nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.'); + waitOnExit = taskShellIntegrationWaitOnExitSequence(nls.localize('reuseTerminal', 'Terminal will be reused by tasks, press any key to close it.')); } else { waitOnExit = true; } @@ -1246,10 +1272,13 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { customPtyImplementation: (id, cols, rows) => new TerminalProcessExtHostProxy(id, cols, rows, this._terminalService), waitOnExit, name: this._createTerminalName(task), - initialText: task.command.presentation && task.command.presentation.echo ? formatMessageForTerminal(`Executing task: ${task._label}`, { excludeLeadingNewLine: true }) : undefined, + initialText: task.command.presentation && task.command.presentation.echo ? formatMessageForTerminal(nls.localize({ + key: 'task.executing', + comment: ['The task command line or label'] + }, 'Executing task: {0}', task._label), { excludeLeadingNewLine: true }) : undefined, isFeatureTerminal: true, - icon: task.configurationProperties.icon ? ThemeIcon.fromId(task.configurationProperties.icon) : undefined, - color: task.configurationProperties.color + icon: task.configurationProperties.icon?.id ? ThemeIcon.fromId(task.configurationProperties.icon.id) : undefined, + color: task.configurationProperties.icon?.color || undefined, }; } else { const resolvedResult: { command: CommandString; args: CommandString[] } = await this._resolveCommandAndArgs(resolver, task.command); @@ -1481,9 +1510,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { throw new Error('Command name should never be undefined here.'); } this._collectVariables(variables, command.name); - if (command.args) { - command.args.forEach(arg => this._collectVariables(variables, arg)); - } + command.args?.forEach(arg => this._collectVariables(variables, arg)); // Try to get a scope. const scope = (task._source).scope; if (scope !== TaskScope.Global) { @@ -1507,9 +1534,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { if (options.shell.executable) { this._collectVariables(variables, options.shell.executable); } - if (options.shell.args) { - options.shell.args.forEach(arg => this._collectVariables(variables, arg)); - } + options.shell.args?.forEach(arg => this._collectVariables(variables, arg)); } } } @@ -1678,8 +1703,12 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { private _appendOutput(output: string): void { const outputChannel = this._outputService.getChannel(this._outputChannelId); - if (outputChannel) { - outputChannel.append(output); - } + outputChannel?.append(output); } } + +function taskShellIntegrationWaitOnExitSequence(message: string): (exitCode: number) => string { + return (exitCode) => { + return `\x1b]633;D;${exitCode}\x07${message}`; + }; +} diff --git a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts index aa52f6e45a6..d455def1f6e 100644 --- a/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts +++ b/src/vs/workbench/contrib/tasks/common/jsonSchema_v2.ts @@ -13,6 +13,7 @@ import { ProblemMatcherRegistry } from 'vs/workbench/contrib/tasks/common/proble import { TaskDefinitionRegistry } from './taskDefinitionRegistry'; import * as ConfigurationResolverUtils from 'vs/workbench/services/configurationResolver/common/configurationResolverUtils'; import { inputsSchema } from 'vs/workbench/services/configurationResolver/common/configurationResolverSchema'; +import { Codicon } from 'vs/base/common/codicons'; function fixReferences(literal: any) { if (Array.isArray(literal)) { @@ -94,14 +95,30 @@ const detail: IJSONSchema = { description: nls.localize('JsonSchema.tasks.detail', 'An optional description of a task that shows in the Run Task quick pick as a detail.') }; -const color: IJSONSchema = { - type: 'string', - description: nls.localize('JsonSchema.tasks.color', 'An optional color for the task icon') -}; - const icon: IJSONSchema = { - type: 'string', - description: nls.localize('JsonSchema.tasks.icon', 'An optional icon for the task') + type: 'object', + properties: { + id: { + description: nls.localize('JsonSchema.tasks.icon.id', 'An optional icon for the task'), + type: 'string', + enum: Array.from(Codicon.getAll(), icon => icon.id), + markdownEnumDescriptions: Array.from(Codicon.getAll(), icon => `$(${icon.id})`), + }, + color: { + description: nls.localize('JsonSchema.tasks.icon.color', 'An optional color to use for the task icon'), + type: ['string', 'null'], + enum: [ + 'terminal.ansiBlack', + 'terminal.ansiRed', + 'terminal.ansiGreen', + 'terminal.ansiYellow', + 'terminal.ansiBlue', + 'terminal.ansiMagenta', + 'terminal.ansiCyan', + 'terminal.ansiWhite' + ], + }, + } }; const presentation: IJSONSchema = { @@ -388,7 +405,6 @@ const taskConfiguration: IJSONSchema = { default: false }, presentation: Objects.deepClone(presentation), - color: Objects.deepClone(color), icon: Objects.deepClone(icon), options: options, problemMatcher: { @@ -467,8 +483,7 @@ taskDescriptionProperties.identifier = Objects.deepClone(identifier); taskDescriptionProperties.type = Objects.deepClone(taskType); taskDescriptionProperties.presentation = Objects.deepClone(presentation); taskDescriptionProperties.terminal = terminal; -taskDescriptionProperties.color = color; -taskDescriptionProperties.icon = icon; +taskDescriptionProperties.icon = Objects.deepClone(icon); taskDescriptionProperties.group = Objects.deepClone(group); taskDescriptionProperties.runOptions = Objects.deepClone(runOptions); taskDescriptionProperties.detail = detail; diff --git a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts index cc0907cc5b3..9aa97b5dff9 100644 --- a/src/vs/workbench/contrib/tasks/common/problemCollectors.ts +++ b/src/vs/workbench/contrib/tasks/common/problemCollectors.ts @@ -224,9 +224,7 @@ export abstract class AbstractProblemCollector implements IDisposable { protected removeResourceToClean(owner: string, resource: string): void { const resourceSet = this.resourcesToClean.get(owner); - if (resourceSet) { - resourceSet.delete(resource); - } + resourceSet?.delete(resource); } private getResourceSetToClean(owner: string): Map { diff --git a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts index 974a6371fb6..ed7b1403249 100644 --- a/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts +++ b/src/vs/workbench/contrib/tasks/common/taskConfiguration.ts @@ -356,7 +356,7 @@ export interface IConfigurationProperties { /** * The icon for this task in the terminal tabs list */ - icon?: string; + icon?: { id: string; color?: string }; /** * The icon's color in the terminal tabs list @@ -1322,7 +1322,6 @@ namespace ConfigurationProperties { { property: 'presentation', type: CommandConfiguration.PresentationOptions }, { property: 'problemMatchers' }, { property: 'options' }, - { property: 'color' }, { property: 'icon' } ]; @@ -1350,12 +1349,7 @@ namespace ConfigurationProperties { if (Types.isString(external.identifier)) { result.identifier = external.identifier; } - if (Types.isString(external.color)) { - result.color = external.color; - } - if (Types.isString(external.icon)) { - result.icon = external.icon; - } + result.icon = external.icon; if (external.isBackground !== undefined) { result.isBackground = !!external.isBackground; @@ -1641,8 +1635,7 @@ namespace CustomTask { { name: configuredProps.configurationProperties.name || contributedTask.configurationProperties.name, identifier: configuredProps.configurationProperties.identifier || contributedTask.configurationProperties.identifier, - icon: contributedTask.configurationProperties.icon, - color: contributedTask.configurationProperties.color + icon: contributedTask.configurationProperties.icon }, ); diff --git a/src/vs/workbench/contrib/tasks/common/tasks.ts b/src/vs/workbench/contrib/tasks/common/tasks.ts index 133e902e25f..1f366dfb6d9 100644 --- a/src/vs/workbench/contrib/tasks/common/tasks.ts +++ b/src/vs/workbench/contrib/tasks/common/tasks.ts @@ -548,12 +548,7 @@ export interface IConfigurationProperties { /** * The icon for this task in the terminal tabs list */ - icon?: string; - - /** - * The icon's color in the terminal tabs list - */ - color?: string; + icon?: { id: string; color?: string }; } export enum RunOnOptions { @@ -914,6 +909,11 @@ export class ContributedTask extends CommonTask { */ command: ICommandConfiguration; + /** + * The icon for the task + */ + icon: { id: string; color?: string } | undefined; + public constructor(id: string, source: IExtensionTaskSource, label: string, type: string | undefined, defines: KeyedTaskIdentifier, command: ICommandConfiguration, hasDefinedMatchers: boolean, runOptions: IRunOptions, configurationProperties: IConfigurationProperties) { @@ -921,6 +921,7 @@ export class ContributedTask extends CommonTask { this.defines = defines; this.hasDefinedMatchers = hasDefinedMatchers; this.command = command; + this.icon = configurationProperties.icon; } public override clone(): ContributedTask { diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 2fc06c7664d..dfcf34f405d 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -25,7 +25,7 @@ import { getMimeTypes } from 'vs/editor/common/services/languagesAssociations'; import { hash } from 'vs/base/common/hash'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; type TelemetryData = { mimeType: string; @@ -56,7 +56,7 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr @IKeybindingService keybindingsService: IKeybindingService, @IWorkbenchThemeService themeService: IWorkbenchThemeService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IConfigurationService configurationService: IConfigurationService, @IPaneCompositePartService paneCompositeService: IPaneCompositePartService, @ITextFileService textFileService: ITextFileService @@ -173,17 +173,17 @@ export class TelemetryContribution extends Disposable implements IWorkbenchContr } // Check for global settings file - if (isEqual(resource, this.userDataProfilesService.currentProfile.settingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.settingsResource)) { return 'global-settings'; } // Check for keybindings file - if (isEqual(resource, this.userDataProfilesService.currentProfile.keybindingsResource)) { + if (isEqual(resource, this.userDataProfileService.currentProfile.keybindingsResource)) { return 'keybindings'; } // Check for snippets - if (isEqualOrParent(resource, this.userDataProfilesService.currentProfile.snippetsHome)) { + if (isEqualOrParent(resource, this.userDataProfileService.currentProfile.snippetsHome)) { return 'snippets'; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 90d402ce3ed..da2439feeef 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -733,14 +733,22 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.navigationModeFocusPrevious', "Focus Previous Line (Navigation Mode)"), original: 'Focus Previous Line (Navigation Mode)' }, f1: true, category, - keybinding: { + keybinding: [{ + primary: KeyCode.UpArrow, + when: ContextKeyExpr.or( + ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ), + weight: KeybindingWeight.WorkbenchContrib + }, + { primary: KeyMod.CtrlCmd | KeyCode.UpArrow, when: ContextKeyExpr.or( ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - }, + }], precondition: TerminalContextKeys.processSupported }); } @@ -755,14 +763,22 @@ export function registerTerminalActions() { title: { value: localize('workbench.action.terminal.navigationModeFocusNext', "Focus Next Line (Navigation Mode)"), original: 'Focus Next Line (Navigation Mode)' }, f1: true, category, - keybinding: { + keybinding: [{ + primary: KeyCode.DownArrow, + when: ContextKeyExpr.or( + ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED, TerminalContextKeys.navigationModeActive), + ), + weight: KeybindingWeight.WorkbenchContrib + }, + { primary: KeyMod.CtrlCmd | KeyCode.DownArrow, when: ContextKeyExpr.or( ContextKeyExpr.and(TerminalContextKeys.a11yTreeFocus, CONTEXT_ACCESSIBILITY_MODE_ENABLED), ContextKeyExpr.and(TerminalContextKeys.focus, CONTEXT_ACCESSIBILITY_MODE_ENABLED) ), weight: KeybindingWeight.WorkbenchContrib - }, + }], precondition: TerminalContextKeys.processSupported }); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts index ec9e1af2191..ec5a3cb0aa9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalFindWidget.ts @@ -123,9 +123,7 @@ export class TerminalFindWidget extends SimpleFindWidget { protected _onFocusTrackerFocus() { const instance = this._terminalService.activeInstance; - if (instance) { - instance.notifyFindWidgetFocusChanged(true); - } + instance?.notifyFindWidgetFocusChanged(true); this._findWidgetFocused.set(true); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index c294f795fa4..e94818dd002 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -393,9 +393,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { const newIndex = index < this._terminalInstances.length ? index : this._terminalInstances.length - 1; this.setActiveInstanceByIndex(newIndex); // TODO: Only focus the new instance if the group had focus? - if (this.activeInstance) { - this.activeInstance.focus(true); - } + this.activeInstance?.focus(true); } else if (index < this._activeInstanceIndex) { // Adjust active instance index if needed this._activeInstanceIndex--; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 119ef061d0a..cd7f404f1dd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -176,6 +176,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { private _horizontalScrollbar: DomScrollableElement | undefined; private _terminalHasTextContextKey: IContextKey; private _terminalA11yTreeFocusContextKey: IContextKey; + private _navigationModeActiveContextKey: IContextKey; private _cols: number = 0; private _rows: number = 0; private _fixedCols: number | undefined; @@ -408,6 +409,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._terminalHasTextContextKey = TerminalContextKeys.textSelected.bindTo(this._contextKeyService); this._terminalA11yTreeFocusContextKey = TerminalContextKeys.a11yTreeFocus.bindTo(this._contextKeyService); + this._navigationModeActiveContextKey = TerminalContextKeys.navigationModeActive.bindTo(this._contextKeyService); this._terminalAltBufferActiveContextKey = TerminalContextKeys.altBufferActive.bindTo(this._contextKeyService); this._logService.trace(`terminalInstance#ctor (instanceId: ${this.instanceId})`, this._shellLaunchConfig); @@ -1630,8 +1632,15 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { if (exitMessage) { xterm.raw.write(formatMessageForTerminal(exitMessage)); } - if (typeof this._shellLaunchConfig.waitOnExit === 'string') { - xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit, { excludeLeadingNewLine: true })); + switch (typeof this._shellLaunchConfig.waitOnExit) { + case 'string': + xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit, { excludeLeadingNewLine: true })); + break; + case 'function': + if (this.exitCode !== undefined) { + xterm.raw.write(formatMessageForTerminal(this._shellLaunchConfig.waitOnExit(this.exitCode), { excludeLeadingNewLine: true })); + } + break; } // Disable all input if the terminal is exiting and listen for next keypress xterm.raw.options.disableStdin = true; @@ -1723,7 +1732,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this.xterm.raw.options.disableStdin = false; this._isExiting = false; } - this.xterm.clearDecorations(); + if (reset) { + this.xterm.clearDecorations(); + } } // Dispose the environment info widget if it exists @@ -1822,7 +1833,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { updateAccessibilitySupport(): void { const isEnabled = this._accessibilityService.isScreenReaderOptimized(); if (isEnabled) { - this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey); + this._navigationModeAddon = new NavigationModeAddon(this._terminalA11yTreeFocusContextKey, this._navigationModeActiveContextKey); this.xterm!.raw.loadAddon(this._navigationModeAddon); } else { this._navigationModeAddon?.dispose(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts index 1529008404b..edd1bf0aeda 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMainContribution.ts @@ -3,26 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from 'vs/base/common/uri'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; +import { localize } from 'vs/nls'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; +import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ITerminalEditorService, ITerminalGroupService, ITerminalService, terminalEditorId } from 'vs/workbench/contrib/terminal/browser/terminal'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { registerLogChannel } from 'vs/workbench/services/output/common/output'; +import { join } from 'vs/base/common/path'; +import { TerminalLogConstants } from 'vs/platform/terminal/common/terminal'; /** * The main contribution for the terminal contrib. This contains calls to other components necessary * to set up the terminal but don't need to be tracked in the long term (where TerminalService would * be more relevant). */ -export class TerminalMainContribution implements IWorkbenchContribution { +export class TerminalMainContribution extends Disposable implements IWorkbenchContribution { constructor( @IEditorResolverService editorResolverService: IEditorResolverService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService private readonly _fileService: IFileService, @ILabelService labelService: ILabelService, + @ILogService private readonly _logService: ILogService, @ITerminalService terminalService: ITerminalService, @ITerminalEditorService terminalEditorService: ITerminalEditorService, @ITerminalGroupService terminalGroupService: ITerminalGroupService ) { + super(); + // Register terminal editors editorResolverService.registerEditor( `${Schemas.vscodeTerminal}:/**`, @@ -40,9 +54,7 @@ export class TerminalMainContribution implements IWorkbenchContribution { const instance = terminalService.getInstanceFromResource(resource); if (instance) { const sourceGroup = terminalGroupService.getGroupForInstance(instance); - if (sourceGroup) { - sourceGroup.removeInstance(instance); - } + sourceGroup?.removeInstance(instance); } const resolvedResource = terminalEditorService.resolveResource(instance || resource); const editor = terminalEditorService.getInputFromResource(resolvedResource) || { editor: resolvedResource }; @@ -66,5 +78,13 @@ export class TerminalMainContribution implements IWorkbenchContribution { separator: '' } }); + + // Register log channel + this._registerLogChannel('ptyHostLog', localize('ptyHost', "Pty Host"), URI.file(join(environmentService.logsPath, `${TerminalLogConstants.FileName}.log`))); + } + + private _registerLogChannel(id: string, label: string, file: URI): void { + const promise = registerLogChannel(id, label, file, this._fileService, this._logService); + this._register(toDisposable(() => promise.cancel())); } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index db29d770391..ba6540e654c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -210,7 +210,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce this._dimensions.rows = rows; this._isScreenReaderModeEnabled = isScreenReaderModeEnabled; - let newProcess: ITerminalChildProcess; + let newProcess: ITerminalChildProcess | undefined; if (shellLaunchConfig.customPtyImplementation) { this._processType = ProcessType.PsuedoTerminal; @@ -251,10 +251,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { await this._terminalProfileResolverService.resolveShellLaunchConfig(shellLaunchConfig, { remoteAuthority: this.remoteAuthority, os: this.os @@ -294,10 +296,12 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (result) { newProcess = result; } else { - this._logService.trace(`Attach to process failed for terminal ${shellLaunchConfig.attachPersistentProcess}`); - return undefined; + // Warn and just create a new terminal if attach failed for some reason + this._logService.warn(`Attach to process failed for terminal`, shellLaunchConfig.attachPersistentProcess); + shellLaunchConfig.attachPersistentProcess = undefined; } - } else { + } + if (!newProcess) { newProcess = await this._launchLocalProcess(backend, shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled, variableResolver); } if (!this._isDisposed) { @@ -335,7 +339,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce if (this._preLaunchInputQueue.length > 0 && this._process) { // Send any queued data that's waiting - newProcess.input(this._preLaunchInputQueue.join('')); + newProcess!.input(this._preLaunchInputQueue.join('')); this._preLaunchInputQueue.length = 0; } }), diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts index 2b346c110b9..6354f5f7b64 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationAddon.ts @@ -215,14 +215,14 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { if (!decoration) { return undefined; } - + if (beforeCommandExecution) { + this._placeholderDecoration = decoration; + } decoration.onRender(element => { if (element.classList.contains(DecorationSelector.OverviewRuler)) { return; } - if (beforeCommandExecution && !this._placeholderDecoration) { - this._placeholderDecoration = decoration; - } else if (!this._decorations.get(decoration.marker.id)) { + if (!this._decorations.get(decoration.marker.id)) { decoration.onDispose(() => this._decorations.delete(decoration.marker.id)); this._decorations.set(decoration.marker.id, { @@ -329,10 +329,12 @@ export class DecorationAddon extends Disposable implements ITerminalAddon { run: () => this._onDidRequestRunCommand.fire({ command, copyAsHtml: true }) }); } - actions.push({ - class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true, - run: () => this._onDidRequestRunCommand.fire({ command }) - }); + if (command.command !== '') { + actions.push({ + class: 'rerun-command', tooltip: 'Rerun Command', dispose: () => { }, id: 'terminal.rerunCommand', label: localize("terminal.rerunCommand", 'Rerun Command'), enabled: true, + run: () => this._onDidRequestRunCommand.fire({ command }) + }); + } actions.push({ class: 'how-does-this-work', tooltip: 'How does this work?', dispose: () => { }, id: 'terminal.howDoesThisWork', label: localize("terminal.howDoesThisWork", 'How does this work?'), enabled: true, run: () => this._openerService.open('https://code.visualstudio.com/docs/editor/integrated-terminal#_shell-integration') diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts index 8487459af57..34f3f4937d9 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/navigationModeAddon.ts @@ -12,7 +12,8 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { private _terminal: Terminal | undefined; constructor( - private _navigationModeContextKey: IContextKey + private _navigationModeContextKey: IContextKey, + private _navigationModeActiveContextKey: IContextKey ) { } activate(terminal: Terminal): void { @@ -27,13 +28,14 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { } this._terminal.scrollToBottom(); this._terminal.focus(); + this._navigationModeActiveContextKey.set(false); } focusPreviousLine(): void { if (!this._terminal || !this._terminal.element) { return; } - + this._navigationModeActiveContextKey.set(true); // Focus previous row if a row is already focused if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) { const element = document.activeElement.previousElementSibling; @@ -76,7 +78,7 @@ export class NavigationModeAddon implements INavigationMode, ITerminalAddon { if (!this._terminal || !this._terminal.element) { return; } - + this._navigationModeActiveContextKey.set(true); // Focus previous row if a row is already focused if (document.activeElement && document.activeElement.parentElement && document.activeElement.parentElement.classList.contains('xterm-accessibility-tree')) { const element = document.activeElement.nextElementSibling; diff --git a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts index 553c0edf9df..72f1792fc71 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalContextKey.ts @@ -22,6 +22,7 @@ export const enum TerminalContextKeyStrings { TabsMouse = 'terminalTabsMouse', AltBufferActive = 'terminalAltBufferActive', A11yTreeFocus = 'terminalA11yTreeFocus', + NavigationModeActive = 'terminalNavigationModeActive', ViewShowing = 'terminalViewShowing', TextSelected = 'terminalTextSelected', FindVisible = 'terminalFindVisible', @@ -84,6 +85,11 @@ export namespace TerminalContextKeys { /** Whether the user is navigating a terminal's the accessibility tree. */ export const a11yTreeFocus = new RawContextKey(TerminalContextKeyStrings.A11yTreeFocus, false, true); + /** + * Whether the user is currently in navigation mode + */ + export const navigationModeActive = new RawContextKey(TerminalContextKeyStrings.NavigationModeActive, false, true); + /** Whether text is selected in the active terminal. */ export const textSelected = new RawContextKey(TerminalContextKeyStrings.TextSelected, false, localize('terminalTextSelectedContextKey', "Whether text is selected in the active terminal.")); diff --git a/src/vs/workbench/contrib/testing/browser/media/testing.css b/src/vs/workbench/contrib/testing/browser/media/testing.css index a58463622f5..7f42e463ff6 100644 --- a/src/vs/workbench/contrib/testing/browser/media/testing.css +++ b/src/vs/workbench/contrib/testing/browser/media/testing.css @@ -21,6 +21,10 @@ align-items: center; } +.test-explorer .test-item .label { + white-space: pre-wrap; +} + .test-explorer .test-item, .test-output-peek-tree .test-peek-item { display: flex; diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 9ab3bc94bd8..0c639a29c53 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -554,6 +554,11 @@ export class TestingExplorerViewModel extends Disposable { followRunningTests = getTestingConfiguration(configurationService, TestingConfigKeys.FollowRunningTest); })); + let alwaysRevealTestAfterStateChange = getTestingConfiguration(configurationService, TestingConfigKeys.AlwaysRevealTestOnStateChange); + this._register(configurationService.onDidChangeConfiguration(() => { + alwaysRevealTestAfterStateChange = getTestingConfiguration(configurationService, TestingConfigKeys.AlwaysRevealTestOnStateChange); + })); + this._register(testResults.onTestChanged(evt => { if (!followRunningTests) { return; @@ -569,7 +574,7 @@ export class TestingExplorerViewModel extends Disposable { return; } - this.revealById(evt.item.item.extId, false, false); + this.revealById(evt.item.item.extId, alwaysRevealTestAfterStateChange, false); })); this._register(testResults.onResultsChanged(() => { diff --git a/src/vs/workbench/contrib/testing/common/configuration.ts b/src/vs/workbench/contrib/testing/common/configuration.ts index e9ab2c0ac56..eabfe6204c1 100644 --- a/src/vs/workbench/contrib/testing/common/configuration.ts +++ b/src/vs/workbench/contrib/testing/common/configuration.ts @@ -17,6 +17,7 @@ export const enum TestingConfigKeys { DefaultGutterClickAction = 'testing.defaultGutterClickAction', GutterEnabled = 'testing.gutterEnabled', SaveBeforeTest = 'testing.saveBeforeTest', + AlwaysRevealTestOnStateChange = 'testing.alwaysRevealTestOnStateChange' } export const enum AutoOpenTesting { @@ -128,6 +129,11 @@ export const testingConfiguation: IConfigurationNode = { default: 'openOnTestStart', description: localize('testing.openTesting', "Controls when the testing view should open.") }, + [TestingConfigKeys.AlwaysRevealTestOnStateChange]: { + markdownDescription: localize('testing.alwaysRevealTestOnStateChange', "Always reveal the executed test when `#testing.followRunningTest#` is on. If this setting is turned off, only failed tests will be revealed."), + type: 'boolean', + default: false, + }, } }; @@ -141,6 +147,7 @@ export interface ITestingConfiguration { [TestingConfigKeys.GutterEnabled]: boolean; [TestingConfigKeys.SaveBeforeTest]: boolean; [TestingConfigKeys.OpenTesting]: AutoOpenTesting; + [TestingConfigKeys.AlwaysRevealTestOnStateChange]: boolean; } export const getTestingConfiguration = (config: IConfigurationService, key: K) => config.getValue(key); diff --git a/src/vs/workbench/contrib/testing/common/testItemCollection.ts b/src/vs/workbench/contrib/testing/common/testItemCollection.ts index 06ad3b1b4b7..9a72a25c1ac 100644 --- a/src/vs/workbench/contrib/testing/common/testItemCollection.ts +++ b/src/vs/workbench/contrib/testing/common/testItemCollection.ts @@ -134,7 +134,7 @@ const diffTestItems = (a: ITestItem, b: ITestItem) => { return output as Partial | undefined; }; -export interface ITestChildrenLike extends Iterable { +export interface ITestChildrenLike extends Iterable<[string, T]> { get(id: string): T | undefined; delete(id: string): void; } @@ -356,7 +356,7 @@ export class TestItemCollection extends Disposable { this.connectItemAndChildren(actual, internal, parent); // Remove any orphaned children. - for (const child of oldChildren) { + for (const [_, child] of oldChildren) { if (!this.options.getChildren(actual).get(child.id)) { this.removeItem(TestId.joinToString(fullId, child.id)); } @@ -417,7 +417,7 @@ export class TestItemCollection extends Disposable { this.connectItem(actual, internal, parent); // Discover any existing children that might have already been added - for (const child of this.options.getChildren(actual)) { + for (const [_, child] of this.options.getChildren(actual)) { this.upsertItem(child, internal); } } @@ -464,7 +464,7 @@ export class TestItemCollection extends Disposable { } const expandRequests: Promise[] = []; - for (const child of this.options.getChildren(internal.actual)) { + for (const [_, child] of this.options.getChildren(internal.actual)) { const promise = this.expand(TestId.joinToString(internal.fullId, child.id), levels); if (isThenable(promise)) { expandRequests.push(promise); @@ -544,7 +544,7 @@ export class TestItemCollection extends Disposable { } this.tree.delete(item.fullId.toString()); - for (const child of this.options.getChildren(item.actual)) { + for (const [_, child] of this.options.getChildren(item.actual)) { queue.push(this.tree.get(TestId.joinToString(item.fullId, child.id))); } } @@ -561,8 +561,8 @@ export class TestItemCollection extends Disposable { } } -/** Implementation os vscode.TestItemCollection */ -export interface ITestItemChildren extends Iterable { +/** Implementation of vscode.TestItemCollection */ +export interface ITestItemChildren extends Iterable<[string, T]> { readonly size: number; replace(items: readonly T[]): void; forEach(callback: (item: T, collection: this) => unknown, thisArg?: unknown): void; @@ -607,6 +607,11 @@ export const createTestItemChildren = (api: ITestItemAp } }, + /** @inheritdoc */ + [Symbol.iterator](): IterableIterator<[string, T]> { + return mapped.entries(); + }, + /** @inheritdoc */ replace(items: Iterable) { const newMapped = new Map(); @@ -670,10 +675,5 @@ export const createTestItemChildren = (api: ITestItemAp toJSON() { return Array.from(mapped.values()); }, - - /** @inheritdoc */ - [Symbol.iterator]() { - return mapped.values(); - }, }; }; diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index a42283c1457..4e5f6525fb0 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -8,7 +8,7 @@ import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { MenuRegistry, MenuId, Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { CATEGORIES } from 'vs/workbench/common/actions'; -import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; +import { IWorkbenchThemeService, IWorkbenchTheme, ThemeSettingTarget, IWorkbenchColorTheme, IWorkbenchFileIconTheme, IWorkbenchProductIconTheme, ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionGalleryService, IExtensionManagementService, IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; @@ -34,6 +34,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; export const manageExtensionIcon = registerIcon('theme-selection-manage-extension', Codicon.gear, localize('manageExtensionIcon', 'Icon for the \'Manage\' action in the theme selection quick pick.')); @@ -583,6 +584,51 @@ registerAction2(class extends Action2 { } }); +const toggleLightDarkThemesCommandId = 'workbench.action.toggleLightDarkThemes'; + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: toggleLightDarkThemesCommandId, + title: { value: localize('toggleLightDarkThemes.label', "Toggle between Light/Dark Themes"), original: 'Toggle between Light/Dark Themes' }, + category: CATEGORIES.Preferences, + f1: true, + }); + } + + override async run(accessor: ServicesAccessor) { + const themeService = accessor.get(IWorkbenchThemeService); + const configurationService = accessor.get(IConfigurationService); + + const currentTheme = themeService.getColorTheme(); + let newSettingsId: string = ThemeSettings.PREFERRED_DARK_THEME; + switch (currentTheme.type) { + case ColorScheme.LIGHT: + newSettingsId = ThemeSettings.PREFERRED_DARK_THEME; + break; + case ColorScheme.DARK: + newSettingsId = ThemeSettings.PREFERRED_LIGHT_THEME; + break; + case ColorScheme.HIGH_CONTRAST_LIGHT: + newSettingsId = ThemeSettings.PREFERRED_HC_DARK_THEME; + break; + case ColorScheme.HIGH_CONTRAST_DARK: + newSettingsId = ThemeSettings.PREFERRED_HC_LIGHT_THEME; + break; + } + + const themeSettingId: string = configurationService.getValue(newSettingsId); + + if (themeSettingId && typeof themeSettingId === 'string') { + const theme = (await themeService.getColorThemes()).find(t => t.settingsId === themeSettingId); + if (theme) { + themeService.setColorTheme(theme.id, 'auto'); + } + } + } +}); + MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { group: '4_themes', command: { @@ -610,7 +656,6 @@ MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { order: 3 }); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '4_themes', command: { diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts index 023f1495974..5e9683717a0 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.contribution.ts @@ -3,37 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions'; +import { UserDataProfilesWorkbenchContribution } from 'vs/workbench/contrib/userDataProfile/browser/userDataProfile'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; -import { PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import '../common/profileActions'; -// import '../common/userDataProfileActions'; - -class UserDataProfileStatusBarEntryContribution implements IWorkbenchContribution { - - constructor( - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, - @IStatusbarService private readonly statusBarService: IStatusbarService - ) { - this.updateStatus(); - } - - private async updateStatus(): Promise { - const profiles = await this.userDataProfilesService.getAllProfiles(); - if (profiles.length) { - this.statusBarService.addEntry({ - name: this.userDataProfilesService.currentProfile.name!, - command: 'workbench.profiles.actions.switchProfile', - ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfilesService.currentProfile.name), - text: `${PROFILES_CATEGORY}: ${this.userDataProfilesService.currentProfile.name!}`, - }, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); - } - } -} +import '../common/userDataProfileActions'; const workbenchRegistry = Registry.as(Extensions.Workbench); -workbenchRegistry.registerWorkbenchContribution(UserDataProfileStatusBarEntryContribution, LifecyclePhase.Eventually); +workbenchRegistry.registerWorkbenchContribution(UserDataProfilesWorkbenchContribution, LifecyclePhase.Ready); diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts new file mode 100644 index 00000000000..2f7a8569e42 --- /dev/null +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -0,0 +1,131 @@ +/*--------------------------------------------------------------------------------------------- + * 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 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { Action2, ISubmenuItem, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { IUserDataProfileManagementService, IUserDataProfileService, ManageProfilesSubMenu, PROFILES_CATEGORY, PROFILES_TTILE } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; + +const CONTEXT_CURRENT_PROFILE = new RawContextKey('currentUserDataProfile', ''); + +export class UserDataProfilesWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + private readonly currentProfileContext: IContextKey; + + constructor( + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, + @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileManagementService private readonly userDataProfileManagementService: IUserDataProfileManagementService, + @IStatusbarService private readonly statusBarService: IStatusbarService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IContextKeyService contextKeyService: IContextKeyService, + ) { + super(); + + this.currentProfileContext = CONTEXT_CURRENT_PROFILE.bindTo(contextKeyService); + this.currentProfileContext.set(this.userDataProfileService.currentProfile.id); + + this.updateStatus(); + this._register(Event.any(this.workspaceContextService.onDidChangeWorkbenchState, this.userDataProfileService.onDidChangeCurrentProfile, this.userDataProfilesService.onDidChangeProfiles)(() => this.updateStatus())); + + this.registerActions(); + } + + private registerActions(): void { + this.registerManageProfilesSubMenu(); + + this.registerProfilesActions(); + this._register(this.userDataProfilesService.onDidChangeProfiles(() => this.registerProfilesActions())); + } + + private registerManageProfilesSubMenu(): void { + const that = this; + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfileService.currentProfile.name); }, + submenu: ManageProfilesSubMenu, + group: '5_profiles', + when, + order: 3 + }); + MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, { + title: PROFILES_TTILE, + submenu: ManageProfilesSubMenu, + group: '5_profiles', + when, + order: 3 + }); + MenuRegistry.appendMenuItem(MenuId.AccountsContext, { + get title() { return localize('manageProfiles', "{0} ({1})", PROFILES_TTILE.value, that.userDataProfileService.currentProfile.name); }, + submenu: ManageProfilesSubMenu, + group: '1_profiles', + when, + }); + } + + private readonly profilesDisposable = this._register(new MutableDisposable()); + private registerProfilesActions(): void { + this.profilesDisposable.value = new DisposableStore(); + for (const profile of this.userDataProfilesService.profiles) { + this.profilesDisposable.value.add(this.registerProfileEntryAction(profile)); + } + } + + private registerProfileEntryAction(profile: IUserDataProfile): IDisposable { + const that = this; + return registerAction2(class ProfileEntryAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: `workbench.profiles.actions.profileEntry.${profile.id}`, + title: profile.name, + toggled: ContextKeyExpr.equals(CONTEXT_CURRENT_PROFILE.key, profile.id), + precondition: ContextKeyExpr.notEquals(CONTEXT_CURRENT_PROFILE.key, profile.id), + menu: [ + { + id: ManageProfilesSubMenu, + group: '0_profiles', + when, + } + ] + }); + } + async run(accessor: ServicesAccessor) { + return that.userDataProfileManagementService.switchProfile(profile); + } + }); + } + + private profileStatusAccessor: IStatusbarEntryAccessor | undefined; + private updateStatus(): void { + if (this.userDataProfilesService.profiles.length > 1 && this.workspaceContextService.getWorkbenchState() !== WorkbenchState.EMPTY) { + const statusBarEntry = { + name: this.userDataProfileService.currentProfile.name!, + command: 'workbench.profiles.actions.switchProfile', + ariaLabel: localize('currentProfile', "Current Settings Profile is {0}", this.userDataProfileService.currentProfile.name), + text: `${PROFILES_CATEGORY}: ${this.userDataProfileService.currentProfile.name!}`, + }; + if (this.profileStatusAccessor) { + this.profileStatusAccessor.update(statusBarEntry); + } else { + this.profileStatusAccessor = this.statusBarService.addEntry(statusBarEntry, 'status.userDataProfile', StatusbarAlignment.LEFT, 1); + } + } else { + if (this.profileStatusAccessor) { + this.profileStatusAccessor.dispose(); + this.profileStatusAccessor = undefined; + } + } + } +} diff --git a/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts index bf473a183e5..698833c1aa8 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/profileActions.ts @@ -14,7 +14,7 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isProfile, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; registerAction2(class ExportProfileAction extends Action2 { @@ -118,7 +118,7 @@ registerAction2(class ImportProfileAction extends Action2 { } const content = (await fileService.readFile(profileLocation[0])).value.toString(); const parsed = JSON.parse(content); - return isProfile(parsed) ? parsed : null; + return isUserDataProfileTemplate(parsed) ? parsed : null; } private async getProfileFromURL(url: string, requestService: IRequestService): Promise { @@ -126,7 +126,7 @@ registerAction2(class ImportProfileAction extends Action2 { const context = await requestService.request(options, CancellationToken.None); if (context.res.statusCode === 200) { const result = await asJson(context); - return isProfile(result) ? result : null; + return isUserDataProfileTemplate(result) ? result : null; } else { const message = await asText(context); throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`); diff --git a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts index ea2911b9457..82cee39c1e6 100644 --- a/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/common/userDataProfileActions.ts @@ -14,20 +14,35 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation import { INotificationService } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { asJson, asText, IRequestService } from 'vs/platform/request/common/request'; -import { IUserDataProfileTemplate, isProfile, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { IUserDataProfileTemplate, isUserDataProfileTemplate, IUserDataProfileManagementService, IUserDataProfileWorkbenchService, PROFILES_CATEGORY, PROFILE_EXTENSION, PROFILE_FILTER, ManageProfilesSubMenu, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IsDevelopmentContext } from 'vs/platform/contextkey/common/contextkeys'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { WorkbenchStateContext } from 'vs/workbench/common/contextkeys'; +import { CATEGORIES } from 'vs/workbench/common/actions'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -registerAction2(class SaveProfileAsAction extends Action2 { +registerAction2(class CreateFromCurrentProfileAction extends Action2 { constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); super({ - id: 'workbench.profiles.actions.saveProfileAs', + id: 'workbench.profiles.actions.createFromCurrentProfile', title: { - value: localize('save profile as', "Save Settings Profile As..."), - original: 'Save Settings Profile As...' + value: localize('save profile as', "Create from Current Profile..."), + original: 'Create from Current Profile...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '1_create_profiles', + when, + order: 1 + } + ] }); } @@ -36,7 +51,43 @@ registerAction2(class SaveProfileAsAction extends Action2 { const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); const name = await quickInputService.input({ placeHolder: localize('name', "Profile name"), - title: localize('save profile as', "Save Settings Profile As..."), + title: localize('save profile as', "Create from Current Profile..."), + }); + if (name) { + await userDataProfileManagementService.createAndEnterProfile(name, undefined, true); + } + } +}); + +registerAction2(class CreateEmptyProfileAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: 'workbench.profiles.actions.createProfile', + title: { + value: localize('create profile', "Create an Empty Profile..."), + original: 'Create an Empty Profile...' + }, + category: PROFILES_CATEGORY, + f1: true, + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '1_create_profiles', + when, + order: 2 + } + ] + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + const name = await quickInputService.input({ + placeHolder: localize('name', "Profile name"), + title: localize('create and enter empty profile', "Create an Empty Profile..."), }); if (name) { await userDataProfileManagementService.createAndEnterProfile(name); @@ -44,6 +95,44 @@ registerAction2(class SaveProfileAsAction extends Action2 { } }); +registerAction2(class RemoveProfileAction extends Action2 { + constructor() { + const when = ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')); + super({ + id: 'workbench.profiles.actions.removeProfile', + title: { + value: localize('remove profile', "Remove Profile..."), + original: 'Remove Profile...' + }, + category: PROFILES_CATEGORY, + f1: true, + precondition: when, + menu: [ + { + id: ManageProfilesSubMenu, + group: '2_manage_profiles', + when + } + ] + }); + } + + async run(accessor: ServicesAccessor) { + const quickInputService = accessor.get(IQuickInputService); + const userDataProfileService = accessor.get(IUserDataProfileService); + const userDataProfilesService = accessor.get(IUserDataProfilesService); + const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + + const profiles = userDataProfilesService.profiles.filter(p => p.name !== userDataProfileService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); + if (profiles.length) { + const pick = await quickInputService.pick(profiles.map(profile => ({ label: profile.name, profile })), { placeHolder: localize('pick profile', "Select Settings Profile") }); + if (pick) { + await userDataProfileManagementService.removeProfile(pick.profile); + } + } + } +}); + registerAction2(class SwitchProfileAction extends Action2 { constructor() { super({ @@ -53,67 +142,67 @@ registerAction2(class SwitchProfileAction extends Action2 { original: 'Switch Settings Profile' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } async run(accessor: ServicesAccessor) { const quickInputService = accessor.get(IQuickInputService); + const userDataProfileService = accessor.get(IUserDataProfileService); const userDataProfilesService = accessor.get(IUserDataProfilesService); const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); - const profiles = await userDataProfilesService.getAllProfiles(); - if (profiles.length) { - const picks: IQuickPickItem[] = profiles.map(p => ({ - label: p.name!, - description: p.name === userDataProfilesService.currentProfile.name ? localize('current', "Current") : undefined, + if (userDataProfilesService.profiles) { + const picks: Array = userDataProfilesService.profiles.map(profile => ({ + label: profile.name!, + description: profile.name === userDataProfileService.currentProfile.name ? localize('current', "Current") : undefined, + profile })); const pick = await quickInputService.pick(picks, { placeHolder: localize('pick profile', "Select Settings Profile") }); if (pick) { - await userDataProfileManagementService.switchProfile(pick.label); + await userDataProfileManagementService.switchProfile(pick.profile); } } } }); -registerAction2(class RemoveProfileAction extends Action2 { +registerAction2(class CleanupProfilesAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.removeProfile', + id: 'workbench.profiles.actions.cleanupProfiles', title: { - value: localize('remove profile', "Remove Settings Profile"), - original: 'Remove Settings Profile' + value: localize('cleanup profile', "Cleanup Profiles"), + original: 'Cleanup Profiles' }, - category: PROFILES_CATEGORY, - f1: true + category: CATEGORIES.Developer, + f1: true, + precondition: IsDevelopmentContext, }); } async run(accessor: ServicesAccessor) { - const quickInputService = accessor.get(IQuickInputService); const userDataProfilesService = accessor.get(IUserDataProfilesService); - const userDataProfileManagementService = accessor.get(IUserDataProfileManagementService); + const fileService = accessor.get(IFileService); + const uriIdentityService = accessor.get(IUriIdentityService); - const profiles = (await userDataProfilesService.getAllProfiles()).filter(p => p.name !== userDataProfilesService.currentProfile.name && p.name !== userDataProfilesService.defaultProfile.name); - if (profiles.length) { - const pick = await quickInputService.pick(profiles.map(p => ({ label: p.name! })), { placeHolder: localize('pick profile', "Select Settings Profile") }); - if (pick) { - await userDataProfileManagementService.removeProfile(pick.label); - } - } + const stat = await fileService.resolve(userDataProfilesService.profilesHome); + await Promise.all((stat.children || [])?.filter(child => child.isDirectory && userDataProfilesService.profiles.every(p => !uriIdentityService.extUri.isEqual(p.location, child.resource))) + .map(child => fileService.del(child.resource, { recursive: true }))); } }); registerAction2(class ExportProfileAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.exportProfile', + id: 'workbench.profiles.actions.exportProfile2', title: { - value: localize('export profile', "Export Settings Profile as a Template..."), - original: 'Export Settings as a Profile as a Template...' + value: localize('export profile', "Export Settings as a Profile (2)..."), + original: 'Export Settings as a Profile as a Profile (2)...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -140,16 +229,17 @@ registerAction2(class ExportProfileAction extends Action2 { } }); -registerAction2(class CreateProfileFromTemplateAction extends Action2 { +registerAction2(class ImportProfileAction extends Action2 { constructor() { super({ - id: 'workbench.profiles.actions.createProfileFromTemplate', + id: 'workbench.profiles.actions.importProfile2', title: { - value: localize('create profile from template', "Create Settings Profile from Template..."), - original: 'Create Settings Profile from Template...' + value: localize('import profile', "Import Settings from a Profile (2)..."), + original: 'Import Settings from a Profile (2)...' }, category: PROFILES_CATEGORY, - f1: true + f1: true, + precondition: ContextKeyExpr.and(IsDevelopmentContext, WorkbenchStateContext.notEqualsTo('empty')), }); } @@ -179,7 +269,7 @@ registerAction2(class CreateProfileFromTemplateAction extends Action2 { if (profile) { const name = await quickInputService.input({ placeHolder: localize('name', "Profile name"), - title: localize('save profile as', "Save Settings Profile As..."), + title: localize('save profile as', "Create from Current Profile..."), }); if (name) { await userDataProfileMangementService.createAndEnterProfileFromTemplate(name, profile); @@ -203,7 +293,7 @@ registerAction2(class CreateProfileFromTemplateAction extends Action2 { } const content = (await fileService.readFile(profileLocation[0])).value.toString(); const parsed = JSON.parse(content); - return isProfile(parsed) ? parsed : null; + return isUserDataProfileTemplate(parsed) ? parsed : null; } private async getProfileFromURL(url: string, requestService: IRequestService): Promise { @@ -211,7 +301,7 @@ registerAction2(class CreateProfileFromTemplateAction extends Action2 { const context = await requestService.request(options, CancellationToken.None); if (context.res.statusCode === 200) { const result = await asJson(context); - return isProfile(result) ? result : null; + return isUserDataProfileTemplate(result) ? result : null; } else { const message = await asText(context); throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 736ead6c5bb..05d341c2206 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -190,7 +190,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo if (requiresInitialization && !this.userDataSyncEnablementService.isEnabled()) { this.updateSyncAfterInitializationContext(true); } else { - this.updateSyncAfterInitializationContext(this.storageService.getBoolean(CONTEXT_SYNC_AFTER_INITIALIZATION.key, StorageScope.GLOBAL, false)); + this.updateSyncAfterInitializationContext(this.storageService.getBoolean(CONTEXT_SYNC_AFTER_INITIALIZATION.key, StorageScope.APPLICATION, false)); } const disposable = this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => { if (this.userDataSyncEnablementService.isEnabled()) { @@ -201,7 +201,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo } private async updateSyncAfterInitializationContext(value: boolean): Promise { - this.storageService.store(CONTEXT_SYNC_AFTER_INITIALIZATION.key, value, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(CONTEXT_SYNC_AFTER_INITIALIZATION.key, value, StorageScope.APPLICATION, StorageTarget.MACHINE); this.syncAfterInitializationContext.set(value); this.updateGlobalActivityBadge(); } diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index cb1fe2362b2..d20cf69cc08 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -197,9 +197,7 @@ export class WatermarkContribution extends Disposable implements IWorkbenchContr this.watermark.remove(); const container = this.layoutService.getContainer(Parts.EDITOR_PART); - if (container) { - container.classList.remove('has-watermark'); - } + container?.classList.remove('has-watermark'); this.watermarkDisposable.clear(); } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 5fac78668bf..0c1cd126ccc 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -815,7 +815,7 @@ export class GettingStartedPage extends EditorPane { this.buildTelemetryFooter(telemetryNotice); footer.appendChild(telemetryNotice); } else if (!this.productService.openToWelcomeMainPage && !someStepsComplete && !this.hasScrolledToFirstCategory) { - const firstSessionDateString = this.storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL) || new Date().toUTCString(); + const firstSessionDateString = this.storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION) || new Date().toUTCString(); const daysSinceFirstSession = ((+new Date()) - (+new Date(firstSessionDateString))) / 1000 / 60 / 60 / 24; const fistContentBehaviour = daysSinceFirstSession < 1 ? 'openToFirstCategory' : 'index'; diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts index 6fef8b0769f..50144d87e1c 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedList.ts @@ -115,9 +115,7 @@ export class GettingStartedIndexList { const keys = e.when?.keys(); - if (keys) { - keys.forEach(key => this.contextKeysToWatch.add(key)); - } + keys?.forEach(key => this.contextKeysToWatch.add(key)); }); this.lastRendered = toRender; diff --git a/src/vs/workbench/electron-sandbox/desktop.main.ts b/src/vs/workbench/electron-sandbox/desktop.main.ts index 2dd345d1d19..a30fad8a969 100644 --- a/src/vs/workbench/electron-sandbox/desktop.main.ts +++ b/src/vs/workbench/electron-sandbox/desktop.main.ts @@ -50,11 +50,12 @@ import { isCI, isMacintosh } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-sandbox/diskFileSystemProvider'; import { FileUserDataProvider } from 'vs/platform/userData/common/fileUserDataProvider'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfilesService, reviveProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { UserDataProfilesNativeService } from 'vs/platform/userDataProfile/electron-sandbox/userDataProfile'; import { PolicyChannelClient } from 'vs/platform/policy/common/policyIpc'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; -import { revive } from 'vs/base/common/marshalling'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class DesktopMain extends Disposable { @@ -240,8 +241,10 @@ export class DesktopMain extends Disposable { serviceCollection.set(IUriIdentityService, uriIdentityService); // User Data Profiles - const userDataProfilesService = new UserDataProfilesNativeService(revive(this.configuration.profiles.default), revive(this.configuration.profiles.current), mainProcessService.getChannel('userDataProfiles'), environmentService, fileService, logService); + const userDataProfilesService = new UserDataProfilesNativeService(this.configuration.profiles.default, mainProcessService.getChannel('userDataProfiles'), environmentService, fileService, logService); serviceCollection.set(IUserDataProfilesService, userDataProfilesService); + const userDataProfileService = new UserDataProfileService(userDataProfilesService.defaultProfile, reviveProfile(this.configuration.profiles.current, userDataProfilesService.profilesHome.scheme)); + serviceCollection.set(IUserDataProfileService, userDataProfileService); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // @@ -259,7 +262,7 @@ export class DesktopMain extends Disposable { const payload = this.resolveWorkspaceInitializationPayload(environmentService); const [configurationService, storageService] = await Promise.all([ - this.createWorkspaceService(payload, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => { + this.createWorkspaceService(payload, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, policyService).then(service => { // Workspace serviceCollection.set(IWorkspaceContextService, service); @@ -270,7 +273,7 @@ export class DesktopMain extends Disposable { return service; }), - this.createStorageService(payload, environmentService, userDataProfilesService, mainProcessService).then(service => { + this.createStorageService(payload, environmentService, userDataProfileService, mainProcessService).then(service => { // Storage serviceCollection.set(IStorageService, service); @@ -340,7 +343,7 @@ export class DesktopMain extends Disposable { private async createWorkspaceService( payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, - userDataProfilesService: IUserDataProfilesService, + userDataProfileService: IUserDataProfileService, fileService: FileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, @@ -348,7 +351,7 @@ export class DesktopMain extends Disposable { policyService: IPolicyService ): Promise { const configurationCache = new ConfigurationCache([Schemas.file, Schemas.vscodeUserData] /* Cache all non native resources */, environmentService, fileService); - const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfilesService, fileService, remoteAgentService, uriIdentityService, logService, policyService); + const workspaceService = new WorkspaceService({ remoteAuthority: environmentService.remoteAuthority, configurationCache }, environmentService, userDataProfileService, fileService, remoteAgentService, uriIdentityService, logService, policyService); try { await workspaceService.initialize(payload); @@ -361,8 +364,8 @@ export class DesktopMain extends Disposable { } } - private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService, mainProcessService: IMainProcessService): Promise { - const storageService = new NativeStorageService(payload, mainProcessService, userDataProfilesService, environmentService); + private async createStorageService(payload: IAnyWorkspaceIdentifier, environmentService: INativeWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService, mainProcessService: IMainProcessService): Promise { + const storageService = new NativeStorageService(payload, userDataProfileService, mainProcessService, environmentService); try { await storageService.initialize(); diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index e4f9c4f5f22..9d0e9a76570 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -27,6 +27,7 @@ export class TitlebarPart extends BrowserTitleBarPart { private dragRegion: HTMLElement | undefined; private resizer: HTMLElement | undefined; private cachedWindowControlStyles: { bgColor: string; fgColor: string } | undefined; + private cachedWindowControlHeight: number | undefined; private getMacTitlebarSize() { const osVersion = this.environmentService.os.release; @@ -37,7 +38,12 @@ export class TitlebarPart extends BrowserTitleBarPart { return 22; } - override get minimumHeight(): number { return isMacintosh ? this.getMacTitlebarSize() / getZoomFactor() : super.minimumHeight; } + override get minimumHeight(): number { + if (!isMacintosh) { + return super.minimumHeight; + } + return (this.isCommandCenterVisible ? 35 : this.getMacTitlebarSize()) / getZoomFactor(); + } override get maximumHeight(): number { return this.minimumHeight; } protected override readonly environmentService: INativeWorkbenchEnvironmentService; @@ -200,7 +206,19 @@ export class TitlebarPart extends BrowserTitleBarPart { if (!this.cachedWindowControlStyles || this.cachedWindowControlStyles.bgColor !== this.element.style.backgroundColor || this.cachedWindowControlStyles.fgColor !== this.element.style.color) { - this.nativeHostService.updateTitleBarOverlay(this.element.style.backgroundColor, this.element.style.color); + this.nativeHostService.updateTitleBarOverlay({ backgroundColor: this.element.style.backgroundColor, foregroundColor: this.element.style.color }); + } + } + } + + override layout(width: number, height: number): void { + super.layout(width, height); + + if (useWindowControlsOverlay(this.configurationService, this.environmentService)) { + const newHeight = Math.trunc(this.element.clientHeight * getZoomFactor()); + if (newHeight !== this.cachedWindowControlHeight) { + this.cachedWindowControlHeight = newHeight; + this.nativeHostService.updateTitleBarOverlay({ height: newHeight }); } } } diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 472a47705b8..6039018ed45 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -26,8 +26,8 @@ import { joinPath } from 'vs/base/common/resources'; import { Registry } from 'vs/platform/registry/common/platform'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { isObject } from 'vs/base/common/types'; -import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { DefaultConfiguration as BaseDefaultConfiguration } from 'vs/platform/configuration/common/configurations'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class DefaultConfiguration extends BaseDefaultConfiguration { @@ -120,7 +120,8 @@ export class UserConfiguration extends Disposable { private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; - private readonly userConfiguration: MutableDisposable = this._register(new MutableDisposable()); + private readonly userConfiguration = this._register(new MutableDisposable()); + private readonly userConfigurationChangeDisposable = this._register(new MutableDisposable()); private readonly reloadConfigurationScheduler: RunOnceScheduler; private readonly configurationParseOptions: ConfigurationParseOptions; @@ -128,19 +129,40 @@ export class UserConfiguration extends Disposable { get hasTasksLoaded(): boolean { return this.userConfiguration.value instanceof FileServiceBasedConfiguration; } constructor( - private readonly userDataProfile: IUserDataProfile, scopes: ConfigurationScope[] | undefined, + private readonly userDataProfileService: IUserDataProfileService, private readonly fileService: IFileService, private readonly uriIdentityService: IUriIdentityService, private readonly logService: ILogService, ) { super(); this.configurationParseOptions = { scopes, skipRestricted: false }; - this.userConfiguration.value = new UserSettings(this.userDataProfile.settingsResource, scopes, uriIdentityService.extUri, this.fileService); - this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); + this.userConfiguration.value = new UserSettings(this.userDataProfileService.currentProfile.settingsResource, scopes, uriIdentityService.extUri, this.fileService); + this._register(this.userDataProfileService.onDidChangeCurrentProfile(e => e.join(this.onDidChangeCurrentProfile()))); + this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule()); this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50)); } + private async onDidChangeCurrentProfile(): Promise { + await this.resetUserConfiguration(); + this.reloadConfigurationScheduler.schedule(); + } + + private async resetUserConfiguration(): Promise { + const folder = this.uriIdentityService.extUri.dirname(this.userDataProfileService.currentProfile.settingsResource); + const standAloneConfigurationResources: [string, URI][] = [[TASKS_CONFIGURATION_KEY, this.userDataProfileService.currentProfile.tasksResource]]; + const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userDataProfileService.currentProfile.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); + const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); + this.userConfiguration.value = fileServiceBasedConfiguration; + + // Check for value because userConfiguration might have been disposed. + if (this.userConfigurationChangeDisposable.value) { + this.userConfigurationChangeDisposable.value = this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule()); + } + + return configurationModel; + } + async initialize(): Promise { return this.userConfiguration.value!.loadConfiguration(); } @@ -149,19 +171,7 @@ export class UserConfiguration extends Disposable { if (this.hasTasksLoaded) { return this.userConfiguration.value!.loadConfiguration(); } - - const folder = this.uriIdentityService.extUri.dirname(this.userDataProfile.settingsResource); - const standAloneConfigurationResources: [string, URI][] = [[TASKS_CONFIGURATION_KEY, this.userDataProfile.tasksResource]]; - const fileServiceBasedConfiguration = new FileServiceBasedConfiguration(folder.toString(), this.userDataProfile.settingsResource, standAloneConfigurationResources, this.configurationParseOptions, this.fileService, this.uriIdentityService, this.logService); - const configurationModel = await fileServiceBasedConfiguration.loadConfiguration(); - this.userConfiguration.value = fileServiceBasedConfiguration; - - // Check for value because userConfiguration might have been disposed. - if (this.userConfiguration.value) { - this._register(this.userConfiguration.value.onDidChange(() => this.reloadConfigurationScheduler.schedule())); - } - - return configurationModel; + return this.resetUserConfiguration(); } reparse(): ConfigurationModel { diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 32551395307..0d227fc057d 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -40,7 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; import { isUndefined } from 'vs/base/common/types'; import { localize } from 'vs/nls'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IPolicyService, NullPolicyService } from 'vs/platform/policy/common/policy'; class Workspace extends BaseWorkspace { @@ -102,7 +102,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat constructor( { remoteAuthority, configurationCache }: { remoteAuthority?: string; configurationCache: IConfigurationCache }, environmentService: IWorkbenchEnvironmentService, - userDataProfilesService: IUserDataProfilesService, + userDataProfileService: IUserDataProfileService, fileService: IFileService, remoteAgentService: IRemoteAgentService, uriIdentityService: IUriIdentityService, @@ -123,7 +123,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat this.logService = logService; this._configuration = new Configuration(this.defaultConfiguration.configurationModel, this.policyConfiguration.configurationModel, new ConfigurationModel(), new ConfigurationModel(), new ConfigurationModel(), new ResourceMap(), new ConfigurationModel(), new ResourceMap(), this.workspace); this.cachedFolderConfigs = new ResourceMap(); - this.localUserConfiguration = this._register(new UserConfiguration(userDataProfilesService.currentProfile, remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, fileService, uriIdentityService, logService)); + this.localUserConfiguration = this._register(new UserConfiguration(remoteAuthority ? LOCAL_MACHINE_SCOPES : undefined, userDataProfileService, fileService, uriIdentityService, logService)); this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (remoteAuthority) { const remoteUserConfiguration = this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(remoteAuthority, configurationCache, fileService, uriIdentityService, remoteAgentService)); @@ -456,9 +456,7 @@ export class WorkspaceService extends Disposable implements IWorkbenchConfigurat if (this.restrictedSettings.workspace) { keys.push(...this.restrictedSettings.workspace); } - if (this.restrictedSettings.workspaceFolder) { - this.restrictedSettings.workspaceFolder.forEach((value) => keys.push(...value)); - } + this.restrictedSettings.workspaceFolder?.forEach((value) => keys.push(...value)); keys = distinct(keys); if (keys.length) { this.triggerConfigurationChange({ keys, overrides: [] }, { data, workspace: this.workspace }, ConfigurationTarget.WORKSPACE); diff --git a/src/vs/workbench/services/configuration/common/configuration.ts b/src/vs/workbench/services/configuration/common/configuration.ts index c3d97f0c5e6..311dd5ab4f5 100644 --- a/src/vs/workbench/services/configuration/common/configuration.ts +++ b/src/vs/workbench/services/configuration/common/configuration.ts @@ -9,6 +9,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { refineServiceDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { ResourceMap } from 'vs/base/common/map'; +import { IAnyWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; export const FOLDER_CONFIG_FOLDER_NAME = '.vscode'; export const FOLDER_SETTINGS_NAME = 'settings'; @@ -72,6 +73,12 @@ export interface IWorkbenchConfigurationService extends IConfigurationService { * The promise is resolved immediately if the window is not remote. */ whenRemoteConfigurationLoaded(): Promise; + + /** + * Initialize configuration service for the given workspace + * @param arg workspace Identifier + */ + initialize(arg: IAnyWorkspaceIdentifier): Promise; } export const TASKS_DEFAULT = '{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": []\n}'; diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index 1bf0adcc9e3..74e507c17b1 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -28,7 +28,7 @@ import { IReference } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Selection } from 'vs/editor/common/core/selection'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export const enum ConfigurationEditingErrorCode { @@ -153,7 +153,7 @@ export class ConfigurationEditingService { constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IFileService private readonly fileService: IFileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @@ -614,9 +614,9 @@ export class ConfigurationEditingService { private getConfigurationFileResource(target: EditableConfigurationTarget, standAloneConfigurationKey: string | undefined, relativePath: string, resource: URI | null | undefined): URI | null { if (target === EditableConfigurationTarget.USER_LOCAL) { if (standAloneConfigurationKey === TASKS_CONFIGURATION_KEY) { - return this.userDataProfilesService.currentProfile.tasksResource; + return this.userDataProfileService.currentProfile.tasksResource; } else { - return this.userDataProfilesService.currentProfile.settingsResource; + return this.userDataProfileService.currentProfile.settingsResource; } } if (target === EditableConfigurationTarget.USER_REMOTE) { diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts index 2ed465e18a6..61c7a7de517 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditingService.test.ts @@ -39,11 +39,13 @@ import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentService'; import { getSingleFolderWorkspaceIdentifier } from 'vs/workbench/services/workspaces/browser/workspaces'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { hash } from 'vs/base/common/hash'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); @@ -57,7 +59,7 @@ export class ConfigurationCache implements IConfigurationCache { suite('ConfigurationEditingService', () => { let instantiationService: TestInstantiationService; - let userDataProfilesService: IUserDataProfilesService; + let userDataProfileService: IUserDataProfileService; let environmentService: IWorkbenchEnvironmentService; let fileService: IFileService; let workspaceService: WorkspaceService; @@ -108,12 +110,13 @@ suite('ConfigurationEditingService', () => { environmentService = TestEnvironmentService; environmentService.policyFile = joinPath(workspaceFolder, 'policies.json'); instantiationService.stub(IEnvironmentService, environmentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + userDataProfileService = new UserDataProfileService(profile, profile); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); disposables.add(fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, logService)))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IRemoteAgentService, remoteAgentService); - workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); + workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); await workspaceService.initialize({ id: hash(workspaceFolder.toString()).toString(16), uri: workspaceFolder @@ -153,7 +156,7 @@ suite('ConfigurationEditingService', () => { }); test('errors cases - invalid configuration', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }, { donotNotifyError: true }); } catch (error) { @@ -217,43 +220,43 @@ suite('ConfigurationEditingService', () => { test('write policy setting - when not set', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.policySetting', value: 'value' }, { donotNotifyError: true }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.policySetting'], 'value'); }); test('write one setting - empty file', async () => { await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); }); test('write one setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: 'value' }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.strictEqual(parsed['configurationEditing.service.testSetting'], 'value'); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove an existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value", "configurationEditing.service.testSetting": "value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); }); test('remove non existing setting - existing file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "my.super.setting": "my.super.value" }')); await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key: 'configurationEditing.service.testSetting', value: undefined }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(Object.keys(parsed), ['my.super.setting']); assert.strictEqual(parsed['my.super.setting'], 'my.super.value'); @@ -264,7 +267,7 @@ suite('ConfigurationEditingService', () => { const value = { 'configurationEditing.service.testSetting': 'overridden value' }; await testObject.writeConfiguration(EditableConfigurationTarget.USER_LOCAL, { key, value }); - const contents = await fileService.readFile(userDataProfilesService.currentProfile.settingsResource); + const contents = await fileService.readFile(userDataProfileService.currentProfile.settingsResource); const parsed = json.parse(contents.value.toString()); assert.deepStrictEqual(parsed[key], value); }); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index b83a65216b5..a7c7d062087 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -44,10 +44,12 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteA import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { hash } from 'vs/base/common/hash'; import { TestProductService } from 'vs/workbench/test/common/workbenchTestServices'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; import { NullPolicyService } from 'vs/platform/policy/common/policy'; import { FilePolicyService } from 'vs/platform/policy/common/filePolicyService'; import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; function convertToWorkspacePayload(folder: URI): ISingleFolderWorkspaceIdentifier { return { @@ -65,6 +67,11 @@ export class ConfigurationCache implements IConfigurationCache { const ROOT = URI.file('tests').with({ scheme: 'vscode-tests' }); +function aUserDataProfileService(environmentService: IEnvironmentService): IUserDataProfileService { + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + return new UserDataProfileService(profile, profile); +} + suite('WorkspaceContextService - Folder', () => { const folderName = 'Folder A'; @@ -83,7 +90,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); }); @@ -123,7 +130,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a')); @@ -143,7 +150,7 @@ suite('WorkspaceContextService - Folder', () => { const environmentService = TestEnvironmentService; fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + const testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, new RemoteAgentService(null, environmentService, TestProductService, new RemoteAuthorityResolverService(TestProductService, undefined, undefined), new SignService(undefined), new NullLogService()), new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); await (testObject).initialize(convertToWorkspacePayload(folder)); @@ -190,7 +197,7 @@ suite('WorkspaceContextService - Workspace', () => { const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService, null)); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -248,7 +255,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, aUserDataProfileService(environmentService), fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); @@ -446,7 +453,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { suite('WorkspaceService - Initialization', () => { - let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let configResource: URI, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -490,9 +497,9 @@ suite('WorkspaceService - Initialization', () => { environmentService = TestEnvironmentService; const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -508,7 +515,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -533,7 +540,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -560,7 +567,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with no configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -583,7 +590,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a multi root workspace from an empty workspace with configuration changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -610,7 +617,7 @@ suite('WorkspaceService - Initialization', () => { (isMacintosh ? test.skip : test)('initialize a folder workspace from a folder workspace with no configuration changes', async () => { await testObject.initialize(convertToWorkspacePayload(joinPath(ROOT, 'a'))); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "initialization.testSetting1": "userValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); testObject.onDidChangeWorkbenchState(target); @@ -676,7 +683,7 @@ suite('WorkspaceService - Initialization', () => { suite('WorkspaceConfigurationService - Folder', () => { - let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let testObject: WorkspaceService, workspaceService: WorkspaceService, fileService: IFileService, environmentService: IWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables: DisposableStore = new DisposableStore(); @@ -750,8 +757,8 @@ suite('WorkspaceConfigurationService - Folder', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + workspaceService = testObject = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new FilePolicyService(environmentService.policyFile, fileService, logService))); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); @@ -771,13 +778,13 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('globals override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'userValue'); }); test('globals', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('testworkbench.editor.tabs'), true); }); @@ -789,21 +796,21 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('workspace settings override user settings', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.testSetting'), 'workspaceValue'); }); test('machine overridable settings override user Settings', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.machineOverridableSetting'), 'workspaceValue'); }); test('workspace settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -820,7 +827,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine overridable settings override user settings after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.newMachineOverridableSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); configurationRegistry.registerConfiguration({ @@ -838,7 +845,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -847,7 +854,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('application settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -856,7 +863,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -865,7 +872,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('machine settings are not read from workspace when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -874,7 +881,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -899,7 +906,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.applicationSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -924,7 +931,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-2": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -949,7 +956,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('get machine scope settings are not loaded after defaults are registered when workspace folder uri is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.machineSetting-3": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -985,7 +992,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('policy settings when policy value is not set', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.policySetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.policySetting": "workspaceValue" }')); await testObject.reloadConfiguration(); assert.strictEqual(testObject.getValue('configurationService.folder.policySetting'), 'workspaceValue'); @@ -993,7 +1000,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration emits events after global configuraiton changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); const target = sinon.spy(); testObject.onDidChangeConfiguration(target); await testObject.reloadConfiguration(); @@ -1009,7 +1016,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('reload configuration should not emit event if no changes', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "testworkbench.editor.tabs": true }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); const target = sinon.spy(); @@ -1033,7 +1040,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.folder.testSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1059,7 +1066,7 @@ suite('WorkspaceConfigurationService - Folder', () => { assert.deepStrictEqual(actual.workspace, []); assert.deepStrictEqual(actual.workspaceFolder, []); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.keys(); assert.ok(actual.default.indexOf('configurationService.folder.testSetting') !== -1); @@ -1214,7 +1221,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('creating workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); await testObject.reloadConfiguration(); await new Promise((c, e) => { const disposable = testObject.onDidChangeConfiguration(e => { @@ -1228,7 +1235,7 @@ suite('WorkspaceConfigurationService - Folder', () => { })); test('deleting workspace settings', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "userValue" }')); const workspaceSettingsResource = joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'); await fileService.writeFile(workspaceSettingsResource, VSBuffer.fromString('{ "configurationService.folder.testSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1243,7 +1250,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read from workspace when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1259,7 +1266,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1277,7 +1284,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to untrusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1292,7 +1299,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1308,7 +1315,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('restricted setting is read when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1326,7 +1333,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('change event is triggered when workspace is changed to trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1339,7 +1346,7 @@ suite('WorkspaceConfigurationService - Folder', () => { }); test('adding an restricted setting triggers change event', () => runWithFakedTimers({ useFakeTimers: true }, async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.restrictedSetting": "userValue" }')); testObject.updateWorkspaceTrust(false); const promise = Event.toPromise(testObject.onDidChangeRestrictedSettings); @@ -1350,7 +1357,7 @@ suite('WorkspaceConfigurationService - Folder', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.folder.unknownSetting'; - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "userValue" }')); await fileService.writeFile(joinPath(workspaceService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.folder.unknownSetting": "workspaceValue" }')); await testObject.reloadConfiguration(); @@ -1365,7 +1372,7 @@ suite('WorkspaceConfigurationService - Folder', () => { suite('WorkspaceConfigurationService-Multiroot', () => { - let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + let workspaceContextService: IWorkspaceContextService, jsonEditingServce: IJSONEditingService, testObject: WorkspaceService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -1441,8 +1448,8 @@ suite('WorkspaceConfigurationService-Multiroot', () => { const remoteAgentService = instantiationService.createInstance(RemoteAgentService, null); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + const workspaceService = disposables.add(new WorkspaceService({ configurationCache: new ConfigurationCache() }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IFileService, fileService); instantiationService.stub(IWorkspaceContextService, workspaceService); @@ -1464,7 +1471,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { teardown(() => disposables.clear()); test('application settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1473,7 +1480,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.applicationSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1482,7 +1489,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1491,7 +1498,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace when folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.folder.machineSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1500,7 +1507,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1525,7 +1532,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('get application scope settings are not loaded after defaults are registered when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newSetting-2": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newSetting-2': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1550,7 +1557,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('workspace settings override user settings after defaults are registered for machine overridable settings ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }')); await jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ path: ['settings'], value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true); await testObject.reloadConfiguration(); @@ -1576,7 +1583,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1585,7 +1592,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.applicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1594,7 +1601,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1603,7 +1610,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('machine settings are not read from workspace folder when workspace folder is passed', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.machineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1612,7 +1619,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewApplicationSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1637,7 +1644,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { }); test('application settings are not read from workspace folder after defaults are registered', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "userValue" }')); await fileService.writeFile(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testNewMachineSetting": "workspaceFolderValue" }')); await testObject.reloadConfiguration(); @@ -1730,7 +1737,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { assert.strictEqual(actual.workspaceFolderValue, undefined); assert.strictEqual(actual.value, 'isSet'); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testResourceSetting": "userValue" }')); await testObject.reloadConfiguration(); actual = testObject.inspect('configurationService.workspace.testResourceSetting'); assert.strictEqual(actual.defaultValue, 'isSet'); @@ -1985,7 +1992,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is read from workspace folders when workspace is trusted', async () => { testObject.updateWorkspaceTrust(true); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -2005,7 +2012,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('restricted setting is not read from workspace when workspace is not trusted', async () => { testObject.updateWorkspaceTrust(false); - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting1": "userValue", "configurationService.workspace.testRestrictedSetting2": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.testRestrictedSetting1': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(testObject.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.testRestrictedSetting2": "workspaceFolder2Value" }')); await testObject.reloadConfiguration(); @@ -2024,7 +2031,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { test('remove an unregistered setting', async () => { const key = 'configurationService.workspace.unknownSetting'; - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "userValue" }')); await jsonEditingServce.write((workspaceContextService.getWorkspace().configuration!), [{ path: ['settings'], value: { 'configurationService.workspace.unknownSetting': 'workspaceValue' } }], true); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[0].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue1" }')); await fileService.writeFile(joinPath(workspaceContextService.getWorkspace().folders[1].uri, '.vscode', 'settings.json'), VSBuffer.fromString('{ "configurationService.workspace.unknownSetting": "workspaceFolderValue2" }')); @@ -2050,7 +2057,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { let testObject: WorkspaceService, folder: URI, machineSettingsResource: URI, remoteSettingsResource: URI, fileSystemProvider: InMemoryFileSystemProvider, resolveRemoteEnvironment: () => void, - instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfilesService: IUserDataProfilesService; + instantiationService: TestInstantiationService, fileService: IFileService, environmentService: BrowserWorkbenchEnvironmentService, userDataProfileService: IUserDataProfileService; const remoteAuthority = 'configuraiton-tests'; const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); const disposables = new DisposableStore(); @@ -2103,8 +2110,8 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); fileService.registerProvider(Schemas.vscodeUserData, disposables.add(new FileUserDataProvider(ROOT.scheme, fileSystemProvider, Schemas.vscodeUserData, new NullLogService()))); const configurationCache: IConfigurationCache = { read: () => Promise.resolve(''), write: () => Promise.resolve(), remove: () => Promise.resolve(), needsCaching: () => false }; - userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService)); - testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfilesService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); + userDataProfileService = instantiationService.stub(IUserDataProfileService, aUserDataProfileService(environmentService)); + testObject = disposables.add(new WorkspaceService({ configurationCache, remoteAuthority }, environmentService, userDataProfileService, fileService, remoteAgentService, new UriIdentityService(fileService), new NullLogService(), new NullPolicyService())); instantiationService.stub(IWorkspaceContextService, testObject); instantiationService.stub(IConfigurationService, testObject); instantiationService.stub(IEnvironmentService, environmentService); @@ -2190,7 +2197,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2198,7 +2205,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.machineOverridableSetting": "globalValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2233,7 +2240,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine settings in local user settings does not override defaults after defalts are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); @@ -2252,7 +2259,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { }); test('machine overridable settings in local user settings does not override defaults after defaults are registered ', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); + await fileService.writeFile(userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString('{ "configurationService.remote.newMachineOverridableSetting": "userValue" }')); registerRemoteFileSystemProvider(); resolveRemoteEnvironment(); await initialize(); diff --git a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts index b20e5d5ead4..059d8e46456 100644 --- a/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/baseConfigurationResolverService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as Types from 'vs/base/common/types'; import { Schemas } from 'vs/base/common/network'; import { SideBySideEditor, EditorResourceAccessor } from 'vs/workbench/common/editor'; -import { IStringDictionary, forEach, fromMap } from 'vs/base/common/collections'; +import { IStringDictionary, forEach } from 'vs/base/common/collections'; import { IConfigurationService, IConfigurationOverrides, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IWorkspaceFolder, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -128,7 +128,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR if (!mapping) { return null; } else if (mapping.size > 0) { - return this.resolveAnyAsync(folder, config, fromMap(mapping)); + return this.resolveAnyAsync(folder, config, Object.fromEntries(mapping)); } else { return config; } diff --git a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts index 868e6da301d..8cbb4be2848 100644 --- a/src/vs/workbench/services/configurationResolver/common/variableResolver.ts +++ b/src/vs/workbench/services/configurationResolver/common/variableResolver.ts @@ -165,9 +165,7 @@ export class AbstractVariableResolverService implements IConfigurationResolverSe let resolvedValue = await this.evaluateSingleVariable(environment, match, variable, folderUri, commandValueMapping); - if (resolvedVariables) { - resolvedVariables.set(variable, resolvedValue); - } + resolvedVariables?.set(variable, resolvedValue); if ((resolvedValue !== match) && types.isString(resolvedValue) && resolvedValue.match(AbstractVariableResolverService.VARIABLE_REGEXP)) { resolvedValue = await this.resolveString(environment, folderUri, resolvedValue, commandValueMapping, resolvedVariables); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 1efe771ec2c..adbb3c495c8 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -205,6 +205,9 @@ export class BrowserWorkbenchEnvironmentService implements IBrowserWorkbenchEnvi @memoize get disableWorkspaceTrust(): boolean { return !this.options.enableWorkspaceTrust; } + @memoize + get editSessionId(): string | undefined { return this.options.editSessionId; } + private payload: Map | undefined; constructor( diff --git a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts index e25063c3b34..264a59acbc7 100644 --- a/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/extensionEnablementService.ts @@ -652,7 +652,8 @@ class ExtensionsManager extends Disposable { this.logService.error(error); } this._register(this.extensionManagementService.onDidInstallExtensions(e => this.onDidInstallExtensions(e.reduce((result, { local, operation }) => { if (local && operation !== InstallOperation.Migrate) { result.push(local); } return result; }, [])))); - this._register(Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error))(e => this.onDidUninstallExtension(e.identifier, e.server))); + this._register(Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error))(e => this.onDidUninstallExtensions([e.identifier], e.server))); + this._register(this.extensionManagementService.onDidChangeProfileExtensions(({ added, removed, server }) => { this.onDidInstallExtensions(added); this.onDidUninstallExtensions(removed.map(({ identifier }) => identifier), server); })); } private onDidInstallExtensions(extensions: IExtension[]): void { @@ -662,10 +663,15 @@ class ExtensionsManager extends Disposable { } } - private onDidUninstallExtension(identifier: IExtensionIdentifier, server: IExtensionManagementServer): void { - const index = this._extensions.findIndex(e => areSameExtensions(e.identifier, identifier) && this.extensionManagementServerService.getExtensionManagementServer(e) === server); - if (index !== -1) { - const removed = this._extensions.splice(index, 1); + private onDidUninstallExtensions(identifiers: IExtensionIdentifier[], server: IExtensionManagementServer): void { + const removed: IExtension[] = []; + for (const identifier of identifiers) { + const index = this._extensions.findIndex(e => areSameExtensions(e.identifier, identifier) && this.extensionManagementServerService.getExtensionManagementServer(e) === server); + if (index !== -1) { + removed.push(...this._extensions.splice(index, 1)); + } + } + if (removed.length) { this._onDidChangeExtensions.fire({ added: [], removed }); } } diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index e19fdd2509b..7209305803b 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -218,7 +218,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } const result: IScannedExtension[] = []; try { - const useCache = this.storageService.get('additionalBuiltinExtensions', StorageScope.GLOBAL, '[]') === JSON.stringify(extensions); + const useCache = this.storageService.get('additionalBuiltinExtensions', StorageScope.APPLICATION, '[]') === JSON.stringify(extensions); const webExtensions = await (useCache ? this.getCustomBuiltinExtensionsFromCache() : this.updateCustomBuiltinExtensionsCache()); if (webExtensions.length) { await Promise.all(webExtensions.map(async webExtension => { @@ -232,7 +232,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } })); } - this.storageService.store('additionalBuiltinExtensions', JSON.stringify(extensions), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store('additionalBuiltinExtensions', JSON.stringify(extensions), StorageScope.APPLICATION, StorageTarget.MACHINE); } catch (error) { this.logService.info('Ignoring following additional builtin extensions as there is an error while fetching them from gallery', extensions.map(({ id }) => id), getErrorMessage(error)); } diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 7e8d96d0bb6..66f0b79da91 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -10,10 +10,17 @@ import { IExtensionManagementService, IGalleryExtension, IExtensionIdentifier, I import { URI } from 'vs/base/common/uri'; import { FileAccess } from 'vs/base/common/network'; +export type DidChangeProfileExtensionsEvent = { readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }; + +export interface IProfileAwareExtensionManagementService extends IExtensionManagementService { + onDidChangeProfileExtensions: Event; + switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise; +} + export interface IExtensionManagementServer { readonly id: string; readonly label: string; - readonly extensionManagementService: IExtensionManagementService; + readonly extensionManagementService: IProfileAwareExtensionManagementService; } export const enum ExtensionInstallLocation { @@ -37,6 +44,7 @@ export const DefaultIconPath = FileAccess.asBrowserUri('./media/defaultIcon.png' export type InstallExtensionOnServerEvent = InstallExtensionEvent & { server: IExtensionManagementServer }; export type UninstallExtensionOnServerEvent = IExtensionIdentifier & { server: IExtensionManagementServer }; export type DidUninstallExtensionOnServerEvent = DidUninstallExtensionEvent & { server: IExtensionManagementServer }; +export type DidChangeProfileExtensionsOnServerEvent = DidChangeProfileExtensionsEvent & { server: IExtensionManagementServer }; export const IWorkbenchExtensionManagementService = refineServiceDecorator(IExtensionManagementService); export interface IWorkbenchExtensionManagementService extends IExtensionManagementService { @@ -46,6 +54,7 @@ export interface IWorkbenchExtensionManagementService extends IExtensionManageme onDidInstallExtensions: Event; onUninstallExtension: Event; onDidUninstallExtension: Event; + onDidChangeProfileExtensions: Event; installVSIX(location: URI, manifest: IExtensionManifest, installOptions?: InstallVSIXOptions): Promise; installWebExtension(location: URI): Promise; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts index ef4da6db30d..233e0bc56c2 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementServerService.ts @@ -14,7 +14,7 @@ import { isWeb } from 'vs/base/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { WebExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/webExtensionManagementService'; import { IExtension } from 'vs/platform/extensions/common/extensions'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; export class ExtensionManagementServerService implements IExtensionManagementServerService { @@ -31,7 +31,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer ) { const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { - const extensionManagementService = new ExtensionManagementChannelClient(remoteAgentConnection.getChannel('extensions'), undefined); + const extensionManagementService = instantiationService.createInstance(NativeProfileAwareExtensionManagementService, remoteAgentConnection.getChannel('extensions'), undefined); this.remoteExtensionManagementServer = { id: 'remote', extensionManagementService, diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index e50218a6c6a..1205ab9b412 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -7,7 +7,7 @@ import { Event, EventMultiplexer } from 'vs/base/common/event'; import { ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionsControlManifest, IGalleryMetadata, IExtensionGalleryService, InstallOptions, UninstallOptions, InstallVSIXOptions, InstallExtensionResult, ExtensionManagementError, ExtensionManagementErrorCode, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { DidChangeProfileExtensionsOnServerEvent, DidUninstallExtensionOnServerEvent, IExtensionManagementServer, IExtensionManagementServerService, InstallExtensionOnServerEvent, IWorkbenchExtensionManagementService, UninstallExtensionOnServerEvent } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionType, isLanguagePackExtension, IExtensionManifest, getWorkspaceSupportTypeMessage, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -40,6 +40,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench readonly onDidInstallExtensions: Event; readonly onUninstallExtension: Event; readonly onDidUninstallExtension: Event; + readonly onDidChangeProfileExtensions: Event; protected readonly servers: IExtensionManagementServer[] = []; @@ -72,6 +73,7 @@ export class ExtensionManagementService extends Disposable implements IWorkbench this.onDidInstallExtensions = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(server.extensionManagementService.onDidInstallExtensions); return emitter; }, new EventMultiplexer())).event; this.onUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onUninstallExtension, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; this.onDidUninstallExtension = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onDidUninstallExtension, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; + this.onDidChangeProfileExtensions = this._register(this.servers.reduce((emitter: EventMultiplexer, server) => { emitter.add(Event.map(server.extensionManagementService.onDidChangeProfileExtensions, e => ({ ...e, server }))); return emitter; }, new EventMultiplexer())).event; } async getInstalled(type?: ExtensionType): Promise { diff --git a/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.ts new file mode 100644 index 00000000000..94d70ade4d3 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService.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. + *--------------------------------------------------------------------------------------------*/ + +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { URI } from 'vs/base/common/uri'; +import { IGalleryExtension, ILocalExtension, InstallOptions, InstallVSIXOptions, UninstallOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionIdentifier, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { Emitter } from 'vs/base/common/event'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { delta } from 'vs/base/common/arrays'; +import { compare } from 'vs/base/common/strings'; + +export class NativeProfileAwareExtensionManagementService extends ExtensionManagementChannelClient implements IProfileAwareExtensionManagementService { + + private readonly _onDidChangeProfileExtensions = this._register(new Emitter<{ readonly added: ILocalExtension[]; readonly removed: ILocalExtension[] }>()); + readonly onDidChangeProfileExtensions = this._onDidChangeProfileExtensions.event; + + constructor(channel: IChannel, private extensionsProfileResource: URI | undefined, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + ) { + super(channel); + } + + override install(vsix: URI, options?: InstallVSIXOptions): Promise { + return super.install(vsix, { ...options, profileLocation: this.extensionsProfileResource }); + } + + override installFromGallery(extension: IGalleryExtension, installOptions?: InstallOptions): Promise { + return super.installFromGallery(extension, { ...installOptions, profileLocation: this.extensionsProfileResource }); + } + + override uninstall(extension: ILocalExtension, options?: UninstallOptions): Promise { + return super.uninstall(extension, { ...options, profileLocation: this.extensionsProfileResource }); + } + + override getInstalled(type: ExtensionType | null = null): Promise { + return super.getInstalled(type, this.extensionsProfileResource); + } + + async switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise { + if (this.uriIdentityService.extUri.isEqual(extensionsProfileResource, this.extensionsProfileResource)) { + return; + } + const oldExtensions = await this.getInstalled(ExtensionType.User); + this.extensionsProfileResource = extensionsProfileResource; + const newExtensions = await this.getInstalled(ExtensionType.User); + const { added, removed } = delta(oldExtensions, newExtensions, (a, b) => compare(ExtensionIdentifier.toKey(a.identifier.id), ExtensionIdentifier.toKey(b.identifier.id))); + if (added.length || removed.length) { + this._onDidChangeProfileExtensions.fire({ added, removed }); + } + } + +} diff --git a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts index 8643e20e99d..7bc61e1b482 100644 --- a/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts @@ -6,8 +6,9 @@ import { ExtensionType, IExtension, IExtensionIdentifier, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; import { IExtensionManagementService, ILocalExtension, IGalleryExtension, IGalleryMetadata, InstallOperation, IExtensionGalleryService, InstallOptions, Metadata } from 'vs/platform/extensionManagement/common/extensionManagement'; import { URI } from 'vs/base/common/uri'; +import { Event } from 'vs/base/common/event'; import { areSameExtensions, getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IProfileAwareExtensionManagementService, IScannedExtension, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; import { CancellationToken } from 'vs/base/common/cancellation'; import { AbstractExtensionManagementService, AbstractExtensionTask, IInstallExtensionTask, IUninstallExtensionTask, UninstallExtensionTaskOptions } from 'vs/platform/extensionManagement/common/abstractExtensionManagementService'; @@ -17,10 +18,12 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { isBoolean, isUndefined } from 'vs/base/common/types'; import { IExtensionsProfileScannerService } from 'vs/platform/extensionManagement/common/extensionsProfileScannerService'; -export class WebExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService { +export class WebExtensionManagementService extends AbstractExtensionManagementService implements IExtensionManagementService, IProfileAwareExtensionManagementService { declare readonly _serviceBrand: undefined; + readonly onDidChangeProfileExtensions = Event.None; + constructor( @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, @ITelemetryService telemetryService: ITelemetryService, @@ -93,6 +96,8 @@ export class WebExtensionManagementService extends AbstractExtensionManagementSe return local; } + async switchExtensionsProfile(extensionsProfileResource: URI | undefined): Promise { } + protected createDefaultInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallOptions): IInstallExtensionTask { return new InstallExtensionTask(manifest, extension, options, this.webExtensionsScannerService); } diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts index 8379d5f86d6..ece4c6091b0 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/extensionManagementServerService.ts @@ -14,15 +14,15 @@ import { NativeRemoteExtensionManagementService } from 'vs/workbench/services/ex import { ILabelService } from 'vs/platform/label/common/label'; import { IExtension } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; +import { Disposable } from 'vs/base/common/lifecycle'; -export class ExtensionManagementServerService implements IExtensionManagementServerService { +export class ExtensionManagementServerService extends Disposable implements IExtensionManagementServerService { declare readonly _serviceBrand: undefined; - private readonly _localExtensionManagementServer: IExtensionManagementServer; - public get localExtensionManagementServer(): IExtensionManagementServer { return this._localExtensionManagementServer; } + readonly localExtensionManagementServer: IExtensionManagementServer; readonly remoteExtensionManagementServer: IExtensionManagementServer | null = null; readonly webExtensionManagementServer: IExtensionManagementServer | null = null; @@ -30,12 +30,13 @@ export class ExtensionManagementServerService implements IExtensionManagementSer @ISharedProcessService sharedProcessService: ISharedProcessService, @IRemoteAgentService remoteAgentService: IRemoteAgentService, @ILabelService labelService: ILabelService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService userDataProfileService: IUserDataProfileService, @IInstantiationService instantiationService: IInstantiationService, ) { - const localExtensionManagementService = new ExtensionManagementChannelClient(sharedProcessService.getChannel('extensions'), userDataProfilesService); - - this._localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; + super(); + const localExtensionManagementService = this._register(instantiationService.createInstance(NativeProfileAwareExtensionManagementService, sharedProcessService.getChannel('extensions'), userDataProfileService.currentProfile.extensionsResource)); + this.localExtensionManagementServer = { extensionManagementService: localExtensionManagementService, id: 'local', label: localize('local', "Local") }; + this._register(userDataProfileService.onDidChangeCurrentProfile(e => e.join(localExtensionManagementService.switchExtensionsProfile(e.profile.extensionsResource)))); const remoteAgentConnection = remoteAgentService.getConnection(); if (remoteAgentConnection) { const extensionManagementService = instantiationService.createInstance(NativeRemoteExtensionManagementService, remoteAgentConnection.getChannel('extensions'), this.localExtensionManagementServer); @@ -45,6 +46,7 @@ export class ExtensionManagementServerService implements IExtensionManagementSer get label() { return labelService.getHostLabel(Schemas.vscodeRemote, remoteAgentConnection!.remoteAuthority) || localize('remote', "Remote"); }, }; } + } getExtensionManagementServer(extension: IExtension): IExtensionManagementServer { diff --git a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts index 3b502201ed4..53c178a68f6 100644 --- a/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-sandbox/remoteExtensionManagementService.ts @@ -21,9 +21,10 @@ import { IExtensionManagementServer } from 'vs/workbench/services/extensionManag import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { Promises } from 'vs/base/common/async'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; -import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { NativeProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/profileAwareExtensionManagementService'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -export class NativeRemoteExtensionManagementService extends ExtensionManagementChannelClient implements IExtensionManagementService { +export class NativeRemoteExtensionManagementService extends NativeProfileAwareExtensionManagementService implements IExtensionManagementService { constructor( channel: IChannel, @@ -34,8 +35,9 @@ export class NativeRemoteExtensionManagementService extends ExtensionManagementC @IProductService private readonly productService: IProductService, @INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService, @IExtensionManifestPropertiesService private readonly extensionManifestPropertiesService: IExtensionManifestPropertiesService, + @IUriIdentityService uriIdentityService: IUriIdentityService, ) { - super(channel, undefined); + super(channel, undefined, uriIdentityService); } override async install(vsix: URI, options?: InstallVSIXOptions): Promise { diff --git a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts index 660c2fcd2e7..75247ea427d 100644 --- a/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test.ts @@ -5,10 +5,10 @@ import * as assert from 'assert'; import * as sinon from 'sinon'; import { IExtensionManagementService, DidUninstallExtensionEvent, ILocalExtension, InstallExtensionEvent, InstallExtensionResult } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, ExtensionInstallLocation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService, ExtensionInstallLocation, IProfileAwareExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ExtensionEnablementService } from 'vs/workbench/services/extensionManagement/browser/extensionEnablementService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IStorageService, InMemoryStorageService } from 'vs/platform/storage/common/storage'; @@ -59,7 +59,7 @@ export class TestExtensionEnablementService extends ExtensionEnablementService { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: { + extensionManagementService: { onInstallExtension: new Emitter().event, onDidInstallExtensions: new Emitter().event, onUninstallExtension: new Emitter().event, @@ -125,9 +125,10 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ id: 'local', label: 'local', - extensionManagementService: { + extensionManagementService: { onDidInstallExtensions: didInstallEvent.event, onDidUninstallExtension: didUninstallEvent.event, + onDidChangeProfileExtensions: Event.None, getInstalled: () => Promise.resolve(installed) }, }, null, null)); @@ -955,7 +956,7 @@ function anExtensionManagementServer(authority: string, instantiationService: Te return { id: authority, label: authority, - extensionManagementService: instantiationService.get(IExtensionManagementService), + extensionManagementService: instantiationService.get(IExtensionManagementService) as IProfileAwareExtensionManagementService, }; } diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index 1384f29e5ac..c9f5de7f2e4 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, IWebExtensionsScannerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -25,7 +25,6 @@ import { Schemas } from 'vs/base/common/network'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IExtensionManifestPropertiesService } from 'vs/workbench/services/extensions/common/extensionManifestPropertiesService'; import { IUserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit'; @@ -45,7 +44,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService configurationService: IConfigurationService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 41668ffe3f6..ec07d5de4b4 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -132,7 +132,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost const iframe = document.createElement('iframe'); iframe.setAttribute('class', 'web-worker-ext-host-iframe'); iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin'); - iframe.setAttribute('allow', 'usb'); + iframe.setAttribute('allow', 'usb; cross-origin-isolated;'); iframe.setAttribute('aria-hidden', 'true'); iframe.style.display = 'none'; @@ -254,9 +254,7 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost return; } this._isTerminating = true; - if (this._protocol) { - this._protocol.send(createMessageOfType(MessageType.Terminate)); - } + this._protocol?.send(createMessageOfType(MessageType.Terminate)); super.dispose(); } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index c7610d0bfc2..861e846d9ca 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -11,7 +11,7 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle' import * as perf from 'vs/base/common/performance'; import { isEqualOrParent } from 'vs/base/common/resources'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWebExtensionsScannerService, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -25,7 +25,7 @@ import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IExtensionManagementService, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkGlobFileExists, checkActivateWorkspaceContainsExtension } from 'vs/workbench/services/extensions/common/workspaceContains'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -181,7 +181,7 @@ export abstract class AbstractExtensionService extends Disposable implements IEx @IWorkbenchExtensionEnablementService protected readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService protected readonly _fileService: IFileService, @IProductService protected readonly _productService: IProductService, - @IExtensionManagementService protected readonly _extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService protected readonly _extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @IConfigurationService protected readonly _configurationService: IConfigurationService, @IExtensionManifestPropertiesService protected readonly _extensionManifestPropertiesService: IExtensionManifestPropertiesService, @@ -235,6 +235,8 @@ export abstract class AbstractExtensionService extends Disposable implements IEx this._handleDeltaExtensions(new DeltaExtensionsQueueItem(toAdd, toRemove)); })); + this._register(this._extensionManagementService.onDidChangeProfileExtensions(({ added, removed }) => this._handleDeltaExtensions(new DeltaExtensionsQueueItem(added, removed)))); + this._register(this._extensionManagementService.onDidInstallExtensions((result) => { const extensions: IExtension[] = []; for (const { local, operation } of result) { diff --git a/src/vs/workbench/services/extensions/common/extensionHostEnv.ts b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts new file mode 100644 index 00000000000..9b952930ecd --- /dev/null +++ b/src/vs/workbench/services/extensions/common/extensionHostEnv.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProcessEnvironment } from 'vs/base/common/platform'; + +export const enum ExtHostConnectionType { + IPC = 1, + Socket = 2, + MessagePort = 3 +} + +/** + * The extension host will connect via named pipe / domain socket to its renderer. + */ +export class IPCExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_IPC_HOOK'; + + public readonly type = ExtHostConnectionType.IPC; + + constructor( + public readonly pipeName: string + ) { } + + public serialize(env: IProcessEnvironment): void { + env[IPCExtHostConnection.ENV_KEY] = this.pipeName; + } +} + +/** + * The extension host will receive via nodejs IPC the socket to its renderer. + */ +export class SocketExtHostConnection { + public static ENV_KEY = 'VSCODE_EXTHOST_WILL_SEND_SOCKET'; + + public readonly type = ExtHostConnectionType.Socket; + + public serialize(env: IProcessEnvironment): void { + env[SocketExtHostConnection.ENV_KEY] = '1'; + } +} + +/** + * The extension host will receive via nodejs IPC the MessagePort to its renderer. + */ +export class MessagePortExtHostConnection { + public static ENV_KEY = 'VSCODE_WILL_SEND_MESSAGE_PORT'; + + public readonly type = ExtHostConnectionType.MessagePort; + + public serialize(env: IProcessEnvironment): void { + env[MessagePortExtHostConnection.ENV_KEY] = '1'; + } +} + +export type ExtHostConnection = IPCExtHostConnection | SocketExtHostConnection | MessagePortExtHostConnection; + +function clean(env: IProcessEnvironment): void { + delete env[IPCExtHostConnection.ENV_KEY]; + delete env[SocketExtHostConnection.ENV_KEY]; + delete env[MessagePortExtHostConnection.ENV_KEY]; +} + +/** + * Write `connection` into `env` and clean up `env`. + */ +export function writeExtHostConnection(connection: ExtHostConnection, env: IProcessEnvironment): void { + // Avoid having two different keys that might introduce amiguity or problems. + clean(env); + connection.serialize(env); +} + +/** + * Read `connection` from `env` and clean up `env`. + */ +export function readExtHostConnection(env: IProcessEnvironment): ExtHostConnection { + if (env[IPCExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new IPCExtHostConnection(env[IPCExtHostConnection.ENV_KEY]!)); + } + if (env[SocketExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new SocketExtHostConnection()); + } + if (env[MessagePortExtHostConnection.ENV_KEY]) { + return cleanAndReturn(env, new MessagePortExtHostConnection()); + } + throw new Error(`No connection information defined in environment!`); +} + +function cleanAndReturn(env: IProcessEnvironment, result: ExtHostConnection): ExtHostConnection { + clean(env); + return result; +} diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 483c4f13eb0..a99692ce7cb 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -16,7 +16,6 @@ export const allApiProposals = Object.freeze({ contribViewsRemote: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsRemote.d.ts', contribViewsWelcome: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribViewsWelcome.d.ts', customEditorMove: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.customEditorMove.d.ts', - dataTransferFiles: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts', diffCommand: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', documentFiltersExclusive: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentFiltersExclusive.d.ts', documentPaste: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.documentPaste.d.ts', @@ -47,9 +46,9 @@ export const allApiProposals = Object.freeze({ quickPickSortByLabel: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.quickPickSortByLabel.d.ts', resolvers: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.resolvers.d.ts', scmActionButton: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmActionButton.d.ts', - scmInput: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmInput.d.ts', scmSelectedProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmSelectedProvider.d.ts', scmValidation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.scmValidation.d.ts', + snippetWorkspaceEdit: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts', taskPresentationGroup: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', telemetry: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetry.d.ts', terminalDataWriteEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalDataWriteEvent.d.ts', diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index 1e38aee257a..33eaef0922b 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -14,7 +14,6 @@ import { IMessage } from 'vs/workbench/services/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription, EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { ExtensionKind } from 'vs/platform/environment/common/environment'; import { allApiProposals } from 'vs/workbench/services/extensions/common/extensionsApiProposals'; -import { values } from 'vs/base/common/collections'; import { productSchemaId } from 'vs/platform/product/common/productService'; const schemaRegistry = Registry.as(Extensions.JSONContribution); @@ -236,7 +235,7 @@ export const schema: IJSONSchema = { items: { type: 'string', enum: Object.keys(allApiProposals), - markdownEnumDescriptions: values(allApiProposals) + markdownEnumDescriptions: Object.values(allApiProposals) } }, activationEvents: { @@ -601,7 +600,7 @@ schemaRegistry.registerSchema(productSchemaId, { items: { type: 'string', enum: Object.keys(allApiProposals), - markdownEnumDescriptions: values(allApiProposals) + markdownEnumDescriptions: Object.values(allApiProposals) } }] } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index c1755f15dff..0a65c761e81 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -24,8 +24,8 @@ export function dedupExtensions(system: IExtensionDescription[], user: IExtensio const extension = result.get(extensionKey); if (extension) { if (extension.isBuiltin) { - if (semver.gt(extension.version, userExtension.version)) { - logService.warn(`Skipping extension ${userExtension.extensionLocation.path} with lower version ${userExtension.version}.`); + if (semver.gte(extension.version, userExtension.version)) { + logService.warn(`Skipping extension ${userExtension.extensionLocation.path} in favour of the builtin extension ${extension.extensionLocation.path}.`); return; } // Overwriting a builtin extension inherits the `isBuiltin` property and it doesn't show a warning @@ -33,6 +33,9 @@ export function dedupExtensions(system: IExtensionDescription[], user: IExtensio } else { logService.warn(localize('overwritingExtension', "Overwriting extension {0} with {1}.", extension.extensionLocation.fsPath, userExtension.extensionLocation.fsPath)); } + } else if (userExtension.isBuiltin) { + logService.warn(`Skipping obsolete builtin extension ${userExtension.extensionLocation.path}`); + return; } result.set(extensionKey, userExtension); }); diff --git a/src/vs/workbench/services/extensions/common/rpcProtocol.ts b/src/vs/workbench/services/extensions/common/rpcProtocol.ts index db4b01da54b..76658baa5ce 100644 --- a/src/vs/workbench/services/extensions/common/rpcProtocol.ts +++ b/src/vs/workbench/services/extensions/common/rpcProtocol.ts @@ -306,9 +306,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { break; } case MessageType.Acknowledged: { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `ack`); this._onDidReceiveAcknowledge(req); break; } @@ -357,9 +355,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } private _receiveRequest(msgLength: number, req: number, rpcId: number, method: string, args: any[], usesCancellationToken: boolean): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveRequest ${getStringIdentifierForProxy(rpcId)}.${method}(`, args); const callId = String(req); let promise: Promise; @@ -379,40 +375,30 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { // Acknowledge the request const msg = MessageIO.serializeAcknowledged(req); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `ack`); this._protocol.send(msg); promise.then((r) => { delete this._cancelInvokedHandlers[callId]; const msg = MessageIO.serializeReplyOK(req, r, this._uriReplacer); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `reply:`, r); this._protocol.send(msg); }, (err) => { delete this._cancelInvokedHandlers[callId]; const msg = MessageIO.serializeReplyErr(req, err); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.OtherSide, `replyErr:`, err); this._protocol.send(msg); }); } private _receiveCancel(msgLength: number, req: number): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.OtherSide, `receiveCancel`); const callId = String(req); this._cancelInvokedHandlers[callId]?.(); } private _receiveReply(msgLength: number, req: number, value: any): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReply:`, value); const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { return; @@ -425,9 +411,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { } private _receiveReplyErr(msgLength: number, req: number, value: any): void { - if (this._logger) { - this._logger.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value); - } + this._logger?.logIncoming(msgLength, req, RequestInitiator.LocalSide, `receiveReplyErr:`, value); const callId = String(req); if (!this._pendingRPCReplies.hasOwnProperty(callId)) { @@ -494,9 +478,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { if (cancellationToken) { cancellationToken.onCancellationRequested(() => { const msg = MessageIO.serializeCancel(req); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `cancel`); this._protocol.send(MessageIO.serializeCancel(req)); }); } @@ -504,9 +486,7 @@ export class RPCProtocol extends Disposable implements IRPCProtocol { this._pendingRPCReplies[callId] = result; this._onWillSendRequest(req); const msg = MessageIO.serializeRequest(req, rpcId, methodName, serializedRequestArguments, !!cancellationToken); - if (this._logger) { - this._logger.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); - } + this._logger?.logOutgoing(msg.byteLength, req, RequestInitiator.LocalSide, `request: ${getStringIdentifierForProxy(rpcId)}.${methodName}(`, args); this._protocol.send(msg); return result; } diff --git a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts index d498ddd7960..888db160609 100644 --- a/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/nativeLocalProcessExtensionHost.ts @@ -10,16 +10,16 @@ import { StopWatch } from 'vs/base/common/stopwatch'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { createRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IExtensionHostProcessOptions } from 'vs/platform/extensions/common/extensionHostStarter'; import { ILogService } from 'vs/platform/log/common/log'; +import { IPCExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; import { createMessageOfType, MessageType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostProcess, ExtHostMessagePortCommunication, IExtHostCommunication, SandboxLocalProcessExtensionHost } from 'vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost'; export class NativeLocalProcessExtensionHost extends SandboxLocalProcessExtensionHost { protected override async _start(): Promise { const canUseUtilityProcess = await this._extensionHostStarter.canUseUtilityProcess(); - if (canUseUtilityProcess && process.env['VSCODE_USE_UTILITY_PROCESS']) { + if (canUseUtilityProcess && this._configurationService.getValue('extensions.experimental.useUtilityProcess')) { const communication = this._toDispose.add(new ExtHostMessagePortCommunication(this._logService)); return this._startWithCommunication(communication); } else { @@ -51,9 +51,7 @@ class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommun const namedPipeServer = createServer(); namedPipeServer.on('error', reject); namedPipeServer.listen(pipeName, () => { - if (namedPipeServer) { - namedPipeServer.removeListener('error', reject); - } + namedPipeServer?.removeListener('error', reject); resolve({ pipeName, namedPipeServer }); }); this._register(toDisposable(() => { @@ -67,7 +65,7 @@ class ExtHostNamedPipeCommunication extends Disposable implements IExtHostCommun establishProtocol(prepared: INamedPipePreparedData, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { const { namedPipeServer, pipeName } = prepared; - opts.env['VSCODE_IPC_HOOK_EXTHOST'] = pipeName; + writeExtHostConnection(new IPCExtHostConnection(pipeName), opts.env); return new Promise((resolve, reject) => { diff --git a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts index 69a2d8f5d53..dd9be154068 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/cachedExtensionScanner.ts @@ -15,7 +15,7 @@ import { localize } from 'vs/nls'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { timeout } from 'vs/base/common/async'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export class CachedExtensionScanner { @@ -27,7 +27,7 @@ export class CachedExtensionScanner { @INotificationService private readonly _notificationService: INotificationService, @IHostService private readonly _hostService: IHostService, @IExtensionsScannerService private readonly _extensionsScannerService: IExtensionsScannerService, - @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly _userDataProfileService: IUserDataProfileService, @ILogService private readonly _logService: ILogService, ) { this.scannedExtensions = new Promise((resolve, reject) => { @@ -43,24 +43,24 @@ export class CachedExtensionScanner { public async startScanningExtensions(): Promise { try { - const { system, user, development } = await this._scanInstalledExtensions(); - const r = dedupExtensions(system, user, development, this._logService); - this._scannedExtensionsResolve(r); + const extensions = await this._scanInstalledExtensions(); + this._scannedExtensionsResolve(extensions); } catch (err) { this._scannedExtensionsReject(err); } } - private async _scanInstalledExtensions(): Promise<{ system: IExtensionDescription[]; user: IExtensionDescription[]; development: IExtensionDescription[] }> { + private async _scanInstalledExtensions(): Promise { try { const language = platform.language; const [scannedSystemExtensions, scannedUserExtensions] = await Promise.all([ this._extensionsScannerService.scanSystemExtensions({ language, useCache: true, checkControlFile: true }), - this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfilesService.currentProfile.extensionsResource, useCache: true })]); + this._extensionsScannerService.scanUserExtensions({ language, profileLocation: this._userDataProfileService.currentProfile.extensionsResource, useCache: true })]); const scannedDevelopedExtensions = await this._extensionsScannerService.scanExtensionsUnderDevelopment({ language }, [...scannedSystemExtensions, ...scannedUserExtensions]); const system = scannedSystemExtensions.map(e => toExtensionDescription(e, false)); const user = scannedUserExtensions.map(e => toExtensionDescription(e, false)); const development = scannedDevelopedExtensions.map(e => toExtensionDescription(e, true)); + const r = dedupExtensions(system, user, development, this._logService); const disposable = this._extensionsScannerService.onDidChangeCache(() => { disposable.dispose(); this._notificationService.prompt( @@ -73,11 +73,11 @@ export class CachedExtensionScanner { ); }); timeout(5000).then(() => disposable.dispose()); - return { system, user, development }; + return r; } catch (err) { this._logService.error(`Error scanning installed extensions:`); this._logService.error(err); - return { system: [], user: [], development: [] }; + return []; } } diff --git a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts index 7b25b8f9142..9a989916aa6 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/electronExtensionService.ts @@ -8,8 +8,8 @@ import { AbstractExtensionService, ExtensionHostCrashTracker, ExtensionRunningPr import * as nls from 'vs/nls'; import { runWhenIdle } from 'vs/base/common/async'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IWebExtensionsScannerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteExtensionHostDataProvider, RemoteExtensionHost, IRemoteExtensionHostInitData } from 'vs/workbench/services/extensions/common/remoteExtensionHost'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -66,7 +66,7 @@ export abstract class ElectronExtensionService extends AbstractExtensionService @IWorkbenchExtensionEnablementService extensionEnablementService: IWorkbenchExtensionEnablementService, @IFileService fileService: IFileService, @IProductService productService: IProductService, - @IExtensionManagementService extensionManagementService: IExtensionManagementService, + @IWorkbenchExtensionManagementService extensionManagementService: IWorkbenchExtensionManagementService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IConfigurationService configurationService: IConfigurationService, @IExtensionManifestPropertiesService extensionManifestPropertiesService: IExtensionManifestPropertiesService, @@ -594,6 +594,12 @@ export abstract class ElectronExtensionService extends AbstractExtensionService // Dispose everything associated with the extension host this.stopExtensionHosts(); + // Dispose the management connection to avoid reconnecting after the extension host exits + const connection = this._remoteAgentService.getConnection(); + if (connection) { + connection.dispose(); + } + if (this._isExtensionDevTestFromCli) { // When CLI testing make sure to exit with proper exit code this._nativeHostService.exit(code); diff --git a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts index 58e38c744c8..2932c6be3ef 100644 --- a/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-sandbox/localProcessExtensionHost.ts @@ -44,6 +44,8 @@ import { process } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { generateUuid } from 'vs/base/common/uuid'; import { acquirePort } from 'vs/base/parts/ipc/electron-sandbox/ipc.mp'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MessagePortExtHostConnection, writeExtHostConnection } from 'vs/workbench/services/extensions/common/extensionHostEnv'; export interface ILocalProcessExtensionHostInitData { readonly autoStart: boolean; @@ -150,6 +152,7 @@ export class SandboxLocalProcessExtensionHost implements IExtensionHost { @IProductService private readonly _productService: IProductService, @IShellEnvironmentService private readonly _shellEnvironmentService: IShellEnvironmentService, @IExtensionHostStarter protected readonly _extensionHostStarter: IExtensionHostStarter, + @IConfigurationService protected readonly _configurationService: IConfigurationService, ) { const devOpts = parseExtensionDevOptions(this._environmentService); this._isExtensionDevHost = devOpts.isExtensionDevHost; @@ -618,7 +621,7 @@ export class ExtHostMessagePortCommunication extends Disposable implements IExtH establishProtocol(prepared: void, extensionHostProcess: ExtensionHostProcess, opts: IExtensionHostProcessOptions): Promise { - opts.env['VSCODE_WILL_SEND_MESSAGE_PORT'] = 'true'; + writeExtHostConnection(new MessagePortExtHostConnection(), opts.env); // Get ready to acquire the message port from the shared process worker const portPromise = acquirePort(undefined /* we trigger the request via service call! */, opts.responseChannel, opts.responseNonce); diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index 7dc22a8528d..5b9c83b5b8e 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -20,6 +20,8 @@ import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { migrateExtensionStorage } from 'vs/workbench/services/extensions/common/extensionStorageMigration'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; suite('ExtensionStorageMigration', () => { @@ -36,7 +38,8 @@ suite('ExtensionStorageMigration', () => { fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider())); instantiationService.stub(IFileService, fileService); const environmentService = instantiationService.stub(IEnvironmentService, >{ userRoamingDataHome: ROOT, workspaceStorageHome }); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); instantiationService.stub(IExtensionStorageService, instantiationService.createInstance(ExtensionStorageService)); }); diff --git a/src/vs/workbench/services/keybinding/browser/keybindingService.ts b/src/vs/workbench/services/keybinding/browser/keybindingService.ts index 97f2d4805d8..fe43ddc55ed 100644 --- a/src/vs/workbench/services/keybinding/browser/keybindingService.ts +++ b/src/vs/workbench/services/keybinding/browser/keybindingService.ts @@ -51,7 +51,7 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { dirname } from 'vs/base/common/resources'; import { getAllUnboundCommands } from 'vs/workbench/services/keybinding/browser/unboundCommands'; import { UserSettingsLabelProvider } from 'vs/base/common/keybindingLabels'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface ContributedKeyBinding { command: string; @@ -191,7 +191,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { @ICommandService commandService: ICommandService, @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, - @IUserDataProfilesService userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService userDataProfileService: IUserDataProfileService, @IConfigurationService configurationService: IConfigurationService, @IHostService private readonly hostService: IHostService, @IExtensionService extensionService: IExtensionService, @@ -224,7 +224,7 @@ export class WorkbenchKeybindingService extends AbstractKeybindingService { this._cachedResolver = null; - this.userKeybindings = this._register(new UserKeybindings(userDataProfilesService.currentProfile.keybindingsResource, fileService, logService)); + this.userKeybindings = this._register(new UserKeybindings(userDataProfileService.currentProfile.keybindingsResource, fileService, logService)); this.userKeybindings.initialize().then(() => { if (this.userKeybindings.keybindings.length) { this.updateResolver(); diff --git a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts index 22d93a2fb09..a50ba251695 100644 --- a/src/vs/workbench/services/keybinding/common/keybindingEditing.ts +++ b/src/vs/workbench/services/keybinding/common/keybindingEditing.ts @@ -25,7 +25,7 @@ import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybindin import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export const IKeybindingEditingService = createDecorator('keybindingEditingService'); @@ -47,14 +47,14 @@ export class KeybindingsEditingService extends Disposable implements IKeybinding public _serviceBrand: undefined; private queue: Queue; - private resource: URI = this.userDataProfilesService.currentProfile.keybindingsResource; + private resource: URI = this.userDataProfileService.currentProfile.keybindingsResource; constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService ) { super(); this.queue = new Queue(); diff --git a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts index 6eb6eba0fe0..eee64e018fe 100644 --- a/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts +++ b/src/vs/workbench/services/keybinding/test/browser/keybindingEditing.test.ts @@ -28,7 +28,9 @@ import { joinPath } from 'vs/base/common/resources'; import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DefaultOptions, toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface Modifiers { metaKey?: boolean; @@ -45,7 +47,7 @@ suite('KeybindingsEditing', () => { let instantiationService: TestInstantiationService; let fileService: IFileService; let environmentService: IEnvironmentService; - let userDataProfilesService: IUserDataProfilesService; + let userDataProfileService: IUserDataProfileService; let testObject: KeybindingsEditingService; setup(async () => { @@ -64,7 +66,8 @@ suite('KeybindingsEditing', () => { const configService = new TestConfigurationService(); configService.setUserConfiguration('files', { 'eol': '\n' }); - userDataProfilesService = new UserDataProfilesService(undefined, undefined, environmentService, fileService, logService); + const profile = toUserDataProfile('temp', environmentService.userRoamingDataHome, DefaultOptions, true); + userDataProfileService = new UserDataProfileService(profile, profile); instantiationService = workbenchInstantiationService({ fileService: () => fileService, @@ -78,7 +81,7 @@ suite('KeybindingsEditing', () => { teardown(() => disposables.clear()); test('errors cases - parse errors', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString(',,,,,,,,,,,,,,')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -88,7 +91,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - parse errors 2', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('[{"key": }]')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail with parse errors'); @@ -105,7 +108,7 @@ suite('KeybindingsEditing', () => { }); test('errors cases - did not find an array', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('{"key": "alt+c", "command": "hello"}')); try { await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape } }), 'alt+c', undefined); assert.fail('Should fail'); @@ -115,7 +118,7 @@ suite('KeybindingsEditing', () => { }); test('edit a default keybinding to an empty file', async () => { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString('')); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString('')); const expected: IUserFriendlyKeybinding[] = [{ key: 'alt+c', command: 'a' }, { key: 'escape', command: '-a' }]; await testObject.editKeybinding(aResolvedKeybindingItem({ firstPart: { keyCode: KeyCode.Escape }, command: 'a' }), 'alt+c', undefined); assert.deepStrictEqual(await getUserKeybindings(), expected); @@ -245,11 +248,11 @@ suite('KeybindingsEditing', () => { }); async function writeToKeybindingsFile(...keybindings: IUserFriendlyKeybinding[]): Promise { - await fileService.writeFile(userDataProfilesService.currentProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); + await fileService.writeFile(userDataProfileService.currentProfile.keybindingsResource, VSBuffer.fromString(JSON.stringify(keybindings || []))); } async function getUserKeybindings(): Promise { - return json.parse((await fileService.readFile(userDataProfilesService.currentProfile.keybindingsResource)).value.toString()); + return json.parse((await fileService.readFile(userDataProfileService.currentProfile.keybindingsResource)).value.toString()); } function aResolvedKeybindingItem({ command, when, isDefault, firstPart, chordPart }: { command?: string; when?: string; isDefault?: boolean; firstPart?: { keyCode: KeyCode; modifiers?: Modifiers }; chordPart?: { keyCode: KeyCode; modifiers?: Modifiers } }): ResolvedKeybindingItem { diff --git a/src/vs/workbench/services/notification/common/notificationService.ts b/src/vs/workbench/services/notification/common/notificationService.ts index ea3df42e4f9..f618b5e9ce0 100644 --- a/src/vs/workbench/services/notification/common/notificationService.ts +++ b/src/vs/workbench/services/notification/common/notificationService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter } from 'vs/platform/notification/common/notification'; +import { INotificationService, INotification, INotificationHandle, Severity, NotificationMessage, INotificationActions, IPromptChoice, IPromptOptions, IStatusMessageOptions, NoOpNotification, NeverShowAgainScope, NotificationsFilter, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; import { NotificationsModel, ChoiceAction, NotificationChangeType } from 'vs/workbench/common/notifications'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; @@ -98,7 +98,7 @@ export class NotificationService extends Disposable implements INotificationServ // Handle neverShowAgain option accordingly if (notification.neverShowAgain) { - const scope = notification.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; + const scope = this.toStorageScope(notification.neverShowAgain); const id = notification.neverShowAgain.id; // If the user already picked to not show the notification @@ -142,12 +142,25 @@ export class NotificationService extends Disposable implements INotificationServ return handle; } + private toStorageScope(options: INeverShowAgainOptions): StorageScope { + switch (options.scope) { + case NeverShowAgainScope.APPLICATION: + return StorageScope.APPLICATION; + case NeverShowAgainScope.GLOBAL: + return StorageScope.GLOBAL; + case NeverShowAgainScope.WORKSPACE: + return StorageScope.WORKSPACE; + default: + return StorageScope.APPLICATION; + } + } + prompt(severity: Severity, message: string, choices: IPromptChoice[], options?: IPromptOptions): INotificationHandle { const toDispose = new DisposableStore(); // Handle neverShowAgain option accordingly if (options?.neverShowAgain) { - const scope = options.neverShowAgain.scope === NeverShowAgainScope.WORKSPACE ? StorageScope.WORKSPACE : StorageScope.GLOBAL; + const scope = this.toStorageScope(options.neverShowAgain); const id = options.neverShowAgain.id; // If the user already picked to not show the notification diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index bf49817490f..fa8078055ce 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -44,7 +44,7 @@ import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEd import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { isArray, isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const emptyEditableSettingsContent = '{\n}'; @@ -66,7 +66,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic @INotificationService private readonly notificationService: INotificationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IKeybindingService keybindingService: IKeybindingService, @IModelService private readonly modelService: IModelService, @@ -93,7 +93,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private readonly defaultSettingsRawResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/defaultSettings.json' }); get userSettingsResource(): URI { - return this.userDataProfilesService.currentProfile.settingsResource; + return this.userDataProfileService.currentProfile.settingsResource; } get workspaceSettingsResource(): URI | null { @@ -304,7 +304,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic options = { pinned: true, revealIfOpened: true, ...options }; if (textual) { const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]'; - const editableKeybindings = this.userDataProfilesService.currentProfile.keybindingsResource; + const editableKeybindings = this.userDataProfileService.currentProfile.keybindingsResource; const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings'); // Create as needed and open in editor diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index 9fb02061b6e..4fff738137d 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -640,9 +640,7 @@ export class DefaultSettings extends Disposable { settingsGroup.sections[settingsGroup.sections.length - 1].settings = configurationSettings; } } - if (config.allOf) { - config.allOf.forEach(c => this.parseConfig(c, result, configurations, settingsGroup, seenSettings)); - } + config.allOf?.forEach(c => this.parseConfig(c, result, configurations, settingsGroup, seenSettings)); return result; } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index 6a899e927be..0054090e83d 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -68,6 +68,7 @@ export interface IRemoteAgentConnection { readonly onReconnecting: Event; readonly onDidStateChange: Event; + dispose(): void; getChannel(channelName: string): T; withChannel(channelName: string, callback: (channel: T) => Promise): Promise; registerChannel>(channelName: string, channel: T): void; diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index c40362672e2..f5127405714 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -171,13 +171,9 @@ export class SearchService extends Disposable implements ISearchService { private getSchemesInQuery(query: ISearchQuery): Set { const schemes = new Set(); - if (query.folderQueries) { - query.folderQueries.forEach(fq => schemes.add(fq.folder.scheme)); - } + query.folderQueries?.forEach(fq => schemes.add(fq.folder.scheme)); - if (query.extraFileResources) { - query.extraFileResources.forEach(extraFile => schemes.add(extraFile.scheme)); - } + query.extraFileResources?.forEach(extraFile => schemes.add(extraFile.scheme)); return schemes; } diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index 82e31e6f31f..6e9251f016f 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -334,9 +334,7 @@ export class SearchService implements IRawSearchService { private doSearch(engine: ISearchEngine, progressCallback: IFileProgressCallback, batchSize: number, token?: CancellationToken): Promise { return new Promise((c, e) => { let batch: IRawFileMatch[] = []; - if (token) { - token.onCancellationRequested(() => engine.cancel()); - } + token?.onCancellationRequested(() => engine.cancel()); engine.search((match) => { if (match) { diff --git a/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts b/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts index 894141cce99..68a8dd657bb 100644 --- a/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts +++ b/src/vs/workbench/services/sessionSync/browser/sessionSyncWorkbenchService.ts @@ -60,14 +60,15 @@ export class SessionSyncWorkbenchService extends Disposable implements ISessionS /** * * @param editSession An object representing edit session state to be restored. + * @returns The ref of the stored edit session state. */ - async write(editSession: EditSession): Promise { + async write(editSession: EditSession): Promise { this.initialized = await this.waitAndInitialize(); if (!this.initialized) { throw new Error('Please sign in to store your edit session.'); } - await this.storeClient?.write('editSessions', JSON.stringify(editSession), null); + return this.storeClient!.write('editSessions', JSON.stringify(editSession), null); } /** diff --git a/src/vs/workbench/services/sessionSync/common/sessionSync.ts b/src/vs/workbench/services/sessionSync/common/sessionSync.ts index 4b91a77f95e..7e4af5123ec 100644 --- a/src/vs/workbench/services/sessionSync/common/sessionSync.ts +++ b/src/vs/workbench/services/sessionSync/common/sessionSync.ts @@ -13,7 +13,7 @@ export interface ISessionSyncWorkbenchService { _serviceBrand: undefined; read(ref: string | undefined): Promise; - write(editSession: EditSession): Promise; + write(editSession: EditSession): Promise; } export enum ChangeType { diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index d720696d8cb..8e86627e42f 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { ApplicationInsights } from '@microsoft/applicationinsights-web'; import { Disposable } from 'vs/base/common/lifecycle'; import { IObservableValue } from 'vs/base/common/observableValue'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -11,98 +10,17 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILoggerService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService } from 'vs/platform/storage/common/storage'; +import { OneDataSystemWebAppender } from 'vs/platform/telemetry/browser/1dsAppender'; +import { WebAppInsightsAppender } from 'vs/platform/telemetry/browser/appInsightsAppender'; import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings'; import { ITelemetryData, ITelemetryInfo, ITelemetryService, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogAppender'; import { ITelemetryServiceConfig, TelemetryService as BaseTelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { ITelemetryAppender, NullTelemetryService, supportsTelemetry, validateTelemetryData } from 'vs/platform/telemetry/common/telemetryUtils'; +import { ITelemetryAppender, NullTelemetryService, supportsTelemetry } from 'vs/platform/telemetry/common/telemetryUtils'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { resolveWorkbenchCommonProperties } from 'vs/workbench/services/telemetry/browser/workbenchCommonProperties'; -class WebAppInsightsAppender implements ITelemetryAppender { - private _aiClient: ApplicationInsights | undefined; - private _aiClientLoaded = false; - private _telemetryCache: { eventName: string; data: any }[] = []; - - constructor(private _eventPrefix: string, aiKey: string) { - const endpointUrl = 'https://mobile.events.data.microsoft.com/collect/v1'; - import('@microsoft/applicationinsights-web').then(aiLibrary => { - this._aiClient = new aiLibrary.ApplicationInsights({ - config: { - instrumentationKey: aiKey, - endpointUrl, - disableAjaxTracking: true, - disableExceptionTracking: true, - disableFetchTracking: true, - disableCorrelationHeaders: true, - disableCookiesUsage: true, - autoTrackPageVisitTime: false, - emitLineDelimitedJson: true, - }, - }); - this._aiClient.loadAppInsights(); - // Client is loaded we can now flush the cached events - this._aiClientLoaded = true; - this._telemetryCache.forEach(cacheEntry => this.log(cacheEntry.eventName, cacheEntry.data)); - this._telemetryCache = []; - - // If we cannot access the endpoint this most likely means it's being blocked - // and we should not attempt to send any telemetry. - fetch(endpointUrl, { method: 'POST' }).catch(() => (this._aiClient = undefined)); - }).catch(err => { - console.error(err); - }); - } - - /** - * Logs a telemetry event with eventName and data - * @param eventName The event name - * @param data The data associated with the events - */ - public log(eventName: string, data: any): void { - if (!this._aiClient && this._aiClientLoaded) { - return; - } else if (!this._aiClient && !this._aiClientLoaded) { - this._telemetryCache.push({ eventName, data }); - return; - } - - data = validateTelemetryData(data); - - // Web does not expect properties and measurements so we must - // spread them out. This is different from desktop which expects them - data = { ...data.properties, ...data.measurements }; - - // undefined assertion is ok since above two if statements cover both cases - this._aiClient!.trackEvent({ name: this._eventPrefix + '/' + eventName }, data); - } - - /** - * Flushes all the telemetry data still in the buffer - */ - public flush(): Promise { - if (this._aiClient) { - this._aiClient.flush(); - this._aiClient = undefined; - } - return Promise.resolve(undefined); - } -} - -class WebTelemetryAppender implements ITelemetryAppender { - - constructor(private _appender: ITelemetryAppender) { } - - log(eventName: string, data: any): void { - this._appender.log(eventName, data); - } - - flush(): Promise { - return this._appender.flush(); - } -} - export class TelemetryService extends Disposable implements ITelemetryService { declare readonly _serviceBrand: undefined; @@ -120,11 +38,20 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (supportsTelemetry(productService, environmentService) && productService.aiConfig?.asimovKey) { + if (supportsTelemetry(productService, environmentService) && productService.aiConfig?.asimovKey && productService.aiConfig?.ariaKey) { // If remote server is present send telemetry through that, else use the client side appender - const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new WebAppInsightsAppender('monacoworkbench', productService.aiConfig?.asimovKey); + const internalTesting = configurationService.getValue('telemetry.internalTesting'); + const appenders = []; + if (internalTesting) { + const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new OneDataSystemWebAppender('monacoworkbench', null, productService.aiConfig?.ariaKey); + appenders.push(telemetryProvider); + } else { + const telemetryProvider: ITelemetryAppender = remoteAgentService.getConnection() !== null ? { log: remoteAgentService.logTelemetry.bind(remoteAgentService), flush: remoteAgentService.flushTelemetry.bind(remoteAgentService) } : new WebAppInsightsAppender('monacoworkbench', productService.aiConfig?.asimovKey); + appenders.push(telemetryProvider); + } + appenders.push(new TelemetryLogAppender(loggerService, environmentService)); const config: ITelemetryServiceConfig = { - appenders: [new WebTelemetryAppender(telemetryProvider), new TelemetryLogAppender(loggerService, environmentService)], + appenders, commonProperties: resolveWorkbenchCommonProperties(storageService, productService.commit, productService.version, environmentService.remoteAuthority, productService.embedderIdentifier, productService.removeTelemetryMachineId, environmentService.options && environmentService.options.resolveCommonTelemetryProperties), sendErrorTelemetry: this.sendErrorTelemetry, }; diff --git a/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts b/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts index 69116d9a458..16bce794a23 100644 --- a/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts +++ b/src/vs/workbench/services/telemetry/browser/workbenchCommonProperties.ts @@ -30,15 +30,15 @@ export async function resolveWorkbenchCommonProperties( resolveAdditionalProperties?: () => { [key: string]: any } ): Promise<{ [name: string]: string | undefined }> { const result: { [name: string]: string | undefined } = Object.create(null); - const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; - const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION)!; + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION)!; let machineId: string | undefined; if (!removeMachineId) { - machineId = storageService.get(machineIdKey, StorageScope.GLOBAL); + machineId = storageService.get(machineIdKey, StorageScope.APPLICATION); if (!machineId) { machineId = uuid.generateUuid(); - storageService.store(machineIdKey, machineId, StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store(machineIdKey, machineId, StorageScope.APPLICATION, StorageTarget.MACHINE); } } else { machineId = `Redacted-${productIdentifier ?? 'web'}`; diff --git a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts index ff36a5231e6..9d5b4282308 100644 --- a/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts +++ b/src/vs/workbench/services/telemetry/electron-sandbox/workbenchCommonProperties.ts @@ -23,8 +23,8 @@ export async function resolveWorkbenchCommonProperties( remoteAuthority?: string ): Promise<{ [name: string]: string | boolean | undefined }> { const result = await resolveCommonProperties(fileService, release, hostname, process.arch, commit, version, machineId, msftInternalDomains, installSourcePath); - const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!; - const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!; + const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.APPLICATION)!; + const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.APPLICATION)!; // __GDPR__COMMON__ "common.version.shell" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" } result['common.version.shell'] = process.versions['electron']; diff --git a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts index ac596534940..8ae51addd3e 100644 --- a/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts +++ b/src/vs/workbench/services/telemetry/test/electron-browser/commonProperties.test.ts @@ -72,7 +72,7 @@ suite('Telemetry - common properties', function () { test('lastSessionDate when available', async function () { - testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.GLOBAL, StorageTarget.MACHINE); + testStorageService.store('telemetry.lastSessionDate', new Date().toUTCString(), StorageScope.APPLICATION, StorageTarget.MACHINE); const props = await resolveWorkbenchCommonProperties(testStorageService, testFileService, release(), hostname(), commit, version, 'someMachineId', undefined, installSource); assert.ok('common.lastSessionDate' in props); // conditional, see below diff --git a/src/vs/workbench/services/textMate/browser/textMateWorker.ts b/src/vs/workbench/services/textMate/browser/textMateWorker.ts index c4d6299742d..9c4339175eb 100644 --- a/src/vs/workbench/services/textMate/browser/textMateWorker.ts +++ b/src/vs/workbench/services/textMate/browser/textMateWorker.ts @@ -214,9 +214,7 @@ export class TextMateWorker { public async acceptTheme(theme: IRawTheme, colorMap: string[]): Promise { const grammarFactory = await this._grammarFactory; - if (grammarFactory) { - grammarFactory.setTheme(theme, colorMap); - } + grammarFactory?.setTheme(theme, colorMap); } public _setTokens(resource: URI, versionId: number, tokens: Uint8Array): void { diff --git a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts index a4c26ca6d6a..f401690b666 100644 --- a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts @@ -79,8 +79,7 @@ const schema: IJSONSchema = { }, iconDefinitions: { description: nls.localize('schema.iconDefinitions', 'Association of icon name to a font character.'), - $ref: iconsSchemaId, - additionalProperties: false + $ref: iconsSchemaId } } }; diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index 92aca9f92cd..57b7ae7430e 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -31,7 +31,7 @@ export interface ITitleService { /** * An event when the title menu is enabled/disabled */ - readonly onDidChangeTitleMenuVisibility: Event; + readonly onDidChangeCommandCenterVisibility: Event; /** * Update some environmental title properties. diff --git a/src/vs/workbench/services/userData/browser/userDataInit.ts b/src/vs/workbench/services/userData/browser/userDataInit.ts index 404c963b5ff..1d7504a2656 100644 --- a/src/vs/workbench/services/userData/browser/userDataInit.ts +++ b/src/vs/workbench/services/userData/browser/userDataInit.ts @@ -87,7 +87,7 @@ export class UserDataInitializationService implements IUserDataInitializationSer return; } - if (!this.storageService.isNew(StorageScope.GLOBAL)) { + if (!this.storageService.isNew(StorageScope.APPLICATION)) { this.logService.trace(`Skipping initializing user data as application was opened before`); return; } diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts index 27336f47e71..c5964e980bc 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileManagement.ts @@ -5,7 +5,7 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; -import { basename, joinPath } from 'vs/base/common/resources'; +import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; @@ -16,10 +16,14 @@ import { IFileService } from 'vs/platform/files/common/files'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { EXTENSIONS_RESOURCE_NAME, IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceContextService, IWorkspaceIdentifier, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IExtensionManagementServerService, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { CreationOptions, IUserDataProfileManagementService, IUserDataProfileService, IUserDataProfileTemplate, PROFILES_CATEGORY } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const DefaultOptions: CreationOptions = { settings: true, @@ -35,6 +39,7 @@ export class UserDataProfileManagementService extends Disposable implements IUse constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IFileService private readonly fileService: IFileService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService, @@ -42,75 +47,104 @@ export class UserDataProfileManagementService extends Disposable implements IUse @IHostService private readonly hostService: IHostService, @IDialogService private readonly dialogService: IDialogService, @IProgressService private readonly progressService: IProgressService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IStorageService private readonly storageService: IStorageService, + @IExtensionService private readonly extensionService: IExtensionService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILogService logService: ILogService ) { super(); } - async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions): Promise { - const promises: Promise[] = []; - const newProfile = this.userDataProfilesService.createProfile(name); - await this.fileService.createFolder(newProfile.location); - if (options?.uiState) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.globalStorageHome, newProfile.globalStorageHome)); + private async checkAndCreateExtensionsProfileResource(): Promise { + if (this.userDataProfileService.currentProfile.extensionsResource) { + return this.userDataProfileService.currentProfile.extensionsResource; } - if (options?.settings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.settingsResource, newProfile.settingsResource)); + if (!this.userDataProfileService.defaultProfile.extensionsResource) { + // Extensions profile is not yet created for default profile, create it now + return this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, EXTENSIONS_RESOURCE_NAME)); } - if (options?.extensions && newProfile.extensionsResource) { - promises.push((async () => { - const extensionsProfileResource = this.userDataProfilesService.currentProfile.extensionsResource ?? await this.createDefaultExtensionsProfile(joinPath(this.userDataProfilesService.defaultProfile.location, basename(newProfile.extensionsResource!))); - this.fileService.copy(extensionsProfileResource, newProfile.extensionsResource!); - })()); - } - if (options?.keybindings) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); - } - if (options?.tasks) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.tasksResource, newProfile.tasksResource)); - } - if (options?.snippets) { - promises.push(this.fileService.copy(this.userDataProfilesService.currentProfile.snippetsHome, newProfile.snippetsHome)); - } - await Promise.allSettled(promises); - await this.doSwitchProfile(name); + throw new Error('Invalid Profile'); } - async removeProfile(name: string): Promise { - if (name === this.userDataProfilesService.defaultProfile.name) { + async createAndEnterProfile(name: string, options: CreationOptions = DefaultOptions, fromExisting?: boolean): Promise { + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace")); + } + const promises: Promise[] = []; + const newProfile = this.userDataProfilesService.newProfile(name); + await this.fileService.createFolder(newProfile.location); + const extensionsProfileResourcePromise = this.checkAndCreateExtensionsProfileResource(); + promises.push(extensionsProfileResourcePromise); + if (fromExisting) { + if (options?.uiState) { + // No op because, migration is handled by storage service while entering profile + } + if (options?.settings) { + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.settingsResource, newProfile.settingsResource)); + } + if (options?.extensions) { + promises.push((async () => this.fileService.copy(await extensionsProfileResourcePromise, newProfile.extensionsResource))()); + } + if (options?.keybindings) { + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.keybindingsResource, newProfile.keybindingsResource)); + } + if (options?.tasks) { + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.tasksResource, newProfile.tasksResource)); + } + if (options?.snippets) { + promises.push(this.fileService.copy(this.userDataProfileService.currentProfile.snippetsHome, newProfile.snippetsHome)); + } + } else { + promises.push(this.fileService.createFolder(newProfile.globalStorageHome)); + } + await Promise.allSettled(promises); + const createdProfile = await this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); + await this.enterProfile(createdProfile, !!fromExisting); + } + + async removeProfile(profile: IUserDataProfile): Promise { + if (!this.userDataProfilesService.profiles.some(p => p.id === profile.id)) { + throw new Error(`Profile ${profile.name} does not exist`); + } + if (profile.isDefault) { throw new Error(localize('cannotDeleteDefaultProfile', "Cannot delete the default profile")); } - if (name === this.userDataProfilesService.currentProfile.name) { + if (profile.id === this.userDataProfileService.currentProfile.id) { throw new Error(localize('cannotDeleteCurrentProfile', "Cannot delete the current profile")); } - const profiles = await this.userDataProfilesService.getAllProfiles(); - const profile = profiles.find(p => p.name === name); - if (!profile) { - throw new Error(`Profile ${name} does not exist`); - } - if (profiles.length === 2) { + await this.userDataProfilesService.removeProfile(profile); + if (this.userDataProfilesService.profiles.length === 2) { await this.fileService.del(this.userDataProfilesService.profilesHome, { recursive: true }); } else { await this.fileService.del(profile.location, { recursive: true }); } } - async switchProfile(name: string): Promise { - const profiles = await this.userDataProfilesService.getAllProfiles(); - const profile = profiles.find(p => p.name === name); - if (!profile) { - throw new Error(`Profile ${name} does not exist`); + async switchProfile(profile: IUserDataProfile): Promise { + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotSwitchProfileInEmptyWorkbench', "Cannot switch a profile in an empty workspace")); } - await this.doSwitchProfile(name); + if (!this.userDataProfilesService.profiles.some(p => p.id === profile.id)) { + throw new Error(`Profile ${profile.name} does not exist`); + } + await this.userDataProfilesService.setProfileForWorkspace(profile, workspaceIdentifier); + await this.enterProfile(profile, false); } async createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options: CreationOptions = DefaultOptions): Promise { - await this.progressService.withProgress({ + const workspaceIdentifier = this.getWorkspaceIdentifier(); + if (!workspaceIdentifier) { + throw new Error(localize('cannotCreateProfileInEmptyWorkbench', "Cannot create a profile in an empty workspace")); + } + const profile = await this.progressService.withProgress({ location: ProgressLocation.Notification, title: localize('profiles.creating', "{0}: Creating...", PROFILES_CATEGORY), }, async progress => { const promises: Promise[] = []; - const newProfile = this.userDataProfilesService.createProfile(name); + const newProfile = this.userDataProfilesService.newProfile(name); await this.fileService.createFolder(newProfile.location); if (template.globalState) { // todo: create global state @@ -122,27 +156,39 @@ export class UserDataProfileManagementService extends Disposable implements IUse promises.push(this.fileService.writeFile(newProfile.extensionsResource, VSBuffer.fromString(template.extensions))); } await Promise.allSettled(promises); + return this.userDataProfilesService.createProfile(newProfile, options, workspaceIdentifier); }); - await this.doSwitchProfile(name); + await this.enterProfile(profile, false); } - async reset(): Promise { - if (this.userDataProfilesService.currentProfile.name !== this.userDataProfilesService.defaultProfile.name) { - throw new Error('Please switch to default profile to reset'); + private getWorkspaceIdentifier(): ISingleFolderWorkspaceIdentifier | IWorkspaceIdentifier | undefined { + const workspace = this.workspaceContextService.getWorkspace(); + switch (this.workspaceContextService.getWorkbenchState()) { + case WorkbenchState.FOLDER: + return { uri: workspace.folders[0].uri, id: workspace.id }; + case WorkbenchState.WORKSPACE: + return { configPath: workspace.configuration!, id: workspace.id }; } - await this.fileService.del(this.userDataProfilesService.profilesHome); + return undefined; } - private async doSwitchProfile(name: string): Promise { - await this.userDataProfilesService.setProfile(name); - const result = await this.dialogService.confirm({ - type: 'info', - message: localize('restart message', "Switching a profile requires restarting VS Code."), - primaryButton: localize('restart button', "&&Restart"), - }); - if (result.confirmed) { - await this.hostService.restart(); + private async enterProfile(profile: IUserDataProfile, preserveData: boolean): Promise { + if (this.environmentService.remoteAuthority) { + const result = await this.dialogService.confirm({ + type: 'info', + message: localize('reload message', "Switching a profile requires reloading VS Code."), + primaryButton: localize('reload button', "&&Reload"), + }); + if (result.confirmed) { + await this.hostService.reload(); + } + return; } + + this.extensionService.stopExtensionHosts(); + await this.storageService.switch(profile, preserveData); + await this.userDataProfileService.updateCurrentProfile(profile); + await this.extensionService.startExtensionHosts(); } private async createDefaultExtensionsProfile(extensionsProfileResource: URI): Promise { diff --git a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts index 8ec179b556f..6cc9a6d284f 100644 --- a/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/settingsProfile.ts @@ -8,10 +8,9 @@ import { ConfigurationScope, Extensions, IConfigurationRegistry } from 'vs/platf import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { Registry } from 'vs/platform/registry/common/platform'; -import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IUserDataProfileService, IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { removeComments, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -import { IResourceProfile, ProfileCreationOptions } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; interface ISettingsContent { settings: string; @@ -21,7 +20,7 @@ export class SettingsProfile implements IResourceProfile { constructor( @IFileService private readonly fileService: IFileService, - @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, + @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, @ILogService private readonly logService: ILogService, ) { @@ -29,7 +28,7 @@ export class SettingsProfile implements IResourceProfile { async getProfileContent(options?: ProfileCreationOptions): Promise { const ignoredSettings = this.getIgnoredSettings(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfileService.currentProfile.settingsResource); const localContent = await this.getLocalFileContent(); let settingsProfileContent = updateIgnoredSettings(localContent || '{}', '{}', ignoredSettings, formattingOptions); if (options?.skipComments) { @@ -45,9 +44,9 @@ export class SettingsProfile implements IResourceProfile { const settingsContent: ISettingsContent = JSON.parse(content); this.logService.trace(`Profile: Applying settings...`); const localSettingsContent = await this.getLocalFileContent(); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfilesService.currentProfile.settingsResource); + const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.userDataProfileService.currentProfile.settingsResource); const contentToUpdate = updateIgnoredSettings(settingsContent.settings, localSettingsContent || '{}', this.getIgnoredSettings(), formattingOptions); - await this.fileService.writeFile(this.userDataProfilesService.currentProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); + await this.fileService.writeFile(this.userDataProfileService.currentProfile.settingsResource, VSBuffer.fromString(contentToUpdate)); this.logService.info(`Profile: Applied settings`); } @@ -59,7 +58,7 @@ export class SettingsProfile implements IResourceProfile { private async getLocalFileContent(): Promise { try { - const content = await this.fileService.readFile(this.userDataProfilesService.currentProfile.settingsResource); + const content = await this.fileService.readFile(this.userDataProfileService.currentProfile.settingsResource); return content.value.toString(); } catch (error) { return null; diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts index 3d43ca85d1f..9a3fefc8b16 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfile.ts @@ -4,8 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import { isUndefined } from 'vs/base/common/types'; +import { Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; export type CreationOptions = { settings?: boolean; @@ -16,16 +19,29 @@ export type CreationOptions = { uiState?: boolean; }; +export interface DidChangeUserDataProfileEvent { + readonly profile: IUserDataProfile; + join(promise: Promise): void; +} + +export const IUserDataProfileService = createDecorator('IUserDataProfileService'); +export interface IUserDataProfileService { + readonly _serviceBrand: undefined; + readonly defaultProfile: IUserDataProfile; + readonly onDidChangeCurrentProfile: Event; + readonly currentProfile: IUserDataProfile; + updateCurrentProfile(currentProfile: IUserDataProfile): Promise; +} + export const IUserDataProfileManagementService = createDecorator('IUserDataProfileManagementService'); export interface IUserDataProfileManagementService { readonly _serviceBrand: undefined; - createAndEnterProfile(name: string, options?: CreationOptions): Promise; + createAndEnterProfile(name: string, options?: CreationOptions, fromExisting?: boolean): Promise; createAndEnterProfileFromTemplate(name: string, template: IUserDataProfileTemplate, options?: CreationOptions): Promise; - removeProfile(name: string): Promise; - switchProfile(name: string): Promise; + removeProfile(profile: IUserDataProfile): Promise; + switchProfile(profile: IUserDataProfile): Promise; - reset(): Promise; } export interface IUserDataProfileTemplate { @@ -35,7 +51,7 @@ export interface IUserDataProfileTemplate { readonly extensions?: string; } -export function isProfile(thing: any): thing is IUserDataProfileTemplate { +export function isUserDataProfileTemplate(thing: unknown): thing is IUserDataProfileTemplate { const candidate = thing as IUserDataProfileTemplate | undefined; return !!(candidate && typeof candidate === 'object' @@ -60,6 +76,8 @@ export interface IResourceProfile { applyProfile(content: string): Promise; } -export const PROFILES_CATEGORY = localize('settings profiles', "Settings Profile"); +export const ManageProfilesSubMenu = new MenuId('Profiles'); +export const PROFILES_TTILE = { value: localize('settings profiles', "Profiles"), original: 'Profiles' }; +export const PROFILES_CATEGORY = PROFILES_TTILE.value; export const PROFILE_EXTENSION = 'code-profile'; -export const PROFILE_FILTER = [{ name: localize('profile', "Settings Profile"), extensions: [PROFILE_EXTENSION] }]; +export const PROFILE_FILTER = [{ name: localize('profile', "Profile"), extensions: [PROFILE_EXTENSION] }]; diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts new file mode 100644 index 00000000000..8e94bbddafa --- /dev/null +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileService.ts @@ -0,0 +1,44 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Promises } from 'vs/base/common/async'; +import { Emitter } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { DidChangeUserDataProfileEvent, IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; + +export class UserDataProfileService extends Disposable implements IUserDataProfileService { + + readonly _serviceBrand: undefined; + + readonly defaultProfile: IUserDataProfile; + + private readonly _onDidChangeCurrentProfile = this._register(new Emitter()); + readonly onDidChangeCurrentProfile = this._onDidChangeCurrentProfile.event; + + private _currentProfile: IUserDataProfile; + get currentProfile(): IUserDataProfile { return this._currentProfile; } + + constructor( + defaultProfile: IUserDataProfile, + currentProfile: IUserDataProfile, + ) { + super(); + this.defaultProfile = defaultProfile; + this._currentProfile = currentProfile; + } + + async updateCurrentProfile(userDataProfile: IUserDataProfile): Promise { + this._currentProfile = userDataProfile; + const joiners: Promise[] = []; + this._onDidChangeCurrentProfile.fire({ + profile: userDataProfile, + join(promise) { + joiners.push(promise); + } + }); + await Promises.settled(joiners); + } +} diff --git a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts index f0b94a22f71..78c27df5d0c 100644 --- a/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts +++ b/src/vs/workbench/services/userDataSync/browser/userDataSyncWorkbenchService.ts @@ -631,7 +631,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat } private onDidChangeStorage(e: IStorageValueChangeEvent): void { - if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL + if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.APPLICATION && this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) { this._cachedCurrentSessionId = null; this.update(); @@ -651,24 +651,24 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat this._cachedCurrentSessionId = cachedSessionId; if (cachedSessionId === undefined) { this.logService.info('Settings Sync: Reset current session'); - this.storageService.remove(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + this.storageService.remove(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); } else { this.logService.info('Settings Sync: Updated current session', cachedSessionId); - this.storageService.store(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, cachedSessionId, StorageScope.APPLICATION, StorageTarget.MACHINE); } } } private getStoredCachedSessionId(): string | undefined { - return this.storageService.get(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.GLOBAL); + return this.storageService.get(UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY, StorageScope.APPLICATION); } private get useWorkbenchSessionId(): boolean { - return !this.storageService.getBoolean(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.GLOBAL, false); + return !this.storageService.getBoolean(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, StorageScope.APPLICATION, false); } private set useWorkbenchSessionId(useWorkbenchSession: boolean) { - this.storageService.store(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(UserDataSyncWorkbenchService.DONOT_USE_WORKBENCH_SESSION_STORAGE_KEY, !useWorkbenchSession, StorageScope.APPLICATION, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index 5e13ba82ef2..e2e9b8029c4 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -20,13 +20,13 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Schemas } from 'vs/base/common/network'; import { SaveReason } from 'vs/workbench/common/editor'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditingService { @@ -35,7 +35,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi constructor( @IJSONEditingService private readonly jsonEditingService: IJSONEditingService, @IWorkspaceContextService protected readonly contextService: WorkspaceService, - @IConfigurationService private readonly configurationService: IConfigurationService, + @IWorkbenchConfigurationService private readonly configurationService: IWorkbenchConfigurationService, @INotificationService private readonly notificationService: INotificationService, @ICommandService private readonly commandService: ICommandService, @IFileService private readonly fileService: IFileService, @@ -359,8 +359,7 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi await this.migrateWorkspaceSettings(workspace); } - const workspaceImpl = this.contextService as WorkspaceService; - await workspaceImpl.initialize(workspace); + await this.configurationService.initialize(workspace); return this.workspacesService.enterWorkspace(workspaceUri); } diff --git a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index b5230e4d641..e62d4d461b5 100644 --- a/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -12,7 +12,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; @@ -21,13 +20,14 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { URI } from 'vs/base/common/uri'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingService { constructor( @IJSONEditingService jsonEditingService: IJSONEditingService, @IWorkspaceContextService contextService: WorkspaceService, - @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService, @INotificationService notificationService: INotificationService, @ICommandService commandService: ICommandService, @IFileService fileService: IFileService, diff --git a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts index e5039e9bc04..e464de637ee 100644 --- a/src/vs/workbench/services/workspaces/common/workspaceTrust.ts +++ b/src/vs/workbench/services/workspaces/common/workspaceTrust.ts @@ -244,7 +244,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private loadTrustInfo(): IWorkspaceTrustInfo { - const infoAsString = this.storageService.get(this.storageKey, StorageScope.GLOBAL); + const infoAsString = this.storageService.get(this.storageKey, StorageScope.APPLICATION); let result: IWorkspaceTrustInfo | undefined; try { @@ -270,7 +270,7 @@ export class WorkspaceTrustManagementService extends Disposable implements IWork } private async saveTrustInfo(): Promise { - this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.GLOBAL, StorageTarget.MACHINE); + this.storageService.store(this.storageKey, JSON.stringify(this._trustStateInfo), StorageScope.APPLICATION, StorageTarget.MACHINE); this._onDidChangeTrustedFolders.fire(); await this.updateWorkspaceTrust(); diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index e63f50d9d35..20b61617f1a 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -20,7 +20,6 @@ import { IFileService } from 'vs/platform/files/common/files'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILifecycleService, ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -32,6 +31,7 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { WorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackupService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IWorkspaceTrustManagementService } from 'vs/platform/workspace/common/workspaceTrust'; +import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingService { @@ -39,7 +39,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi @IJSONEditingService jsonEditingService: IJSONEditingService, @IWorkspaceContextService contextService: WorkspaceService, @INativeHostService private nativeHostService: INativeHostService, - @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchConfigurationService configurationService: IWorkbenchConfigurationService, @IStorageService private storageService: IStorageService, @IExtensionService private extensionService: IExtensionService, @IWorkingCopyBackupService private workingCopyBackupService: IWorkingCopyBackupService, @@ -164,7 +164,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi if (result) { // Migrate storage to new workspace - await this.storageService.migrate(result.workspace); + await this.storageService.switch(result.workspace, true /* preserve data */); // Reinitialize backup service if (this.workingCopyBackupService instanceof WorkingCopyBackupService) { diff --git a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts index cb8808efc50..ab6f9d78a7d 100644 --- a/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts +++ b/src/vs/workbench/services/workspaces/test/common/workspaceTrust.test.ts @@ -109,7 +109,7 @@ suite('Workspace Trust', () => { test('empty workspace - trusted, open trusted file', async () => { await configurationService.setUserConfiguration('security', getUserSettings(true, true)); const trustInfo: IWorkspaceTrustInfo = { uriTrustInfo: [{ uri: URI.parse('file:///Folder'), trusted: true }] }; - storageService.store(WORKSPACE_TRUST_STORAGE_KEY, JSON.stringify(trustInfo), StorageScope.GLOBAL, StorageTarget.MACHINE); + storageService.store(WORKSPACE_TRUST_STORAGE_KEY, JSON.stringify(trustInfo), StorageScope.APPLICATION, StorageTarget.MACHINE); (environmentService as any).filesToOpenOrCreate = [{ fileUri: URI.parse('file:///Folder/file.txt') }]; instantiationService.stub(IWorkbenchEnvironmentService, { ...environmentService }); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 0609ec29d76..d4caf868681 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -161,6 +161,8 @@ import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection' import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -281,7 +283,8 @@ export function workbenchInstantiationService( instantiationService.stub(IModelService, disposables.add(instantiationService.createInstance(ModelService))); const fileService = overrides?.fileService ? overrides.fileService(instantiationService) : new TestFileService(); instantiationService.stub(IFileService, fileService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, environmentService, fileService, new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, environmentService, fileService, new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); instantiationService.stub(IUriIdentityService, new UriIdentityService(fileService)); instantiationService.stub(IWorkingCopyBackupService, new TestWorkingCopyBackupService()); instantiationService.stub(ITelemetryService, NullTelemetryService); diff --git a/src/vs/workbench/test/common/memento.test.ts b/src/vs/workbench/test/common/memento.test.ts index 2f84c262933..3ba00fa5c6d 100644 --- a/src/vs/workbench/test/common/memento.test.ts +++ b/src/vs/workbench/test/common/memento.test.ts @@ -9,11 +9,11 @@ import { Memento } from 'vs/workbench/common/memento'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; suite('Memento', () => { - const context: StorageScope | undefined = undefined; let storage: IStorageService; setup(() => { storage = new TestStorageService(); + Memento.clear(StorageScope.APPLICATION); Memento.clear(StorageScope.GLOBAL); Memento.clear(StorageScope.WORKSPACE); }); @@ -22,8 +22,14 @@ suite('Memento', () => { const myMemento = new Memento('memento.test', storage); // Global - let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + let memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); memento.foo = [1, 2, 3]; + let applicationMemento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(applicationMemento, memento); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento.foo = [4, 5, 6]; let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -34,62 +40,76 @@ suite('Memento', () => { myMemento.saveMemento(); + // Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); + applicationMemento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(applicationMemento, memento); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: [4, 5, 6] }); + globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(globalMemento, memento); + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, { foo: 'Hello World' }); + + // Assert the Mementos are stored properly in storage + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.APPLICATION)!), { foo: [1, 2, 3] }); + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.GLOBAL)!), { foo: [4, 5, 6] }); + assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.WORKSPACE)!), { foo: 'Hello World' }); + + // Delete Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + delete memento.foo; + + // Delete Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + delete memento.foo; + + // Delete Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + delete memento.foo; + + myMemento.saveMemento(); + + // Application + memento = myMemento.getMemento(StorageScope.APPLICATION, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Global + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert.deepStrictEqual(memento, {}); + + // Assert the Mementos are also removed from storage + assert.strictEqual(storage.get('memento/memento.test', StorageScope.APPLICATION, null!), null); + assert.strictEqual(storage.get('memento/memento.test', StorageScope.GLOBAL, null!), null); + assert.strictEqual(storage.get('memento/memento.test', StorageScope.WORKSPACE, null!), null); + }); + + test('Save and Load', () => { + const myMemento = new Memento('memento.test', storage); + + // Global + let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); + memento.foo = [1, 2, 3]; + + // Workspace + memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); + assert(memento); + memento.foo = 'Hello World'; + + myMemento.saveMemento(); + // Global memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); - globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); - assert.deepStrictEqual(globalMemento, memento); - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, { foo: 'Hello World' }); - - // Assert the Mementos are stored properly in storage - assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.GLOBAL)!), { foo: [1, 2, 3] }); - - assert.deepStrictEqual(JSON.parse(storage.get('memento/memento.test', StorageScope.WORKSPACE)!), { foo: 'Hello World' }); - - // Delete Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - delete memento.foo; - - // Delete Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - delete memento.foo; - - myMemento.saveMemento(); - - // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, {}); - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, {}); - - // Assert the Mementos are also removed from storage - assert.strictEqual(storage.get('memento/memento.test', StorageScope.GLOBAL, null!), null); - - assert.strictEqual(storage.get('memento/memento.test', StorageScope.WORKSPACE, null!), null); - }); - - test('Save and Load', () => { - const myMemento = new Memento('memento.test', storage); - - // Global - let memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - memento.foo = [1, 2, 3]; - - // Workspace - memento = myMemento.getMemento(StorageScope.WORKSPACE, StorageTarget.MACHINE); - assert(memento); - memento.foo = 'Hello World'; - - myMemento.saveMemento(); - - // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); - assert.deepStrictEqual(memento, { foo: [1, 2, 3] }); let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -98,7 +118,7 @@ suite('Memento', () => { assert.deepStrictEqual(memento, { foo: 'Hello World' }); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.foo = [4, 5, 6]; // Workspace @@ -109,7 +129,7 @@ suite('Memento', () => { myMemento.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [4, 5, 6] }); globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); @@ -119,7 +139,7 @@ suite('Memento', () => { assert.deepStrictEqual(memento, { foo: 'World Hello' }); // Delete Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); delete memento.foo; // Delete Workspace @@ -129,7 +149,7 @@ suite('Memento', () => { myMemento.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, {}); // Workspace @@ -142,10 +162,10 @@ suite('Memento', () => { const myMemento2 = new Memento('memento.test', storage); // Global - let memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + let memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.foo = [1, 2, 3]; - memento = myMemento2.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); memento.bar = [1, 2, 3]; // Workspace @@ -161,12 +181,12 @@ suite('Memento', () => { myMemento2.saveMemento(); // Global - memento = myMemento.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); let globalMemento = myMemento.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); - memento = myMemento2.getMemento(context!, StorageTarget.MACHINE); + memento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(memento, { foo: [1, 2, 3], bar: [1, 2, 3] }); globalMemento = myMemento2.getMemento(StorageScope.GLOBAL, StorageTarget.MACHINE); assert.deepStrictEqual(globalMemento, memento); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 1738a1903dc..aa599aa191c 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -51,6 +51,8 @@ import { IPartsSplash } from 'vs/platform/theme/common/themeService'; import { IUserDataProfilesService, UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { FileService } from 'vs/platform/files/common/fileService'; import { joinPath } from 'vs/base/common/resources'; +import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; +import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; const args = parseArgs(process.argv, OPTIONS); @@ -58,6 +60,7 @@ const homeDir = homedir(); const NULL_PROFILE = { name: '', id: '', + isDefault: false, location: URI.file(homeDir), settingsResource: joinPath(URI.file(homeDir), 'settings.json'), globalStorageHome: joinPath(URI.file(homeDir), 'globalStorage'), @@ -220,7 +223,7 @@ export class TestNativeHostService implements INativeHostService { async maximizeWindow(): Promise { } async unmaximizeWindow(): Promise { } async minimizeWindow(): Promise { } - async updateTitleBarOverlay(backgroundColor: string, foregroundColor: string): Promise { } + async updateTitleBarOverlay(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { } async setMinimumSize(width: number | undefined, height: number | undefined): Promise { } async saveWindowSplash(value: IPartsSplash): Promise { } async focusWindow(options?: { windowId?: number | undefined } | undefined): Promise { } @@ -286,7 +289,8 @@ export function workbenchInstantiationService(disposables = new DisposableStore( instantiationService.stub(INativeEnvironmentService, TestEnvironmentService); instantiationService.stub(IWorkbenchEnvironmentService, TestEnvironmentService); instantiationService.stub(INativeWorkbenchEnvironmentService, TestEnvironmentService); - instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, undefined, TestEnvironmentService, new FileService(new NullLogService()), new NullLogService())); + const userDataProfilesService = instantiationService.stub(IUserDataProfilesService, new UserDataProfilesService(undefined, TestEnvironmentService, new FileService(new NullLogService()), new NullLogService())); + instantiationService.stub(IUserDataProfileService, new UserDataProfileService(userDataProfilesService.defaultProfile, userDataProfilesService.defaultProfile)); return instantiationService; } diff --git a/src/vscode-dts/vscode.d.ts b/src/vscode-dts/vscode.d.ts index cac3633e6a3..070cb2adafc 100644 --- a/src/vscode-dts/vscode.d.ts +++ b/src/vscode-dts/vscode.d.ts @@ -5980,7 +5980,7 @@ declare module 'vscode' { * To get an instance of a `DiagnosticCollection` use * {@link languages.createDiagnosticCollection createDiagnosticCollection}. */ - export interface DiagnosticCollection { + export interface DiagnosticCollection extends Iterable<[uri: Uri, diagnostics: readonly Diagnostic[]]> { /** * The name of this diagnostic collection, for instance `typescript`. Every diagnostic @@ -10054,7 +10054,28 @@ declare module 'vscode' { * `true` if the {@link TreeView tree view} is visible otherwise `false`. */ readonly visible: boolean; + } + /** + * A file associated with a {@linkcode DataTransferItem}. + */ + export interface DataTransferFile { + /** + * The name of the file. + */ + readonly name: string; + + /** + * The full file path of the file. + * + * May be `undefined` on web. + */ + readonly uri?: Uri; + + /** + * The full file contents of the file. + */ + data(): Thenable; } /** @@ -10068,6 +10089,16 @@ declare module 'vscode' { */ asString(): Thenable; + /** + * Try getting the {@link DataTransferFile file} associated with this data transfer item. + * + * Note that the file object is only valid for the scope of the drag and drop operation. + * + * @returns The file for the data transfer or `undefined` if the item is either not a file or the + * file data cannot be accessed. + */ + asFile(): DataTransferFile | undefined; + /** * Custom data stored on this item. * @@ -10089,7 +10120,7 @@ declare module 'vscode' { * data transfer. These additional mime types will only be included in the `handleDrop` when the the drag was initiated from * an element in the same drag and drop controller. */ - export class DataTransfer { + export class DataTransfer implements Iterable<[mimeType: string, item: DataTransferItem]> { /** * Retrieves the data transfer item for a given mime type. * @@ -10115,6 +10146,11 @@ declare module 'vscode' { * @param thisArg The `this` context used when invoking the handler function. */ forEach(callbackfn: (value: DataTransferItem, key: string, dataTransfer: DataTransfer) => void, thisArg?: any): void; + + /** + * Get a new iterator with the `[mime, item]` pairs for each element in this data transfer. + */ + [Symbol.iterator](): IterableIterator<[mimeType: string, item: DataTransferItem]>; } /** @@ -10781,7 +10817,7 @@ declare module 'vscode' { /** * A collection of mutations that an extension can apply to a process environment. */ - export interface EnvironmentVariableCollection { + export interface EnvironmentVariableCollection extends Iterable<[variable: string, mutator: EnvironmentVariableMutator]> { /** * Whether the collection should be cached for the workspace and applied to the terminal * across window reloads. When true the collection will be active immediately such when the @@ -13770,6 +13806,11 @@ declare module 'vscode' { */ placeholder: string; + /** + * Controls whether the input box is enabled (default is `true`). + */ + enabled: boolean; + /** * Controls whether the input box is visible (default is `true`). */ @@ -15645,7 +15686,7 @@ declare module 'vscode' { * Collection of test items, found in {@link TestItem.children} and * {@link TestController.items}. */ - export interface TestItemCollection { + export interface TestItemCollection extends Iterable<[id: string, testItem: TestItem]> { /** * Gets the number of items in the collection. */ diff --git a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts b/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts deleted file mode 100644 index 68df3c07f4a..00000000000 --- a/src/vscode-dts/vscode.proposed.dataTransferFiles.d.ts +++ /dev/null @@ -1,71 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -declare module 'vscode' { - - // https://github.com/microsoft/vscode/issues/147481 - - /** - * A file associated with a {@linkcode DataTransferItem}. - */ - interface DataTransferFile { - /** - * The name of the file. - */ - readonly name: string; - - /** - * The full file path of the file. - * - * May be undefined on web. - */ - readonly uri?: Uri; - - /** - * The full file contents of the file. - */ - data(): Thenable; - } - - /** - * Identifies the kind of a {@link DataTransferItem}. May either be {@linkcode DataTransferItemKind.String String} or {@linkcode DataTransferItemKind.File File}. - */ - enum DataTransferItemKind { - - /** - * The {@link DataTransferItem} is a string. - * - * Use {@link DataTransferItem.asString} to get a string representation of this item or - * {@link DataTransferItem.value} to access the original value if them item was created - * by an extension. - */ - String = 1, - - /** - * The {@link DataTransferItem} is for a file. - * - * Use {@link DataTransferItem.asFile} to get the underlying file data. - */ - File = 2, - } - - export interface DataTransferItem { - - /** - * The kind of the {@link DataTransferItem}. - */ - readonly kind: DataTransferItemKind; - - /** - * Try getting the file associated with this data transfer item. - * - * Note that the file object is only valid for the scope of the drag and drop operation. - * - * @returns The file for the data transfer or `undefined` if the item is either not a file or the - * file data cannot be accessed. - */ - asFile(): DataTransferFile | undefined; - } -} diff --git a/src/vscode-dts/vscode.proposed.scmInput.d.ts b/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts similarity index 59% rename from src/vscode-dts/vscode.proposed.scmInput.d.ts rename to src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts index 6efdb57ae45..43305c7b56e 100644 --- a/src/vscode-dts/vscode.proposed.scmInput.d.ts +++ b/src/vscode-dts/vscode.proposed.snippetWorkspaceEdit.d.ts @@ -5,16 +5,12 @@ declare module 'vscode' { - // https://github.com/microsoft/vscode/issues/150268 + // https://github.com/microsoft/vscode/issues/145374 - /** - * Represents the input box in the Source Control viewlet. - */ - export interface SourceControlInputBox { + export interface TextEdit { - /** - * Controls whether the input box is enabled (default is `true`). - */ - enabled: boolean; + // will be merged with newText + // will NOT be supported everywhere, only: `workspace.applyEdit` + newText2?: string | SnippetString; } } diff --git a/test/automation/src/electron.ts b/test/automation/src/electron.ts index a13e3b1c040..ffde8728787 100644 --- a/test/automation/src/electron.ts +++ b/test/automation/src/electron.ts @@ -42,7 +42,11 @@ export async function resolveElectronConfiguration(options: LaunchOptions): Prom } if (process.platform === 'linux') { - args.push('--disable-gpu'); // Linux has trouble in VMs to render properly with GPU enabled + // --disable-dev-shm-usage: when run on docker containers where size of /dev/shm + // partition < 64MB which causes OOM failure for chromium compositor that uses + // this partition for shared memory. + // Refs https://github.com/microsoft/vscode/issues/152143 + args.push('--disable-dev-shm-usage'); } if (remote) { diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index f4e28668ab0..cfbae17dd00 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -44,7 +44,7 @@ async function launchServer(options: LaunchOptions) { const args = [ '--disable-telemetry', '--disable-workspace-trust', - `--port${port++}`, + `--port=${port++}`, '--enable-smoke-test-driver', `--extensions-dir=${extensionsPath}`, `--server-data-dir=${agentFolder}`, diff --git a/test/automation/src/settings.ts b/test/automation/src/settings.ts index cda80632998..49cd35a5daa 100644 --- a/test/automation/src/settings.ts +++ b/test/automation/src/settings.ts @@ -12,6 +12,12 @@ export class SettingsEditor { constructor(private code: Code, private editors: Editors, private editor: Editor, private quickaccess: QuickAccess) { } + /** + * Write a single setting key value pair. + * + * Warning: You may need to set `editor.wordWrap` to `"on"` if this is called with a really long + * setting. + */ async addUserSetting(setting: string, value: string): Promise { await this.openUserSettingsFile(); @@ -20,6 +26,20 @@ export class SettingsEditor { await this.editors.saveOpenedFile(); } + /** + * Write several settings faster than multiple calls to {@link addUserSetting}. + * + * Warning: You will likely also need to set `editor.wordWrap` to `"on"` if `addUserSetting` is + * called after this in the test. + */ + async addUserSettings(settings: [key: string, value: string][]): Promise { + await this.openUserSettingsFile(); + + await this.code.dispatchKeybinding('right'); + await this.editor.waitForTypeInEditor('settings.json', settings.map(v => `"${v[0]}": ${v[1]},`).join('')); + await this.editors.saveOpenedFile(); + } + async clearUserSettings(): Promise { await this.openUserSettingsFile(); await this.quickaccess.runCommand('editor.action.selectAll'); diff --git a/test/automation/src/terminal.ts b/test/automation/src/terminal.ts index 1dfeac42aeb..dfb24636716 100644 --- a/test/automation/src/terminal.ts +++ b/test/automation/src/terminal.ts @@ -83,8 +83,16 @@ export class Terminal { await this.code.dispatchKeybinding('enter'); await this.quickinput.waitForQuickInputClosed(); } - if (commandId === TerminalCommandId.Show || commandId === TerminalCommandId.CreateNewEditor || commandId === TerminalCommandId.CreateNew || commandId === TerminalCommandId.NewWithProfile) { - return await this._waitForTerminal(expectedLocation === 'editor' || commandId === TerminalCommandId.CreateNewEditor ? 'editor' : 'panel'); + switch (commandId) { + case TerminalCommandId.Show: + case TerminalCommandId.CreateNewEditor: + case TerminalCommandId.CreateNew: + case TerminalCommandId.NewWithProfile: + await this._waitForTerminal(expectedLocation === 'editor' || commandId === TerminalCommandId.CreateNewEditor ? 'editor' : 'panel'); + break; + case TerminalCommandId.KillAll: + await this.code.waitForElements(Selector.Xterm, true, e => e.length === 0); + break; } } diff --git a/test/smoke/src/areas/terminal/terminal-helpers.ts b/test/smoke/src/areas/terminal/terminal-helpers.ts index 24eccb0c2db..c333df0bc5a 100644 --- a/test/smoke/src/areas/terminal/terminal-helpers.ts +++ b/test/smoke/src/areas/terminal/terminal-helpers.ts @@ -5,12 +5,18 @@ import { Application } from '../../../../automation'; -export async function setTerminalTestSettings(app: Application) { - // Always show tabs to make getting terminal groups easier - await app.workbench.settingsEditor.addUserSetting('terminal.integrated.tabs.hideCondition', '"never"'); - // Use the DOM renderer for smoke tests so they can be inspected in the playwright trace - // viewer - await app.workbench.settingsEditor.addUserSetting('terminal.integrated.gpuAcceleration', '"off"'); +export async function setTerminalTestSettings(app: Application, additionalSettings: [key: string, value: string][] = []) { + await app.workbench.settingsEditor.addUserSettings([ + // Work wrap is required when calling settingsEditor.addUserSetting multiple times or the + // click to focus will fail + ['editor.wordWrap', '"on"'], + // Always show tabs to make getting terminal groups easier + ['terminal.integrated.tabs.hideCondition', '"never"'], + // Use the DOM renderer for smoke tests so they can be inspected in the playwright trace + // viewer + ['terminal.integrated.gpuAcceleration', '"off"'], + ...additionalSettings + ]); // Close the settings editor await app.workbench.quickaccess.runCommand('workbench.action.closeAllEditors'); diff --git a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts index 47d624633f2..200ab28b019 100644 --- a/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts +++ b/test/smoke/src/areas/terminal/terminal-shellIntegration.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue } from '../../../../automation'; +import { Application, Terminal, SettingsEditor, TerminalCommandIdWithValue, TerminalCommandId } from '../../../../automation'; import { setTerminalTestSettings } from './terminal-helpers'; export function setup() { @@ -16,8 +16,11 @@ export function setup() { app = this.app as Application; terminal = app.workbench.terminal; settingsEditor = app.workbench.settingsEditor; - await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.enabled', 'true'); - await setTerminalTestSettings(app); + await setTerminalTestSettings(app, [['terminal.integrated.shellIntegration.enabled', 'true']]); + }); + + afterEach(async function () { + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); }); after(async function () { @@ -28,22 +31,23 @@ export function setup() { await terminal.runCommandWithValue(TerminalCommandIdWithValue.NewWithProfile, process.platform === 'win32' ? 'PowerShell' : 'bash'); } - // TODO: These are currently flaky https://github.com/microsoft/vscode/issues/150478 - describe.skip('Shell integration', function () { + // TODO: Some agents may not have pwsh installed? + (process.platform === 'win32' ? describe.skip : describe)(`Shell integration`, function () { describe('Decorations', function () { describe('Should show default icons', function () { + it('Placeholder', async () => { await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); }); it('Success', async () => { await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`ls`); + await terminal.runCommandInTerminal(`echo "success"`); await terminal.assertCommandDecorations({ placeholder: 1, success: 1, error: 0 }); }); it('Error', async () => { await createShellIntegrationProfile(); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await terminal.runCommandInTerminal(`false`); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 1 }); }); }); @@ -51,12 +55,13 @@ export function setup() { it('Should update and show custom icons', async () => { await createShellIntegrationProfile(); await terminal.assertCommandDecorations({ placeholder: 1, success: 0, error: 0 }); - await terminal.runCommandInTerminal(`ls`); - await terminal.runCommandInTerminal(`fsdkfsjdlfksjdkf`); + await terminal.runCommandInTerminal(`echo "foo"`); + await terminal.runCommandInTerminal(`bar`); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIcon', '"zap"'); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconSuccess', '"zap"'); await settingsEditor.addUserSetting('terminal.integrated.shellIntegration.decorationIconError', '"zap"'); await terminal.assertCommandDecorations(undefined, { updatedIcon: "zap", count: 3 }); + await app.workbench.terminal.runCommand(TerminalCommandId.KillAll); }); }); }); diff --git a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts b/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts index 8f1eb1eed71..750c9f090b4 100644 --- a/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts +++ b/test/smoke/src/areas/terminal/terminal-splitCwd.test.ts @@ -15,8 +15,9 @@ export function setup() { const app = this.app as Application; terminal = app.workbench.terminal; settingsEditor = app.workbench.settingsEditor; - await settingsEditor.addUserSetting('terminal.integrated.splitCwd', '"inherited"'); - await setTerminalTestSettings(app); + await setTerminalTestSettings(app, [ + ['terminal.integrated.splitCwd', '"inherited"'] + ]); }); after(async function () { diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 42175985f48..d888604ca6c 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { setupSearchTests(logger); setupNotebookTests(logger); setupLanguagesTests(logger); - if (opts.web) { setupTerminalTests(logger); } // Tests require playwright driver (https://github.com/microsoft/vscode/issues/146811) + if (opts.web) { setupTerminalTests(logger); } // Not stable on desktop/remote https://github.com/microsoft/vscode/issues/146811 setupStatusbarTests(logger); if (quality !== Quality.Dev && quality !== Quality.OSS) { setupExtensionTests(logger); } setupMultirootTests(logger); diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index cd0682bfb99..305d78b819a 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -147,18 +147,22 @@ async function runTestsInBrowser(testModules, browserType) { withReporter(browserType, new EchoRunner(emitter, browserType.toUpperCase())); // collection failures for console printing - const fails = []; + const failingModuleIds = []; + const failingTests = []; emitter.on('fail', (test, err) => { if (err.stack) { const regex = /(vs\/.*\.test)\.js/; for (const line of String(err.stack).split('\n')) { const match = regex.exec(line); if (match) { - fails.push(match[1]); - break; + failingModuleIds.push(match[1]); + return; } } } + + // We could not determine the module id + failingTests.push(test.fullTitle); }); try { @@ -172,8 +176,11 @@ async function runTestsInBrowser(testModules, browserType) { } await browser.close(); - if (fails.length > 0) { - return `to DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${fails.map(module => `m=${module}`).join('&')}`; + if (failingModuleIds.length > 0) { + return `to DEBUG, open ${browserType.toUpperCase()} and navigate to ${target.href}?${failingModuleIds.map(module => `m=${module}`).join('&')}`; + } + if (failingTests.length > 0) { + return `The followings tests are failing:\n - ${failingTests.join('\n - ')}`; } } diff --git a/yarn.lock b/yarn.lock index 1e613966025..b8d06e9ce7c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4335,10 +4335,10 @@ electron-to-chromium@^1.4.17: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.45.tgz#cf1144091d6683cbd45a231954a745f02fb24598" integrity sha512-czF9eYVuOmlY/vxyMQz2rGlNSjZpxNQYBe1gmQv7al171qOIhgyO9k7D5AKlgeTCSPKk+LHhj5ZyIdmEub9oNg== -electron@18.3.2: - version "18.3.2" - resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.2.tgz#015a8f4c92c62855d7f33206f2166d3e33b053b7" - integrity sha512-Q1ciZ1M90L71WvyLbkD8Iwaq4YCwo8NUpBiLQUsd6M4E7i5vrzsA4g5Ylfzyela8DgRCNVknDVDfj6s+7YVWpA== +electron@18.3.4: + version "18.3.4" + resolved "https://registry.yarnpkg.com/electron/-/electron-18.3.4.tgz#efbc0d1c794bccac62e95556d4e355e8a3eca9a4" + integrity sha512-MIxQnaQR7NzWZOuAhRbJAf+pPyoXXGNge9E6pz7nGLXE/DxeN6BUbKb0iWVCCMkxIpqdIhXdc5sViUCX74dvsw== dependencies: "@electron/get" "^1.13.0" "@types/node" "^16.11.26" @@ -12261,20 +12261,20 @@ xterm-addon-search@0.9.0-beta.39: resolved "https://registry.yarnpkg.com/xterm-addon-search/-/xterm-addon-search-0.9.0-beta.39.tgz#e8376e1485ee7d763c07d1a8f1354114f65b3e3e" integrity sha512-h45wkecgfqXXoAUqgNytAfSd6g0xNT6rZy/enVaEU0aes7QoL9pxHUKkCry8PP6hs03Slk0VxQ4AGsbSZGvK/w== -xterm-addon-serialize@0.7.0-beta.12: - version "0.7.0-beta.12" - resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.12.tgz#4f845d8b1a9f9b7ae3f910455ce8c58b041babc7" - integrity sha512-b4Ug0B/RSJMux+KAcp+PXVqubVyXjN1yCQw1FOkgVYTpmd9AH/X+EcxKml5Lz8DsKmsXqfD9AlV3WpEeT+OtMw== +xterm-addon-serialize@0.7.0-beta.13: + version "0.7.0-beta.13" + resolved "https://registry.yarnpkg.com/xterm-addon-serialize/-/xterm-addon-serialize-0.7.0-beta.13.tgz#5c859c8657cab7f28405aab1a0715daf54bc7714" + integrity sha512-TYFlm/gds0pOmpzXw7ZWx8Cy48lMaOZZqZgfm5pWU37HPvzfKxXSVdYL1biWpRCH2zCH+3cWmOma8W1pBRr+Eg== xterm-addon-unicode11@0.4.0-beta.3: version "0.4.0-beta.3" resolved "https://registry.yarnpkg.com/xterm-addon-unicode11/-/xterm-addon-unicode11-0.4.0-beta.3.tgz#f350184155fafd5ad0d6fbf31d13e6ca7dea1efa" integrity sha512-FryZAVwbUjKTmwXnm1trch/2XO60F5JsDvOkZhzobV1hm10sFLVuZpFyHXiUx7TFeeFsvNP+S77LAtWoeT5z+Q== -xterm-addon-webgl@0.12.0-beta.38: - version "0.12.0-beta.38" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.38.tgz#59fc8f3711e38b9a238fd460cd070007b7904e7a" - integrity sha512-fqRhfwUx/WZlDLFsZX26irLYTdRGMf1xuRwoqfr4KiFoQvHye55U8VJ4WLHdWglN2Kzh8PbINk5HfMhIwYHe9Q== +xterm-addon-webgl@0.12.0-beta.41: + version "0.12.0-beta.41" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.12.0-beta.41.tgz#17dbca975b6e9b34526ebc57f4de59ee295da1b6" + integrity sha512-wvQxC5diMYEJEMaILfz+4CWB2GgtzjzNQRNDnK7R7Y9wDI+P4idDlQKgyH0nA93sl9R4zgqlBVha//wuq4vfZg== xterm-headless@4.19.0-beta.60: version "4.19.0-beta.60"