mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-04-02 10:00:29 -04:00
Merge from vscode 2f984aad710215f4e4684a035bb02f55d1a9e2cc (#9819)
This commit is contained in:
@@ -6,7 +6,6 @@
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
// @ts-ignore review
|
|
||||||
const { remote } = require('electron');
|
const { remote } = require('electron');
|
||||||
const dialog = remote.dialog;
|
const dialog = remote.dialog;
|
||||||
|
|
||||||
|
|||||||
@@ -455,10 +455,8 @@ function createTscCompileTask(watch) {
|
|||||||
// e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'.
|
// e.g. src/vs/base/common/strings.ts(663,5): error TS2322: Type '1234' is not assignable to type 'string'.
|
||||||
let fullpath = path.join(root, match[1]);
|
let fullpath = path.join(root, match[1]);
|
||||||
let message = match[3];
|
let message = match[3];
|
||||||
// @ts-ignore
|
|
||||||
reporter(fullpath + message);
|
reporter(fullpath + message);
|
||||||
} else {
|
} else {
|
||||||
// @ts-ignore
|
|
||||||
reporter(str);
|
reporter(str);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,10 +36,8 @@ const { compileBuildTask } = require('./gulpfile.compile');
|
|||||||
const { compileExtensionsBuildTask } = require('./gulpfile.extensions');
|
const { compileExtensionsBuildTask } = require('./gulpfile.extensions');
|
||||||
|
|
||||||
const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname));
|
const productionDependencies = deps.getProductionDependencies(path.dirname(__dirname));
|
||||||
|
|
||||||
const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n));
|
const baseModules = Object.keys(process.binding('natives')).filter(n => !/^_|\//.test(n));
|
||||||
// {{SQL CARBON EDIT}}
|
const nodeModules = [ // {{SQL CARBON EDIT}}
|
||||||
const nodeModules = [
|
|
||||||
'electron',
|
'electron',
|
||||||
'original-fs',
|
'original-fs',
|
||||||
'rxjs/Observable',
|
'rxjs/Observable',
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ function prepareDebPackage(arch) {
|
|||||||
const postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' })
|
const postinst = gulp.src('resources/linux/debian/postinst.template', { base: '.' })
|
||||||
.pipe(replace('@@NAME@@', product.applicationName))
|
.pipe(replace('@@NAME@@', product.applicationName))
|
||||||
.pipe(replace('@@ARCHITECTURE@@', debArch))
|
.pipe(replace('@@ARCHITECTURE@@', debArch))
|
||||||
// @ts-ignore JSON checking: quality is optional
|
|
||||||
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
|
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
|
||||||
// @ts-ignore JSON checking: updateUrl is optional
|
|
||||||
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
|
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
|
||||||
.pipe(rename('DEBIAN/postinst'));
|
.pipe(rename('DEBIAN/postinst'));
|
||||||
|
|
||||||
@@ -169,9 +167,7 @@ function prepareRpmPackage(arch) {
|
|||||||
.pipe(replace('@@RELEASE@@', linuxPackageRevision))
|
.pipe(replace('@@RELEASE@@', linuxPackageRevision))
|
||||||
.pipe(replace('@@ARCHITECTURE@@', rpmArch))
|
.pipe(replace('@@ARCHITECTURE@@', rpmArch))
|
||||||
.pipe(replace('@@LICENSE@@', product.licenseName))
|
.pipe(replace('@@LICENSE@@', product.licenseName))
|
||||||
// @ts-ignore JSON checking: quality is optional
|
|
||||||
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
|
.pipe(replace('@@QUALITY@@', product.quality || '@@QUALITY@@'))
|
||||||
// @ts-ignore JSON checking: updateUrl is optional
|
|
||||||
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
|
.pipe(replace('@@UPDATEURL@@', product.updateUrl || '@@UPDATEURL@@'))
|
||||||
.pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', ')))
|
.pipe(replace('@@DEPENDENCIES@@', rpmDependencies[rpmArch].join(', ')))
|
||||||
.pipe(rename('SPECS/' + product.applicationName + '.spec'));
|
.pipe(rename('SPECS/' + product.applicationName + '.spec'));
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ function watch(root) {
|
|||||||
var child = cp.spawn(watcherPath, [root]);
|
var child = cp.spawn(watcherPath, [root]);
|
||||||
|
|
||||||
child.stdout.on('data', function (data) {
|
child.stdout.on('data', function (data) {
|
||||||
// @ts-ignore
|
|
||||||
var lines = data.toString('utf8').split('\n');
|
var lines = data.toString('utf8').split('\n');
|
||||||
for (var i = 0; i < lines.length; i++) {
|
for (var i = 0; i < lines.length; i++) {
|
||||||
var line = lines[i].trim();
|
var line = lines[i].trim();
|
||||||
@@ -47,7 +46,6 @@ function watch(root) {
|
|||||||
path: changePathFull,
|
path: changePathFull,
|
||||||
base: root
|
base: root
|
||||||
});
|
});
|
||||||
//@ts-ignore
|
|
||||||
file.event = toChangeType(changeType);
|
file.event = toChangeType(changeType);
|
||||||
result.emit('data', file);
|
result.emit('data', file);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,5 +89,85 @@
|
|||||||
"prependLicenseText": [
|
"prependLicenseText": [
|
||||||
"Copyright (c) Microsoft Corporation. All rights reserved."
|
"Copyright (c) Microsoft Corporation. All rights reserved."
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Reason: The license at https://github.com/reem/rust-unreachable/blob/master/LICENSE-MIT
|
||||||
|
// cannot be found by the OSS tool automatically.
|
||||||
|
"name": "reem/rust-unreachable",
|
||||||
|
"fullLicenseText": [
|
||||||
|
"Copyright (c) 2015 The rust-unreachable Developers",
|
||||||
|
"",
|
||||||
|
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||||
|
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||||
|
"in the Software without restriction, including without limitation the rights",
|
||||||
|
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||||
|
"copies of the Software, and to permit persons to whom the Software is",
|
||||||
|
"furnished to do so, subject to the following conditions:",
|
||||||
|
"",
|
||||||
|
"The above copyright notice and this permission notice shall be included in all",
|
||||||
|
"copies or substantial portions of the Software.",
|
||||||
|
"",
|
||||||
|
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||||
|
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||||
|
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||||
|
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||||
|
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||||
|
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
|
||||||
|
"SOFTWARE."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Reason: The license at https://github.com/reem/rust-void/blob/master/LICENSE-MIT
|
||||||
|
// cannot be found by the OSS tool automatically.
|
||||||
|
"name": "reem/rust-void",
|
||||||
|
"fullLicenseText": [
|
||||||
|
"Copyright (c) 2015 The rust-void Developers",
|
||||||
|
"",
|
||||||
|
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||||
|
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||||
|
"in the Software without restriction, including without limitation the rights",
|
||||||
|
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||||
|
"copies of the Software, and to permit persons to whom the Software is",
|
||||||
|
"furnished to do so, subject to the following conditions:",
|
||||||
|
"",
|
||||||
|
"The above copyright notice and this permission notice shall be included in all",
|
||||||
|
"copies or substantial portions of the Software.",
|
||||||
|
"",
|
||||||
|
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||||
|
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||||
|
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||||
|
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||||
|
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||||
|
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
|
||||||
|
"SOFTWARE."
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Reason: The license at https://github.com/mrhooray/crc-rs/blob/master/LICENSE-MIT
|
||||||
|
// cannot be found by the OSS tool automatically.
|
||||||
|
"name": "mrhooray/crc-rs",
|
||||||
|
"fullLicenseText": [
|
||||||
|
"MIT License",
|
||||||
|
"",
|
||||||
|
"Copyright (c) 2017 crc-rs Developers",
|
||||||
|
"",
|
||||||
|
"Permission is hereby granted, free of charge, to any person obtaining a copy",
|
||||||
|
"of this software and associated documentation files (the \"Software\"), to deal",
|
||||||
|
"in the Software without restriction, including without limitation the rights",
|
||||||
|
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
|
||||||
|
"copies of the Software, and to permit persons to whom the Software is",
|
||||||
|
"furnished to do so, subject to the following conditions:",
|
||||||
|
"",
|
||||||
|
"The above copyright notice and this permission notice shall be included in all",
|
||||||
|
"copies or substantial portions of the Software.",
|
||||||
|
"",
|
||||||
|
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
|
||||||
|
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
|
||||||
|
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
|
||||||
|
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
|
||||||
|
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
|
||||||
|
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
|
||||||
|
"SOFTWARE."
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class PreviewManager implements vscode.CustomEditorProvider {
|
|||||||
) { }
|
) { }
|
||||||
|
|
||||||
public async openCustomDocument(uri: vscode.Uri) {
|
public async openCustomDocument(uri: vscode.Uri) {
|
||||||
return new vscode.CustomDocument(PreviewManager.viewType, uri);
|
return new vscode.CustomDocument(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async resolveCustomEditor(
|
public async resolveCustomEditor(
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ export class MarkdownPreviewManager extends Disposable implements vscode.Webview
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async openCustomDocument(uri: vscode.Uri) {
|
public async openCustomDocument(uri: vscode.Uri) {
|
||||||
return new vscode.CustomDocument(this.customEditorViewType, uri);
|
return new vscode.CustomDocument(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async resolveCustomTextEditor(
|
public async resolveCustomTextEditor(
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ module.exports = function withDefaults(/**@type WebpackConfig*/extConfig) {
|
|||||||
// yes, really source maps
|
// yes, really source maps
|
||||||
devtool: 'source-map',
|
devtool: 'source-map',
|
||||||
plugins: [
|
plugins: [
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
new CopyWebpackPlugin([
|
new CopyWebpackPlugin([
|
||||||
{ from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] }
|
{ from: 'src', to: '.', ignore: ['**/test/**', '*.ts'] }
|
||||||
]),
|
]),
|
||||||
|
|||||||
@@ -27,7 +27,20 @@
|
|||||||
"title": "%signOut%",
|
"title": "%signOut%",
|
||||||
"category": "%displayName%"
|
"category": "%displayName%"
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"configuration": {
|
||||||
|
"title": "Microsoft Account",
|
||||||
|
"properties": {
|
||||||
|
"microsoftAccount.logLevel": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"info",
|
||||||
|
"trace"
|
||||||
|
],
|
||||||
|
"default": "info"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "npm run compile",
|
"vscode:prepublish": "npm run compile",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ export class Keychain {
|
|||||||
|
|
||||||
async setToken(token: string): Promise<void> {
|
async setToken(token: string): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
Logger.trace('Writing to keychain', token);
|
||||||
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
|
return await this.keytar.setPassword(SERVICE_ID, ACCOUNT_ID, token);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
@@ -59,7 +60,9 @@ export class Keychain {
|
|||||||
|
|
||||||
async getToken(): Promise<string | null | undefined> {
|
async getToken(): Promise<string | null | undefined> {
|
||||||
try {
|
try {
|
||||||
return await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
|
const result = await this.keytar.getPassword(SERVICE_ID, ACCOUNT_ID);
|
||||||
|
Logger.trace('Reading from keychain', result);
|
||||||
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
Logger.error(`Getting token failed: ${e}`);
|
Logger.error(`Getting token failed: ${e}`);
|
||||||
|
|||||||
@@ -7,11 +7,23 @@ import * as vscode from 'vscode';
|
|||||||
|
|
||||||
type LogLevel = 'Trace' | 'Info' | 'Error';
|
type LogLevel = 'Trace' | 'Info' | 'Error';
|
||||||
|
|
||||||
|
enum Level {
|
||||||
|
Trace = 'trace',
|
||||||
|
Info = 'Info'
|
||||||
|
}
|
||||||
|
|
||||||
class Log {
|
class Log {
|
||||||
private output: vscode.OutputChannel;
|
private output: vscode.OutputChannel;
|
||||||
|
private level: Level;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.output = vscode.window.createOutputChannel('Account');
|
this.output = vscode.window.createOutputChannel('Account');
|
||||||
|
this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
|
||||||
|
vscode.workspace.onDidChangeConfiguration(e => {
|
||||||
|
if (e.affectsConfiguration('microsoftAccount.logLevel')) {
|
||||||
|
this.level = vscode.workspace.getConfiguration('microsoftAccount').get('logLevel') || Level.Info;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private data2String(data: any): string {
|
private data2String(data: any): string {
|
||||||
@@ -32,6 +44,12 @@ class Log {
|
|||||||
this.logLevel('Error', message, data);
|
this.logLevel('Error', message, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public trace(message: string, data?: any): void {
|
||||||
|
if (this.level === Level.Trace) {
|
||||||
|
this.logLevel('Trace', message, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public logLevel(level: LogLevel, message: string, data?: any): void {
|
public logLevel(level: LogLevel, message: string, data?: any): void {
|
||||||
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
|
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
|
||||||
if (data) {
|
if (data) {
|
||||||
|
|||||||
@@ -38,21 +38,12 @@
|
|||||||
"description": "A test modifier"
|
"description": "A test modifier"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"semanticTokenStyleDefaults": [
|
"semanticTokenScopes": [
|
||||||
{
|
{
|
||||||
"selector": "testToken",
|
"scopes": {
|
||||||
"scope": [ "entity.name.function.special" ]
|
"testToken": [
|
||||||
},
|
"entity.name.function.special"
|
||||||
{
|
]
|
||||||
"selector": "*.testModifier",
|
|
||||||
"light": {
|
|
||||||
"fontStyle": "bold"
|
|
||||||
},
|
|
||||||
"dark": {
|
|
||||||
"fontStyle": "bold"
|
|
||||||
},
|
|
||||||
"highContrast": {
|
|
||||||
"fontStyle": "bold"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export function activate(context: vscode.ExtensionContext): any {
|
|||||||
};
|
};
|
||||||
jsoncParser.visit(document.getText(), visitor);
|
jsoncParser.visit(document.getText(), visitor);
|
||||||
|
|
||||||
return new vscode.SemanticTokens(builder.build());
|
return builder.build();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -57,6 +57,7 @@
|
|||||||
"jquery": "3.4.0",
|
"jquery": "3.4.0",
|
||||||
"jschardet": "2.1.1",
|
"jschardet": "2.1.1",
|
||||||
"keytar": "^4.11.0",
|
"keytar": "^4.11.0",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
"native-is-elevated": "0.4.1",
|
"native-is-elevated": "0.4.1",
|
||||||
"native-keymap": "2.1.1",
|
"native-keymap": "2.1.1",
|
||||||
"native-watchdog": "1.3.0",
|
"native-watchdog": "1.3.0",
|
||||||
@@ -72,7 +73,6 @@
|
|||||||
"spdlog": "^0.11.1",
|
"spdlog": "^0.11.1",
|
||||||
"sudo-prompt": "9.1.1",
|
"sudo-prompt": "9.1.1",
|
||||||
"v8-inspect-profiler": "^0.0.20",
|
"v8-inspect-profiler": "^0.0.20",
|
||||||
"vscode-minimist": "^1.2.2",
|
|
||||||
"vscode-nsfw": "1.2.8",
|
"vscode-nsfw": "1.2.8",
|
||||||
"vscode-proxy-agent": "^0.5.2",
|
"vscode-proxy-agent": "^0.5.2",
|
||||||
"vscode-ripgrep": "^1.5.8",
|
"vscode-ripgrep": "^1.5.8",
|
||||||
@@ -98,6 +98,7 @@
|
|||||||
"@types/http-proxy-agent": "^2.0.1",
|
"@types/http-proxy-agent": "^2.0.1",
|
||||||
"@types/iconv-lite": "0.0.1",
|
"@types/iconv-lite": "0.0.1",
|
||||||
"@types/keytar": "^4.4.0",
|
"@types/keytar": "^4.4.0",
|
||||||
|
"@types/minimist": "^1.2.0",
|
||||||
"@types/mocha": "2.2.39",
|
"@types/mocha": "2.2.39",
|
||||||
"@types/node": "^12.11.7",
|
"@types/node": "^12.11.7",
|
||||||
"@types/plotly.js": "^1.44.9",
|
"@types/plotly.js": "^1.44.9",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"iconv-lite": "0.5.0",
|
"iconv-lite": "0.5.0",
|
||||||
"jquery": "3.4.0",
|
"jquery": "3.4.0",
|
||||||
"jschardet": "2.1.1",
|
"jschardet": "2.1.1",
|
||||||
|
"minimist": "^1.2.5",
|
||||||
"native-watchdog": "1.3.0",
|
"native-watchdog": "1.3.0",
|
||||||
"ng2-charts": "^1.6.0",
|
"ng2-charts": "^1.6.0",
|
||||||
"node-pty": "^0.10.0-beta2",
|
"node-pty": "^0.10.0-beta2",
|
||||||
@@ -33,7 +34,6 @@
|
|||||||
"semver-umd": "^5.5.5",
|
"semver-umd": "^5.5.5",
|
||||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.32",
|
"slickgrid": "github:anthonydresser/SlickGrid#2.3.32",
|
||||||
"spdlog": "^0.11.1",
|
"spdlog": "^0.11.1",
|
||||||
"vscode-minimist": "^1.2.2",
|
|
||||||
"vscode-nsfw": "1.2.8",
|
"vscode-nsfw": "1.2.8",
|
||||||
"vscode-proxy-agent": "^0.5.2",
|
"vscode-proxy-agent": "^0.5.2",
|
||||||
"vscode-ripgrep": "^1.5.8",
|
"vscode-ripgrep": "^1.5.8",
|
||||||
|
|||||||
@@ -454,6 +454,11 @@ minimist@0.0.8:
|
|||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
|
||||||
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
|
||||||
|
|
||||||
|
minimist@^1.2.5:
|
||||||
|
version "1.2.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
|
||||||
|
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
|
||||||
|
|
||||||
mkdirp@^0.5.1:
|
mkdirp@^0.5.1:
|
||||||
version "0.5.1"
|
version "0.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
|
||||||
@@ -695,11 +700,6 @@ util-deprecate@^1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||||
|
|
||||||
vscode-minimist@^1.2.2:
|
|
||||||
version "1.2.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.2.tgz#65403f44f0c6010d259b2271d36eb5c6f4ad8aab"
|
|
||||||
integrity sha512-DXMNG2QgrXn1jOP12LzjVfvxVkzxv/0Qa27JrMBj/XP2esj+fJ/wP2T4YUH5derj73Lc96dC8F25WyfDUbTpxQ==
|
|
||||||
|
|
||||||
vscode-nsfw@1.2.8:
|
vscode-nsfw@1.2.8:
|
||||||
version "1.2.8"
|
version "1.2.8"
|
||||||
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.2.8.tgz#1bf452e72ff1304934de63692870d039a2d972af"
|
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-1.2.8.tgz#1bf452e72ff1304934de63692870d039a2d972af"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const opn = require('opn');
|
const opn = require('opn');
|
||||||
const minimist = require('vscode-minimist');
|
const minimist = require('minimist');
|
||||||
|
|
||||||
const APP_ROOT = path.dirname(__dirname);
|
const APP_ROOT = path.dirname(__dirname);
|
||||||
const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
|
const EXTENSIONS_ROOT = path.join(APP_ROOT, 'extensions');
|
||||||
|
|||||||
@@ -55,6 +55,9 @@ function code() {
|
|||||||
|
|
||||||
function code-wsl()
|
function code-wsl()
|
||||||
{
|
{
|
||||||
|
HOST_IP=$(powershell.exe -Command "& {(Get-NetIPAddress | Where-Object {\$_.InterfaceAlias -like '*WSL*' -and \$_.AddressFamily -eq 'IPv4'}).IPAddress | Write-Host -NoNewline}")
|
||||||
|
export DISPLAY="$HOST_IP:0"
|
||||||
|
|
||||||
# in a wsl shell
|
# in a wsl shell
|
||||||
ELECTRON="$ROOT/.build/electron/Code - OSS.exe"
|
ELECTRON="$ROOT/.build/electron/Code - OSS.exe"
|
||||||
if [ -f "$ELECTRON" ]; then
|
if [ -f "$ELECTRON" ]; then
|
||||||
|
|||||||
2
src/bootstrap-fork.js
vendored
2
src/bootstrap-fork.js
vendored
@@ -142,13 +142,11 @@ function pipeLoggingToParent() {
|
|||||||
function handleExceptions() {
|
function handleExceptions() {
|
||||||
|
|
||||||
// Handle uncaught exceptions
|
// Handle uncaught exceptions
|
||||||
// @ts-ignore
|
|
||||||
process.on('uncaughtException', function (err) {
|
process.on('uncaughtException', function (err) {
|
||||||
console.error('Uncaught Exception: ', err);
|
console.error('Uncaught Exception: ', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Handle unhandled promise rejections
|
// Handle unhandled promise rejections
|
||||||
// @ts-ignore
|
|
||||||
process.on('unhandledRejection', function (reason) {
|
process.on('unhandledRejection', function (reason) {
|
||||||
console.error('Unhandled Promise Rejection: ', reason);
|
console.error('Unhandled Promise Rejection: ', reason);
|
||||||
});
|
});
|
||||||
|
|||||||
4
src/bootstrap-window.js
vendored
4
src/bootstrap-window.js
vendored
@@ -25,7 +25,6 @@ exports.assign = function assign(destination, source) {
|
|||||||
*/
|
*/
|
||||||
exports.load = function (modulePaths, resultCallback, options) {
|
exports.load = function (modulePaths, resultCallback, options) {
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const webFrame = require('electron').webFrame;
|
const webFrame = require('electron').webFrame;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@@ -49,7 +48,6 @@ exports.load = function (modulePaths, resultCallback, options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Error handler
|
// Error handler
|
||||||
// @ts-ignore
|
|
||||||
process.on('uncaughtException', function (error) {
|
process.on('uncaughtException', function (error) {
|
||||||
onUnexpectedError(error, enableDeveloperTools);
|
onUnexpectedError(error, enableDeveloperTools);
|
||||||
});
|
});
|
||||||
@@ -184,7 +182,6 @@ function parseURLQueryArgs() {
|
|||||||
*/
|
*/
|
||||||
function registerDeveloperKeybindings(disallowReloadKeybinding) {
|
function registerDeveloperKeybindings(disallowReloadKeybinding) {
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const ipc = require('electron').ipcRenderer;
|
const ipc = require('electron').ipcRenderer;
|
||||||
|
|
||||||
const extractKey = function (e) {
|
const extractKey = function (e) {
|
||||||
@@ -223,7 +220,6 @@ function registerDeveloperKeybindings(disallowReloadKeybinding) {
|
|||||||
|
|
||||||
function onUnexpectedError(error, enableDeveloperTools) {
|
function onUnexpectedError(error, enableDeveloperTools) {
|
||||||
|
|
||||||
// @ts-ignore
|
|
||||||
const ipc = require('electron').ipcRenderer;
|
const ipc = require('electron').ipcRenderer;
|
||||||
|
|
||||||
if (enableDeveloperTools) {
|
if (enableDeveloperTools) {
|
||||||
|
|||||||
@@ -323,7 +323,7 @@ function getUserDataPath(cliArgs) {
|
|||||||
* @returns {ParsedArgs}
|
* @returns {ParsedArgs}
|
||||||
*/
|
*/
|
||||||
function parseCLIArgs() {
|
function parseCLIArgs() {
|
||||||
const minimist = require('vscode-minimist');
|
const minimist = require('minimist');
|
||||||
|
|
||||||
return minimist(process.argv, {
|
return minimist(process.argv, {
|
||||||
string: [
|
string: [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//@ts-check
|
//@ts-check
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
// const pkg = require('../package.json');
|
// const pkg = require('../package.json');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
|||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { ScrollableDirective } from 'sql/base/browser/ui/scrollable/scrollable.directive';
|
import { ScrollableDirective } from 'sql/base/browser/ui/scrollable/scrollable.directive';
|
||||||
import { values } from 'vs/base/common/collections';
|
import { values } from 'vs/base/common/collections';
|
||||||
import { fill } from 'vs/base/common/arrays';
|
|
||||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||||
|
|
||||||
export interface GridCellConfig {
|
export interface GridCellConfig {
|
||||||
@@ -243,7 +242,7 @@ export class DashboardGridContainer extends DashboardTab implements OnDestroy {
|
|||||||
|
|
||||||
private createIndexes(indexes: number[]) {
|
private createIndexes(indexes: number[]) {
|
||||||
const max = Math.max(...indexes) + 1;
|
const max = Math.max(...indexes) + 1;
|
||||||
return fill(max, 0).map((x, i) => i);
|
return new Array(max).fill(0).map((x, i) => i);
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import { tableBackground, cellBackground, cellBorderColor } from 'sql/platform/t
|
|||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||||
import { find, fill } from 'vs/base/common/arrays';
|
import { find } from 'vs/base/common/arrays';
|
||||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||||
|
|
||||||
@@ -672,7 +672,7 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
|
|||||||
// if the durations are all 0 secs, show minimal chart
|
// if the durations are all 0 secs, show minimal chart
|
||||||
// instead of nothing
|
// instead of nothing
|
||||||
if (zeroDurationJobCount === jobHistories.length) {
|
if (zeroDurationJobCount === jobHistories.length) {
|
||||||
return fill(jobHistories.length, '5px');
|
return new Array(jobHistories.length).fill('5px');
|
||||||
} else {
|
} else {
|
||||||
return chartHeights;
|
return chartHeights;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||||
import { find, fill } from 'vs/base/common/arrays';
|
import { find } from 'vs/base/common/arrays';
|
||||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||||
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
import { IColorTheme } from 'vs/platform/theme/common/themeService';
|
||||||
|
|
||||||
@@ -731,7 +731,7 @@ export class NotebooksViewComponent extends JobManagementView implements OnInit,
|
|||||||
// if the durations are all 0 secs, show minimal chart
|
// if the durations are all 0 secs, show minimal chart
|
||||||
// instead of nothing
|
// instead of nothing
|
||||||
if (zeroDurationJobCount === jobHistories.length) {
|
if (zeroDurationJobCount === jobHistories.length) {
|
||||||
return fill(jobHistories.length, '5px');
|
return new Array(jobHistories.length).fill('5px');
|
||||||
} else {
|
} else {
|
||||||
return chartHeights;
|
return chartHeights;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "codicon";
|
font-family: "codicon";
|
||||||
src: url("./codicon.ttf?70edf2c63384951357ed8517386759dd") format("truetype");
|
src: url("./codicon.ttf?a76e99e42eab7c1a55601640b708d820") format("truetype");
|
||||||
}
|
}
|
||||||
|
|
||||||
.codicon[class*='codicon-'] {
|
.codicon[class*='codicon-'] {
|
||||||
@@ -416,11 +416,12 @@
|
|||||||
.codicon-feedback:before { content: "\eb96" }
|
.codicon-feedback:before { content: "\eb96" }
|
||||||
.codicon-group-by-ref-type:before { content: "\eb97" }
|
.codicon-group-by-ref-type:before { content: "\eb97" }
|
||||||
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
|
.codicon-ungroup-by-ref-type:before { content: "\eb98" }
|
||||||
.codicon-account:before { content: "\f101" }
|
.codicon-account:before { content: "\eb99" }
|
||||||
.codicon-bell-dot:before { content: "\f102" }
|
.codicon-bell-dot:before { content: "\eb9a" }
|
||||||
.codicon-debug-alt-2:before { content: "\f103" }
|
.codicon-debug-console:before { content: "\eb9b" }
|
||||||
.codicon-debug-alt:before { content: "\f104" }
|
.codicon-library:before { content: "\eb9c" }
|
||||||
.codicon-debug-console:before { content: "\f105" }
|
.codicon-output:before { content: "\eb9d" }
|
||||||
.codicon-library:before { content: "\f106" }
|
.codicon-run-all:before { content: "\eb9e" }
|
||||||
.codicon-output:before { content: "\f107" }
|
.codicon-sync-ignored:before { content: "\eb9f" }
|
||||||
.codicon-run-all:before { content: "\f108" }
|
.codicon-debug-alt-2:before { content: "\f101" }
|
||||||
|
.codicon-debug-alt:before { content: "\f102" }
|
||||||
|
|||||||
Binary file not shown.
@@ -105,7 +105,7 @@ export abstract class Pane extends Disposable implements IView {
|
|||||||
get minimumSize(): number {
|
get minimumSize(): number {
|
||||||
const headerSize = this.headerSize;
|
const headerSize = this.headerSize;
|
||||||
const expanded = !this.headerVisible || this.isExpanded();
|
const expanded = !this.headerVisible || this.isExpanded();
|
||||||
const minimumBodySize = expanded ? this._minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
const minimumBodySize = expanded ? this.minimumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
||||||
|
|
||||||
return headerSize + minimumBodySize;
|
return headerSize + minimumBodySize;
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ export abstract class Pane extends Disposable implements IView {
|
|||||||
get maximumSize(): number {
|
get maximumSize(): number {
|
||||||
const headerSize = this.headerSize;
|
const headerSize = this.headerSize;
|
||||||
const expanded = !this.headerVisible || this.isExpanded();
|
const expanded = !this.headerVisible || this.isExpanded();
|
||||||
const maximumBodySize = expanded ? this._maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
const maximumBodySize = expanded ? this.maximumBodySize : this._orientation === Orientation.HORIZONTAL ? 50 : 0;
|
||||||
|
|
||||||
return headerSize + maximumBodySize;
|
return headerSize + maximumBodySize;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -472,17 +472,6 @@ export function range(arg: number, to?: number): number[] {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated ES6: use `Array.fill`
|
|
||||||
*/
|
|
||||||
export function fill<T>(num: number, value: T, arr: T[] = []): T[] {
|
|
||||||
for (let i = 0; i < num; i++) {
|
|
||||||
arr[i] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function index<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T; };
|
export function index<T>(array: ReadonlyArray<T>, indexer: (t: T) => string): { [key: string]: T; };
|
||||||
export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; };
|
export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, merger?: (t: T, r: R) => R): { [key: string]: R; };
|
||||||
export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } {
|
export function index<T, R>(array: ReadonlyArray<T>, indexer: (t: T) => string, merger: (t: T, r: R) => R = t => t as any): { [key: string]: R; } {
|
||||||
|
|||||||
@@ -94,8 +94,14 @@ export class VSBuffer {
|
|||||||
return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end));
|
return new VSBuffer(this.buffer.subarray(start!/*bad lib.d.ts*/, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
set(array: VSBuffer, offset?: number): void {
|
set(array: VSBuffer, offset?: number): void;
|
||||||
|
set(array: Uint8Array, offset?: number): void;
|
||||||
|
set(array: VSBuffer | Uint8Array, offset?: number): void {
|
||||||
|
if (array instanceof VSBuffer) {
|
||||||
this.buffer.set(array.buffer, offset);
|
this.buffer.set(array.buffer, offset);
|
||||||
|
} else {
|
||||||
|
this.buffer.set(array, offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
readUInt32BE(offset: number): number {
|
readUInt32BE(offset: number): number {
|
||||||
@@ -106,6 +112,14 @@ export class VSBuffer {
|
|||||||
writeUInt32BE(this.buffer, value, offset);
|
writeUInt32BE(this.buffer, value, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readUInt32LE(offset: number): number {
|
||||||
|
return readUInt32LE(this.buffer, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUInt32LE(value: number, offset: number): void {
|
||||||
|
writeUInt32LE(this.buffer, value, offset);
|
||||||
|
}
|
||||||
|
|
||||||
readUInt8(offset: number): number {
|
readUInt8(offset: number): number {
|
||||||
return readUInt8(this.buffer, offset);
|
return readUInt8(this.buffer, offset);
|
||||||
}
|
}
|
||||||
@@ -117,15 +131,15 @@ export class VSBuffer {
|
|||||||
|
|
||||||
export function readUInt16LE(source: Uint8Array, offset: number): number {
|
export function readUInt16LE(source: Uint8Array, offset: number): number {
|
||||||
return (
|
return (
|
||||||
source[offset]
|
((source[offset + 0] << 0) >>> 0) |
|
||||||
+ source[offset + 1] * 2 ** 8
|
((source[offset + 1] << 8) >>> 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {
|
export function writeUInt16LE(destination: Uint8Array, value: number, offset: number): void {
|
||||||
destination[offset] = value;
|
destination[offset + 0] = (value & 0b11111111);
|
||||||
value = value >>> 8;
|
value = value >>> 8;
|
||||||
destination[offset + 1] = value;
|
destination[offset + 1] = (value & 0b11111111);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readUInt32BE(source: Uint8Array, offset: number): number {
|
export function readUInt32BE(source: Uint8Array, offset: number): number {
|
||||||
@@ -147,6 +161,25 @@ export function writeUInt32BE(destination: Uint8Array, value: number, offset: nu
|
|||||||
destination[offset] = value;
|
destination[offset] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readUInt32LE(source: Uint8Array, offset: number): number {
|
||||||
|
return (
|
||||||
|
((source[offset + 0] << 0) >>> 0) |
|
||||||
|
((source[offset + 1] << 8) >>> 0) |
|
||||||
|
((source[offset + 2] << 16) >>> 0) |
|
||||||
|
((source[offset + 3] << 24) >>> 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function writeUInt32LE(destination: Uint8Array, value: number, offset: number): void {
|
||||||
|
destination[offset + 0] = (value & 0b11111111);
|
||||||
|
value = value >>> 8;
|
||||||
|
destination[offset + 1] = (value & 0b11111111);
|
||||||
|
value = value >>> 8;
|
||||||
|
destination[offset + 2] = (value & 0b11111111);
|
||||||
|
value = value >>> 8;
|
||||||
|
destination[offset + 3] = (value & 0b11111111);
|
||||||
|
}
|
||||||
|
|
||||||
export function readUInt8(source: Uint8Array, offset: number): number {
|
export function readUInt8(source: Uint8Array, offset: number): number {
|
||||||
return source[offset];
|
return source[offset];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
|
||||||
import { sep } from 'vs/base/common/path';
|
import { sep } from 'vs/base/common/path';
|
||||||
import { IdleValue } from 'vs/base/common/async';
|
import { IdleValue } from 'vs/base/common/async';
|
||||||
|
|
||||||
@@ -133,8 +132,8 @@ export function compareAnything(one: string, other: string, lookFor: string): nu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sort suffix matches over non suffix matches
|
// Sort suffix matches over non suffix matches
|
||||||
const elementASuffixMatch = strings.endsWith(elementAName, lookFor);
|
const elementASuffixMatch = elementAName.endsWith(lookFor);
|
||||||
const elementBSuffixMatch = strings.endsWith(elementBName, lookFor);
|
const elementBSuffixMatch = elementBName.endsWith(lookFor);
|
||||||
if (elementASuffixMatch !== elementBSuffixMatch) {
|
if (elementASuffixMatch !== elementBSuffixMatch) {
|
||||||
return elementASuffixMatch ? -1 : 1;
|
return elementASuffixMatch ? -1 : 1;
|
||||||
}
|
}
|
||||||
@@ -154,8 +153,8 @@ export function compareByPrefix(one: string, other: string, lookFor: string): nu
|
|||||||
const elementBName = other.toLowerCase();
|
const elementBName = other.toLowerCase();
|
||||||
|
|
||||||
// Sort prefix matches over non prefix matches
|
// Sort prefix matches over non prefix matches
|
||||||
const elementAPrefixMatch = strings.startsWith(elementAName, lookFor);
|
const elementAPrefixMatch = elementAName.startsWith(lookFor);
|
||||||
const elementBPrefixMatch = strings.startsWith(elementBName, lookFor);
|
const elementBPrefixMatch = elementBName.startsWith(lookFor);
|
||||||
if (elementAPrefixMatch !== elementBPrefixMatch) {
|
if (elementAPrefixMatch !== elementBPrefixMatch) {
|
||||||
return elementAPrefixMatch ? -1 : 1;
|
return elementAPrefixMatch ? -1 : 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { isWindows } from 'vs/base/common/platform';
|
import { isWindows } from 'vs/base/common/platform';
|
||||||
import { startsWithIgnoreCase, equalsIgnoreCase, endsWith, rtrim } from 'vs/base/common/strings';
|
import { startsWithIgnoreCase, equalsIgnoreCase, rtrim } from 'vs/base/common/strings';
|
||||||
import { CharCode } from 'vs/base/common/charCode';
|
import { CharCode } from 'vs/base/common/charCode';
|
||||||
import { sep, posix, isAbsolute, join, normalize } from 'vs/base/common/path';
|
import { sep, posix, isAbsolute, join, normalize } from 'vs/base/common/path';
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ export function isWindowsDriveLetter(char0: number): boolean {
|
|||||||
export function sanitizeFilePath(candidate: string, cwd: string): string {
|
export function sanitizeFilePath(candidate: string, cwd: string): string {
|
||||||
|
|
||||||
// Special case: allow to open a drive letter without trailing backslash
|
// Special case: allow to open a drive letter without trailing backslash
|
||||||
if (isWindows && endsWith(candidate, ':')) {
|
if (isWindows && candidate.endsWith(':')) {
|
||||||
candidate += sep;
|
candidate += sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,7 +252,7 @@ export function sanitizeFilePath(candidate: string, cwd: string): string {
|
|||||||
candidate = rtrim(candidate, sep);
|
candidate = rtrim(candidate, sep);
|
||||||
|
|
||||||
// Special case: allow to open drive root ('C:\')
|
// Special case: allow to open drive root ('C:\')
|
||||||
if (endsWith(candidate, ':')) {
|
if (candidate.endsWith(':')) {
|
||||||
candidate += sep;
|
candidate += sep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ export function score(target: string, query: IPreparedQuery, fuzzy: boolean): Sc
|
|||||||
return scoreMultiple(target, query.values, fuzzy);
|
return scoreMultiple(target, query.values, fuzzy);
|
||||||
}
|
}
|
||||||
|
|
||||||
return scoreSingle(target, query.value, query.valueLowercase, fuzzy);
|
return scoreSingle(target, query.normalized, query.normalizedLowercase, fuzzy);
|
||||||
}
|
}
|
||||||
|
|
||||||
function scoreMultiple(target: string, query: IPreparedQueryPiece[], fuzzy: boolean): Score {
|
function scoreMultiple(target: string, query: IPreparedQueryPiece[], fuzzy: boolean): Score {
|
||||||
let totalScore = NO_MATCH;
|
let totalScore = NO_MATCH;
|
||||||
const totalPositions: number[] = [];
|
const totalPositions: number[] = [];
|
||||||
|
|
||||||
for (const { value, valueLowercase } of query) {
|
for (const { normalized, normalizedLowercase } of query) {
|
||||||
const [scoreValue, positions] = scoreSingle(target, value, valueLowercase, fuzzy);
|
const [scoreValue, positions] = scoreSingle(target, normalized, normalizedLowercase, fuzzy);
|
||||||
if (scoreValue === NO_MATCH) {
|
if (scoreValue === NO_MATCH) {
|
||||||
// if a single query value does not match, return with
|
// if a single query value does not match, return with
|
||||||
// no score entirely, we require all queries to match
|
// no score entirely, we require all queries to match
|
||||||
@@ -338,11 +338,26 @@ const LABEL_CAMELCASE_SCORE = 1 << 16;
|
|||||||
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
const LABEL_SCORE_THRESHOLD = 1 << 15;
|
||||||
|
|
||||||
export interface IPreparedQueryPiece {
|
export interface IPreparedQueryPiece {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The original query as provided as input.
|
||||||
|
*/
|
||||||
original: string;
|
original: string;
|
||||||
originalLowercase: string;
|
originalLowercase: string;
|
||||||
|
|
||||||
value: string;
|
/**
|
||||||
valueLowercase: string;
|
* Original normalized to platform separators:
|
||||||
|
* - Windows: \
|
||||||
|
* - Posix: /
|
||||||
|
*/
|
||||||
|
pathNormalized: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In addition to the normalized path, will have
|
||||||
|
* whitespace and wildcards removed.
|
||||||
|
*/
|
||||||
|
normalized: string;
|
||||||
|
normalizedLowercase: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IPreparedQuery extends IPreparedQueryPiece {
|
export interface IPreparedQuery extends IPreparedQueryPiece {
|
||||||
@@ -364,17 +379,21 @@ export function prepareQuery(original: string): IPreparedQuery {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const originalLowercase = original.toLowerCase();
|
const originalLowercase = original.toLowerCase();
|
||||||
const value = prepareQueryValue(original);
|
const { pathNormalized, normalized, normalizedLowercase } = normalizeQuery(original);
|
||||||
const valueLowercase = value.toLowerCase();
|
const containsPathSeparator = pathNormalized.indexOf(sep) >= 0;
|
||||||
const containsPathSeparator = value.indexOf(sep) >= 0;
|
|
||||||
|
|
||||||
let values: IPreparedQueryPiece[] | undefined = undefined;
|
let values: IPreparedQueryPiece[] | undefined = undefined;
|
||||||
|
|
||||||
const originalSplit = original.split(MULTIPL_QUERY_VALUES_SEPARATOR);
|
const originalSplit = original.split(MULTIPL_QUERY_VALUES_SEPARATOR);
|
||||||
if (originalSplit.length > 1) {
|
if (originalSplit.length > 1) {
|
||||||
for (const originalPiece of originalSplit) {
|
for (const originalPiece of originalSplit) {
|
||||||
const valuePiece = prepareQueryValue(originalPiece);
|
const {
|
||||||
if (valuePiece) {
|
pathNormalized: pathNormalizedPiece,
|
||||||
|
normalized: normalizedPiece,
|
||||||
|
normalizedLowercase: normalizedLowercasePiece
|
||||||
|
} = normalizeQuery(originalPiece);
|
||||||
|
|
||||||
|
if (normalizedPiece) {
|
||||||
if (!values) {
|
if (!values) {
|
||||||
values = [];
|
values = [];
|
||||||
}
|
}
|
||||||
@@ -382,29 +401,36 @@ export function prepareQuery(original: string): IPreparedQuery {
|
|||||||
values.push({
|
values.push({
|
||||||
original: originalPiece,
|
original: originalPiece,
|
||||||
originalLowercase: originalPiece.toLowerCase(),
|
originalLowercase: originalPiece.toLowerCase(),
|
||||||
value: valuePiece,
|
pathNormalized: pathNormalizedPiece,
|
||||||
valueLowercase: valuePiece.toLowerCase()
|
normalized: normalizedPiece,
|
||||||
|
normalizedLowercase: normalizedLowercasePiece
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { original, originalLowercase, value, valueLowercase, values, containsPathSeparator };
|
return { original, originalLowercase, pathNormalized, normalized, normalizedLowercase, values, containsPathSeparator };
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareQueryValue(original: string): string {
|
function normalizeQuery(original: string): { pathNormalized: string, normalized: string, normalizedLowercase: string } {
|
||||||
let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace
|
let pathNormalized: string;
|
||||||
if (isWindows) {
|
if (isWindows) {
|
||||||
value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
pathNormalized = original.replace(/\//g, sep); // Help Windows users to search for paths when using slash
|
||||||
} else {
|
} else {
|
||||||
value = value.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
pathNormalized = original.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
const normalized = stripWildcards(pathNormalized).replace(/\s/g, '');
|
||||||
|
|
||||||
|
return {
|
||||||
|
pathNormalized,
|
||||||
|
normalized,
|
||||||
|
normalizedLowercase: normalized.toLowerCase()
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
|
||||||
if (!item || !query.value) {
|
if (!item || !query.normalized) {
|
||||||
return NO_ITEM_SCORE; // we need an item and query to score on at least
|
return NO_ITEM_SCORE; // we need an item and query to score on at least
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,9 +443,9 @@ export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, acc
|
|||||||
|
|
||||||
let cacheHash: string;
|
let cacheHash: string;
|
||||||
if (description) {
|
if (description) {
|
||||||
cacheHash = `${label}${description}${query.value}${fuzzy}`;
|
cacheHash = `${label}${description}${query.normalized}${fuzzy}`;
|
||||||
} else {
|
} else {
|
||||||
cacheHash = `${label}${query.value}${fuzzy}`;
|
cacheHash = `${label}${query.normalized}${fuzzy}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cached = cache[cacheHash];
|
const cached = cache[cacheHash];
|
||||||
@@ -455,7 +481,7 @@ function createMatches(offsets: undefined | number[]): IMatch[] {
|
|||||||
function doScoreItem(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
function doScoreItem(label: string, description: string | undefined, path: string | undefined, query: IPreparedQuery, fuzzy: boolean): IItemScore {
|
||||||
|
|
||||||
// 1.) treat identity matches on full path highest
|
// 1.) treat identity matches on full path highest
|
||||||
if (path && (isLinux ? query.original === path : equalsIgnoreCase(query.original, path))) {
|
if (path && (isLinux ? query.pathNormalized === path : equalsIgnoreCase(query.pathNormalized, path))) {
|
||||||
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined };
|
return { score: PATH_IDENTITY_SCORE, labelMatch: [{ start: 0, end: label.length }], descriptionMatch: description ? [{ start: 0, end: description.length }] : undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,13 +490,13 @@ function doScoreItem(label: string, description: string | undefined, path: strin
|
|||||||
if (preferLabelMatches) {
|
if (preferLabelMatches) {
|
||||||
|
|
||||||
// 2.) treat prefix matches on the label second highest
|
// 2.) treat prefix matches on the label second highest
|
||||||
const prefixLabelMatch = matchesPrefix(query.value, label);
|
const prefixLabelMatch = matchesPrefix(query.normalized, label);
|
||||||
if (prefixLabelMatch) {
|
if (prefixLabelMatch) {
|
||||||
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
|
return { score: LABEL_PREFIX_SCORE, labelMatch: prefixLabelMatch };
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.) treat camelcase matches on the label third highest
|
// 3.) treat camelcase matches on the label third highest
|
||||||
const camelcaseLabelMatch = matchesCamelCase(query.value, label);
|
const camelcaseLabelMatch = matchesCamelCase(query.normalized, label);
|
||||||
if (camelcaseLabelMatch) {
|
if (camelcaseLabelMatch) {
|
||||||
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
|
return { score: LABEL_CAMELCASE_SCORE, labelMatch: camelcaseLabelMatch };
|
||||||
}
|
}
|
||||||
@@ -702,17 +728,17 @@ function fallbackCompare<T>(itemA: T, itemB: T, query: IPreparedQuery, accessor:
|
|||||||
|
|
||||||
// compare by label
|
// compare by label
|
||||||
if (labelA !== labelB) {
|
if (labelA !== labelB) {
|
||||||
return compareAnything(labelA, labelB, query.value);
|
return compareAnything(labelA, labelB, query.normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare by description
|
// compare by description
|
||||||
if (descriptionA && descriptionB && descriptionA !== descriptionB) {
|
if (descriptionA && descriptionB && descriptionA !== descriptionB) {
|
||||||
return compareAnything(descriptionA, descriptionB, query.value);
|
return compareAnything(descriptionA, descriptionB, query.normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
// compare by path
|
// compare by path
|
||||||
if (pathA && pathB && pathA !== pathB) {
|
if (pathA && pathB && pathA !== pathB) {
|
||||||
return compareAnything(pathA, pathB, query.value);
|
return compareAnything(pathA, pathB, query.normalized);
|
||||||
}
|
}
|
||||||
|
|
||||||
// equal
|
// equal
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ function parsePattern(arg1: string | IRelativePattern, options: IGlobOptions): P
|
|||||||
if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check
|
if (T1.test(pattern)) { // common pattern: **/*.txt just need endsWith check
|
||||||
const base = pattern.substr(4); // '**/*'.length === 4
|
const base = pattern.substr(4); // '**/*'.length === 4
|
||||||
parsedPattern = function (path, basename) {
|
parsedPattern = function (path, basename) {
|
||||||
return typeof path === 'string' && strings.endsWith(path, base) ? pattern : null;
|
return typeof path === 'string' && path.endsWith(base) ? pattern : null;
|
||||||
};
|
};
|
||||||
} else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check
|
} else if (match = T2.exec(trimForExclusions(pattern, options))) { // common pattern: **/some.txt just need basename check
|
||||||
parsedPattern = trivia2(match[1], pattern);
|
parsedPattern = trivia2(match[1], pattern);
|
||||||
@@ -339,7 +339,7 @@ function wrapRelativePattern(parsedPattern: ParsedStringPattern, arg2: string |
|
|||||||
}
|
}
|
||||||
|
|
||||||
function trimForExclusions(pattern: string, options: IGlobOptions): string {
|
function trimForExclusions(pattern: string, options: IGlobOptions): string {
|
||||||
return options.trimForExclusions && strings.endsWith(pattern, '/**') ? pattern.substr(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later
|
return options.trimForExclusions && pattern.endsWith('/**') ? pattern.substr(0, pattern.length - 2) : pattern; // dropping **, tailing / is dropped later
|
||||||
}
|
}
|
||||||
|
|
||||||
// common pattern: **/some.txt just need basename check
|
// common pattern: **/some.txt just need basename check
|
||||||
@@ -353,7 +353,7 @@ function trivia2(base: string, originalPattern: string): ParsedStringPattern {
|
|||||||
if (basename) {
|
if (basename) {
|
||||||
return basename === base ? originalPattern : null;
|
return basename === base ? originalPattern : null;
|
||||||
}
|
}
|
||||||
return path === base || strings.endsWith(path, slashBase) || strings.endsWith(path, backslashBase) ? originalPattern : null;
|
return path === base || path.endsWith(slashBase) || path.endsWith(backslashBase) ? originalPattern : null;
|
||||||
};
|
};
|
||||||
const basenames = [base];
|
const basenames = [base];
|
||||||
parsedPattern.basenames = basenames;
|
parsedPattern.basenames = basenames;
|
||||||
@@ -398,7 +398,7 @@ function trivia4and5(path: string, pattern: string, matchPathEnds: boolean): Par
|
|||||||
const nativePath = paths.sep !== paths.posix.sep ? path.replace(ALL_FORWARD_SLASHES, paths.sep) : path;
|
const nativePath = paths.sep !== paths.posix.sep ? path.replace(ALL_FORWARD_SLASHES, paths.sep) : path;
|
||||||
const nativePathEnd = paths.sep + nativePath;
|
const nativePathEnd = paths.sep + nativePath;
|
||||||
const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) {
|
const parsedPattern: ParsedStringPattern = matchPathEnds ? function (path, basename) {
|
||||||
return typeof path === 'string' && (path === nativePath || strings.endsWith(path, nativePathEnd)) ? pattern : null;
|
return typeof path === 'string' && (path === nativePath || path.endsWith(nativePathEnd)) ? pattern : null;
|
||||||
} : function (path, basename) {
|
} : function (path, basename) {
|
||||||
return typeof path === 'string' && path === nativePath ? pattern : null;
|
return typeof path === 'string' && path === nativePath ? pattern : null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { posix, normalize, win32, sep } from 'vs/base/common/path';
|
import { posix, normalize, win32, sep } from 'vs/base/common/path';
|
||||||
import { endsWith, startsWithIgnoreCase, rtrim, startsWith } from 'vs/base/common/strings';
|
import { startsWithIgnoreCase, rtrim } from 'vs/base/common/strings';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||||
import { isEqual, basename, relativePath } from 'vs/base/common/resources';
|
import { isEqual, basename, relativePath } from 'vs/base/common/resources';
|
||||||
@@ -117,7 +117,7 @@ export function tildify(path: string, userHome: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Linux: case sensitive, macOS: case insensitive
|
// Linux: case sensitive, macOS: case insensitive
|
||||||
if (isLinux ? startsWith(path, normalizedUserHome) : startsWithIgnoreCase(path, normalizedUserHome)) {
|
if (isLinux ? path.startsWith(normalizedUserHome) : startsWithIgnoreCase(path, normalizedUserHome)) {
|
||||||
path = `~/${path.substr(normalizedUserHome.length)}`;
|
path = `~/${path.substr(normalizedUserHome.length)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +210,7 @@ export function shorten(paths: string[], pathSeparator: string = sep): string[]
|
|||||||
// Adding separator as prefix for subpath, such that 'endsWith(src, trgt)' considers subpath as directory name instead of plain string.
|
// Adding separator as prefix for subpath, such that 'endsWith(src, trgt)' considers subpath as directory name instead of plain string.
|
||||||
// prefix is not added when either subpath is root directory or path[otherPathIndex] does not have multiple directories.
|
// prefix is not added when either subpath is root directory or path[otherPathIndex] does not have multiple directories.
|
||||||
const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(pathSeparator) > -1) ? pathSeparator + subpath : subpath;
|
const subpathWithSep: string = (start > 0 && paths[otherPathIndex].indexOf(pathSeparator) > -1) ? pathSeparator + subpath : subpath;
|
||||||
const isOtherPathEnding: boolean = endsWith(paths[otherPathIndex], subpathWithSep);
|
const isOtherPathEnding: boolean = paths[otherPathIndex].endsWith(subpathWithSep);
|
||||||
|
|
||||||
match = !isSubpathEnding || isOtherPathEnding;
|
match = !isSubpathEnding || isOtherPathEnding;
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ export function shorten(paths: string[], pathSeparator: string = sep): string[]
|
|||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
// preserve disk drive or root prefix
|
// preserve disk drive or root prefix
|
||||||
if (endsWith(segments[0], ':') || prefix !== '') {
|
if (segments[0].endsWith(':') || prefix !== '') {
|
||||||
if (start === 1) {
|
if (start === 1) {
|
||||||
// extend subpath to include disk drive prefix
|
// extend subpath to include disk drive prefix
|
||||||
start = 0;
|
start = 0;
|
||||||
|
|||||||
@@ -56,32 +56,6 @@ export function setToString<K>(set: Set<K>): string {
|
|||||||
return `Set(${set.size}) {${entries.join(', ')}}`;
|
return `Set(${set.size}) {${entries.join(', ')}}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated ES6: use `...Map.entries()`
|
|
||||||
*/
|
|
||||||
export function mapToSerializable(map: Map<string, string>): [string, string][] {
|
|
||||||
const serializable: [string, string][] = [];
|
|
||||||
|
|
||||||
map.forEach((value, key) => {
|
|
||||||
serializable.push([key, value]);
|
|
||||||
});
|
|
||||||
|
|
||||||
return serializable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated ES6: use `new Map([[key1, value1],[key2, value2]])`
|
|
||||||
*/
|
|
||||||
export function serializableToMap(serializable: [string, string][]): Map<string, string> {
|
|
||||||
const items = new Map<string, string>();
|
|
||||||
|
|
||||||
for (const [key, value] of serializable) {
|
|
||||||
items.set(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IKeyIterator {
|
export interface IKeyIterator {
|
||||||
reset(key: string): this;
|
reset(key: string): this;
|
||||||
next(): this;
|
next(): this;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { basename, posix, extname } from 'vs/base/common/path';
|
import { basename, posix, extname } from 'vs/base/common/path';
|
||||||
import { endsWith, startsWithUTF8BOM, startsWith } from 'vs/base/common/strings';
|
import { startsWithUTF8BOM } from 'vs/base/common/strings';
|
||||||
import { coalesce } from 'vs/base/common/arrays';
|
import { coalesce } from 'vs/base/common/arrays';
|
||||||
import { match } from 'vs/base/common/glob';
|
import { match } from 'vs/base/common/glob';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
@@ -185,7 +185,7 @@ function guessMimeTypeByPath(path: string, filename: string, associations: IText
|
|||||||
// Longest extension match
|
// Longest extension match
|
||||||
if (association.extension) {
|
if (association.extension) {
|
||||||
if (!extensionMatch || association.extension.length > extensionMatch.extension!.length) {
|
if (!extensionMatch || association.extension.length > extensionMatch.extension!.length) {
|
||||||
if (endsWith(filename, association.extensionLowercase!)) {
|
if (filename.endsWith(association.extensionLowercase!)) {
|
||||||
extensionMatch = association;
|
extensionMatch = association;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -259,11 +259,11 @@ export function suggestFilename(mode: string | undefined, prefix: string): strin
|
|||||||
.map(assoc => assoc.extension);
|
.map(assoc => assoc.extension);
|
||||||
|
|
||||||
const extensionsWithDotFirst = coalesce(extensions)
|
const extensionsWithDotFirst = coalesce(extensions)
|
||||||
.filter(assoc => startsWith(assoc, '.'));
|
.filter(assoc => assoc.startsWith('.'));
|
||||||
|
|
||||||
if (extensionsWithDotFirst.length > 0) {
|
if (extensionsWithDotFirst.length > 0) {
|
||||||
const candidateExtension = extensionsWithDotFirst[0];
|
const candidateExtension = extensionsWithDotFirst[0];
|
||||||
if (endsWith(prefix, candidateExtension)) {
|
if (prefix.endsWith(candidateExtension)) {
|
||||||
// do not add the prefix if it already exists
|
// do not add the prefix if it already exists
|
||||||
// https://github.com/microsoft/vscode/issues/83603
|
// https://github.com/microsoft/vscode/issues/83603
|
||||||
return prefix;
|
return prefix;
|
||||||
|
|||||||
@@ -588,11 +588,11 @@ function _makeFsPath(uri: URI, keepDriveLetterCasing: boolean): string {
|
|||||||
&& (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)
|
&& (uri.path.charCodeAt(1) >= CharCode.A && uri.path.charCodeAt(1) <= CharCode.Z || uri.path.charCodeAt(1) >= CharCode.a && uri.path.charCodeAt(1) <= CharCode.z)
|
||||||
&& uri.path.charCodeAt(2) === CharCode.Colon
|
&& uri.path.charCodeAt(2) === CharCode.Colon
|
||||||
) {
|
) {
|
||||||
// windows drive letter: file:///c:/far/boo
|
|
||||||
if (!keepDriveLetterCasing) {
|
if (!keepDriveLetterCasing) {
|
||||||
|
// windows drive letter: file:///c:/far/boo
|
||||||
value = uri.path[1].toLowerCase() + uri.path.substr(2);
|
value = uri.path[1].toLowerCase() + uri.path.substr(2);
|
||||||
} else {
|
} else {
|
||||||
value = uri.path.substr(1, 2);
|
value = uri.path.substr(1);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// other path
|
// other path
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import * as fs from 'fs';
|
|||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as platform from 'vs/base/common/platform';
|
import * as platform from 'vs/base/common/platform';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { endsWith } from 'vs/base/common/strings';
|
|
||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
import { isRootOrDriveLetter } from 'vs/base/common/extpath';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
@@ -492,7 +491,7 @@ export async function move(source: string, target: string): Promise<void> {
|
|||||||
//
|
//
|
||||||
// 2.) The user tries to rename a file/folder that ends with a dot. This is not
|
// 2.) The user tries to rename a file/folder that ends with a dot. This is not
|
||||||
// really possible to move then, at least on UNC devices.
|
// really possible to move then, at least on UNC devices.
|
||||||
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || endsWith(source, '.')) {
|
if (source.toLowerCase() !== target.toLowerCase() && error.code === 'EXDEV' || source.endsWith('.')) {
|
||||||
await copy(source, target);
|
await copy(source, target);
|
||||||
await rimraf(source, RimRafMode.MOVE);
|
await rimraf(source, RimRafMode.MOVE);
|
||||||
await updateMtime(target);
|
await updateMtime(target);
|
||||||
|
|||||||
@@ -246,8 +246,8 @@
|
|||||||
|
|
||||||
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
|
.quick-input-list .quick-input-list-entry-action-bar .action-label.codicon {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
width: 19px;
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
padding: 0 2px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { timeout } from 'vs/base/common/async';
|
|||||||
import { mapToString, setToString } from 'vs/base/common/map';
|
import { mapToString, setToString } from 'vs/base/common/map';
|
||||||
import { basename } from 'vs/base/common/path';
|
import { basename } from 'vs/base/common/path';
|
||||||
import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs';
|
import { copy, renameIgnoreError, unlink } from 'vs/base/node/pfs';
|
||||||
import { fill } from 'vs/base/common/arrays';
|
|
||||||
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
|
import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/base/parts/storage/common/storage';
|
||||||
|
|
||||||
interface IDatabaseConnection {
|
interface IDatabaseConnection {
|
||||||
@@ -97,7 +96,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
keysValuesChunks.forEach(keysValuesChunk => {
|
keysValuesChunks.forEach(keysValuesChunk => {
|
||||||
this.prepare(connection, `INSERT INTO ItemTable VALUES ${fill(keysValuesChunk.length / 2, '(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => {
|
this.prepare(connection, `INSERT INTO ItemTable VALUES ${new Array(keysValuesChunk.length / 2).fill('(?,?)').join(',')}`, stmt => stmt.run(keysValuesChunk), () => {
|
||||||
const keys: string[] = [];
|
const keys: string[] = [];
|
||||||
let length = 0;
|
let length = 0;
|
||||||
toInsert.forEach((value, key) => {
|
toInsert.forEach((value, key) => {
|
||||||
@@ -132,7 +131,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
|
|||||||
});
|
});
|
||||||
|
|
||||||
keysChunks.forEach(keysChunk => {
|
keysChunks.forEach(keysChunk => {
|
||||||
this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${fill(keysChunk.length, '?').join(',')})`, stmt => stmt.run(keysChunk), () => {
|
this.prepare(connection, `DELETE FROM ItemTable WHERE key IN (${new Array(keysChunk.length).fill('?').join(',')})`, stmt => stmt.run(keysChunk), () => {
|
||||||
const keys: string[] = [];
|
const keys: string[] = [];
|
||||||
toDelete.forEach(key => {
|
toDelete.forEach(key => {
|
||||||
keys.push(key);
|
keys.push(key);
|
||||||
|
|||||||
@@ -857,41 +857,58 @@ suite('Fuzzy Scorer', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('prepareQuery', () => {
|
test('prepareQuery', () => {
|
||||||
assert.equal(scorer.prepareQuery(' f*a ').value, 'fa');
|
assert.equal(scorer.prepareQuery(' f*a ').normalized, 'fa');
|
||||||
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
|
||||||
assert.equal(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
|
assert.equal(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
|
||||||
assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts');
|
assert.equal(scorer.prepareQuery('model Tester.ts').normalized, 'modelTester.ts');
|
||||||
assert.equal(scorer.prepareQuery('Model Tester.ts').valueLowercase, 'modeltester.ts');
|
assert.equal(scorer.prepareQuery('Model Tester.ts').normalizedLowercase, 'modeltester.ts');
|
||||||
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
|
||||||
assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
|
||||||
|
|
||||||
// with spaces
|
// with spaces
|
||||||
let query = scorer.prepareQuery('He*llo World');
|
let query = scorer.prepareQuery('He*llo World');
|
||||||
assert.equal(query.original, 'He*llo World');
|
assert.equal(query.original, 'He*llo World');
|
||||||
assert.equal(query.value, 'HelloWorld');
|
assert.equal(query.normalized, 'HelloWorld');
|
||||||
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
|
assert.equal(query.normalizedLowercase, 'HelloWorld'.toLowerCase());
|
||||||
assert.equal(query.values?.length, 2);
|
assert.equal(query.values?.length, 2);
|
||||||
assert.equal(query.values?.[0].original, 'He*llo');
|
assert.equal(query.values?.[0].original, 'He*llo');
|
||||||
assert.equal(query.values?.[0].value, 'Hello');
|
assert.equal(query.values?.[0].normalized, 'Hello');
|
||||||
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
|
assert.equal(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());
|
||||||
assert.equal(query.values?.[1].original, 'World');
|
assert.equal(query.values?.[1].original, 'World');
|
||||||
assert.equal(query.values?.[1].value, 'World');
|
assert.equal(query.values?.[1].normalized, 'World');
|
||||||
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
|
assert.equal(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
|
||||||
|
|
||||||
// with spaces that are empty
|
// with spaces that are empty
|
||||||
query = scorer.prepareQuery(' Hello World ');
|
query = scorer.prepareQuery(' Hello World ');
|
||||||
assert.equal(query.original, ' Hello World ');
|
assert.equal(query.original, ' Hello World ');
|
||||||
assert.equal(query.originalLowercase, ' Hello World '.toLowerCase());
|
assert.equal(query.originalLowercase, ' Hello World '.toLowerCase());
|
||||||
assert.equal(query.value, 'HelloWorld');
|
assert.equal(query.normalized, 'HelloWorld');
|
||||||
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
|
assert.equal(query.normalizedLowercase, 'HelloWorld'.toLowerCase());
|
||||||
assert.equal(query.values?.length, 2);
|
assert.equal(query.values?.length, 2);
|
||||||
assert.equal(query.values?.[0].original, 'Hello');
|
assert.equal(query.values?.[0].original, 'Hello');
|
||||||
assert.equal(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());
|
assert.equal(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());
|
||||||
assert.equal(query.values?.[0].value, 'Hello');
|
assert.equal(query.values?.[0].normalized, 'Hello');
|
||||||
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
|
assert.equal(query.values?.[0].normalizedLowercase, 'Hello'.toLowerCase());
|
||||||
assert.equal(query.values?.[1].original, 'World');
|
assert.equal(query.values?.[1].original, 'World');
|
||||||
assert.equal(query.values?.[1].originalLowercase, 'World'.toLowerCase());
|
assert.equal(query.values?.[1].originalLowercase, 'World'.toLowerCase());
|
||||||
assert.equal(query.values?.[1].value, 'World');
|
assert.equal(query.values?.[1].normalized, 'World');
|
||||||
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
|
assert.equal(query.values?.[1].normalizedLowercase, 'World'.toLowerCase());
|
||||||
|
|
||||||
|
// Path related
|
||||||
|
if (isWindows) {
|
||||||
|
assert.equal(scorer.prepareQuery('C:\\some\\path').pathNormalized, 'C:\\some\\path');
|
||||||
|
assert.equal(scorer.prepareQuery('C:\\some\\path').normalized, 'C:\\some\\path');
|
||||||
|
assert.equal(scorer.prepareQuery('C:\\some\\path').containsPathSeparator, true);
|
||||||
|
assert.equal(scorer.prepareQuery('C:/some/path').pathNormalized, 'C:\\some\\path');
|
||||||
|
assert.equal(scorer.prepareQuery('C:/some/path').normalized, 'C:\\some\\path');
|
||||||
|
assert.equal(scorer.prepareQuery('C:/some/path').containsPathSeparator, true);
|
||||||
|
} else {
|
||||||
|
assert.equal(scorer.prepareQuery('/some/path').pathNormalized, '/some/path');
|
||||||
|
assert.equal(scorer.prepareQuery('/some/path').normalized, '/some/path');
|
||||||
|
assert.equal(scorer.prepareQuery('/some/path').containsPathSeparator, true);
|
||||||
|
assert.equal(scorer.prepareQuery('\\some\\path').pathNormalized, '/some/path');
|
||||||
|
assert.equal(scorer.prepareQuery('\\some\\path').normalized, '/some/path');
|
||||||
|
assert.equal(scorer.prepareQuery('\\some\\path').containsPathSeparator, true);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, mapToSerializable, serializableToMap } from 'vs/base/common/map';
|
import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
|
|
||||||
@@ -629,17 +629,4 @@ suite('Map', () => {
|
|||||||
// assert.equal(map.get(windowsFile), 'true');
|
// assert.equal(map.get(windowsFile), 'true');
|
||||||
// assert.equal(map.get(uncFile), 'true');
|
// assert.equal(map.get(uncFile), 'true');
|
||||||
// });
|
// });
|
||||||
|
|
||||||
test('mapToSerializable / serializableToMap', function () {
|
|
||||||
const map = new Map<string, string>();
|
|
||||||
map.set('1', 'foo');
|
|
||||||
map.set('2', null!);
|
|
||||||
map.set('3', 'bar');
|
|
||||||
|
|
||||||
const map2 = serializableToMap(mapToSerializable(map));
|
|
||||||
assert.equal(map2.size, map.size);
|
|
||||||
assert.equal(map2.get('1'), map.get('1'));
|
|
||||||
assert.equal(map2.get('2'), map.get('2'));
|
|
||||||
assert.equal(map2.get('3'), map.get('3'));
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -563,5 +563,8 @@ suite('URI', () => {
|
|||||||
|
|
||||||
assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/foo/bazz', false);
|
assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/foo/bazz', false);
|
||||||
assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/foo/bazz', false);
|
assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/foo/bazz', false);
|
||||||
|
|
||||||
|
//https://github.com/microsoft/vscode/issues/93831
|
||||||
|
assertJoined('file:///c:/foo/bar', './other/foo.img', 'file:///c:/foo/bar/other/foo.img', false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -401,9 +401,9 @@ suite('Paths (Node Implementation)', () => {
|
|||||||
];
|
];
|
||||||
resolveTests.forEach((test) => {
|
resolveTests.forEach((test) => {
|
||||||
const resolve = test[0];
|
const resolve = test[0];
|
||||||
//@ts-ignore
|
//@ts-expect-error
|
||||||
test[1].forEach((test) => {
|
test[1].forEach((test) => {
|
||||||
//@ts-ignore
|
//@ts-expect-error
|
||||||
const actual = resolve.apply(null, test[0]);
|
const actual = resolve.apply(null, test[0]);
|
||||||
let actualAlt;
|
let actualAlt;
|
||||||
const os = resolve === path.win32.resolve ? 'win32' : 'posix';
|
const os = resolve === path.win32.resolve ? 'win32' : 'posix';
|
||||||
@@ -579,9 +579,9 @@ suite('Paths (Node Implementation)', () => {
|
|||||||
];
|
];
|
||||||
relativeTests.forEach((test) => {
|
relativeTests.forEach((test) => {
|
||||||
const relative = test[0];
|
const relative = test[0];
|
||||||
//@ts-ignore
|
//@ts-expect-error
|
||||||
test[1].forEach((test) => {
|
test[1].forEach((test) => {
|
||||||
//@ts-ignore
|
//@ts-expect-error
|
||||||
const actual = relative(test[0], test[1]);
|
const actual = relative(test[0], test[1]);
|
||||||
const expected = test[2];
|
const expected = test[2];
|
||||||
const os = relative === path.win32.relative ? 'win32' : 'posix';
|
const os = relative === path.win32.relative ? 'win32' : 'posix';
|
||||||
|
|||||||
@@ -61,7 +61,6 @@ import { Schemas } from 'vs/base/common/network';
|
|||||||
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
|
import { SnapUpdateService } from 'vs/platform/update/electron-main/updateService.snap';
|
||||||
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
|
import { IStorageMainService, StorageMainService } from 'vs/platform/storage/node/storageMainService';
|
||||||
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
|
import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc';
|
||||||
import { startsWith } from 'vs/base/common/strings';
|
|
||||||
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
|
import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService';
|
||||||
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
|
import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
|
||||||
import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
|
import { WorkspacesHistoryMainService, IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService';
|
||||||
@@ -178,7 +177,7 @@ export class CodeApplication extends Disposable {
|
|||||||
const srcUri = URI.parse(source).fsPath.toLowerCase();
|
const srcUri = URI.parse(source).fsPath.toLowerCase();
|
||||||
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
|
const rootUri = URI.file(this.environmentService.appRoot).fsPath.toLowerCase();
|
||||||
|
|
||||||
return startsWith(srcUri, rootUri + sep);
|
return srcUri.startsWith(rootUri + sep);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Ensure defaults
|
// Ensure defaults
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as path from 'vs/base/common/path';
|
import * as path from 'vs/base/common/path';
|
||||||
import * as objects from 'vs/base/common/objects';
|
import * as objects from 'vs/base/common/objects';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { Event as CommonEvent, Emitter } from 'vs/base/common/event';
|
import { Emitter } from 'vs/base/common/event';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron';
|
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron';
|
||||||
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||||
@@ -26,7 +26,6 @@ import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
|||||||
import * as perf from 'vs/base/common/performance';
|
import * as perf from 'vs/base/common/performance';
|
||||||
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService';
|
||||||
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService';
|
||||||
import { endsWith } from 'vs/base/common/strings';
|
|
||||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
||||||
@@ -69,13 +68,13 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32
|
private static readonly MAX_URL_LENGTH = 2 * 1024 * 1024; // https://cs.chromium.org/chromium/src/url/url_constants.cc?l=32
|
||||||
|
|
||||||
private readonly _onClose = this._register(new Emitter<void>());
|
private readonly _onClose = this._register(new Emitter<void>());
|
||||||
readonly onClose: CommonEvent<void> = this._onClose.event;
|
readonly onClose = this._onClose.event;
|
||||||
|
|
||||||
private readonly _onDestroy = this._register(new Emitter<void>());
|
private readonly _onDestroy = this._register(new Emitter<void>());
|
||||||
readonly onDestroy: CommonEvent<void> = this._onDestroy.event;
|
readonly onDestroy = this._onDestroy.event;
|
||||||
|
|
||||||
private readonly _onLoad = this._register(new Emitter<void>());
|
private readonly _onLoad = this._register(new Emitter<void>());
|
||||||
readonly onLoad: CommonEvent<void> = this._onLoad.event;
|
readonly onLoad = this._onLoad.event;
|
||||||
|
|
||||||
private hiddenTitleBarStyle: boolean | undefined;
|
private hiddenTitleBarStyle: boolean | undefined;
|
||||||
private showTimeoutHandle: NodeJS.Timeout | undefined;
|
private showTimeoutHandle: NodeJS.Timeout | undefined;
|
||||||
@@ -83,7 +82,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
private _readyState: ReadyState;
|
private _readyState: ReadyState;
|
||||||
private windowState: IWindowState;
|
private windowState: IWindowState;
|
||||||
private currentMenuBarVisibility: MenuBarVisibility | undefined;
|
private currentMenuBarVisibility: MenuBarVisibility | undefined;
|
||||||
|
|
||||||
private representedFilename: string | undefined;
|
private representedFilename: string | undefined;
|
||||||
|
private documentEdited: boolean | undefined;
|
||||||
|
|
||||||
private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[];
|
private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[];
|
||||||
|
|
||||||
@@ -271,6 +272,22 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
return this.representedFilename;
|
return this.representedFilename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setDocumentEdited(edited: boolean): void {
|
||||||
|
if (isMacintosh) {
|
||||||
|
this._win.setDocumentEdited(edited);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.documentEdited = edited;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDocumentEdited(): boolean {
|
||||||
|
if (isMacintosh) {
|
||||||
|
return this._win.isDocumentEdited();
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!this.documentEdited;
|
||||||
|
}
|
||||||
|
|
||||||
focus(): void {
|
focus(): void {
|
||||||
if (!this._win) {
|
if (!this._win) {
|
||||||
return;
|
return;
|
||||||
@@ -349,7 +366,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => {
|
this._win.webContents.session.webRequest.onBeforeRequest(null!, (details, callback) => {
|
||||||
if (details.url.indexOf('.svg') > 0) {
|
if (details.url.indexOf('.svg') > 0) {
|
||||||
const uri = URI.parse(details.url);
|
const uri = URI.parse(details.url);
|
||||||
if (uri && !uri.scheme.match(/file/i) && endsWith(uri.path, '.svg')) {
|
if (uri && !uri.scheme.match(/file/i) && uri.path.endsWith('.svg')) {
|
||||||
return callback({ cancel: true });
|
return callback({ cancel: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -586,9 +603,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Clear Document Edited if needed
|
// Clear Document Edited if needed
|
||||||
if (isMacintosh && this._win.isDocumentEdited()) {
|
if (this.isDocumentEdited()) {
|
||||||
if (!isReload || !this.backupMainService.isHotExitEnabled()) {
|
if (!isReload || !this.backupMainService.isHotExitEnabled()) {
|
||||||
this._win.setDocumentEdited(false);
|
this.setDocumentEdited(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ var CSSBuildLoaderPlugin;
|
|||||||
BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
|
BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
|
||||||
this._pendingLoads++;
|
this._pendingLoads++;
|
||||||
var head = document.head || document.getElementsByTagName('head')[0];
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script');
|
var other = head.getElementsByTagName('link') || head.getElementsByTagName('script');
|
||||||
if (other.length > 0) {
|
if (other.length > 0) {
|
||||||
head.insertBefore(linkNode, other[other.length - 1]);
|
head.insertBefore(linkNode, other[other.length - 1]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ var CSSLoaderPlugin;
|
|||||||
BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
|
BrowserCSSLoader.prototype._insertLinkNode = function (linkNode) {
|
||||||
this._pendingLoads++;
|
this._pendingLoads++;
|
||||||
var head = document.head || document.getElementsByTagName('head')[0];
|
var head = document.head || document.getElementsByTagName('head')[0];
|
||||||
var other = head.getElementsByTagName('link') || document.head.getElementsByTagName('script');
|
var other = head.getElementsByTagName('link') || head.getElementsByTagName('script');
|
||||||
if (other.length > 0) {
|
if (other.length > 0) {
|
||||||
head.insertBefore(linkNode, other[other.length - 1]);
|
head.insertBefore(linkNode, other[other.length - 1]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,34 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
import { IDimension } from 'vs/editor/common/editorCommon';
|
import { IDimension } from 'vs/editor/common/editorCommon';
|
||||||
import * as dom from 'vs/base/browser/dom';
|
|
||||||
|
interface ResizeObserver {
|
||||||
|
observe(target: Element): void;
|
||||||
|
unobserve(target: Element): void;
|
||||||
|
disconnect(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserverSize {
|
||||||
|
inlineSize: number;
|
||||||
|
blockSize: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ResizeObserverEntry {
|
||||||
|
readonly target: Element;
|
||||||
|
readonly contentRect: DOMRectReadOnly;
|
||||||
|
readonly borderBoxSize: ResizeObserverSize;
|
||||||
|
readonly contentBoxSize: ResizeObserverSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResizeObserverCallback = (entries: ReadonlyArray<ResizeObserverEntry>, observer: ResizeObserver) => void;
|
||||||
|
|
||||||
|
declare const ResizeObserver: {
|
||||||
|
prototype: ResizeObserver;
|
||||||
|
new(callback: ResizeObserverCallback): ResizeObserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export class ElementSizeObserver extends Disposable {
|
export class ElementSizeObserver extends Disposable {
|
||||||
|
|
||||||
@@ -13,8 +38,8 @@ export class ElementSizeObserver extends Disposable {
|
|||||||
private readonly changeCallback: () => void;
|
private readonly changeCallback: () => void;
|
||||||
private width: number;
|
private width: number;
|
||||||
private height: number;
|
private height: number;
|
||||||
private mutationObserver: MutationObserver | null;
|
private resizeObserver: ResizeObserver | null;
|
||||||
private windowSizeListener: IDisposable | null;
|
private measureReferenceDomElementToken: number;
|
||||||
|
|
||||||
constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) {
|
constructor(referenceDomElement: HTMLElement | null, dimension: IDimension | undefined, changeCallback: () => void) {
|
||||||
super();
|
super();
|
||||||
@@ -22,8 +47,8 @@ export class ElementSizeObserver extends Disposable {
|
|||||||
this.changeCallback = changeCallback;
|
this.changeCallback = changeCallback;
|
||||||
this.width = -1;
|
this.width = -1;
|
||||||
this.height = -1;
|
this.height = -1;
|
||||||
this.mutationObserver = null;
|
this.resizeObserver = null;
|
||||||
this.windowSizeListener = null;
|
this.measureReferenceDomElementToken = -1;
|
||||||
this.measureReferenceDomElement(false, dimension);
|
this.measureReferenceDomElement(false, dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,25 +66,33 @@ export class ElementSizeObserver extends Disposable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public startObserving(): void {
|
public startObserving(): void {
|
||||||
if (!this.mutationObserver && this.referenceDomElement) {
|
if (typeof ResizeObserver !== 'undefined') {
|
||||||
this.mutationObserver = new MutationObserver(() => this._onDidMutate());
|
if (!this.resizeObserver && this.referenceDomElement) {
|
||||||
this.mutationObserver.observe(this.referenceDomElement, {
|
this.resizeObserver = new ResizeObserver((entries) => {
|
||||||
attributes: true,
|
if (entries && entries[0] && entries[0].contentRect) {
|
||||||
});
|
this.observe({ width: entries[0].contentRect.width, height: entries[0].contentRect.height });
|
||||||
|
} else {
|
||||||
|
this.observe();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.resizeObserver.observe(this.referenceDomElement);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (this.measureReferenceDomElementToken === -1) {
|
||||||
|
// setInterval type defaults to NodeJS.Timeout instead of number, so specify it as a number
|
||||||
|
this.measureReferenceDomElementToken = <number><any>setInterval(() => this.observe(), 100);
|
||||||
}
|
}
|
||||||
if (!this.windowSizeListener) {
|
|
||||||
this.windowSizeListener = dom.addDisposableListener(window, 'resize', () => this._onDidResizeWindow());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public stopObserving(): void {
|
public stopObserving(): void {
|
||||||
if (this.mutationObserver) {
|
if (this.resizeObserver) {
|
||||||
this.mutationObserver.disconnect();
|
this.resizeObserver.disconnect();
|
||||||
this.mutationObserver = null;
|
this.resizeObserver = null;
|
||||||
}
|
}
|
||||||
if (this.windowSizeListener) {
|
if (this.measureReferenceDomElementToken !== -1) {
|
||||||
this.windowSizeListener.dispose();
|
clearInterval(this.measureReferenceDomElementToken);
|
||||||
this.windowSizeListener = null;
|
this.measureReferenceDomElementToken = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,14 +100,6 @@ export class ElementSizeObserver extends Disposable {
|
|||||||
this.measureReferenceDomElement(true, dimension);
|
this.measureReferenceDomElement(true, dimension);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _onDidMutate(): void {
|
|
||||||
this.measureReferenceDomElement(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onDidResizeWindow(): void {
|
|
||||||
this.measureReferenceDomElement(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void {
|
private measureReferenceDomElement(callChangeCallback: boolean, dimension?: IDimension): void {
|
||||||
let observedWidth = 0;
|
let observedWidth = 0;
|
||||||
let observedHeight = 0;
|
let observedHeight = 0;
|
||||||
|
|||||||
@@ -2722,10 +2722,6 @@ export interface ISuggestOptions {
|
|||||||
* Overwrite word ends on accept. Default to false.
|
* Overwrite word ends on accept. Default to false.
|
||||||
*/
|
*/
|
||||||
insertMode?: 'insert' | 'replace';
|
insertMode?: 'insert' | 'replace';
|
||||||
/**
|
|
||||||
* Show a highlight when suggestion replaces or keep text after the cursor. Defaults to false.
|
|
||||||
*/
|
|
||||||
insertHighlight?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* Enable graceful matching. Defaults to true.
|
* Enable graceful matching. Defaults to true.
|
||||||
*/
|
*/
|
||||||
@@ -2876,7 +2872,6 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||||||
constructor() {
|
constructor() {
|
||||||
const defaults: InternalSuggestOptions = {
|
const defaults: InternalSuggestOptions = {
|
||||||
insertMode: 'insert',
|
insertMode: 'insert',
|
||||||
insertHighlight: true,
|
|
||||||
filterGraceful: true,
|
filterGraceful: true,
|
||||||
snippetsPreventQuickSuggestions: true,
|
snippetsPreventQuickSuggestions: true,
|
||||||
localityBonus: false,
|
localityBonus: false,
|
||||||
@@ -2927,11 +2922,6 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||||||
default: defaults.insertMode,
|
default: defaults.insertMode,
|
||||||
description: nls.localize('suggest.insertMode', "Controls whether words are overwritten when accepting completions. Note that this depends on extensions opting into this feature.")
|
description: nls.localize('suggest.insertMode', "Controls whether words are overwritten when accepting completions. Note that this depends on extensions opting into this feature.")
|
||||||
},
|
},
|
||||||
'editor.suggest.insertHighlight': {
|
|
||||||
type: 'boolean',
|
|
||||||
default: defaults.insertHighlight,
|
|
||||||
description: nls.localize('suggest.insertHighlight', "Controls whether unexpected text modifications while accepting completions should be highlighted, e.g `insertMode` is `replace` but the completion only supports `insert`.")
|
|
||||||
},
|
|
||||||
'editor.suggest.filterGraceful': {
|
'editor.suggest.filterGraceful': {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: defaults.filterGraceful,
|
default: defaults.filterGraceful,
|
||||||
@@ -3124,7 +3114,6 @@ class EditorSuggest extends BaseEditorOption<EditorOption.suggest, InternalSugge
|
|||||||
const input = _input as ISuggestOptions;
|
const input = _input as ISuggestOptions;
|
||||||
return {
|
return {
|
||||||
insertMode: EditorStringEnumOption.stringSet(input.insertMode, this.defaultValue.insertMode, ['insert', 'replace']),
|
insertMode: EditorStringEnumOption.stringSet(input.insertMode, this.defaultValue.insertMode, ['insert', 'replace']),
|
||||||
insertHighlight: EditorBooleanOption.boolean(input.insertHighlight, this.defaultValue.insertHighlight),
|
|
||||||
filterGraceful: EditorBooleanOption.boolean(input.filterGraceful, this.defaultValue.filterGraceful),
|
filterGraceful: EditorBooleanOption.boolean(input.filterGraceful, this.defaultValue.filterGraceful),
|
||||||
snippetsPreventQuickSuggestions: EditorBooleanOption.boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
|
snippetsPreventQuickSuggestions: EditorBooleanOption.boolean(input.snippetsPreventQuickSuggestions, this.defaultValue.filterGraceful),
|
||||||
localityBonus: EditorBooleanOption.boolean(input.localityBonus, this.defaultValue.localityBonus),
|
localityBonus: EditorBooleanOption.boolean(input.localityBonus, this.defaultValue.localityBonus),
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ export class WordOperations {
|
|||||||
const left = lineContent.charCodeAt(column - 2);
|
const left = lineContent.charCodeAt(column - 2);
|
||||||
const right = lineContent.charCodeAt(column - 1);
|
const right = lineContent.charCodeAt(column - 1);
|
||||||
|
|
||||||
if (left !== CharCode.Underline && right === CharCode.Underline) {
|
if (left === CharCode.Underline && right !== CharCode.Underline) {
|
||||||
// snake_case_variables
|
// snake_case_variables
|
||||||
return new Position(lineNumber, column);
|
return new Position(lineNumber, column);
|
||||||
}
|
}
|
||||||
@@ -340,7 +340,7 @@ export class WordOperations {
|
|||||||
const left = lineContent.charCodeAt(column - 2);
|
const left = lineContent.charCodeAt(column - 2);
|
||||||
const right = lineContent.charCodeAt(column - 1);
|
const right = lineContent.charCodeAt(column - 1);
|
||||||
|
|
||||||
if (left === CharCode.Underline && right !== CharCode.Underline) {
|
if (left !== CharCode.Underline && right === CharCode.Underline) {
|
||||||
// snake_case_variables
|
// snake_case_variables
|
||||||
return new Position(lineNumber, column);
|
return new Position(lineNumber, column);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,18 @@ export interface IStringBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let _platformTextDecoder: TextDecoder | null;
|
let _platformTextDecoder: TextDecoder | null;
|
||||||
function getPlatformTextDecoder(): TextDecoder {
|
export function getPlatformTextDecoder(): TextDecoder {
|
||||||
if (!_platformTextDecoder) {
|
if (!_platformTextDecoder) {
|
||||||
_platformTextDecoder = new TextDecoder(platform.isLittleEndian() ? 'UTF-16LE' : 'UTF-16BE');
|
_platformTextDecoder = new TextDecoder(platform.isLittleEndian() ? 'UTF-16LE' : 'UTF-16BE');
|
||||||
}
|
}
|
||||||
return _platformTextDecoder;
|
return _platformTextDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const hasTextDecoder = (typeof TextDecoder !== 'undefined');
|
||||||
export let createStringBuilder: (capacity: number) => IStringBuilder;
|
export let createStringBuilder: (capacity: number) => IStringBuilder;
|
||||||
export let decodeUTF16LE: (source: Uint8Array, offset: number, len: number) => string;
|
export let decodeUTF16LE: (source: Uint8Array, offset: number, len: number) => string;
|
||||||
|
|
||||||
if (typeof TextDecoder !== 'undefined') {
|
if (hasTextDecoder) {
|
||||||
createStringBuilder = (capacity) => new StringBuilder(capacity);
|
createStringBuilder = (capacity) => new StringBuilder(capacity);
|
||||||
decodeUTF16LE = standardDecodeUTF16LE;
|
decodeUTF16LE = standardDecodeUTF16LE;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -827,7 +827,17 @@ export interface ITextModel {
|
|||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
setSemanticTokens(tokens: MultilineTokens2[] | null): void;
|
setSemanticTokens(tokens: MultilineTokens2[] | null, isComplete: boolean): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[] | null): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
hasSemanticTokens(): boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flush all tokenization state.
|
* Flush all tokenization state.
|
||||||
|
|||||||
@@ -10,10 +10,13 @@ import { EndOfLineSequence, ICursorStateComputer, IIdentifiedSingleEditOperation
|
|||||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||||
import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
|
import { IUndoRedoService, IResourceUndoRedoElement, UndoRedoElementType, IWorkspaceUndoRedoElement } from 'vs/platform/undoRedo/common/undoRedo';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources';
|
|
||||||
import { TextChange, compressConsecutiveTextChanges } from 'vs/editor/common/model/textChange';
|
import { TextChange, compressConsecutiveTextChanges } from 'vs/editor/common/model/textChange';
|
||||||
import * as buffer from 'vs/base/common/buffer';
|
import * as buffer from 'vs/base/common/buffer';
|
||||||
|
|
||||||
|
function uriGetComparisonKey(resource: URI): string {
|
||||||
|
return resource.toString();
|
||||||
|
}
|
||||||
|
|
||||||
class SingleModelEditStackData {
|
class SingleModelEditStackData {
|
||||||
|
|
||||||
public static create(model: ITextModel, beforeCursorState: Selection[] | null): SingleModelEditStackData {
|
public static create(model: ITextModel, beforeCursorState: Selection[] | null): SingleModelEditStackData {
|
||||||
|
|||||||
@@ -1793,8 +1793,8 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public setSemanticTokens(tokens: MultilineTokens2[] | null): void {
|
public setSemanticTokens(tokens: MultilineTokens2[] | null, isComplete: boolean): void {
|
||||||
this._tokens2.set(tokens);
|
this._tokens2.set(tokens, isComplete);
|
||||||
|
|
||||||
this._emitModelTokensChangedEvent({
|
this._emitModelTokensChangedEvent({
|
||||||
tokenizationSupportChanged: false,
|
tokenizationSupportChanged: false,
|
||||||
@@ -1803,6 +1803,23 @@ export class TextModel extends Disposable implements model.ITextModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hasSemanticTokens(): boolean {
|
||||||
|
return this._tokens2.isComplete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPartialSemanticTokens(range: Range, tokens: MultilineTokens2[]): void {
|
||||||
|
if (this.hasSemanticTokens()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const changedRange = this._tokens2.setPartial(range, tokens);
|
||||||
|
|
||||||
|
this._emitModelTokensChangedEvent({
|
||||||
|
tokenizationSupportChanged: false,
|
||||||
|
semanticTokensApplied: true,
|
||||||
|
ranges: [{ fromLineNumber: changedRange.startLineNumber, toLineNumber: changedRange.endLineNumber }]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public tokenizeViewport(startLineNumber: number, endLineNumber: number): void {
|
public tokenizeViewport(startLineNumber: number, endLineNumber: number): void {
|
||||||
startLineNumber = Math.max(1, startLineNumber);
|
startLineNumber = Math.max(1, startLineNumber);
|
||||||
endLineNumber = Math.min(this._buffer.getLineCount(), endLineNumber);
|
endLineNumber = Math.min(this._buffer.getLineCount(), endLineNumber);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import * as arrays from 'vs/base/common/arrays';
|
import * as arrays from 'vs/base/common/arrays';
|
||||||
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||||
import { Position } from 'vs/editor/common/core/position';
|
import { Position } from 'vs/editor/common/core/position';
|
||||||
import { IRange } from 'vs/editor/common/core/range';
|
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||||
import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
import { ColorId, FontStyle, LanguageId, MetadataConsts, StandardTokenType, TokenMetadata } from 'vs/editor/common/modes';
|
||||||
import { writeUInt32BE, readUInt32BE } from 'vs/base/common/buffer';
|
import { writeUInt32BE, readUInt32BE } from 'vs/base/common/buffer';
|
||||||
import { CharCode } from 'vs/base/common/charCode';
|
import { CharCode } from 'vs/base/common/charCode';
|
||||||
@@ -124,20 +124,7 @@ export class MultilineTokensBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IEncodedTokens {
|
export class SparseEncodedTokens {
|
||||||
getTokenCount(): number;
|
|
||||||
getDeltaLine(tokenIndex: number): number;
|
|
||||||
getMaxDeltaLine(): number;
|
|
||||||
getStartCharacter(tokenIndex: number): number;
|
|
||||||
getEndCharacter(tokenIndex: number): number;
|
|
||||||
getMetadata(tokenIndex: number): number;
|
|
||||||
|
|
||||||
clear(): void;
|
|
||||||
acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void;
|
|
||||||
acceptInsertText(deltaLine: number, character: number, eolCount: number, firstLineLength: number, lastLineLength: number, firstCharCode: number): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class SparseEncodedTokens implements IEncodedTokens {
|
|
||||||
/**
|
/**
|
||||||
* The encoding of tokens is:
|
* The encoding of tokens is:
|
||||||
* 4*i deltaLine (from `startLineNumber`)
|
* 4*i deltaLine (from `startLineNumber`)
|
||||||
@@ -145,7 +132,7 @@ export class SparseEncodedTokens implements IEncodedTokens {
|
|||||||
* 4*i+2 endCharacter (from the line start)
|
* 4*i+2 endCharacter (from the line start)
|
||||||
* 4*i+3 metadata
|
* 4*i+3 metadata
|
||||||
*/
|
*/
|
||||||
private _tokens: Uint32Array;
|
private readonly _tokens: Uint32Array;
|
||||||
private _tokenCount: number;
|
private _tokenCount: number;
|
||||||
|
|
||||||
constructor(tokens: Uint32Array) {
|
constructor(tokens: Uint32Array) {
|
||||||
@@ -153,38 +140,167 @@ export class SparseEncodedTokens implements IEncodedTokens {
|
|||||||
this._tokenCount = tokens.length / 4;
|
this._tokenCount = tokens.length / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public toString(startLineNumber: number): string {
|
||||||
|
let pieces: string[] = [];
|
||||||
|
for (let i = 0; i < this._tokenCount; i++) {
|
||||||
|
pieces.push(`(${this._getDeltaLine(i) + startLineNumber},${this._getStartCharacter(i)}-${this._getEndCharacter(i)})`);
|
||||||
|
}
|
||||||
|
return `[${pieces.join(',')}]`;
|
||||||
|
}
|
||||||
|
|
||||||
public getMaxDeltaLine(): number {
|
public getMaxDeltaLine(): number {
|
||||||
const tokenCount = this.getTokenCount();
|
const tokenCount = this._getTokenCount();
|
||||||
if (tokenCount === 0) {
|
if (tokenCount === 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return this.getDeltaLine(tokenCount - 1);
|
return this._getDeltaLine(tokenCount - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTokenCount(): number {
|
public getRange(): Range | null {
|
||||||
|
const tokenCount = this._getTokenCount();
|
||||||
|
if (tokenCount === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const startChar = this._getStartCharacter(0);
|
||||||
|
const maxDeltaLine = this._getDeltaLine(tokenCount - 1);
|
||||||
|
const endChar = this._getEndCharacter(tokenCount - 1);
|
||||||
|
return new Range(0, startChar + 1, maxDeltaLine, endChar + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getTokenCount(): number {
|
||||||
return this._tokenCount;
|
return this._tokenCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDeltaLine(tokenIndex: number): number {
|
private _getDeltaLine(tokenIndex: number): number {
|
||||||
return this._tokens[4 * tokenIndex];
|
return this._tokens[4 * tokenIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStartCharacter(tokenIndex: number): number {
|
private _getStartCharacter(tokenIndex: number): number {
|
||||||
return this._tokens[4 * tokenIndex + 1];
|
return this._tokens[4 * tokenIndex + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEndCharacter(tokenIndex: number): number {
|
private _getEndCharacter(tokenIndex: number): number {
|
||||||
return this._tokens[4 * tokenIndex + 2];
|
return this._tokens[4 * tokenIndex + 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMetadata(tokenIndex: number): number {
|
public isEmpty(): boolean {
|
||||||
return this._tokens[4 * tokenIndex + 3];
|
return (this._getTokenCount() === 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getLineTokens(deltaLine: number): LineTokens2 | null {
|
||||||
|
let low = 0;
|
||||||
|
let high = this._getTokenCount() - 1;
|
||||||
|
|
||||||
|
while (low < high) {
|
||||||
|
const mid = low + Math.floor((high - low) / 2);
|
||||||
|
const midDeltaLine = this._getDeltaLine(mid);
|
||||||
|
|
||||||
|
if (midDeltaLine < deltaLine) {
|
||||||
|
low = mid + 1;
|
||||||
|
} else if (midDeltaLine > deltaLine) {
|
||||||
|
high = mid - 1;
|
||||||
|
} else {
|
||||||
|
let min = mid;
|
||||||
|
while (min > low && this._getDeltaLine(min - 1) === deltaLine) {
|
||||||
|
min--;
|
||||||
|
}
|
||||||
|
let max = mid;
|
||||||
|
while (max < high && this._getDeltaLine(max + 1) === deltaLine) {
|
||||||
|
max++;
|
||||||
|
}
|
||||||
|
return new LineTokens2(this._tokens.subarray(4 * min, 4 * max + 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._getDeltaLine(low) === deltaLine) {
|
||||||
|
return new LineTokens2(this._tokens.subarray(4 * low, 4 * low + 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public clear(): void {
|
public clear(): void {
|
||||||
this._tokenCount = 0;
|
this._tokenCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public removeTokens(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): number {
|
||||||
|
const tokens = this._tokens;
|
||||||
|
const tokenCount = this._tokenCount;
|
||||||
|
let newTokenCount = 0;
|
||||||
|
let hasDeletedTokens = false;
|
||||||
|
let firstDeltaLine = 0;
|
||||||
|
for (let i = 0; i < tokenCount; i++) {
|
||||||
|
const srcOffset = 4 * i;
|
||||||
|
const tokenDeltaLine = tokens[srcOffset];
|
||||||
|
const tokenStartCharacter = tokens[srcOffset + 1];
|
||||||
|
const tokenEndCharacter = tokens[srcOffset + 2];
|
||||||
|
const tokenMetadata = tokens[srcOffset + 3];
|
||||||
|
|
||||||
|
if (
|
||||||
|
(tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar))
|
||||||
|
&& (tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar))
|
||||||
|
) {
|
||||||
|
hasDeletedTokens = true;
|
||||||
|
} else {
|
||||||
|
if (newTokenCount === 0) {
|
||||||
|
firstDeltaLine = tokenDeltaLine;
|
||||||
|
}
|
||||||
|
if (hasDeletedTokens) {
|
||||||
|
// must move the token to the left
|
||||||
|
const destOffset = 4 * newTokenCount;
|
||||||
|
tokens[destOffset] = tokenDeltaLine - firstDeltaLine;
|
||||||
|
tokens[destOffset + 1] = tokenStartCharacter;
|
||||||
|
tokens[destOffset + 2] = tokenEndCharacter;
|
||||||
|
tokens[destOffset + 3] = tokenMetadata;
|
||||||
|
}
|
||||||
|
newTokenCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tokenCount = newTokenCount;
|
||||||
|
|
||||||
|
return firstDeltaLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
public split(startDeltaLine: number, startChar: number, endDeltaLine: number, endChar: number): [SparseEncodedTokens, SparseEncodedTokens, number] {
|
||||||
|
const tokens = this._tokens;
|
||||||
|
const tokenCount = this._tokenCount;
|
||||||
|
let aTokens: number[] = [];
|
||||||
|
let bTokens: number[] = [];
|
||||||
|
let destTokens: number[] = aTokens;
|
||||||
|
let destOffset = 0;
|
||||||
|
let destFirstDeltaLine: number = 0;
|
||||||
|
for (let i = 0; i < tokenCount; i++) {
|
||||||
|
const srcOffset = 4 * i;
|
||||||
|
const tokenDeltaLine = tokens[srcOffset];
|
||||||
|
const tokenStartCharacter = tokens[srcOffset + 1];
|
||||||
|
const tokenEndCharacter = tokens[srcOffset + 2];
|
||||||
|
const tokenMetadata = tokens[srcOffset + 3];
|
||||||
|
|
||||||
|
if ((tokenDeltaLine > startDeltaLine || (tokenDeltaLine === startDeltaLine && tokenEndCharacter >= startChar))) {
|
||||||
|
if ((tokenDeltaLine < endDeltaLine || (tokenDeltaLine === endDeltaLine && tokenStartCharacter <= endChar))) {
|
||||||
|
// this token is touching the range
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// this token is after the range
|
||||||
|
if (destTokens !== bTokens) {
|
||||||
|
// this token is the first token after the range
|
||||||
|
destTokens = bTokens;
|
||||||
|
destOffset = 0;
|
||||||
|
destFirstDeltaLine = tokenDeltaLine;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destTokens[destOffset++] = tokenDeltaLine - destFirstDeltaLine;
|
||||||
|
destTokens[destOffset++] = tokenStartCharacter;
|
||||||
|
destTokens[destOffset++] = tokenEndCharacter;
|
||||||
|
destTokens[destOffset++] = tokenMetadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [new SparseEncodedTokens(new Uint32Array(aTokens)), new SparseEncodedTokens(new Uint32Array(bTokens)), destFirstDeltaLine];
|
||||||
|
}
|
||||||
|
|
||||||
public acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void {
|
public acceptDeleteRange(horizontalShiftForFirstLineTokens: number, startDeltaLine: number, startCharacter: number, endDeltaLine: number, endCharacter: number): void {
|
||||||
// This is a bit complex, here are the cases I used to think about this:
|
// This is a bit complex, here are the cases I used to think about this:
|
||||||
//
|
//
|
||||||
@@ -414,30 +530,26 @@ export class SparseEncodedTokens implements IEncodedTokens {
|
|||||||
|
|
||||||
export class LineTokens2 {
|
export class LineTokens2 {
|
||||||
|
|
||||||
private readonly _actual: IEncodedTokens;
|
private readonly _tokens: Uint32Array;
|
||||||
private readonly _startTokenIndex: number;
|
|
||||||
private readonly _endTokenIndex: number;
|
|
||||||
|
|
||||||
constructor(actual: IEncodedTokens, startTokenIndex: number, endTokenIndex: number) {
|
constructor(tokens: Uint32Array) {
|
||||||
this._actual = actual;
|
this._tokens = tokens;
|
||||||
this._startTokenIndex = startTokenIndex;
|
|
||||||
this._endTokenIndex = endTokenIndex;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getCount(): number {
|
public getCount(): number {
|
||||||
return this._endTokenIndex - this._startTokenIndex + 1;
|
return this._tokens.length / 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
public getStartCharacter(tokenIndex: number): number {
|
public getStartCharacter(tokenIndex: number): number {
|
||||||
return this._actual.getStartCharacter(this._startTokenIndex + tokenIndex);
|
return this._tokens[4 * tokenIndex + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getEndCharacter(tokenIndex: number): number {
|
public getEndCharacter(tokenIndex: number): number {
|
||||||
return this._actual.getEndCharacter(this._startTokenIndex + tokenIndex);
|
return this._tokens[4 * tokenIndex + 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
public getMetadata(tokenIndex: number): number {
|
public getMetadata(tokenIndex: number): number {
|
||||||
return this._actual.getMetadata(this._startTokenIndex + tokenIndex);
|
return this._tokens[4 * tokenIndex + 3];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -445,59 +557,58 @@ export class MultilineTokens2 {
|
|||||||
|
|
||||||
public startLineNumber: number;
|
public startLineNumber: number;
|
||||||
public endLineNumber: number;
|
public endLineNumber: number;
|
||||||
public tokens: IEncodedTokens;
|
public tokens: SparseEncodedTokens;
|
||||||
|
|
||||||
constructor(startLineNumber: number, tokens: IEncodedTokens) {
|
constructor(startLineNumber: number, tokens: SparseEncodedTokens) {
|
||||||
this.startLineNumber = startLineNumber;
|
this.startLineNumber = startLineNumber;
|
||||||
this.tokens = tokens;
|
this.tokens = tokens;
|
||||||
this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine();
|
this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public toString(): string {
|
||||||
|
return this.tokens.toString(this.startLineNumber);
|
||||||
|
}
|
||||||
|
|
||||||
private _updateEndLineNumber(): void {
|
private _updateEndLineNumber(): void {
|
||||||
this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine();
|
this.endLineNumber = this.startLineNumber + this.tokens.getMaxDeltaLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public isEmpty(): boolean {
|
||||||
|
return this.tokens.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public getLineTokens(lineNumber: number): LineTokens2 | null {
|
public getLineTokens(lineNumber: number): LineTokens2 | null {
|
||||||
if (this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber) {
|
if (this.startLineNumber <= lineNumber && lineNumber <= this.endLineNumber) {
|
||||||
const findResult = MultilineTokens2._findTokensWithLine(this.tokens, lineNumber - this.startLineNumber);
|
return this.tokens.getLineTokens(lineNumber - this.startLineNumber);
|
||||||
if (findResult) {
|
|
||||||
const [startTokenIndex, endTokenIndex] = findResult;
|
|
||||||
return new LineTokens2(this.tokens, startTokenIndex, endTokenIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _findTokensWithLine(tokens: IEncodedTokens, deltaLine: number): [number, number] | null {
|
public getRange(): Range | null {
|
||||||
let low = 0;
|
const deltaRange = this.tokens.getRange();
|
||||||
let high = tokens.getTokenCount() - 1;
|
if (!deltaRange) {
|
||||||
|
return deltaRange;
|
||||||
while (low < high) {
|
|
||||||
const mid = low + Math.floor((high - low) / 2);
|
|
||||||
const midDeltaLine = tokens.getDeltaLine(mid);
|
|
||||||
|
|
||||||
if (midDeltaLine < deltaLine) {
|
|
||||||
low = mid + 1;
|
|
||||||
} else if (midDeltaLine > deltaLine) {
|
|
||||||
high = mid - 1;
|
|
||||||
} else {
|
|
||||||
let min = mid;
|
|
||||||
while (min > low && tokens.getDeltaLine(min - 1) === deltaLine) {
|
|
||||||
min--;
|
|
||||||
}
|
|
||||||
let max = mid;
|
|
||||||
while (max < high && tokens.getDeltaLine(max + 1) === deltaLine) {
|
|
||||||
max++;
|
|
||||||
}
|
|
||||||
return [min, max];
|
|
||||||
}
|
}
|
||||||
|
return new Range(this.startLineNumber + deltaRange.startLineNumber, deltaRange.startColumn, this.startLineNumber + deltaRange.endLineNumber, deltaRange.endColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tokens.getDeltaLine(low) === deltaLine) {
|
public removeTokens(range: Range): void {
|
||||||
return [low, low];
|
const startLineIndex = range.startLineNumber - this.startLineNumber;
|
||||||
|
const endLineIndex = range.endLineNumber - this.startLineNumber;
|
||||||
|
|
||||||
|
this.startLineNumber += this.tokens.removeTokens(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1);
|
||||||
|
this._updateEndLineNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
public split(range: Range): [MultilineTokens2, MultilineTokens2] {
|
||||||
|
// split tokens to two:
|
||||||
|
// a) all the tokens before `range`
|
||||||
|
// b) all the tokens after `range`
|
||||||
|
const startLineIndex = range.startLineNumber - this.startLineNumber;
|
||||||
|
const endLineIndex = range.endLineNumber - this.startLineNumber;
|
||||||
|
|
||||||
|
const [a, b, bDeltaLine] = this.tokens.split(startLineIndex, range.startColumn - 1, endLineIndex, range.endColumn - 1);
|
||||||
|
return [new MultilineTokens2(this.startLineNumber, a), new MultilineTokens2(this.startLineNumber + bDeltaLine, b)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyEdit(range: IRange, text: string): void {
|
public applyEdit(range: IRange, text: string): void {
|
||||||
@@ -761,17 +872,91 @@ function toUint32Array(arr: Uint32Array | ArrayBuffer): Uint32Array {
|
|||||||
export class TokensStore2 {
|
export class TokensStore2 {
|
||||||
|
|
||||||
private _pieces: MultilineTokens2[];
|
private _pieces: MultilineTokens2[];
|
||||||
|
private _isComplete: boolean;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._pieces = [];
|
this._pieces = [];
|
||||||
|
this._isComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public flush(): void {
|
public flush(): void {
|
||||||
this._pieces = [];
|
this._pieces = [];
|
||||||
|
this._isComplete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public set(pieces: MultilineTokens2[] | null) {
|
public set(pieces: MultilineTokens2[] | null, isComplete: boolean): void {
|
||||||
this._pieces = pieces || [];
|
this._pieces = pieces || [];
|
||||||
|
this._isComplete = isComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setPartial(_range: Range, pieces: MultilineTokens2[]): Range {
|
||||||
|
if (pieces.length === 0) {
|
||||||
|
return _range;
|
||||||
|
}
|
||||||
|
const _firstRange = pieces[0].getRange();
|
||||||
|
const _lastRange = pieces[pieces.length - 1].getRange();
|
||||||
|
if (!_firstRange || !_lastRange) {
|
||||||
|
return _range;
|
||||||
|
}
|
||||||
|
const range = _range.plusRange(_firstRange).plusRange(_lastRange);
|
||||||
|
let insertPosition: { index: number; } | null = null;
|
||||||
|
for (let i = 0, len = this._pieces.length; i < len; i++) {
|
||||||
|
const piece = this._pieces[i];
|
||||||
|
if (piece.endLineNumber < range.startLineNumber) {
|
||||||
|
// this piece is before the range
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (piece.startLineNumber > range.endLineNumber) {
|
||||||
|
// this piece is after the range, so mark the spot before this piece
|
||||||
|
// as a good insertion position and stop looping
|
||||||
|
insertPosition = insertPosition || { index: i };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this piece might intersect with the range
|
||||||
|
piece.removeTokens(range);
|
||||||
|
|
||||||
|
if (piece.isEmpty()) {
|
||||||
|
// remove the piece if it became empty
|
||||||
|
this._pieces.splice(i, 1);
|
||||||
|
i--;
|
||||||
|
len--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (piece.endLineNumber < range.startLineNumber) {
|
||||||
|
// after removal, this piece is before the range
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (piece.startLineNumber > range.endLineNumber) {
|
||||||
|
// after removal, this piece is after the range
|
||||||
|
insertPosition = insertPosition || { index: i };
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// after removal, this piece contains the range
|
||||||
|
const [a, b] = piece.split(range);
|
||||||
|
this._pieces.splice(i, 1, a, b);
|
||||||
|
i++;
|
||||||
|
len++;
|
||||||
|
|
||||||
|
insertPosition = insertPosition || { index: i };
|
||||||
|
}
|
||||||
|
|
||||||
|
insertPosition = insertPosition || { index: this._pieces.length };
|
||||||
|
|
||||||
|
this._pieces = arrays.arrayInsert(this._pieces, insertPosition.index, pieces);
|
||||||
|
|
||||||
|
// console.log(`I HAVE ${this._pieces.length} pieces`);
|
||||||
|
// console.log(`${this._pieces.map(p => p.toString()).join(', ')}`);
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public isComplete(): boolean {
|
||||||
|
return this._isComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
public addSemanticTokens(lineNumber: number, aTokens: LineTokens): LineTokens {
|
public addSemanticTokens(lineNumber: number, aTokens: LineTokens): LineTokens {
|
||||||
@@ -782,7 +967,7 @@ export class TokensStore2 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const pieceIndex = TokensStore2._findFirstPieceWithLine(pieces, lineNumber);
|
const pieceIndex = TokensStore2._findFirstPieceWithLine(pieces, lineNumber);
|
||||||
const bTokens = this._pieces[pieceIndex].getLineTokens(lineNumber);
|
const bTokens = pieces[pieceIndex].getLineTokens(lineNumber);
|
||||||
|
|
||||||
if (!bTokens) {
|
if (!bTokens) {
|
||||||
return aTokens;
|
return aTokens;
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as strings from 'vs/base/common/strings';
|
import * as strings from 'vs/base/common/strings';
|
||||||
|
import * as stringBuilder from 'vs/editor/common/core/stringBuilder';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
import { LanguageIdentifier } from 'vs/editor/common/modes';
|
||||||
import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration';
|
import { CharacterPair } from 'vs/editor/common/modes/languageConfiguration';
|
||||||
@@ -264,14 +265,24 @@ function createBracketOrRegExp(pieces: string[]): RegExp {
|
|||||||
return strings.createRegExp(regexStr, true);
|
return strings.createRegExp(regexStr, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let toReversedString = (function () {
|
const toReversedString = (function () {
|
||||||
|
|
||||||
function reverse(str: string): string {
|
function reverse(str: string): string {
|
||||||
let reversedStr = '';
|
if (stringBuilder.hasTextDecoder) {
|
||||||
|
// create a Uint16Array and then use a TextDecoder to create a string
|
||||||
|
const arr = new Uint16Array(str.length);
|
||||||
|
let offset = 0;
|
||||||
for (let i = str.length - 1; i >= 0; i--) {
|
for (let i = str.length - 1; i >= 0; i--) {
|
||||||
reversedStr += str.charAt(i);
|
arr[offset++] = str.charCodeAt(i);
|
||||||
|
}
|
||||||
|
return stringBuilder.getPlatformTextDecoder().decode(arr);
|
||||||
|
} else {
|
||||||
|
let result: string[] = [], resultLen = 0;
|
||||||
|
for (let i = str.length - 1; i >= 0; i--) {
|
||||||
|
result[resultLen++] = str.charAt(i);
|
||||||
|
}
|
||||||
|
return result.join('');
|
||||||
}
|
}
|
||||||
return reversedStr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let lastInput: string | null = null;
|
let lastInput: string | null = null;
|
||||||
|
|||||||
@@ -8,9 +8,13 @@ import { URI } from 'vs/base/common/uri';
|
|||||||
import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
|
import { ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
|
||||||
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
|
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { DocumentSemanticTokensProvider, DocumentRangeSemanticTokensProvider } from 'vs/editor/common/modes';
|
||||||
|
import { SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
|
||||||
|
|
||||||
export const IModelService = createDecorator<IModelService>('modelService');
|
export const IModelService = createDecorator<IModelService>('modelService');
|
||||||
|
|
||||||
|
export type DocumentTokensProvider = DocumentSemanticTokensProvider | DocumentRangeSemanticTokensProvider;
|
||||||
|
|
||||||
export interface IModelService {
|
export interface IModelService {
|
||||||
_serviceBrand: undefined;
|
_serviceBrand: undefined;
|
||||||
|
|
||||||
@@ -28,6 +32,8 @@ export interface IModelService {
|
|||||||
|
|
||||||
getModel(resource: URI): ITextModel | null;
|
getModel(resource: URI): ITextModel | null;
|
||||||
|
|
||||||
|
getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling;
|
||||||
|
|
||||||
onModelAdded: Event<ITextModel>;
|
onModelAdded: Event<ITextModel>;
|
||||||
|
|
||||||
onModelRemoved: Event<ITextModel>;
|
onModelRemoved: Event<ITextModel>;
|
||||||
|
|||||||
@@ -12,26 +12,26 @@ import { URI } from 'vs/base/common/uri';
|
|||||||
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
import { EDITOR_MODEL_DEFAULTS } from 'vs/editor/common/config/editorOptions';
|
||||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions, IValidEditOperation } from 'vs/editor/common/model';
|
import { DefaultEndOfLine, EndOfLinePreference, EndOfLineSequence, IIdentifiedSingleEditOperation, ITextBuffer, ITextBufferFactory, ITextModel, ITextModelCreationOptions } from 'vs/editor/common/model';
|
||||||
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
|
import { TextModel, createTextBuffer } from 'vs/editor/common/model/textModel';
|
||||||
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
import { IModelLanguageChangedEvent, IModelContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
|
||||||
import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokensLegend, SemanticTokens, SemanticTokensEdits, TokenMetadata, FontStyle, MetadataConsts } from 'vs/editor/common/modes';
|
import { LanguageIdentifier, DocumentSemanticTokensProviderRegistry, DocumentSemanticTokensProvider, SemanticTokens, SemanticTokensEdits } from 'vs/editor/common/modes';
|
||||||
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
|
import { PLAINTEXT_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/modesRegistry';
|
||||||
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
|
import { ILanguageSelection } from 'vs/editor/common/services/modeService';
|
||||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
import { IModelService, DocumentTokensProvider } from 'vs/editor/common/services/modelService';
|
||||||
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||||
import { SparseEncodedTokens, MultilineTokens2 } from 'vs/editor/common/model/tokensStore';
|
|
||||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { IUndoRedoService, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
|
import { IUndoRedoService, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
|
||||||
import { StringSHA1 } from 'vs/base/common/hash';
|
import { StringSHA1 } from 'vs/base/common/hash';
|
||||||
import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement } from 'vs/editor/common/model/editStack';
|
import { SingleModelEditStackElement, MultiModelEditStackElement, EditStackElement } from 'vs/editor/common/model/editStack';
|
||||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
|
import { SemanticTokensProviderStyling, toMultilineTokens2 } from 'vs/editor/common/services/semanticTokensProviderStyling';
|
||||||
|
|
||||||
export const MAINTAIN_UNDO_REDO_STACK = true;
|
export const MAINTAIN_UNDO_REDO_STACK = true;
|
||||||
|
|
||||||
@@ -171,6 +171,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
|||||||
*/
|
*/
|
||||||
private readonly _models: { [modelId: string]: ModelData; };
|
private readonly _models: { [modelId: string]: ModelData; };
|
||||||
private readonly _disposedModels: Map<string, DisposedModelInfo>;
|
private readonly _disposedModels: Map<string, DisposedModelInfo>;
|
||||||
|
private readonly _semanticStyling: SemanticStyling;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
@IConfigurationService private readonly _configurationService: IConfigurationService,
|
||||||
@@ -184,11 +185,12 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
|||||||
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
|
this._modelCreationOptionsByLanguageAndResource = Object.create(null);
|
||||||
this._models = {};
|
this._models = {};
|
||||||
this._disposedModels = new Map<string, DisposedModelInfo>();
|
this._disposedModels = new Map<string, DisposedModelInfo>();
|
||||||
|
this._semanticStyling = this._register(new SemanticStyling(this._themeService, this._logService));
|
||||||
|
|
||||||
this._register(this._configurationService.onDidChangeConfiguration(e => this._updateModelOptions()));
|
this._register(this._configurationService.onDidChangeConfiguration(() => this._updateModelOptions()));
|
||||||
this._updateModelOptions();
|
this._updateModelOptions();
|
||||||
|
|
||||||
this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._logService));
|
this._register(new SemanticColoringFeature(this, this._themeService, this._configurationService, this._semanticStyling));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
|
private static _readModelOptions(config: IRawConfig, isForSimpleWidget: boolean): ITextModelCreationOptions {
|
||||||
@@ -380,7 +382,7 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
|||||||
model.pushEditOperations(
|
model.pushEditOperations(
|
||||||
[],
|
[],
|
||||||
ModelServiceImpl._computeEdits(model, textBuffer),
|
ModelServiceImpl._computeEdits(model, textBuffer),
|
||||||
(inverseEditOperations: IValidEditOperation[]) => []
|
() => []
|
||||||
);
|
);
|
||||||
model.pushStackElement();
|
model.pushStackElement();
|
||||||
}
|
}
|
||||||
@@ -542,6 +544,10 @@ export class ModelServiceImpl extends Disposable implements IModelService {
|
|||||||
return modelData.model;
|
return modelData.model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getSemanticTokensProviderStyling(provider: DocumentTokensProvider): SemanticTokensProviderStyling {
|
||||||
|
return this._semanticStyling.get(provider);
|
||||||
|
}
|
||||||
|
|
||||||
// --- end IModelService
|
// --- end IModelService
|
||||||
|
|
||||||
private _onWillDispose(model: ITextModel): void {
|
private _onWillDispose(model: ITextModel): void {
|
||||||
@@ -575,13 +581,13 @@ class SemanticColoringFeature extends Disposable {
|
|||||||
|
|
||||||
private static readonly SETTING_ID = 'editor.semanticHighlighting';
|
private static readonly SETTING_ID = 'editor.semanticHighlighting';
|
||||||
|
|
||||||
private _watchers: Record<string, ModelSemanticColoring>;
|
private readonly _watchers: Record<string, ModelSemanticColoring>;
|
||||||
private _semanticStyling: SemanticStyling;
|
private readonly _semanticStyling: SemanticStyling;
|
||||||
|
|
||||||
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, logService: ILogService) {
|
constructor(modelService: IModelService, themeService: IThemeService, configurationService: IConfigurationService, semanticStyling: SemanticStyling) {
|
||||||
super();
|
super();
|
||||||
this._watchers = Object.create(null);
|
this._watchers = Object.create(null);
|
||||||
this._semanticStyling = this._register(new SemanticStyling(themeService, logService));
|
this._semanticStyling = semanticStyling;
|
||||||
|
|
||||||
const isSemanticColoringEnabled = (model: ITextModel) => {
|
const isSemanticColoringEnabled = (model: ITextModel) => {
|
||||||
if (!themeService.getColorTheme().semanticHighlighting) {
|
if (!themeService.getColorTheme().semanticHighlighting) {
|
||||||
@@ -633,204 +639,27 @@ class SemanticColoringFeature extends Disposable {
|
|||||||
|
|
||||||
class SemanticStyling extends Disposable {
|
class SemanticStyling extends Disposable {
|
||||||
|
|
||||||
private _caches: WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>;
|
private _caches: WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _themeService: IThemeService,
|
private readonly _themeService: IThemeService,
|
||||||
private readonly _logService: ILogService
|
private readonly _logService: ILogService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
|
||||||
this._register(this._themeService.onDidColorThemeChange(() => {
|
this._register(this._themeService.onDidColorThemeChange(() => {
|
||||||
this._caches = new WeakMap<DocumentSemanticTokensProvider, SemanticColoringProviderStyling>();
|
this._caches = new WeakMap<DocumentTokensProvider, SemanticTokensProviderStyling>();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
public get(provider: DocumentSemanticTokensProvider): SemanticColoringProviderStyling {
|
public get(provider: DocumentTokensProvider): SemanticTokensProviderStyling {
|
||||||
if (!this._caches.has(provider)) {
|
if (!this._caches.has(provider)) {
|
||||||
this._caches.set(provider, new SemanticColoringProviderStyling(provider.getLegend(), this._themeService, this._logService));
|
this._caches.set(provider, new SemanticTokensProviderStyling(provider.getLegend(), this._themeService, this._logService));
|
||||||
}
|
}
|
||||||
return this._caches.get(provider)!;
|
return this._caches.get(provider)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const enum Constants {
|
|
||||||
NO_STYLING = 0b01111111111111111111111111111111
|
|
||||||
}
|
|
||||||
|
|
||||||
class HashTableEntry {
|
|
||||||
public readonly tokenTypeIndex: number;
|
|
||||||
public readonly tokenModifierSet: number;
|
|
||||||
public readonly languageId: number;
|
|
||||||
public readonly metadata: number;
|
|
||||||
public next: HashTableEntry | null;
|
|
||||||
|
|
||||||
constructor(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number) {
|
|
||||||
this.tokenTypeIndex = tokenTypeIndex;
|
|
||||||
this.tokenModifierSet = tokenModifierSet;
|
|
||||||
this.languageId = languageId;
|
|
||||||
this.metadata = metadata;
|
|
||||||
this.next = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class HashTable {
|
|
||||||
|
|
||||||
private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143];
|
|
||||||
|
|
||||||
private _elementsCount: number;
|
|
||||||
private _currentLengthIndex: number;
|
|
||||||
private _currentLength: number;
|
|
||||||
private _growCount: number;
|
|
||||||
private _elements: (HashTableEntry | null)[];
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this._elementsCount = 0;
|
|
||||||
this._currentLengthIndex = 0;
|
|
||||||
this._currentLength = HashTable._SIZES[this._currentLengthIndex];
|
|
||||||
this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
|
|
||||||
this._elements = [];
|
|
||||||
HashTable._nullOutEntries(this._elements, this._currentLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void {
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
entries[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): number {
|
|
||||||
const hash = (n1: number, n2: number) => (((n1 << 5) - n1) + n2) | 0; // n1 * 31 + n2, keep as int32
|
|
||||||
return hash(hash(tokenTypeIndex, tokenModifierSet), languageId) % this._currentLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
public get(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): HashTableEntry | null {
|
|
||||||
const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet, languageId);
|
|
||||||
|
|
||||||
let p = this._elements[hash];
|
|
||||||
while (p) {
|
|
||||||
if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet && p.languageId === languageId) {
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
p = p.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public add(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number): void {
|
|
||||||
this._elementsCount++;
|
|
||||||
if (this._growCount !== 0 && this._elementsCount >= this._growCount) {
|
|
||||||
// expand!
|
|
||||||
const oldElements = this._elements;
|
|
||||||
|
|
||||||
this._currentLengthIndex++;
|
|
||||||
this._currentLength = HashTable._SIZES[this._currentLengthIndex];
|
|
||||||
this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
|
|
||||||
this._elements = [];
|
|
||||||
HashTable._nullOutEntries(this._elements, this._currentLength);
|
|
||||||
|
|
||||||
for (const first of oldElements) {
|
|
||||||
let p = first;
|
|
||||||
while (p) {
|
|
||||||
const oldNext = p.next;
|
|
||||||
p.next = null;
|
|
||||||
this._add(p);
|
|
||||||
p = oldNext;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, languageId, metadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
private _add(element: HashTableEntry): void {
|
|
||||||
const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet, element.languageId);
|
|
||||||
element.next = this._elements[hash];
|
|
||||||
this._elements[hash] = element;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SemanticColoringProviderStyling {
|
|
||||||
|
|
||||||
private readonly _hashTable: HashTable;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly _legend: SemanticTokensLegend,
|
|
||||||
private readonly _themeService: IThemeService,
|
|
||||||
private readonly _logService: ILogService
|
|
||||||
) {
|
|
||||||
this._hashTable = new HashTable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number {
|
|
||||||
const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet, languageId.id);
|
|
||||||
let metadata: number;
|
|
||||||
if (entry) {
|
|
||||||
metadata = entry.metadata;
|
|
||||||
} else {
|
|
||||||
const tokenType = this._legend.tokenTypes[tokenTypeIndex];
|
|
||||||
const tokenModifiers: string[] = [];
|
|
||||||
let modifierSet = tokenModifierSet;
|
|
||||||
for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) {
|
|
||||||
if (modifierSet & 1) {
|
|
||||||
tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]);
|
|
||||||
}
|
|
||||||
modifierSet = modifierSet >> 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers, languageId.language);
|
|
||||||
if (typeof tokenStyle === 'undefined') {
|
|
||||||
metadata = Constants.NO_STYLING;
|
|
||||||
} else {
|
|
||||||
metadata = 0;
|
|
||||||
if (typeof tokenStyle.italic !== 'undefined') {
|
|
||||||
const italicBit = (tokenStyle.italic ? FontStyle.Italic : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
|
||||||
metadata |= italicBit | MetadataConsts.SEMANTIC_USE_ITALIC;
|
|
||||||
}
|
|
||||||
if (typeof tokenStyle.bold !== 'undefined') {
|
|
||||||
const boldBit = (tokenStyle.bold ? FontStyle.Bold : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
|
||||||
metadata |= boldBit | MetadataConsts.SEMANTIC_USE_BOLD;
|
|
||||||
}
|
|
||||||
if (typeof tokenStyle.underline !== 'undefined') {
|
|
||||||
const underlineBit = (tokenStyle.underline ? FontStyle.Underline : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
|
||||||
metadata |= underlineBit | MetadataConsts.SEMANTIC_USE_UNDERLINE;
|
|
||||||
}
|
|
||||||
if (tokenStyle.foreground) {
|
|
||||||
const foregroundBits = (tokenStyle.foreground) << MetadataConsts.FOREGROUND_OFFSET;
|
|
||||||
metadata |= foregroundBits | MetadataConsts.SEMANTIC_USE_FOREGROUND;
|
|
||||||
}
|
|
||||||
if (metadata === 0) {
|
|
||||||
// Nothing!
|
|
||||||
metadata = Constants.NO_STYLING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._hashTable.add(tokenTypeIndex, tokenModifierSet, languageId.id, metadata);
|
|
||||||
}
|
|
||||||
if (this._logService.getLevel() === LogLevel.Trace) {
|
|
||||||
const type = this._legend.tokenTypes[tokenTypeIndex];
|
|
||||||
const modifiers = tokenModifierSet ? ' ' + this._legend.tokenModifiers.filter((_, i) => tokenModifierSet & (1 << i)).join(' ') : '';
|
|
||||||
this._logService.trace(`tokenStyleMetadata ${entry ? '[CACHED] ' : ''}${type}${modifiers}: foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`);
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const enum SemanticColoringConstants {
|
|
||||||
/**
|
|
||||||
* Let's aim at having 8KB buffers if possible...
|
|
||||||
* So that would be 8192 / (5 * 4) = 409.6 tokens per area
|
|
||||||
*/
|
|
||||||
DesiredTokensPerArea = 400,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Try to keep the total number of areas under 1024 if possible,
|
|
||||||
* simply compensate by having more tokens per area...
|
|
||||||
*/
|
|
||||||
DesiredMaxAreas = 1024,
|
|
||||||
}
|
|
||||||
|
|
||||||
class SemanticTokensResponse {
|
class SemanticTokensResponse {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _provider: DocumentSemanticTokensProvider,
|
private readonly _provider: DocumentSemanticTokensProvider,
|
||||||
@@ -848,10 +677,10 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
private _isDisposed: boolean;
|
private _isDisposed: boolean;
|
||||||
private readonly _model: ITextModel;
|
private readonly _model: ITextModel;
|
||||||
private readonly _semanticStyling: SemanticStyling;
|
private readonly _semanticStyling: SemanticStyling;
|
||||||
private readonly _fetchSemanticTokens: RunOnceScheduler;
|
private readonly _fetchDocumentSemanticTokens: RunOnceScheduler;
|
||||||
private _currentResponse: SemanticTokensResponse | null;
|
private _currentDocumentResponse: SemanticTokensResponse | null;
|
||||||
private _currentRequestCancellationTokenSource: CancellationTokenSource | null;
|
private _currentDocumentRequestCancellationTokenSource: CancellationTokenSource | null;
|
||||||
private _providersChangeListeners: IDisposable[];
|
private _documentProvidersChangeListeners: IDisposable[];
|
||||||
|
|
||||||
constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) {
|
constructor(model: ITextModel, themeService: IThemeService, stylingProvider: SemanticStyling) {
|
||||||
super();
|
super();
|
||||||
@@ -859,57 +688,57 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
this._isDisposed = false;
|
this._isDisposed = false;
|
||||||
this._model = model;
|
this._model = model;
|
||||||
this._semanticStyling = stylingProvider;
|
this._semanticStyling = stylingProvider;
|
||||||
this._fetchSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchSemanticTokensNow(), 300));
|
this._fetchDocumentSemanticTokens = this._register(new RunOnceScheduler(() => this._fetchDocumentSemanticTokensNow(), 300));
|
||||||
this._currentResponse = null;
|
this._currentDocumentResponse = null;
|
||||||
this._currentRequestCancellationTokenSource = null;
|
this._currentDocumentRequestCancellationTokenSource = null;
|
||||||
this._providersChangeListeners = [];
|
this._documentProvidersChangeListeners = [];
|
||||||
|
|
||||||
this._register(this._model.onDidChangeContent(e => {
|
this._register(this._model.onDidChangeContent(() => {
|
||||||
if (!this._fetchSemanticTokens.isScheduled()) {
|
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
|
||||||
this._fetchSemanticTokens.schedule();
|
this._fetchDocumentSemanticTokens.schedule();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
const bindChangeListeners = () => {
|
const bindDocumentChangeListeners = () => {
|
||||||
dispose(this._providersChangeListeners);
|
dispose(this._documentProvidersChangeListeners);
|
||||||
this._providersChangeListeners = [];
|
this._documentProvidersChangeListeners = [];
|
||||||
for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) {
|
for (const provider of DocumentSemanticTokensProviderRegistry.all(model)) {
|
||||||
if (typeof provider.onDidChange === 'function') {
|
if (typeof provider.onDidChange === 'function') {
|
||||||
this._providersChangeListeners.push(provider.onDidChange(() => this._fetchSemanticTokens.schedule(0)));
|
this._documentProvidersChangeListeners.push(provider.onDidChange(() => this._fetchDocumentSemanticTokens.schedule(0)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
bindChangeListeners();
|
bindDocumentChangeListeners();
|
||||||
this._register(DocumentSemanticTokensProviderRegistry.onDidChange(e => {
|
this._register(DocumentSemanticTokensProviderRegistry.onDidChange(() => {
|
||||||
bindChangeListeners();
|
bindDocumentChangeListeners();
|
||||||
this._fetchSemanticTokens.schedule();
|
this._fetchDocumentSemanticTokens.schedule();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._register(themeService.onDidColorThemeChange(_ => {
|
this._register(themeService.onDidColorThemeChange(_ => {
|
||||||
// clear out existing tokens
|
// clear out existing tokens
|
||||||
this._setSemanticTokens(null, null, null, []);
|
this._setDocumentSemanticTokens(null, null, null, []);
|
||||||
this._fetchSemanticTokens.schedule();
|
this._fetchDocumentSemanticTokens.schedule();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._fetchSemanticTokens.schedule(0);
|
this._fetchDocumentSemanticTokens.schedule(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
if (this._currentResponse) {
|
if (this._currentDocumentResponse) {
|
||||||
this._currentResponse.dispose();
|
this._currentDocumentResponse.dispose();
|
||||||
this._currentResponse = null;
|
this._currentDocumentResponse = null;
|
||||||
}
|
}
|
||||||
if (this._currentRequestCancellationTokenSource) {
|
if (this._currentDocumentRequestCancellationTokenSource) {
|
||||||
this._currentRequestCancellationTokenSource.cancel();
|
this._currentDocumentRequestCancellationTokenSource.cancel();
|
||||||
this._currentRequestCancellationTokenSource = null;
|
this._currentDocumentRequestCancellationTokenSource = null;
|
||||||
}
|
}
|
||||||
this._setSemanticTokens(null, null, null, []);
|
this._setDocumentSemanticTokens(null, null, null, []);
|
||||||
this._isDisposed = true;
|
this._isDisposed = true;
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _fetchSemanticTokensNow(): void {
|
private _fetchDocumentSemanticTokensNow(): void {
|
||||||
if (this._currentRequestCancellationTokenSource) {
|
if (this._currentDocumentRequestCancellationTokenSource) {
|
||||||
// there is already a request running, let it finish...
|
// there is already a request running, let it finish...
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -917,7 +746,7 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
if (!provider) {
|
if (!provider) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._currentRequestCancellationTokenSource = new CancellationTokenSource();
|
this._currentDocumentRequestCancellationTokenSource = new CancellationTokenSource();
|
||||||
|
|
||||||
const pendingChanges: IModelContentChangedEvent[] = [];
|
const pendingChanges: IModelContentChangedEvent[] = [];
|
||||||
const contentChangeListener = this._model.onDidChangeContent((e) => {
|
const contentChangeListener = this._model.onDidChangeContent((e) => {
|
||||||
@@ -926,13 +755,13 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
|
|
||||||
const styling = this._semanticStyling.get(provider);
|
const styling = this._semanticStyling.get(provider);
|
||||||
|
|
||||||
const lastResultId = this._currentResponse ? this._currentResponse.resultId || null : null;
|
const lastResultId = this._currentDocumentResponse ? this._currentDocumentResponse.resultId || null : null;
|
||||||
const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentRequestCancellationTokenSource.token));
|
const request = Promise.resolve(provider.provideDocumentSemanticTokens(this._model, lastResultId, this._currentDocumentRequestCancellationTokenSource.token));
|
||||||
|
|
||||||
request.then((res) => {
|
request.then((res) => {
|
||||||
this._currentRequestCancellationTokenSource = null;
|
this._currentDocumentRequestCancellationTokenSource = null;
|
||||||
contentChangeListener.dispose();
|
contentChangeListener.dispose();
|
||||||
this._setSemanticTokens(provider, res || null, styling, pendingChanges);
|
this._setDocumentSemanticTokens(provider, res || null, styling, pendingChanges);
|
||||||
}, (err) => {
|
}, (err) => {
|
||||||
if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) {
|
if (!err || typeof err.message !== 'string' || err.message.indexOf('busy') === -1) {
|
||||||
errors.onUnexpectedError(err);
|
errors.onUnexpectedError(err);
|
||||||
@@ -940,13 +769,13 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
|
|
||||||
// Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
|
// Semantic tokens eats up all errors and considers errors to mean that the result is temporarily not available
|
||||||
// The API does not have a special error kind to express this...
|
// The API does not have a special error kind to express this...
|
||||||
this._currentRequestCancellationTokenSource = null;
|
this._currentDocumentRequestCancellationTokenSource = null;
|
||||||
contentChangeListener.dispose();
|
contentChangeListener.dispose();
|
||||||
|
|
||||||
if (pendingChanges.length > 0) {
|
if (pendingChanges.length > 0) {
|
||||||
// More changes occurred while the request was running
|
// More changes occurred while the request was running
|
||||||
if (!this._fetchSemanticTokens.isScheduled()) {
|
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
|
||||||
this._fetchSemanticTokens.schedule();
|
this._fetchDocumentSemanticTokens.schedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -966,11 +795,11 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticColoringProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
|
private _setDocumentSemanticTokens(provider: DocumentSemanticTokensProvider | null, tokens: SemanticTokens | SemanticTokensEdits | null, styling: SemanticTokensProviderStyling | null, pendingChanges: IModelContentChangedEvent[]): void {
|
||||||
const currentResponse = this._currentResponse;
|
const currentResponse = this._currentDocumentResponse;
|
||||||
if (this._currentResponse) {
|
if (this._currentDocumentResponse) {
|
||||||
this._currentResponse.dispose();
|
this._currentDocumentResponse.dispose();
|
||||||
this._currentResponse = null;
|
this._currentDocumentResponse = null;
|
||||||
}
|
}
|
||||||
if (this._isDisposed) {
|
if (this._isDisposed) {
|
||||||
// disposed!
|
// disposed!
|
||||||
@@ -979,15 +808,19 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!provider || !tokens || !styling) {
|
if (!provider || !styling) {
|
||||||
this._model.setSemanticTokens(null);
|
this._model.setSemanticTokens(null, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!tokens) {
|
||||||
|
this._model.setSemanticTokens(null, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ModelSemanticColoring._isSemanticTokensEdits(tokens)) {
|
if (ModelSemanticColoring._isSemanticTokensEdits(tokens)) {
|
||||||
if (!currentResponse) {
|
if (!currentResponse) {
|
||||||
// not possible!
|
// not possible!
|
||||||
this._model.setSemanticTokens(null);
|
this._model.setSemanticTokens(null, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tokens.edits.length === 0) {
|
if (tokens.edits.length === 0) {
|
||||||
@@ -1037,80 +870,9 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
|
|
||||||
if (ModelSemanticColoring._isSemanticTokens(tokens)) {
|
if (ModelSemanticColoring._isSemanticTokens(tokens)) {
|
||||||
|
|
||||||
this._currentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
|
this._currentDocumentResponse = new SemanticTokensResponse(provider, tokens.resultId, tokens.data);
|
||||||
|
|
||||||
const srcData = tokens.data;
|
const result = toMultilineTokens2(tokens, styling, this._model.getLanguageIdentifier());
|
||||||
const tokenCount = (tokens.data.length / 5) | 0;
|
|
||||||
const tokensPerArea = Math.max(Math.ceil(tokenCount / SemanticColoringConstants.DesiredMaxAreas), SemanticColoringConstants.DesiredTokensPerArea);
|
|
||||||
|
|
||||||
const result: MultilineTokens2[] = [];
|
|
||||||
|
|
||||||
const languageId = this._model.getLanguageIdentifier();
|
|
||||||
|
|
||||||
let tokenIndex = 0;
|
|
||||||
let lastLineNumber = 1;
|
|
||||||
let lastStartCharacter = 0;
|
|
||||||
while (tokenIndex < tokenCount) {
|
|
||||||
const tokenStartIndex = tokenIndex;
|
|
||||||
let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount);
|
|
||||||
|
|
||||||
// Keep tokens on the same line in the same area...
|
|
||||||
if (tokenEndIndex < tokenCount) {
|
|
||||||
|
|
||||||
let smallTokenEndIndex = tokenEndIndex;
|
|
||||||
while (smallTokenEndIndex - 1 > tokenStartIndex && srcData[5 * smallTokenEndIndex] === 0) {
|
|
||||||
smallTokenEndIndex--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (smallTokenEndIndex - 1 === tokenStartIndex) {
|
|
||||||
// there are so many tokens on this line that our area would be empty, we must now go right
|
|
||||||
let bigTokenEndIndex = tokenEndIndex;
|
|
||||||
while (bigTokenEndIndex + 1 < tokenCount && srcData[5 * bigTokenEndIndex] === 0) {
|
|
||||||
bigTokenEndIndex++;
|
|
||||||
}
|
|
||||||
tokenEndIndex = bigTokenEndIndex;
|
|
||||||
} else {
|
|
||||||
tokenEndIndex = smallTokenEndIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4);
|
|
||||||
let destOffset = 0;
|
|
||||||
let areaLine = 0;
|
|
||||||
while (tokenIndex < tokenEndIndex) {
|
|
||||||
const srcOffset = 5 * tokenIndex;
|
|
||||||
const deltaLine = srcData[srcOffset];
|
|
||||||
const deltaCharacter = srcData[srcOffset + 1];
|
|
||||||
const lineNumber = lastLineNumber + deltaLine;
|
|
||||||
const startCharacter = (deltaLine === 0 ? lastStartCharacter + deltaCharacter : deltaCharacter);
|
|
||||||
const length = srcData[srcOffset + 2];
|
|
||||||
const tokenTypeIndex = srcData[srcOffset + 3];
|
|
||||||
const tokenModifierSet = srcData[srcOffset + 4];
|
|
||||||
const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId);
|
|
||||||
|
|
||||||
if (metadata !== Constants.NO_STYLING) {
|
|
||||||
if (areaLine === 0) {
|
|
||||||
areaLine = lineNumber;
|
|
||||||
}
|
|
||||||
destData[destOffset] = lineNumber - areaLine;
|
|
||||||
destData[destOffset + 1] = startCharacter;
|
|
||||||
destData[destOffset + 2] = startCharacter + length;
|
|
||||||
destData[destOffset + 3] = metadata;
|
|
||||||
destOffset += 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
lastLineNumber = lineNumber;
|
|
||||||
lastStartCharacter = startCharacter;
|
|
||||||
tokenIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (destOffset !== destData.length) {
|
|
||||||
destData = destData.subarray(0, destOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokens = new MultilineTokens2(areaLine, new SparseEncodedTokens(destData));
|
|
||||||
result.push(tokens);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust incoming semantic tokens
|
// Adjust incoming semantic tokens
|
||||||
if (pendingChanges.length > 0) {
|
if (pendingChanges.length > 0) {
|
||||||
@@ -1126,16 +888,16 @@ class ModelSemanticColoring extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._fetchSemanticTokens.isScheduled()) {
|
if (!this._fetchDocumentSemanticTokens.isScheduled()) {
|
||||||
this._fetchSemanticTokens.schedule();
|
this._fetchDocumentSemanticTokens.schedule();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._model.setSemanticTokens(result);
|
this._model.setSemanticTokens(result, true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._model.setSemanticTokens(null);
|
this._model.setSemanticTokens(null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null {
|
private _getSemanticColoringProvider(): DocumentSemanticTokensProvider | null {
|
||||||
|
|||||||
261
src/vs/editor/common/services/semanticTokensProviderStyling.ts
Normal file
261
src/vs/editor/common/services/semanticTokensProviderStyling.ts
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { SemanticTokensLegend, TokenMetadata, FontStyle, MetadataConsts, SemanticTokens, LanguageIdentifier } from 'vs/editor/common/modes';
|
||||||
|
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||||
|
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||||
|
import { MultilineTokens2, SparseEncodedTokens } from 'vs/editor/common/model/tokensStore';
|
||||||
|
|
||||||
|
export const enum SemanticTokensProviderStylingConstants {
|
||||||
|
NO_STYLING = 0b01111111111111111111111111111111
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SemanticTokensProviderStyling {
|
||||||
|
|
||||||
|
private readonly _hashTable: HashTable;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private readonly _legend: SemanticTokensLegend,
|
||||||
|
private readonly _themeService: IThemeService,
|
||||||
|
private readonly _logService: ILogService
|
||||||
|
) {
|
||||||
|
this._hashTable = new HashTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number {
|
||||||
|
const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet, languageId.id);
|
||||||
|
let metadata: number;
|
||||||
|
if (entry) {
|
||||||
|
metadata = entry.metadata;
|
||||||
|
} else {
|
||||||
|
const tokenType = this._legend.tokenTypes[tokenTypeIndex];
|
||||||
|
const tokenModifiers: string[] = [];
|
||||||
|
let modifierSet = tokenModifierSet;
|
||||||
|
for (let modifierIndex = 0; modifierSet > 0 && modifierIndex < this._legend.tokenModifiers.length; modifierIndex++) {
|
||||||
|
if (modifierSet & 1) {
|
||||||
|
tokenModifiers.push(this._legend.tokenModifiers[modifierIndex]);
|
||||||
|
}
|
||||||
|
modifierSet = modifierSet >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers, languageId.language);
|
||||||
|
if (typeof tokenStyle === 'undefined') {
|
||||||
|
metadata = SemanticTokensProviderStylingConstants.NO_STYLING;
|
||||||
|
} else {
|
||||||
|
metadata = 0;
|
||||||
|
if (typeof tokenStyle.italic !== 'undefined') {
|
||||||
|
const italicBit = (tokenStyle.italic ? FontStyle.Italic : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
||||||
|
metadata |= italicBit | MetadataConsts.SEMANTIC_USE_ITALIC;
|
||||||
|
}
|
||||||
|
if (typeof tokenStyle.bold !== 'undefined') {
|
||||||
|
const boldBit = (tokenStyle.bold ? FontStyle.Bold : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
||||||
|
metadata |= boldBit | MetadataConsts.SEMANTIC_USE_BOLD;
|
||||||
|
}
|
||||||
|
if (typeof tokenStyle.underline !== 'undefined') {
|
||||||
|
const underlineBit = (tokenStyle.underline ? FontStyle.Underline : 0) << MetadataConsts.FONT_STYLE_OFFSET;
|
||||||
|
metadata |= underlineBit | MetadataConsts.SEMANTIC_USE_UNDERLINE;
|
||||||
|
}
|
||||||
|
if (tokenStyle.foreground) {
|
||||||
|
const foregroundBits = (tokenStyle.foreground) << MetadataConsts.FOREGROUND_OFFSET;
|
||||||
|
metadata |= foregroundBits | MetadataConsts.SEMANTIC_USE_FOREGROUND;
|
||||||
|
}
|
||||||
|
if (metadata === 0) {
|
||||||
|
// Nothing!
|
||||||
|
metadata = SemanticTokensProviderStylingConstants.NO_STYLING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._hashTable.add(tokenTypeIndex, tokenModifierSet, languageId.id, metadata);
|
||||||
|
}
|
||||||
|
if (this._logService.getLevel() === LogLevel.Trace) {
|
||||||
|
const type = this._legend.tokenTypes[tokenTypeIndex];
|
||||||
|
const modifiers = tokenModifierSet ? ' ' + this._legend.tokenModifiers.filter((_, i) => tokenModifierSet & (1 << i)).join(' ') : '';
|
||||||
|
this._logService.trace(`tokenStyleMetadata ${entry ? '[CACHED] ' : ''}${type}${modifiers}: foreground ${TokenMetadata.getForeground(metadata)}, fontStyle ${TokenMetadata.getFontStyle(metadata).toString(2)}`);
|
||||||
|
}
|
||||||
|
return metadata;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const enum SemanticColoringConstants {
|
||||||
|
/**
|
||||||
|
* Let's aim at having 8KB buffers if possible...
|
||||||
|
* So that would be 8192 / (5 * 4) = 409.6 tokens per area
|
||||||
|
*/
|
||||||
|
DesiredTokensPerArea = 400,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Try to keep the total number of areas under 1024 if possible,
|
||||||
|
* simply compensate by having more tokens per area...
|
||||||
|
*/
|
||||||
|
DesiredMaxAreas = 1024,
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toMultilineTokens2(tokens: SemanticTokens, styling: SemanticTokensProviderStyling, languageId: LanguageIdentifier): MultilineTokens2[] {
|
||||||
|
const srcData = tokens.data;
|
||||||
|
const tokenCount = (tokens.data.length / 5) | 0;
|
||||||
|
const tokensPerArea = Math.max(Math.ceil(tokenCount / SemanticColoringConstants.DesiredMaxAreas), SemanticColoringConstants.DesiredTokensPerArea);
|
||||||
|
const result: MultilineTokens2[] = [];
|
||||||
|
|
||||||
|
let tokenIndex = 0;
|
||||||
|
let lastLineNumber = 1;
|
||||||
|
let lastStartCharacter = 0;
|
||||||
|
while (tokenIndex < tokenCount) {
|
||||||
|
const tokenStartIndex = tokenIndex;
|
||||||
|
let tokenEndIndex = Math.min(tokenStartIndex + tokensPerArea, tokenCount);
|
||||||
|
|
||||||
|
// Keep tokens on the same line in the same area...
|
||||||
|
if (tokenEndIndex < tokenCount) {
|
||||||
|
|
||||||
|
let smallTokenEndIndex = tokenEndIndex;
|
||||||
|
while (smallTokenEndIndex - 1 > tokenStartIndex && srcData[5 * smallTokenEndIndex] === 0) {
|
||||||
|
smallTokenEndIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (smallTokenEndIndex - 1 === tokenStartIndex) {
|
||||||
|
// there are so many tokens on this line that our area would be empty, we must now go right
|
||||||
|
let bigTokenEndIndex = tokenEndIndex;
|
||||||
|
while (bigTokenEndIndex + 1 < tokenCount && srcData[5 * bigTokenEndIndex] === 0) {
|
||||||
|
bigTokenEndIndex++;
|
||||||
|
}
|
||||||
|
tokenEndIndex = bigTokenEndIndex;
|
||||||
|
} else {
|
||||||
|
tokenEndIndex = smallTokenEndIndex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let destData = new Uint32Array((tokenEndIndex - tokenStartIndex) * 4);
|
||||||
|
let destOffset = 0;
|
||||||
|
let areaLine = 0;
|
||||||
|
while (tokenIndex < tokenEndIndex) {
|
||||||
|
const srcOffset = 5 * tokenIndex;
|
||||||
|
const deltaLine = srcData[srcOffset];
|
||||||
|
const deltaCharacter = srcData[srcOffset + 1];
|
||||||
|
const lineNumber = lastLineNumber + deltaLine;
|
||||||
|
const startCharacter = (deltaLine === 0 ? lastStartCharacter + deltaCharacter : deltaCharacter);
|
||||||
|
const length = srcData[srcOffset + 2];
|
||||||
|
const tokenTypeIndex = srcData[srcOffset + 3];
|
||||||
|
const tokenModifierSet = srcData[srcOffset + 4];
|
||||||
|
const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId);
|
||||||
|
|
||||||
|
if (metadata !== SemanticTokensProviderStylingConstants.NO_STYLING) {
|
||||||
|
if (areaLine === 0) {
|
||||||
|
areaLine = lineNumber;
|
||||||
|
}
|
||||||
|
destData[destOffset] = lineNumber - areaLine;
|
||||||
|
destData[destOffset + 1] = startCharacter;
|
||||||
|
destData[destOffset + 2] = startCharacter + length;
|
||||||
|
destData[destOffset + 3] = metadata;
|
||||||
|
destOffset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastLineNumber = lineNumber;
|
||||||
|
lastStartCharacter = startCharacter;
|
||||||
|
tokenIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (destOffset !== destData.length) {
|
||||||
|
destData = destData.subarray(0, destOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tokens = new MultilineTokens2(areaLine, new SparseEncodedTokens(destData));
|
||||||
|
result.push(tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
class HashTableEntry {
|
||||||
|
public readonly tokenTypeIndex: number;
|
||||||
|
public readonly tokenModifierSet: number;
|
||||||
|
public readonly languageId: number;
|
||||||
|
public readonly metadata: number;
|
||||||
|
public next: HashTableEntry | null;
|
||||||
|
|
||||||
|
constructor(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number) {
|
||||||
|
this.tokenTypeIndex = tokenTypeIndex;
|
||||||
|
this.tokenModifierSet = tokenModifierSet;
|
||||||
|
this.languageId = languageId;
|
||||||
|
this.metadata = metadata;
|
||||||
|
this.next = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HashTable {
|
||||||
|
|
||||||
|
private static _SIZES = [3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, 262139, 524287, 1048573, 2097143];
|
||||||
|
|
||||||
|
private _elementsCount: number;
|
||||||
|
private _currentLengthIndex: number;
|
||||||
|
private _currentLength: number;
|
||||||
|
private _growCount: number;
|
||||||
|
private _elements: (HashTableEntry | null)[];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this._elementsCount = 0;
|
||||||
|
this._currentLengthIndex = 0;
|
||||||
|
this._currentLength = HashTable._SIZES[this._currentLengthIndex];
|
||||||
|
this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
|
||||||
|
this._elements = [];
|
||||||
|
HashTable._nullOutEntries(this._elements, this._currentLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _nullOutEntries(entries: (HashTableEntry | null)[], length: number): void {
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
entries[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hash2(n1: number, n2: number): number {
|
||||||
|
return (((n1 << 5) - n1) + n2) | 0; // n1 * 31 + n2, keep as int32
|
||||||
|
}
|
||||||
|
|
||||||
|
private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): number {
|
||||||
|
return this._hash2(this._hash2(tokenTypeIndex, tokenModifierSet), languageId) % this._currentLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): HashTableEntry | null {
|
||||||
|
const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet, languageId);
|
||||||
|
|
||||||
|
let p = this._elements[hash];
|
||||||
|
while (p) {
|
||||||
|
if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet && p.languageId === languageId) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
p = p.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public add(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number): void {
|
||||||
|
this._elementsCount++;
|
||||||
|
if (this._growCount !== 0 && this._elementsCount >= this._growCount) {
|
||||||
|
// expand!
|
||||||
|
const oldElements = this._elements;
|
||||||
|
|
||||||
|
this._currentLengthIndex++;
|
||||||
|
this._currentLength = HashTable._SIZES[this._currentLengthIndex];
|
||||||
|
this._growCount = Math.round(this._currentLengthIndex + 1 < HashTable._SIZES.length ? 2 / 3 * this._currentLength : 0);
|
||||||
|
this._elements = [];
|
||||||
|
HashTable._nullOutEntries(this._elements, this._currentLength);
|
||||||
|
|
||||||
|
for (const first of oldElements) {
|
||||||
|
let p = first;
|
||||||
|
while (p) {
|
||||||
|
const oldNext = p.next;
|
||||||
|
p.next = null;
|
||||||
|
this._add(p);
|
||||||
|
p = oldNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, languageId, metadata));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _add(element: HashTableEntry): void {
|
||||||
|
const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet, element.languageId);
|
||||||
|
element.next = this._elements[hash];
|
||||||
|
this._elements[hash] = element;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -813,7 +813,11 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
|
|||||||
let prevPartContentCnt = 0;
|
let prevPartContentCnt = 0;
|
||||||
let partAbsoluteOffset = 0;
|
let partAbsoluteOffset = 0;
|
||||||
|
|
||||||
|
if (containsRTL) {
|
||||||
|
sb.appendASCIIString('<span dir="ltr">');
|
||||||
|
} else {
|
||||||
sb.appendASCIIString('<span>');
|
sb.appendASCIIString('<span>');
|
||||||
|
}
|
||||||
|
|
||||||
for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
|
for (let partIndex = 0, tokensLen = parts.length; partIndex < tokensLen; partIndex++) {
|
||||||
partAbsoluteOffset += prevPartContentCnt;
|
partAbsoluteOffset += prevPartContentCnt;
|
||||||
@@ -890,9 +894,6 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render
|
|||||||
|
|
||||||
let partContentCnt = 0;
|
let partContentCnt = 0;
|
||||||
|
|
||||||
if (containsRTL) {
|
|
||||||
sb.appendASCIIString(' dir="ltr"');
|
|
||||||
}
|
|
||||||
sb.appendASCII(CharCode.GreaterThan);
|
sb.appendASCII(CharCode.GreaterThan);
|
||||||
|
|
||||||
for (; charIndex < partEndIndex; charIndex++) {
|
for (; charIndex < partEndIndex; charIndex++) {
|
||||||
|
|||||||
@@ -563,8 +563,11 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||||||
const disposables = new DisposableStore();
|
const disposables = new DisposableStore();
|
||||||
const actionsElement = dom.append(hoverElement, $('div.actions'));
|
const actionsElement = dom.append(hoverElement, $('div.actions'));
|
||||||
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
|
if (markerHover.marker.severity === MarkerSeverity.Error || markerHover.marker.severity === MarkerSeverity.Warning || markerHover.marker.severity === MarkerSeverity.Info) {
|
||||||
|
const peekProblemLabel = nls.localize('peek problem', "Peek Problem");
|
||||||
|
const peekProblemKeybinding = this._keybindingService.lookupKeybinding(NextMarkerAction.ID);
|
||||||
|
const peekProblemKeybindingLabel = peekProblemKeybinding && peekProblemKeybinding.getLabel();
|
||||||
disposables.add(this.renderAction(actionsElement, {
|
disposables.add(this.renderAction(actionsElement, {
|
||||||
label: nls.localize('peek problem', "Peek Problem"),
|
label: peekProblemKeybindingLabel ? nls.localize('titleAndKb', "{0} ({1})", peekProblemLabel, peekProblemKeybindingLabel) : peekProblemLabel,
|
||||||
commandId: NextMarkerAction.ID,
|
commandId: NextMarkerAction.ID,
|
||||||
run: () => {
|
run: () => {
|
||||||
this.hide();
|
this.hide();
|
||||||
@@ -581,7 +584,6 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||||||
quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes...");
|
quickfixPlaceholderElement.textContent = nls.localize('checkingForQuickFixes', "Checking for quick fixes...");
|
||||||
disposables.add(toDisposable(() => quickfixPlaceholderElement.remove()));
|
disposables.add(toDisposable(() => quickfixPlaceholderElement.remove()));
|
||||||
|
|
||||||
|
|
||||||
const codeActionsPromise = this.getCodeActions(markerHover.marker);
|
const codeActionsPromise = this.getCodeActions(markerHover.marker);
|
||||||
disposables.add(toDisposable(() => codeActionsPromise.cancel()));
|
disposables.add(toDisposable(() => codeActionsPromise.cancel()));
|
||||||
codeActionsPromise.then(actions => {
|
codeActionsPromise.then(actions => {
|
||||||
@@ -602,8 +604,12 @@ export class ModesContentHoverWidget extends ContentHoverWidget {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const quickFixLabel = nls.localize('quick fixes', "Quick Fix...");
|
||||||
|
const quickFixKeybinding = this._keybindingService.lookupKeybinding(QuickFixAction.Id);
|
||||||
|
const quickFixKeybindingLabel = quickFixKeybinding && quickFixKeybinding.getLabel();
|
||||||
|
|
||||||
disposables.add(this.renderAction(actionsElement, {
|
disposables.add(this.renderAction(actionsElement, {
|
||||||
label: nls.localize('quick fixes', "Quick Fix..."),
|
label: quickFixKeybindingLabel ? nls.localize('titleAndKb', "{0} ({1})", quickFixLabel, quickFixKeybindingLabel) : quickFixLabel,
|
||||||
commandId: QuickFixAction.Id,
|
commandId: QuickFixAction.Id,
|
||||||
run: (target) => {
|
run: (target) => {
|
||||||
showing = true;
|
showing = true;
|
||||||
|
|||||||
@@ -786,11 +786,13 @@ class SelectionHighlighterState {
|
|||||||
public readonly searchText: string;
|
public readonly searchText: string;
|
||||||
public readonly matchCase: boolean;
|
public readonly matchCase: boolean;
|
||||||
public readonly wordSeparators: string | null;
|
public readonly wordSeparators: string | null;
|
||||||
|
public readonly modelVersionId: number;
|
||||||
|
|
||||||
constructor(searchText: string, matchCase: boolean, wordSeparators: string | null) {
|
constructor(searchText: string, matchCase: boolean, wordSeparators: string | null, modelVersionId: number) {
|
||||||
this.searchText = searchText;
|
this.searchText = searchText;
|
||||||
this.matchCase = matchCase;
|
this.matchCase = matchCase;
|
||||||
this.wordSeparators = wordSeparators;
|
this.wordSeparators = wordSeparators;
|
||||||
|
this.modelVersionId = modelVersionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -807,6 +809,7 @@ class SelectionHighlighterState {
|
|||||||
a.searchText === b.searchText
|
a.searchText === b.searchText
|
||||||
&& a.matchCase === b.matchCase
|
&& a.matchCase === b.matchCase
|
||||||
&& a.wordSeparators === b.wordSeparators
|
&& a.wordSeparators === b.wordSeparators
|
||||||
|
&& a.modelVersionId === b.modelVersionId
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -857,6 +860,11 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
|
|||||||
this._register(editor.onDidChangeModel((e) => {
|
this._register(editor.onDidChangeModel((e) => {
|
||||||
this._setState(null);
|
this._setState(null);
|
||||||
}));
|
}));
|
||||||
|
this._register(editor.onDidChangeModelContent((e) => {
|
||||||
|
if (this._isEnabled) {
|
||||||
|
this.updateSoon.schedule();
|
||||||
|
}
|
||||||
|
}));
|
||||||
this._register(CommonFindController.get(editor).getState().onFindReplaceStateChange((e) => {
|
this._register(CommonFindController.get(editor).getState().onFindReplaceStateChange((e) => {
|
||||||
this._update();
|
this._update();
|
||||||
}));
|
}));
|
||||||
@@ -939,7 +947,7 @@ export class SelectionHighlighter extends Disposable implements IEditorContribut
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null);
|
return new SelectionHighlighterState(r.searchText, r.matchCase, r.wholeWord ? editor.getOption(EditorOption.wordSeparators) : null, editor.getModel().getVersionId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setState(state: SelectionHighlighterState | null): void {
|
private _setState(state: SelectionHighlighterState | null): void {
|
||||||
|
|||||||
@@ -134,10 +134,10 @@ export abstract class AbstractGotoLineQuickAccessProvider extends AbstractEditor
|
|||||||
const position = editor.getPosition() || { lineNumber: 1, column: 1 };
|
const position = editor.getPosition() || { lineNumber: 1, column: 1 };
|
||||||
const lineCount = this.lineCount(editor);
|
const lineCount = this.lineCount(editor);
|
||||||
if (lineCount > 1) {
|
if (lineCount > 1) {
|
||||||
return localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Column: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, lineCount);
|
return localize('gotoLineLabelEmptyWithLimit', "Current Line: {0}, Character: {1}. Type a line number between 1 and {2} to navigate to.", position.lineNumber, position.column, lineCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
return localize('gotoLineLabelEmpty', "Current Line: {0}, Column: {1}. Type a line number to navigate to.", position.lineNumber, position.column);
|
return localize('gotoLineLabelEmpty', "Current Line: {0}, Character: {1}. Type a line number to navigate to.", position.lineNumber, position.column);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isValidLineNumber(editor: IEditor, lineNumber: number | undefined): boolean {
|
private isValidLineNumber(editor: IEditor, lineNumber: number | undefined): boolean {
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
protected provideWithoutTextEditor(picker: IQuickPick<IGotoSymbolQuickPickItem>): IDisposable {
|
||||||
const label = localize('cannotRunGotoSymbolWithoutEditor', "Open a text editor first to go to a symbol.");
|
const label = localize('cannotRunGotoSymbolWithoutEditor', "To go to a symbol, first open a text editor with symbol information.");
|
||||||
|
|
||||||
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
||||||
picker.ariaLabel = label;
|
picker.ariaLabel = label;
|
||||||
@@ -70,7 +70,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
|
|||||||
const disposables = new DisposableStore();
|
const disposables = new DisposableStore();
|
||||||
|
|
||||||
// Generic pick for not having any symbol information
|
// Generic pick for not having any symbol information
|
||||||
const label = localize('cannotRunGotoSymbolWithoutSymbolProvider', "Open a text editor with symbol information first to go to a symbol.");
|
const label = localize('cannotRunGotoSymbolWithoutSymbolProvider', "The active text editor does not provide symbol information.");
|
||||||
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
picker.items = [{ label, index: 0, kind: SymbolKind.String }];
|
||||||
picker.ariaLabel = label;
|
picker.ariaLabel = label;
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ import { IPosition, Position } from 'vs/editor/common/core/position';
|
|||||||
import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
import { TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model';
|
||||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
||||||
import * as platform from 'vs/base/common/platform';
|
import * as platform from 'vs/base/common/platform';
|
||||||
import { SuggestRangeHighlighter } from 'vs/editor/contrib/suggest/suggestRangeHighlighter';
|
|
||||||
import { MenuRegistry } from 'vs/platform/actions/common/actions';
|
import { MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||||
|
|
||||||
// sticky suggest widget which doesn't disappear on focus out and such
|
// sticky suggest widget which doesn't disappear on focus out and such
|
||||||
@@ -233,9 +232,6 @@ export class SuggestController implements IEditorContribution {
|
|||||||
};
|
};
|
||||||
this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig()));
|
this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig()));
|
||||||
updateFromConfig();
|
updateFromConfig();
|
||||||
|
|
||||||
// create range highlighter
|
|
||||||
this._toDispose.add(new SuggestRangeHighlighter(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
|
|||||||
@@ -1,128 +0,0 @@
|
|||||||
/*---------------------------------------------------------------------------------------------
|
|
||||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
||||||
*--------------------------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
import { DisposableStore, dispose, IDisposable } from 'vs/base/common/lifecycle';
|
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
|
||||||
import { EditorOption } from 'vs/editor/common/config/editorOptions';
|
|
||||||
import { CompletionItem } from 'vs/editor/contrib/suggest/suggest';
|
|
||||||
import { IModelDeltaDecoration } from 'vs/editor/common/model';
|
|
||||||
import { SuggestController } from 'vs/editor/contrib/suggest/suggestController';
|
|
||||||
import { Emitter } from 'vs/base/common/event';
|
|
||||||
import { domEvent } from 'vs/base/browser/event';
|
|
||||||
import { domContentLoaded } from 'vs/base/browser/dom';
|
|
||||||
|
|
||||||
export class SuggestRangeHighlighter {
|
|
||||||
|
|
||||||
private readonly _disposables = new DisposableStore();
|
|
||||||
|
|
||||||
private _decorations: string[] = [];
|
|
||||||
private _widgetListener?: IDisposable;
|
|
||||||
private _shiftKeyListener?: IDisposable;
|
|
||||||
private _currentItem?: CompletionItem;
|
|
||||||
|
|
||||||
constructor(private readonly _controller: SuggestController) {
|
|
||||||
|
|
||||||
this._disposables.add(_controller.model.onDidSuggest(e => {
|
|
||||||
if (!e.shy) {
|
|
||||||
const widget = this._controller.widget.getValue();
|
|
||||||
const focused = widget.getFocusedItem();
|
|
||||||
if (focused) {
|
|
||||||
this._highlight(focused.item);
|
|
||||||
}
|
|
||||||
if (!this._widgetListener) {
|
|
||||||
this._widgetListener = widget.onDidFocus(e => this._highlight(e.item));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
this._disposables.add(_controller.model.onDidCancel(() => {
|
|
||||||
this._reset();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
this._reset();
|
|
||||||
this._disposables.dispose();
|
|
||||||
dispose(this._widgetListener);
|
|
||||||
dispose(this._shiftKeyListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _reset(): void {
|
|
||||||
this._decorations = this._controller.editor.deltaDecorations(this._decorations, []);
|
|
||||||
if (this._shiftKeyListener) {
|
|
||||||
this._shiftKeyListener.dispose();
|
|
||||||
this._shiftKeyListener = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _highlight(item: CompletionItem) {
|
|
||||||
|
|
||||||
this._currentItem = item;
|
|
||||||
const opts = this._controller.editor.getOption(EditorOption.suggest);
|
|
||||||
let newDeco: IModelDeltaDecoration[] = [];
|
|
||||||
|
|
||||||
if (opts.insertHighlight) {
|
|
||||||
if (!this._shiftKeyListener) {
|
|
||||||
this._shiftKeyListener = shiftKey.event(() => this._highlight(this._currentItem!));
|
|
||||||
}
|
|
||||||
|
|
||||||
const info = this._controller.getOverwriteInfo(item, shiftKey.isPressed);
|
|
||||||
const position = this._controller.editor.getPosition()!;
|
|
||||||
|
|
||||||
if (opts.insertMode === 'insert' && info.overwriteAfter > 0) {
|
|
||||||
// wants inserts but got replace-mode -> highlight AFTER range
|
|
||||||
newDeco = [{
|
|
||||||
range: new Range(position.lineNumber, position.column, position.lineNumber, position.column + info.overwriteAfter),
|
|
||||||
options: { inlineClassName: 'suggest-insert-unexpected' }
|
|
||||||
}];
|
|
||||||
|
|
||||||
} else if (opts.insertMode === 'replace' && info.overwriteAfter === 0) {
|
|
||||||
// want replace but likely got insert -> highlight AFTER range
|
|
||||||
const wordInfo = this._controller.editor.getModel()?.getWordAtPosition(position);
|
|
||||||
if (wordInfo && wordInfo.endColumn > position.column) {
|
|
||||||
newDeco = [{
|
|
||||||
range: new Range(position.lineNumber, position.column, position.lineNumber, wordInfo.endColumn),
|
|
||||||
options: { inlineClassName: 'suggest-insert-unexpected' }
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update editor decorations
|
|
||||||
this._decorations = this._controller.editor.deltaDecorations(this._decorations, newDeco);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shiftKey = new class ShiftKey extends Emitter<boolean> {
|
|
||||||
|
|
||||||
private readonly _subscriptions = new DisposableStore();
|
|
||||||
private _isPressed: boolean = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
domContentLoaded().then(() => {
|
|
||||||
this._subscriptions.add(domEvent(document.body, 'keydown')(e => this.isPressed = e.shiftKey));
|
|
||||||
this._subscriptions.add(domEvent(document.body, 'keyup')(() => this.isPressed = false));
|
|
||||||
this._subscriptions.add(domEvent(document.body, 'mouseleave')(() => this.isPressed = false));
|
|
||||||
this._subscriptions.add(domEvent(document.body, 'blur')(() => this.isPressed = false));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get isPressed(): boolean {
|
|
||||||
return this._isPressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
set isPressed(value: boolean) {
|
|
||||||
if (this._isPressed !== value) {
|
|
||||||
this._isPressed = value;
|
|
||||||
this.fire(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose() {
|
|
||||||
this._subscriptions.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { RunOnceScheduler, createCancelablePromise, CancelablePromise } from 'vs/base/common/async';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
|
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||||
|
import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
|
||||||
|
import { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||||
|
import { ITextModel } from 'vs/editor/common/model';
|
||||||
|
import { DocumentRangeSemanticTokensProviderRegistry, DocumentRangeSemanticTokensProvider, SemanticTokens } from 'vs/editor/common/modes';
|
||||||
|
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||||
|
import { toMultilineTokens2, SemanticTokensProviderStyling } from 'vs/editor/common/services/semanticTokensProviderStyling';
|
||||||
|
|
||||||
|
class ViewportSemanticTokensContribution extends Disposable implements IEditorContribution {
|
||||||
|
|
||||||
|
public static readonly ID = 'editor.contrib.viewportSemanticTokens';
|
||||||
|
|
||||||
|
public static get(editor: ICodeEditor): ViewportSemanticTokensContribution {
|
||||||
|
return editor.getContribution<ViewportSemanticTokensContribution>(ViewportSemanticTokensContribution.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly _editor: ICodeEditor;
|
||||||
|
private readonly _tokenizeViewport: RunOnceScheduler;
|
||||||
|
private _outstandingRequests: CancelablePromise<SemanticTokens | null | undefined>[];
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
editor: ICodeEditor,
|
||||||
|
@IModelService private readonly _modelService: IModelService
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
this._editor = editor;
|
||||||
|
this._tokenizeViewport = new RunOnceScheduler(() => this._tokenizeViewportNow(), 100);
|
||||||
|
this._outstandingRequests = [];
|
||||||
|
this._register(this._editor.onDidScrollChange(() => {
|
||||||
|
this._tokenizeViewport.schedule();
|
||||||
|
}));
|
||||||
|
this._register(this._editor.onDidChangeModel(() => {
|
||||||
|
this._cancelAll();
|
||||||
|
this._tokenizeViewport.schedule();
|
||||||
|
}));
|
||||||
|
this._register(this._editor.onDidChangeModelContent((e) => {
|
||||||
|
this._cancelAll();
|
||||||
|
this._tokenizeViewport.schedule();
|
||||||
|
}));
|
||||||
|
this._register(DocumentRangeSemanticTokensProviderRegistry.onDidChange(() => {
|
||||||
|
this._cancelAll();
|
||||||
|
this._tokenizeViewport.schedule();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static _getSemanticColoringProvider(model: ITextModel): DocumentRangeSemanticTokensProvider | null {
|
||||||
|
const result = DocumentRangeSemanticTokensProviderRegistry.ordered(model);
|
||||||
|
return (result.length > 0 ? result[0] : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _cancelAll(): void {
|
||||||
|
for (const request of this._outstandingRequests) {
|
||||||
|
request.cancel();
|
||||||
|
}
|
||||||
|
this._outstandingRequests = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private _removeOutstandingRequest(req: CancelablePromise<SemanticTokens | null | undefined>): void {
|
||||||
|
for (let i = 0, len = this._outstandingRequests.length; i < len; i++) {
|
||||||
|
if (this._outstandingRequests[i] === req) {
|
||||||
|
this._outstandingRequests.splice(i, 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _tokenizeViewportNow(): void {
|
||||||
|
if (!this._editor.hasModel()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const model = this._editor.getModel();
|
||||||
|
if (model.hasSemanticTokens()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const provider = ViewportSemanticTokensContribution._getSemanticColoringProvider(model);
|
||||||
|
if (!provider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const styling = this._modelService.getSemanticTokensProviderStyling(provider);
|
||||||
|
const visibleRanges = this._editor.getVisibleRanges();
|
||||||
|
|
||||||
|
this._outstandingRequests = this._outstandingRequests.concat(visibleRanges.map(range => this._requestRange(model, range, provider, styling)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private _requestRange(model: ITextModel, range: Range, provider: DocumentRangeSemanticTokensProvider, styling: SemanticTokensProviderStyling): CancelablePromise<SemanticTokens | null | undefined> {
|
||||||
|
const requestVersionId = model.getVersionId();
|
||||||
|
const request = createCancelablePromise(token => Promise.resolve(provider.provideDocumentRangeSemanticTokens(model, range, token)));
|
||||||
|
request.then((r) => {
|
||||||
|
if (!r || model.isDisposed() || model.getVersionId() !== requestVersionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
model.setPartialSemanticTokens(range, toMultilineTokens2(r, styling, model.getLanguageIdentifier()));
|
||||||
|
}).then(() => this._removeOutstandingRequest(request), () => this._removeOutstandingRequest(request));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerEditorContribution(ViewportSemanticTokensContribution.ID, ViewportSemanticTokensContribution);
|
||||||
@@ -37,7 +37,7 @@ suite('WordPartOperations', () => {
|
|||||||
test('cursorWordPartLeft - basic', () => {
|
test('cursorWordPartLeft - basic', () => {
|
||||||
const EXPECTED = [
|
const EXPECTED = [
|
||||||
'|start| |line|',
|
'|start| |line|',
|
||||||
'|this|Is|A|Camel|Case|Var| |this|_is|_a|_snake|_case|_var| |THIS|_IS|_CAPS|_SNAKE| |this|_IS|Mixed|Use|',
|
'|this|Is|A|Camel|Case|Var| |this_|is_|a_|snake_|case_|var| |THIS_|IS_|CAPS_|SNAKE| |this_|IS|Mixed|Use|',
|
||||||
'|end| |line'
|
'|end| |line'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
@@ -67,7 +67,7 @@ suite('WordPartOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('cursorWordPartLeft - issue #53899: underscores', () => {
|
test('cursorWordPartLeft - issue #53899: underscores', () => {
|
||||||
const EXPECTED = '|myvar| |=| |\'|demonstration|_____of| |selection| |with| |space|\'';
|
const EXPECTED = '|myvar| |=| |\'|demonstration_____|of| |selection| |with| |space|\'';
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
const actualStops = testRepeatedActionAndExtractPositions(
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
text,
|
text,
|
||||||
@@ -83,7 +83,7 @@ suite('WordPartOperations', () => {
|
|||||||
test('cursorWordPartRight - basic', () => {
|
test('cursorWordPartRight - basic', () => {
|
||||||
const EXPECTED = [
|
const EXPECTED = [
|
||||||
'start| |line|',
|
'start| |line|',
|
||||||
'|this|Is|A|Camel|Case|Var| |this_|is_|a_|snake_|case_|var| |THIS_|IS_|CAPS_|SNAKE| |this_|IS|Mixed|Use|',
|
'|this|Is|A|Camel|Case|Var| |this|_is|_a|_snake|_case|_var| |THIS|_IS|_CAPS|_SNAKE| |this|_IS|Mixed|Use|',
|
||||||
'|end| |line|'
|
'|end| |line|'
|
||||||
].join('\n');
|
].join('\n');
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
@@ -113,7 +113,7 @@ suite('WordPartOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('cursorWordPartRight - issue #53899: underscores', () => {
|
test('cursorWordPartRight - issue #53899: underscores', () => {
|
||||||
const EXPECTED = 'myvar| |=| |\'|demonstration_____|of| |selection| |with| |space|\'|';
|
const EXPECTED = 'myvar| |=| |\'|demonstration|_____of| |selection| |with| |space|\'|';
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
const actualStops = testRepeatedActionAndExtractPositions(
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
text,
|
text,
|
||||||
@@ -145,8 +145,40 @@ suite('WordPartOperations', () => {
|
|||||||
assert.deepEqual(actual, EXPECTED);
|
assert.deepEqual(actual, EXPECTED);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('issue #93239 - cursorWordPartRight', () => {
|
||||||
|
const EXPECTED = [
|
||||||
|
'foo|_bar|',
|
||||||
|
].join('\n');
|
||||||
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
|
text,
|
||||||
|
new Position(1, 1),
|
||||||
|
ed => cursorWordPartRight(ed),
|
||||||
|
ed => ed.getPosition()!,
|
||||||
|
ed => ed.getPosition()!.equals(new Position(1, 8))
|
||||||
|
);
|
||||||
|
const actual = serializePipePositions(text, actualStops);
|
||||||
|
assert.deepEqual(actual, EXPECTED);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('issue #93239 - cursorWordPartLeft', () => {
|
||||||
|
const EXPECTED = [
|
||||||
|
'|foo_|bar',
|
||||||
|
].join('\n');
|
||||||
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
|
text,
|
||||||
|
new Position(1, 8),
|
||||||
|
ed => cursorWordPartLeft(ed),
|
||||||
|
ed => ed.getPosition()!,
|
||||||
|
ed => ed.getPosition()!.equals(new Position(1, 1))
|
||||||
|
);
|
||||||
|
const actual = serializePipePositions(text, actualStops);
|
||||||
|
assert.deepEqual(actual, EXPECTED);
|
||||||
|
});
|
||||||
|
|
||||||
test('deleteWordPartLeft - basic', () => {
|
test('deleteWordPartLeft - basic', () => {
|
||||||
const EXPECTED = '| |/*| |Just| |some| |text| |a|+=| |3| |+|5|-|3| |*/| |this|Is|A|Camel|Case|Var| |this|_is|_a|_snake|_case|_var| |THIS|_IS|_CAPS|_SNAKE| |this|_IS|Mixed|Use';
|
const EXPECTED = '| |/*| |Just| |some| |text| |a|+=| |3| |+|5|-|3| |*/| |this|Is|A|Camel|Case|Var| |this_|is_|a_|snake_|case_|var| |THIS_|IS_|CAPS_|SNAKE| |this_|IS|Mixed|Use';
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
const actualStops = testRepeatedActionAndExtractPositions(
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
text,
|
text,
|
||||||
@@ -160,7 +192,7 @@ suite('WordPartOperations', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('deleteWordPartRight - basic', () => {
|
test('deleteWordPartRight - basic', () => {
|
||||||
const EXPECTED = ' |/*| |Just| |some| |text| |a|+=| |3| |+|5|-|3| |*/| |this|Is|A|Camel|Case|Var| |this_|is_|a_|snake_|case_|var| |THIS_|IS_|CAPS_|SNAKE| |this_|IS|Mixed|Use|';
|
const EXPECTED = ' |/*| |Just| |some| |text| |a|+=| |3| |+|5|-|3| |*/| |this|Is|A|Camel|Case|Var| |this|_is|_a|_snake|_case|_var| |THIS|_IS|_CAPS|_SNAKE| |this|_IS|Mixed|Use|';
|
||||||
const [text,] = deserializePipePositions(EXPECTED);
|
const [text,] = deserializePipePositions(EXPECTED);
|
||||||
const actualStops = testRepeatedActionAndExtractPositions(
|
const actualStops = testRepeatedActionAndExtractPositions(
|
||||||
text,
|
text,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import 'vs/editor/contrib/find/findController';
|
|||||||
import 'vs/editor/contrib/folding/folding';
|
import 'vs/editor/contrib/folding/folding';
|
||||||
import 'vs/editor/contrib/fontZoom/fontZoom';
|
import 'vs/editor/contrib/fontZoom/fontZoom';
|
||||||
import 'vs/editor/contrib/format/formatActions';
|
import 'vs/editor/contrib/format/formatActions';
|
||||||
|
import 'vs/editor/contrib/gotoSymbol/documentSymbols';
|
||||||
import 'vs/editor/contrib/gotoSymbol/goToCommands';
|
import 'vs/editor/contrib/gotoSymbol/goToCommands';
|
||||||
import 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition';
|
import 'vs/editor/contrib/gotoSymbol/link/goToDefinitionAtPosition';
|
||||||
import 'vs/editor/contrib/gotoError/gotoError';
|
import 'vs/editor/contrib/gotoError/gotoError';
|
||||||
@@ -39,6 +40,7 @@ import 'vs/editor/contrib/snippet/snippetController2';
|
|||||||
import 'vs/editor/contrib/suggest/suggestController';
|
import 'vs/editor/contrib/suggest/suggestController';
|
||||||
import 'vs/editor/contrib/tokenization/tokenization';
|
import 'vs/editor/contrib/tokenization/tokenization';
|
||||||
import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
|
import 'vs/editor/contrib/toggleTabFocusMode/toggleTabFocusMode';
|
||||||
|
import 'vs/editor/contrib/viewportSemanticTokens/viewportSemanticTokens';
|
||||||
import 'vs/editor/contrib/wordHighlighter/wordHighlighter';
|
import 'vs/editor/contrib/wordHighlighter/wordHighlighter';
|
||||||
import 'vs/editor/contrib/wordOperations/wordOperations';
|
import 'vs/editor/contrib/wordOperations/wordOperations';
|
||||||
import 'vs/editor/contrib/wordPartOperations/wordPartOperations';
|
import 'vs/editor/contrib/wordPartOperations/wordPartOperations';
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import { MultilineTokens2, SparseEncodedTokens } from 'vs/editor/common/model/tokensStore';
|
import { MultilineTokens2, SparseEncodedTokens, TokensStore2 } from 'vs/editor/common/model/tokensStore';
|
||||||
import { Range } from 'vs/editor/common/core/range';
|
import { Range } from 'vs/editor/common/core/range';
|
||||||
import { TextModel } from 'vs/editor/common/model/textModel';
|
import { TextModel } from 'vs/editor/common/model/textModel';
|
||||||
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model';
|
||||||
import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes';
|
import { MetadataConsts, TokenMetadata } from 'vs/editor/common/modes';
|
||||||
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
|
||||||
|
import { LineTokens } from 'vs/editor/common/core/lineTokens';
|
||||||
|
|
||||||
suite('TokensStore', () => {
|
suite('TokensStore', () => {
|
||||||
|
|
||||||
@@ -98,7 +99,7 @@ suite('TokensStore', () => {
|
|||||||
function testTokensAdjustment(rawInitialState: string[], edits: IIdentifiedSingleEditOperation[], rawFinalState: string[]) {
|
function testTokensAdjustment(rawInitialState: string[], edits: IIdentifiedSingleEditOperation[], rawFinalState: string[]) {
|
||||||
const initialState = parseTokensState(rawInitialState);
|
const initialState = parseTokensState(rawInitialState);
|
||||||
const model = createTextModel(initialState.text);
|
const model = createTextModel(initialState.text);
|
||||||
model.setSemanticTokens([initialState.tokens]);
|
model.setSemanticTokens([initialState.tokens], true);
|
||||||
|
|
||||||
model.applyEdits(edits);
|
model.applyEdits(edits);
|
||||||
|
|
||||||
@@ -183,7 +184,7 @@ suite('TokensStore', () => {
|
|||||||
0, 38, 42, 245768,
|
0, 38, 42, 245768,
|
||||||
0, 43, 47, 180232,
|
0, 43, 47, 180232,
|
||||||
])))
|
])))
|
||||||
]);
|
], true);
|
||||||
const lineTokens = model.getLineTokens(1);
|
const lineTokens = model.getLineTokens(1);
|
||||||
let decodedTokens: number[] = [];
|
let decodedTokens: number[] = [];
|
||||||
for (let i = 0, len = lineTokens.getCount(); i < len; i++) {
|
for (let i = 0, len = lineTokens.getCount(); i < len; i++) {
|
||||||
@@ -212,4 +213,114 @@ suite('TokensStore', () => {
|
|||||||
model.dispose();
|
model.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('partial tokens 1', () => {
|
||||||
|
const store = new TokensStore2();
|
||||||
|
|
||||||
|
// setPartial: [1,1 -> 31,2], [(5,5-10),(10,5-10),(15,5-10),(20,5-10),(25,5-10),(30,5-10)]
|
||||||
|
store.setPartial(new Range(1, 1, 31, 2), [
|
||||||
|
new MultilineTokens2(5, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 1,
|
||||||
|
5, 5, 10, 2,
|
||||||
|
10, 5, 10, 3,
|
||||||
|
15, 5, 10, 4,
|
||||||
|
20, 5, 10, 5,
|
||||||
|
25, 5, 10, 6,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
// setPartial: [18,1 -> 42,1], [(20,5-10),(25,5-10),(30,5-10),(35,5-10),(40,5-10)]
|
||||||
|
store.setPartial(new Range(18, 1, 42, 1), [
|
||||||
|
new MultilineTokens2(20, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 4,
|
||||||
|
5, 5, 10, 5,
|
||||||
|
10, 5, 10, 6,
|
||||||
|
15, 5, 10, 7,
|
||||||
|
20, 5, 10, 8,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
// setPartial: [1,1 -> 31,2], [(5,5-10),(10,5-10),(15,5-10),(20,5-10),(25,5-10),(30,5-10)]
|
||||||
|
store.setPartial(new Range(1, 1, 31, 2), [
|
||||||
|
new MultilineTokens2(5, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 1,
|
||||||
|
5, 5, 10, 2,
|
||||||
|
10, 5, 10, 3,
|
||||||
|
15, 5, 10, 4,
|
||||||
|
20, 5, 10, 5,
|
||||||
|
25, 5, 10, 6,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const lineTokens = store.addSemanticTokens(10, new LineTokens(new Uint32Array([12, 1]), `enum Enum1 {`));
|
||||||
|
assert.equal(lineTokens.getCount(), 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('partial tokens 2', () => {
|
||||||
|
const store = new TokensStore2();
|
||||||
|
|
||||||
|
// setPartial: [1,1 -> 31,2], [(5,5-10),(10,5-10),(15,5-10),(20,5-10),(25,5-10),(30,5-10)]
|
||||||
|
store.setPartial(new Range(1, 1, 31, 2), [
|
||||||
|
new MultilineTokens2(5, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 1,
|
||||||
|
5, 5, 10, 2,
|
||||||
|
10, 5, 10, 3,
|
||||||
|
15, 5, 10, 4,
|
||||||
|
20, 5, 10, 5,
|
||||||
|
25, 5, 10, 6,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
// setPartial: [6,1 -> 36,2], [(10,5-10),(15,5-10),(20,5-10),(25,5-10),(30,5-10),(35,5-10)]
|
||||||
|
store.setPartial(new Range(6, 1, 36, 2), [
|
||||||
|
new MultilineTokens2(10, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 2,
|
||||||
|
5, 5, 10, 3,
|
||||||
|
10, 5, 10, 4,
|
||||||
|
15, 5, 10, 5,
|
||||||
|
20, 5, 10, 6,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
// setPartial: [17,1 -> 42,1], [(20,5-10),(25,5-10),(30,5-10),(35,5-10),(40,5-10)]
|
||||||
|
store.setPartial(new Range(17, 1, 42, 1), [
|
||||||
|
new MultilineTokens2(20, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 4,
|
||||||
|
5, 5, 10, 5,
|
||||||
|
10, 5, 10, 6,
|
||||||
|
15, 5, 10, 7,
|
||||||
|
20, 5, 10, 8,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const lineTokens = store.addSemanticTokens(20, new LineTokens(new Uint32Array([12, 1]), `enum Enum1 {`));
|
||||||
|
assert.equal(lineTokens.getCount(), 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('partial tokens 3', () => {
|
||||||
|
const store = new TokensStore2();
|
||||||
|
|
||||||
|
// setPartial: [1,1 -> 31,2], [(5,5-10),(10,5-10),(15,5-10),(20,5-10),(25,5-10),(30,5-10)]
|
||||||
|
store.setPartial(new Range(1, 1, 31, 2), [
|
||||||
|
new MultilineTokens2(5, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 1,
|
||||||
|
5, 5, 10, 2,
|
||||||
|
10, 5, 10, 3,
|
||||||
|
15, 5, 10, 4,
|
||||||
|
20, 5, 10, 5,
|
||||||
|
25, 5, 10, 6,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
// setPartial: [11,1 -> 16,2], [(15,5-10),(20,5-10)]
|
||||||
|
store.setPartial(new Range(11, 1, 16, 2), [
|
||||||
|
new MultilineTokens2(10, new SparseEncodedTokens(new Uint32Array([
|
||||||
|
0, 5, 10, 3,
|
||||||
|
5, 5, 10, 4,
|
||||||
|
])))
|
||||||
|
]);
|
||||||
|
|
||||||
|
const lineTokens = store.addSemanticTokens(5, new LineTokens(new Uint32Array([12, 1]), `enum Enum1 {`));
|
||||||
|
assert.equal(lineTokens.getCount(), 3);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -459,10 +459,10 @@ suite('viewLineRenderer.renderLine', () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
let expectedOutput = [
|
let expectedOutput = [
|
||||||
'<span class="mtk6" dir="ltr">var</span>',
|
'<span class="mtk6">var</span>',
|
||||||
'<span class="mtk1" dir="ltr">\u00a0קודמות\u00a0=\u00a0</span>',
|
'<span class="mtk1">\u00a0קודמות\u00a0=\u00a0</span>',
|
||||||
'<span class="mtk20" dir="ltr">"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"</span>',
|
'<span class="mtk20">"מיותר\u00a0קודמות\u00a0צ\'ט\u00a0של,\u00a0אם\u00a0לשון\u00a0העברית\u00a0שינויים\u00a0ויש,\u00a0אם"</span>',
|
||||||
'<span class="mtk1" dir="ltr">;</span>'
|
'<span class="mtk1">;</span>'
|
||||||
].join('');
|
].join('');
|
||||||
|
|
||||||
let _actual = renderViewLine(new RenderLineInput(
|
let _actual = renderViewLine(new RenderLineInput(
|
||||||
@@ -487,7 +487,7 @@ suite('viewLineRenderer.renderLine', () => {
|
|||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
assert.equal(_actual.html, '<span>' + expectedOutput + '</span>');
|
assert.equal(_actual.html, '<span dir="ltr">' + expectedOutput + '</span>');
|
||||||
assert.equal(_actual.containsRTL, true);
|
assert.equal(_actual.containsRTL, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -676,7 +676,7 @@ suite('viewLineRenderer.renderLine', () => {
|
|||||||
let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.';
|
let lineText = 'את גרמנית בהתייחסות שמו, שנתי המשפט אל חפש, אם כתב אחרים ולחבר. של התוכן אודות בויקיפדיה כלל, של עזרה כימיה היא. על עמוד יוצרים מיתולוגיה סדר, אם שכל שתפו לעברית שינויים, אם שאלות אנגלית עזה. שמות בקלות מה סדר.';
|
||||||
let lineParts = createViewLineTokens([createPart(lineText.length, 1)]);
|
let lineParts = createViewLineTokens([createPart(lineText.length, 1)]);
|
||||||
let expectedOutput = [
|
let expectedOutput = [
|
||||||
'<span class="mtk1" dir="ltr">את\u00a0גרמנית\u00a0בהתייחסות\u00a0שמו,\u00a0שנתי\u00a0המשפט\u00a0אל\u00a0חפש,\u00a0אם\u00a0כתב\u00a0אחרים\u00a0ולחבר.\u00a0של\u00a0התוכן\u00a0אודות\u00a0בויקיפדיה\u00a0כלל,\u00a0של\u00a0עזרה\u00a0כימיה\u00a0היא.\u00a0על\u00a0עמוד\u00a0יוצרים\u00a0מיתולוגיה\u00a0סדר,\u00a0אם\u00a0שכל\u00a0שתפו\u00a0לעברית\u00a0שינויים,\u00a0אם\u00a0שאלות\u00a0אנגלית\u00a0עזה.\u00a0שמות\u00a0בקלות\u00a0מה\u00a0סדר.</span>'
|
'<span class="mtk1">את\u00a0גרמנית\u00a0בהתייחסות\u00a0שמו,\u00a0שנתי\u00a0המשפט\u00a0אל\u00a0חפש,\u00a0אם\u00a0כתב\u00a0אחרים\u00a0ולחבר.\u00a0של\u00a0התוכן\u00a0אודות\u00a0בויקיפדיה\u00a0כלל,\u00a0של\u00a0עזרה\u00a0כימיה\u00a0היא.\u00a0על\u00a0עמוד\u00a0יוצרים\u00a0מיתולוגיה\u00a0סדר,\u00a0אם\u00a0שכל\u00a0שתפו\u00a0לעברית\u00a0שינויים,\u00a0אם\u00a0שאלות\u00a0אנגלית\u00a0עזה.\u00a0שמות\u00a0בקלות\u00a0מה\u00a0סדר.</span>'
|
||||||
];
|
];
|
||||||
let actual = renderViewLine(new RenderLineInput(
|
let actual = renderViewLine(new RenderLineInput(
|
||||||
false,
|
false,
|
||||||
@@ -699,7 +699,7 @@ suite('viewLineRenderer.renderLine', () => {
|
|||||||
false,
|
false,
|
||||||
null
|
null
|
||||||
));
|
));
|
||||||
assert.equal(actual.html, '<span>' + expectedOutput.join('') + '</span>');
|
assert.equal(actual.html, '<span dir="ltr">' + expectedOutput.join('') + '</span>');
|
||||||
assert.equal(actual.containsRTL, true);
|
assert.equal(actual.containsRTL, true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function parseTest(fileName: string): ITest {
|
|||||||
return { content, assertions };
|
return { content, assertions };
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-expect-error
|
||||||
function executeTest(fileName: string, parseFunc: IParseFunc): void {
|
function executeTest(fileName: string, parseFunc: IParseFunc): void {
|
||||||
const { content, assertions } = parseTest(fileName);
|
const { content, assertions } = parseTest(fileName);
|
||||||
const actual = parseFunc(content);
|
const actual = parseFunc(content);
|
||||||
|
|||||||
4
src/vs/monaco.d.ts
vendored
4
src/vs/monaco.d.ts
vendored
@@ -3649,10 +3649,6 @@ declare namespace monaco.editor {
|
|||||||
* Overwrite word ends on accept. Default to false.
|
* Overwrite word ends on accept. Default to false.
|
||||||
*/
|
*/
|
||||||
insertMode?: 'insert' | 'replace';
|
insertMode?: 'insert' | 'replace';
|
||||||
/**
|
|
||||||
* Show a highlight when suggestion replaces or keep text after the cursor. Defaults to false.
|
|
||||||
*/
|
|
||||||
insertHighlight?: boolean;
|
|
||||||
/**
|
/**
|
||||||
* Enable graceful matching. Defaults to true.
|
* Enable graceful matching. Defaults to true.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -14,6 +14,13 @@
|
|||||||
*---------------------------------------------------------------------------------------------
|
*---------------------------------------------------------------------------------------------
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'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;
|
var NLSLoaderPlugin;
|
||||||
(function (NLSLoaderPlugin) {
|
(function (NLSLoaderPlugin) {
|
||||||
var Environment = /** @class */ (function () {
|
var Environment = /** @class */ (function () {
|
||||||
@@ -94,7 +101,7 @@ var NLSLoaderPlugin;
|
|||||||
for (var _i = 2; _i < arguments.length; _i++) {
|
for (var _i = 2; _i < arguments.length; _i++) {
|
||||||
args[_i - 2] = arguments[_i];
|
args[_i - 2] = arguments[_i];
|
||||||
}
|
}
|
||||||
return localize.apply(void 0, [_this._env, data, message].concat(args));
|
return localize.apply(void 0, __spreadArrays([_this._env, data, message], args));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
NLSPlugin.prototype.setPseudoTranslation = function (value) {
|
NLSPlugin.prototype.setPseudoTranslation = function (value) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
import { IWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
||||||
|
|
||||||
@@ -15,6 +15,12 @@ export interface IWorkspaceBackupInfo {
|
|||||||
remoteAuthority?: string;
|
remoteAuthority?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isWorkspaceBackupInfo(obj: unknown): obj is IWorkspaceBackupInfo {
|
||||||
|
const candidate = obj as IWorkspaceBackupInfo;
|
||||||
|
|
||||||
|
return candidate && isWorkspaceIdentifier(candidate.workspace);
|
||||||
|
}
|
||||||
|
|
||||||
export interface IBackupMainService {
|
export interface IBackupMainService {
|
||||||
_serviceBrand: undefined;
|
_serviceBrand: undefined;
|
||||||
|
|
||||||
@@ -31,4 +37,12 @@ export interface IBackupMainService {
|
|||||||
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void;
|
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void;
|
||||||
unregisterFolderBackupSync(folderUri: URI): void;
|
unregisterFolderBackupSync(folderUri: URI): void;
|
||||||
unregisterEmptyWindowBackupSync(backupFolder: string): void;
|
unregisterEmptyWindowBackupSync(backupFolder: string): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All folders or workspaces that are known to have
|
||||||
|
* backups stored. This call is long running because
|
||||||
|
* it checks for each backup location if any backups
|
||||||
|
* are stored.
|
||||||
|
*/
|
||||||
|
getDirtyWorkspaces(): Promise<Array<IWorkspaceIdentifier | URI>>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ import * as crypto from 'crypto';
|
|||||||
import * as path from 'vs/base/common/path';
|
import * as path from 'vs/base/common/path';
|
||||||
import * as platform from 'vs/base/common/platform';
|
import * as platform from 'vs/base/common/platform';
|
||||||
import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs';
|
import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs';
|
||||||
import * as arrays from 'vs/base/common/arrays';
|
import { IBackupMainService, IWorkspaceBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
|
||||||
import { IBackupMainService, IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup';
|
|
||||||
import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||||
@@ -28,9 +27,9 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
protected backupHome: string;
|
protected backupHome: string;
|
||||||
protected workspacesJsonPath: string;
|
protected workspacesJsonPath: string;
|
||||||
|
|
||||||
private rootWorkspaces: IWorkspaceBackupInfo[] = [];
|
private workspaces: IWorkspaceBackupInfo[] = [];
|
||||||
private folderWorkspaces: URI[] = [];
|
private folders: URI[] = [];
|
||||||
private emptyWorkspaces: IEmptyWindowBackupInfo[] = [];
|
private emptyWindows: IEmptyWindowBackupInfo[] = [];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@IEnvironmentService environmentService: IEnvironmentService,
|
@IEnvironmentService environmentService: IEnvironmentService,
|
||||||
@@ -51,31 +50,31 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
// read empty workspaces backups first
|
// read empty workspaces backups first
|
||||||
if (backups.emptyWorkspaceInfos) {
|
if (backups.emptyWorkspaceInfos) {
|
||||||
this.emptyWorkspaces = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos);
|
this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaceInfos);
|
||||||
} else if (Array.isArray(backups.emptyWorkspaces)) {
|
} else if (Array.isArray(backups.emptyWorkspaces)) {
|
||||||
// read legacy entries
|
// read legacy entries
|
||||||
this.emptyWorkspaces = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(backupFolder => ({ backupFolder })));
|
this.emptyWindows = await this.validateEmptyWorkspaces(backups.emptyWorkspaces.map(emptyWindow => ({ backupFolder: emptyWindow })));
|
||||||
}
|
}
|
||||||
|
|
||||||
// read workspace backups
|
// read workspace backups
|
||||||
let rootWorkspaces: IWorkspaceBackupInfo[] = [];
|
let rootWorkspaces: IWorkspaceBackupInfo[] = [];
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(backups.rootURIWorkspaces)) {
|
if (Array.isArray(backups.rootURIWorkspaces)) {
|
||||||
rootWorkspaces = backups.rootURIWorkspaces.map(f => ({ workspace: { id: f.id, configPath: URI.parse(f.configURIPath) }, remoteAuthority: f.remoteAuthority }));
|
rootWorkspaces = backups.rootURIWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) }, remoteAuthority: workspace.remoteAuthority }));
|
||||||
} else if (Array.isArray(backups.rootWorkspaces)) {
|
} else if (Array.isArray(backups.rootWorkspaces)) {
|
||||||
rootWorkspaces = backups.rootWorkspaces.map(f => ({ workspace: { id: f.id, configPath: URI.file(f.configPath) } }));
|
rootWorkspaces = backups.rootWorkspaces.map(workspace => ({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }));
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// ignore URI parsing exceptions
|
// ignore URI parsing exceptions
|
||||||
}
|
}
|
||||||
|
|
||||||
this.rootWorkspaces = await this.validateWorkspaces(rootWorkspaces);
|
this.workspaces = await this.validateWorkspaces(rootWorkspaces);
|
||||||
|
|
||||||
// read folder backups
|
// read folder backups
|
||||||
let workspaceFolders: URI[] = [];
|
let workspaceFolders: URI[] = [];
|
||||||
try {
|
try {
|
||||||
if (Array.isArray(backups.folderURIWorkspaces)) {
|
if (Array.isArray(backups.folderURIWorkspaces)) {
|
||||||
workspaceFolders = backups.folderURIWorkspaces.map(f => URI.parse(f));
|
workspaceFolders = backups.folderURIWorkspaces.map(folder => URI.parse(folder));
|
||||||
} else if (Array.isArray(backups.folderWorkspaces)) {
|
} else if (Array.isArray(backups.folderWorkspaces)) {
|
||||||
// migrate legacy folder paths
|
// migrate legacy folder paths
|
||||||
workspaceFolders = [];
|
workspaceFolders = [];
|
||||||
@@ -93,7 +92,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
// ignore URI parsing exceptions
|
// ignore URI parsing exceptions
|
||||||
}
|
}
|
||||||
|
|
||||||
this.folderWorkspaces = await this.validateFolders(workspaceFolders);
|
this.folders = await this.validateFolders(workspaceFolders);
|
||||||
|
|
||||||
// save again in case some workspaces or folders have been removed
|
// save again in case some workspaces or folders have been removed
|
||||||
await this.save();
|
await this.save();
|
||||||
@@ -106,7 +105,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.rootWorkspaces.slice(0); // return a copy
|
return this.workspaces.slice(0); // return a copy
|
||||||
}
|
}
|
||||||
|
|
||||||
getFolderBackupPaths(): URI[] {
|
getFolderBackupPaths(): URI[] {
|
||||||
@@ -116,7 +115,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.folderWorkspaces.slice(0); // return a copy
|
return this.folders.slice(0); // return a copy
|
||||||
}
|
}
|
||||||
|
|
||||||
isHotExitEnabled(): boolean {
|
isHotExitEnabled(): boolean {
|
||||||
@@ -134,12 +133,12 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getEmptyWindowBackupPaths(): IEmptyWindowBackupInfo[] {
|
getEmptyWindowBackupPaths(): IEmptyWindowBackupInfo[] {
|
||||||
return this.emptyWorkspaces.slice(0); // return a copy
|
return this.emptyWindows.slice(0); // return a copy
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWorkspaceBackupSync(workspaceInfo: IWorkspaceBackupInfo, migrateFrom?: string): string {
|
registerWorkspaceBackupSync(workspaceInfo: IWorkspaceBackupInfo, migrateFrom?: string): string {
|
||||||
if (!this.rootWorkspaces.some(window => workspaceInfo.workspace.id === window.workspace.id)) {
|
if (!this.workspaces.some(workspace => workspaceInfo.workspace.id === workspace.workspace.id)) {
|
||||||
this.rootWorkspaces.push(workspaceInfo);
|
this.workspaces.push(workspaceInfo);
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,16 +187,16 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void {
|
unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void {
|
||||||
const id = workspace.id;
|
const id = workspace.id;
|
||||||
let index = arrays.firstIndex(this.rootWorkspaces, w => w.workspace.id === id);
|
const index = this.workspaces.findIndex(workspace => workspace.workspace.id === id);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.rootWorkspaces.splice(index, 1);
|
this.workspaces.splice(index, 1);
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
registerFolderBackupSync(folderUri: URI): string {
|
registerFolderBackupSync(folderUri: URI): string {
|
||||||
if (!this.folderWorkspaces.some(uri => areResourcesEquals(folderUri, uri))) {
|
if (!this.folders.some(folder => areResourcesEquals(folderUri, folder))) {
|
||||||
this.folderWorkspaces.push(folderUri);
|
this.folders.push(folderUri);
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,9 +204,9 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unregisterFolderBackupSync(folderUri: URI): void {
|
unregisterFolderBackupSync(folderUri: URI): void {
|
||||||
let index = arrays.firstIndex(this.folderWorkspaces, uri => areResourcesEquals(folderUri, uri));
|
const index = this.folders.findIndex(folder => areResourcesEquals(folderUri, folder));
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.folderWorkspaces.splice(index, 1);
|
this.folders.splice(index, 1);
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,8 +215,8 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
// Generate a new folder if this is a new empty workspace
|
// Generate a new folder if this is a new empty workspace
|
||||||
const backupFolder = backupFolderCandidate || this.getRandomEmptyWindowId();
|
const backupFolder = backupFolderCandidate || this.getRandomEmptyWindowId();
|
||||||
if (!this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, backupFolder, !platform.isLinux))) {
|
if (!this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux))) {
|
||||||
this.emptyWorkspaces.push({ backupFolder, remoteAuthority });
|
this.emptyWindows.push({ backupFolder, remoteAuthority });
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,9 +224,9 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unregisterEmptyWindowBackupSync(backupFolder: string): void {
|
unregisterEmptyWindowBackupSync(backupFolder: string): void {
|
||||||
let index = arrays.firstIndex(this.emptyWorkspaces, w => !!w.backupFolder && isEqual(w.backupFolder, backupFolder, !platform.isLinux));
|
const index = this.emptyWindows.findIndex(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, backupFolder, !platform.isLinux));
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
this.emptyWorkspaces.splice(index, 1);
|
this.emptyWindows.splice(index, 1);
|
||||||
this.saveSync();
|
this.saveSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,7 +254,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
seenIds.add(workspace.id);
|
seenIds.add(workspace.id);
|
||||||
|
|
||||||
const backupPath = this.getBackupPath(workspace.id);
|
const backupPath = this.getBackupPath(workspace.id);
|
||||||
const hasBackups = await this.hasBackups(backupPath);
|
const hasBackups = await this.doHasBackups(backupPath);
|
||||||
|
|
||||||
// If the workspace has no backups, ignore it
|
// If the workspace has no backups, ignore it
|
||||||
if (hasBackups) {
|
if (hasBackups) {
|
||||||
@@ -287,7 +286,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
seenIds.add(key);
|
seenIds.add(key);
|
||||||
|
|
||||||
const backupPath = this.getBackupPath(this.getFolderHash(folderURI));
|
const backupPath = this.getBackupPath(this.getFolderHash(folderURI));
|
||||||
const hasBackups = await this.hasBackups(backupPath);
|
const hasBackups = await this.doHasBackups(backupPath);
|
||||||
|
|
||||||
// If the folder has no backups, ignore it
|
// If the folder has no backups, ignore it
|
||||||
if (hasBackups) {
|
if (hasBackups) {
|
||||||
@@ -325,7 +324,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
seenIds.add(backupFolder);
|
seenIds.add(backupFolder);
|
||||||
|
|
||||||
const backupPath = this.getBackupPath(backupFolder);
|
const backupPath = this.getBackupPath(backupFolder);
|
||||||
if (await this.hasBackups(backupPath)) {
|
if (await this.doHasBackups(backupPath)) {
|
||||||
result.push(backupInfo);
|
result.push(backupInfo);
|
||||||
} else {
|
} else {
|
||||||
await this.deleteStaleBackup(backupPath);
|
await this.deleteStaleBackup(backupPath);
|
||||||
@@ -350,7 +349,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
// New empty window backup
|
// New empty window backup
|
||||||
let newBackupFolder = this.getRandomEmptyWindowId();
|
let newBackupFolder = this.getRandomEmptyWindowId();
|
||||||
while (this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, newBackupFolder, platform.isLinux))) {
|
while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) {
|
||||||
newBackupFolder = this.getRandomEmptyWindowId();
|
newBackupFolder = this.getRandomEmptyWindowId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +361,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.emptyWorkspaces.push({ backupFolder: newBackupFolder });
|
this.emptyWindows.push({ backupFolder: newBackupFolder });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -371,7 +370,7 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
// New empty window backup
|
// New empty window backup
|
||||||
let newBackupFolder = this.getRandomEmptyWindowId();
|
let newBackupFolder = this.getRandomEmptyWindowId();
|
||||||
while (this.emptyWorkspaces.some(window => !!window.backupFolder && isEqual(window.backupFolder, newBackupFolder, platform.isLinux))) {
|
while (this.emptyWindows.some(emptyWindow => !!emptyWindow.backupFolder && isEqual(emptyWindow.backupFolder, newBackupFolder, platform.isLinux))) {
|
||||||
newBackupFolder = this.getRandomEmptyWindowId();
|
newBackupFolder = this.getRandomEmptyWindowId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -383,12 +382,53 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
this.logService.error(`Backup: Could not rename backup folder: ${ex.toString()}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.emptyWorkspaces.push({ backupFolder: newBackupFolder });
|
this.emptyWindows.push({ backupFolder: newBackupFolder });
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async hasBackups(backupPath: string): Promise<boolean> {
|
async getDirtyWorkspaces(): Promise<Array<IWorkspaceIdentifier | URI>> {
|
||||||
|
const dirtyWorkspaces: Array<IWorkspaceIdentifier | URI> = [];
|
||||||
|
|
||||||
|
// Workspaces with backups
|
||||||
|
for (const workspace of this.workspaces) {
|
||||||
|
if ((await this.hasBackups(workspace))) {
|
||||||
|
dirtyWorkspaces.push(workspace.workspace);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folders with backups
|
||||||
|
for (const folder of this.folders) {
|
||||||
|
if ((await this.hasBackups(folder))) {
|
||||||
|
dirtyWorkspaces.push(folder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirtyWorkspaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
private hasBackups(backupLocation: IWorkspaceBackupInfo | IEmptyWindowBackupInfo | URI): Promise<boolean> {
|
||||||
|
let backupPath: string;
|
||||||
|
|
||||||
|
// Folder
|
||||||
|
if (URI.isUri(backupLocation)) {
|
||||||
|
backupPath = this.getBackupPath(this.getFolderHash(backupLocation));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workspace
|
||||||
|
else if (isWorkspaceBackupInfo(backupLocation)) {
|
||||||
|
backupPath = this.getBackupPath(backupLocation.workspace.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty
|
||||||
|
else {
|
||||||
|
backupPath = backupLocation.backupFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.doHasBackups(backupPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async doHasBackups(backupPath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const backupSchemas = await readdir(backupPath);
|
const backupSchemas = await readdir(backupPath);
|
||||||
|
|
||||||
@@ -427,10 +467,10 @@ export class BackupMainService implements IBackupMainService {
|
|||||||
|
|
||||||
private serializeBackups(): IBackupWorkspacesFormat {
|
private serializeBackups(): IBackupWorkspacesFormat {
|
||||||
return {
|
return {
|
||||||
rootURIWorkspaces: this.rootWorkspaces.map(f => ({ id: f.workspace.id, configURIPath: f.workspace.configPath.toString(), remoteAuthority: f.remoteAuthority })),
|
rootURIWorkspaces: this.workspaces.map(workspace => ({ id: workspace.workspace.id, configURIPath: workspace.workspace.configPath.toString(), remoteAuthority: workspace.remoteAuthority })),
|
||||||
folderURIWorkspaces: this.folderWorkspaces.map(f => f.toString()),
|
folderURIWorkspaces: this.folders.map(folder => folder.toString()),
|
||||||
emptyWorkspaceInfos: this.emptyWorkspaces,
|
emptyWorkspaceInfos: this.emptyWindows,
|
||||||
emptyWorkspaces: this.emptyWorkspaces.map(info => info.backupFolder)
|
emptyWorkspaces: this.emptyWindows.map(emptyWindow => emptyWindow.backupFolder)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
|||||||
import { createHash } from 'crypto';
|
import { createHash } from 'crypto';
|
||||||
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
|
||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
|
import { isEqual } from 'vs/base/common/resources';
|
||||||
|
|
||||||
suite('BackupMainService', () => {
|
suite('BackupMainService', () => {
|
||||||
|
|
||||||
@@ -731,4 +732,45 @@ suite('BackupMainService', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
suite('getDirtyWorkspaces', () => {
|
||||||
|
test('should report if a workspace or folder has backups', async () => {
|
||||||
|
const folderBackupPath = service.registerFolderBackupSync(fooFile);
|
||||||
|
|
||||||
|
const backupWorkspaceInfo = toWorkspaceBackupInfo(fooFile.fsPath);
|
||||||
|
const workspaceBackupPath = service.registerWorkspaceBackupSync(backupWorkspaceInfo);
|
||||||
|
|
||||||
|
assert.equal(((await service.getDirtyWorkspaces()).length), 0);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pfs.mkdirp(path.join(folderBackupPath, Schemas.file));
|
||||||
|
await pfs.mkdirp(path.join(workspaceBackupPath, Schemas.untitled));
|
||||||
|
} catch (error) {
|
||||||
|
// ignore - folder might exist already
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(((await service.getDirtyWorkspaces()).length), 0);
|
||||||
|
|
||||||
|
fs.writeFileSync(path.join(folderBackupPath, Schemas.file, '594a4a9d82a277a899d4713a5b08f504'), '');
|
||||||
|
fs.writeFileSync(path.join(workspaceBackupPath, Schemas.untitled, '594a4a9d82a277a899d4713a5b08f504'), '');
|
||||||
|
|
||||||
|
const dirtyWorkspaces = await service.getDirtyWorkspaces();
|
||||||
|
assert.equal(dirtyWorkspaces.length, 2);
|
||||||
|
|
||||||
|
let found = 0;
|
||||||
|
for (const dirtyWorkpspace of dirtyWorkspaces) {
|
||||||
|
if (URI.isUri(dirtyWorkpspace)) {
|
||||||
|
if (isEqual(fooFile, dirtyWorkpspace)) {
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isEqual(backupWorkspaceInfo.workspace.configPath, dirtyWorkpspace.configPath)) {
|
||||||
|
found++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.equal(found, 2);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -54,8 +54,7 @@ export class BrowserClipboardService implements IClipboardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
readFindText(): string {
|
readFindText(): string {
|
||||||
// @ts-ignore
|
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
writeFindText(text: string): void { }
|
writeFindText(text: string): void { }
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
|
|||||||
fileService: IFileService
|
fileService: IFileService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._register(fileService.watch(settingsResource));
|
|
||||||
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
|
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
|
||||||
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,8 @@ export class ElectronMainService implements IElectronMainService {
|
|||||||
workspace: window.openedWorkspace,
|
workspace: window.openedWorkspace,
|
||||||
folderUri: window.openedFolderUri,
|
folderUri: window.openedFolderUri,
|
||||||
title: window.win.getTitle(),
|
title: window.win.getTitle(),
|
||||||
filename: window.getRepresentedFilename()
|
filename: window.getRepresentedFilename(),
|
||||||
|
dirty: window.isDocumentEdited()
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,7 +272,7 @@ export class ElectronMainService implements IElectronMainService {
|
|||||||
async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise<void> {
|
async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise<void> {
|
||||||
const window = this.windowById(windowId);
|
const window = this.windowById(windowId);
|
||||||
if (window) {
|
if (window) {
|
||||||
window.win.setDocumentEdited(edited);
|
window.setDocumentEdited(edited);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ export interface ParsedArgs {
|
|||||||
'nolazy'?: boolean;
|
'nolazy'?: boolean;
|
||||||
'force-device-scale-factor'?: string;
|
'force-device-scale-factor'?: string;
|
||||||
'force-renderer-accessibility'?: boolean;
|
'force-renderer-accessibility'?: boolean;
|
||||||
'ignore-certificate-error'?: boolean;
|
'ignore-certificate-errors'?: boolean;
|
||||||
'allow-insecure-localhost'?: boolean;
|
'allow-insecure-localhost'?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -154,6 +154,7 @@ export interface IEnvironmentService extends IUserHomeProvider {
|
|||||||
extensionsPath?: string;
|
extensionsPath?: string;
|
||||||
extensionDevelopmentLocationURI?: URI[];
|
extensionDevelopmentLocationURI?: URI[];
|
||||||
extensionTestsLocationURI?: URI;
|
extensionTestsLocationURI?: URI;
|
||||||
|
extensionEnabledProposedApi?: string[] | undefined;
|
||||||
logExtensionHostCommunication?: boolean;
|
logExtensionHostCommunication?: boolean;
|
||||||
|
|
||||||
debugExtensionHost: IExtensionHostDebugParams;
|
debugExtensionHost: IExtensionHostDebugParams;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as minimist from 'vscode-minimist';
|
import * as minimist from 'minimist';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||||
@@ -128,7 +128,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
|
|||||||
'nolazy': { type: 'boolean' }, // node inspect
|
'nolazy': { type: 'boolean' }, // node inspect
|
||||||
'force-device-scale-factor': { type: 'string' },
|
'force-device-scale-factor': { type: 'string' },
|
||||||
'force-renderer-accessibility': { type: 'boolean' },
|
'force-renderer-accessibility': { type: 'boolean' },
|
||||||
'ignore-certificate-error': { type: 'boolean' },
|
'ignore-certificate-errors': { type: 'boolean' },
|
||||||
'allow-insecure-localhost': { type: 'boolean' },
|
'allow-insecure-localhost': { type: 'boolean' },
|
||||||
'_urls': { type: 'string[]' },
|
'_urls': { type: 'string[]' },
|
||||||
|
|
||||||
|
|||||||
@@ -233,6 +233,18 @@ export class EnvironmentService implements IEnvironmentService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get extensionEnabledProposedApi(): string[] | undefined {
|
||||||
|
if (Array.isArray(this.args['enable-proposed-api'])) {
|
||||||
|
return this.args['enable-proposed-api'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('enable-proposed-api' in this.args) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
@memoize
|
@memoize
|
||||||
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); }
|
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); }
|
||||||
@memoize
|
@memoize
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
|
|||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { ReadableStreamEvents, transform } from 'vs/base/common/stream';
|
import { ReadableStreamEvents, transform } from 'vs/base/common/stream';
|
||||||
import { createReadStream } from 'vs/platform/files/common/io';
|
import { createReadStream } from 'vs/platform/files/common/io';
|
||||||
|
import { insert } from 'vs/base/common/arrays';
|
||||||
|
|
||||||
export interface IWatcherOptions {
|
export interface IWatcherOptions {
|
||||||
pollingInterval?: number;
|
pollingInterval?: number;
|
||||||
@@ -524,7 +525,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
|||||||
|
|
||||||
// Add to list of folders to watch recursively
|
// Add to list of folders to watch recursively
|
||||||
const folderToWatch = { path: this.toFilePath(resource), excludes };
|
const folderToWatch = { path: this.toFilePath(resource), excludes };
|
||||||
this.recursiveFoldersToWatch.push(folderToWatch);
|
const remove = insert(this.recursiveFoldersToWatch, folderToWatch);
|
||||||
|
|
||||||
// Trigger update
|
// Trigger update
|
||||||
this.refreshRecursiveWatchers();
|
this.refreshRecursiveWatchers();
|
||||||
@@ -532,7 +533,7 @@ export class DiskFileSystemProvider extends Disposable implements
|
|||||||
return toDisposable(() => {
|
return toDisposable(() => {
|
||||||
|
|
||||||
// Remove from list of folders to watch recursively
|
// Remove from list of folders to watch recursively
|
||||||
this.recursiveFoldersToWatch.splice(this.recursiveFoldersToWatch.indexOf(folderToWatch), 1);
|
remove();
|
||||||
|
|
||||||
// Trigger update
|
// Trigger update
|
||||||
this.refreshRecursiveWatchers();
|
this.refreshRecursiveWatchers();
|
||||||
@@ -543,10 +544,8 @@ export class DiskFileSystemProvider extends Disposable implements
|
|||||||
|
|
||||||
// Buffer requests for recursive watching to decide on right watcher
|
// Buffer requests for recursive watching to decide on right watcher
|
||||||
// that supports potentially watching more than one folder at once
|
// that supports potentially watching more than one folder at once
|
||||||
this.recursiveWatchRequestDelayer.trigger(() => {
|
this.recursiveWatchRequestDelayer.trigger(async () => {
|
||||||
this.doRefreshRecursiveWatchers();
|
this.doRefreshRecursiveWatchers();
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { IDiskFileChange, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
import { IDiskFileChange, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||||
import { OutOfProcessWin32FolderWatcher } from 'vs/platform/files/node/watcher/win32/csharpWatcherService';
|
import { OutOfProcessWin32FolderWatcher } from 'vs/platform/files/node/watcher/win32/csharpWatcherService';
|
||||||
import { posix } from 'vs/base/common/path';
|
import { posix } from 'vs/base/common/path';
|
||||||
import { rtrim, endsWith } from 'vs/base/common/strings';
|
import { rtrim } from 'vs/base/common/strings';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
export class FileWatcher implements IDisposable {
|
export class FileWatcher implements IDisposable {
|
||||||
@@ -22,7 +22,7 @@ export class FileWatcher implements IDisposable {
|
|||||||
) {
|
) {
|
||||||
this.folder = folders[0];
|
this.folder = folders[0];
|
||||||
|
|
||||||
if (this.folder.path.indexOf('\\\\') === 0 && endsWith(this.folder.path, posix.sep)) {
|
if (this.folder.path.indexOf('\\\\') === 0 && this.folder.path.endsWith(posix.sep)) {
|
||||||
// for some weird reason, node adds a trailing slash to UNC paths
|
// for some weird reason, node adds a trailing slash to UNC paths
|
||||||
// we never ever want trailing slashes as our base path unless
|
// we never ever want trailing slashes as our base path unless
|
||||||
// someone opens root ("/").
|
// someone opens root ("/").
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
|||||||
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||||
import { localize } from 'vs/nls';
|
import { localize } from 'vs/nls';
|
||||||
import { isEqualOrParent, basename } from 'vs/base/common/resources';
|
import { isEqualOrParent, basename } from 'vs/base/common/resources';
|
||||||
import { endsWith } from 'vs/base/common/strings';
|
|
||||||
|
|
||||||
export interface ILabelService {
|
export interface ILabelService {
|
||||||
_serviceBrand: undefined;
|
_serviceBrand: undefined;
|
||||||
@@ -61,7 +60,7 @@ export function getSimpleWorkspaceLabel(workspace: IWorkspaceIdentifier | URI, w
|
|||||||
}
|
}
|
||||||
|
|
||||||
let filename = basename(workspace.configPath);
|
let filename = basename(workspace.configPath);
|
||||||
if (endsWith(filename, WORKSPACE_EXTENSION)) {
|
if (filename.endsWith(WORKSPACE_EXTENSION)) {
|
||||||
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||||
}
|
}
|
||||||
return localize('workspaceName', "{0} (Workspace)", filename);
|
return localize('workspaceName', "{0} (Workspace)", filename);
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export interface INeverShowAgainOptions {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to persist the choice in the current workspace or for all workspaces. By
|
* Whether to persist the choice in the current workspace or for all workspaces. By
|
||||||
* default it will be persisted for all workspaces.
|
* default it will be persisted for all workspaces (= `NeverShowAgainScope.GLOBAL`).
|
||||||
*/
|
*/
|
||||||
readonly scope?: NeverShowAgainScope;
|
readonly scope?: NeverShowAgainScope;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
|||||||
disposables.add(picker.onDidAccept(() => {
|
disposables.add(picker.onDidAccept(() => {
|
||||||
const [item] = picker.selectedItems;
|
const [item] = picker.selectedItems;
|
||||||
if (item) {
|
if (item) {
|
||||||
this.quickInputService.quickAccess.show(item.prefix);
|
this.quickInputService.quickAccess.show(item.prefix, { preserveValue: true });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export class HelpQuickAccessProvider implements IQuickAccessProvider {
|
|||||||
disposables.add(picker.onDidChangeValue(value => {
|
disposables.add(picker.onDidChangeValue(value => {
|
||||||
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
const providerDescriptor = this.registry.getQuickAccessProvider(value.substr(HelpQuickAccessProvider.PREFIX.length));
|
||||||
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
if (providerDescriptor && providerDescriptor.prefix && providerDescriptor.prefix !== HelpQuickAccessProvider.PREFIX) {
|
||||||
this.quickInputService.quickAccess.show(providerDescriptor.prefix);
|
this.quickInputService.quickAccess.show(providerDescriptor.prefix, { preserveValue: true });
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|||||||
@@ -11,14 +11,6 @@ import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cance
|
|||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { once } from 'vs/base/common/functional';
|
import { once } from 'vs/base/common/functional';
|
||||||
|
|
||||||
interface IInternalQuickAccessOptions extends IQuickAccessOptions {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal option to not rewrite the filter value at all but use it as is.
|
|
||||||
*/
|
|
||||||
preserveFilterValue?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QuickAccessController extends Disposable implements IQuickAccessController {
|
export class QuickAccessController extends Disposable implements IQuickAccessController {
|
||||||
|
|
||||||
private readonly registry = Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess);
|
private readonly registry = Registry.as<IQuickAccessRegistry>(Extensions.Quickaccess);
|
||||||
@@ -39,7 +31,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
show(value = '', options?: IInternalQuickAccessOptions): void {
|
show(value = '', options?: IQuickAccessOptions): void {
|
||||||
|
|
||||||
// Find provider for the value to show
|
// Find provider for the value to show
|
||||||
const [provider, descriptor] = this.getOrInstantiateProvider(value);
|
const [provider, descriptor] = this.getOrInstantiateProvider(value);
|
||||||
@@ -51,7 +43,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
|
|
||||||
// Apply value only if it is more specific than the prefix
|
// Apply value only if it is more specific than the prefix
|
||||||
// from the provider and we are not instructed to preserve
|
// from the provider and we are not instructed to preserve
|
||||||
if (value !== descriptor.prefix && !options?.preserveFilterValue) {
|
if (value !== descriptor.prefix && !options?.preserveValue) {
|
||||||
visibleQuickAccess.picker.value = value;
|
visibleQuickAccess.picker.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +54,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite the filter value based on certain rules unless disabled
|
// Rewrite the filter value based on certain rules unless disabled
|
||||||
if (descriptor && !options?.preserveFilterValue) {
|
if (descriptor && !options?.preserveValue) {
|
||||||
let newValue: string | undefined = undefined;
|
let newValue: string | undefined = undefined;
|
||||||
|
|
||||||
// If we have a visible provider with a value, take it's filter value but
|
// If we have a visible provider with a value, take it's filter value but
|
||||||
@@ -116,11 +108,11 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
picker.show();
|
picker.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private adjustValueSelection(picker: IQuickPick<IQuickPickItem>, descriptor?: IQuickAccessProviderDescriptor, options?: IInternalQuickAccessOptions): void {
|
private adjustValueSelection(picker: IQuickPick<IQuickPickItem>, descriptor?: IQuickAccessProviderDescriptor, options?: IQuickAccessOptions): void {
|
||||||
let valueSelection: [number, number];
|
let valueSelection: [number, number];
|
||||||
|
|
||||||
// Preserve: just always put the cursor at the end
|
// Preserve: just always put the cursor at the end
|
||||||
if (options?.preserveFilterValue) {
|
if (options?.preserveValue) {
|
||||||
valueSelection = [picker.value.length, picker.value.length];
|
valueSelection = [picker.value.length, picker.value.length];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,7 +139,7 @@ export class QuickAccessController extends Disposable implements IQuickAccessCon
|
|||||||
disposables.add(picker.onDidChangeValue(value => {
|
disposables.add(picker.onDidChangeValue(value => {
|
||||||
const [providerForValue] = this.getOrInstantiateProvider(value);
|
const [providerForValue] = this.getOrInstantiateProvider(value);
|
||||||
if (providerForValue !== provider) {
|
if (providerForValue !== provider) {
|
||||||
this.show(value, { preserveFilterValue: true } /* do not rewrite value from user typing! */);
|
this.show(value, { preserveValue: true } /* do not rewrite value from user typing! */);
|
||||||
} else {
|
} else {
|
||||||
visibleQuickAccess.value = value; // remember the value in our visible one
|
visibleQuickAccess.value = value; // remember the value in our visible one
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,7 @@
|
|||||||
import { IQuickPick, IQuickPickItem, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput';
|
import { IQuickPick, IQuickPickItem, IQuickNavigateConfiguration } from 'vs/platform/quickinput/common/quickInput';
|
||||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||||
import { Registry } from 'vs/platform/registry/common/platform';
|
import { Registry } from 'vs/platform/registry/common/platform';
|
||||||
import { first, coalesce } from 'vs/base/common/arrays';
|
import { coalesce } from 'vs/base/common/arrays';
|
||||||
import { startsWith } from 'vs/base/common/strings';
|
|
||||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { ItemActivation } from 'vs/base/parts/quickinput/common/quickInput';
|
import { ItemActivation } from 'vs/base/parts/quickinput/common/quickInput';
|
||||||
|
|
||||||
@@ -22,7 +21,13 @@ export interface IQuickAccessOptions {
|
|||||||
* Allows to configure a different item activation strategy.
|
* Allows to configure a different item activation strategy.
|
||||||
* By default the first item in the list will get activated.
|
* By default the first item in the list will get activated.
|
||||||
*/
|
*/
|
||||||
itemActivation?: ItemActivation
|
itemActivation?: ItemActivation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether to take the input value as is and not restore it
|
||||||
|
* from any existing value if quick access is visible.
|
||||||
|
*/
|
||||||
|
preserveValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IQuickAccessController {
|
export interface IQuickAccessController {
|
||||||
@@ -177,7 +182,7 @@ export class QuickAccessRegistry implements IQuickAccessRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
getQuickAccessProvider(prefix: string): IQuickAccessProviderDescriptor | undefined {
|
||||||
const result = prefix ? (first(this.providers, provider => startsWith(prefix, provider.prefix)) || undefined) : undefined;
|
const result = prefix ? (this.providers.find(provider => prefix.startsWith(provider.prefix)) || undefined) : undefined;
|
||||||
|
|
||||||
return result || this.defaultProvider;
|
return result || this.defaultProvider;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import { IStorage, Storage, IStorageDatabase, IStorageItemsChangeEvent, IUpdateR
|
|||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { joinPath } from 'vs/base/common/resources';
|
import { joinPath } from 'vs/base/common/resources';
|
||||||
import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async';
|
import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async';
|
||||||
import { serializableToMap, mapToSerializable } from 'vs/base/common/map';
|
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
|
|||||||
|
|
||||||
this.ensureWatching(); // now that the file must exist, ensure we watch it for changes
|
this.ensureWatching(); // now that the file must exist, ensure we watch it for changes
|
||||||
|
|
||||||
return serializableToMap(JSON.parse(itemsRaw.value.toString()));
|
return new Map(JSON.parse(itemsRaw.value.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateItems(request: IUpdateRequest): Promise<void> {
|
async updateItems(request: IUpdateRequest): Promise<void> {
|
||||||
@@ -311,7 +310,7 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
|
|||||||
try {
|
try {
|
||||||
this._hasPendingUpdate = true;
|
this._hasPendingUpdate = true;
|
||||||
|
|
||||||
await this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(mapToSerializable(items))));
|
await this.fileService.writeFile(this.file, VSBuffer.fromString(JSON.stringify(Array.from(items.entries()))));
|
||||||
|
|
||||||
this.ensureWatching(); // now that the file must exist, ensure we watch it for changes
|
this.ensureWatching(); // now that the file must exist, ensure we watch it for changes
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
|||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService';
|
import { IStorageChangeEvent, IStorageMainService } from 'vs/platform/storage/node/storageMainService';
|
||||||
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage';
|
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/parts/storage/common/storage';
|
||||||
import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map';
|
|
||||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||||
import { ILogService } from 'vs/platform/log/common/log';
|
import { ILogService } from 'vs/platform/log/common/log';
|
||||||
import { generateUuid } from 'vs/base/common/uuid';
|
import { generateUuid } from 'vs/base/common/uuid';
|
||||||
@@ -117,7 +116,10 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { changed: mapToSerializable(changed), deleted: values(deleted) };
|
return {
|
||||||
|
changed: Array.from(changed.entries()),
|
||||||
|
deleted: Array.from(deleted.values())
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
listen(_: unknown, event: string): Event<any> {
|
listen(_: unknown, event: string): Event<any> {
|
||||||
@@ -136,7 +138,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
|||||||
// handle call
|
// handle call
|
||||||
switch (command) {
|
switch (command) {
|
||||||
case 'getItems': {
|
case 'getItems': {
|
||||||
return mapToSerializable(this.storageMainService.items);
|
return Array.from(this.storageMainService.items.entries());
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'updateItems': {
|
case 'updateItems': {
|
||||||
@@ -182,7 +184,7 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
|||||||
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
|
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
|
||||||
if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
|
if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
|
||||||
this._onDidChangeItemsExternal.fire({
|
this._onDidChangeItemsExternal.fire({
|
||||||
changed: e.changed ? serializableToMap(e.changed) : undefined,
|
changed: e.changed ? new Map(e.changed) : undefined,
|
||||||
deleted: e.deleted ? new Set<string>(e.deleted) : undefined
|
deleted: e.deleted ? new Set<string>(e.deleted) : undefined
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -191,18 +193,18 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
|||||||
async getItems(): Promise<Map<string, string>> {
|
async getItems(): Promise<Map<string, string>> {
|
||||||
const items: Item[] = await this.channel.call('getItems');
|
const items: Item[] = await this.channel.call('getItems');
|
||||||
|
|
||||||
return serializableToMap(items);
|
return new Map(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateItems(request: IUpdateRequest): Promise<void> {
|
updateItems(request: IUpdateRequest): Promise<void> {
|
||||||
const serializableRequest: ISerializableUpdateRequest = Object.create(null);
|
const serializableRequest: ISerializableUpdateRequest = Object.create(null);
|
||||||
|
|
||||||
if (request.insert) {
|
if (request.insert) {
|
||||||
serializableRequest.insert = mapToSerializable(request.insert);
|
serializableRequest.insert = Array.from(request.insert.entries());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.delete) {
|
if (request.delete) {
|
||||||
serializableRequest.delete = values(request.delete);
|
serializableRequest.delete = Array.from(request.delete.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.channel.call('updateItems', serializableRequest);
|
return this.channel.call('updateItems', serializableRequest);
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
|
|||||||
|
|
||||||
export interface TokenSelector {
|
export interface TokenSelector {
|
||||||
match(type: string, modifiers: string[], language: string): number;
|
match(type: string, modifiers: string[], language: string): number;
|
||||||
readonly selectorString: string;
|
readonly id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TokenTypeOrModifierContribution {
|
export interface TokenTypeOrModifierContribution {
|
||||||
@@ -155,7 +155,7 @@ export namespace TokenStylingRule {
|
|||||||
}
|
}
|
||||||
export function toJSONObject(rule: TokenStylingRule): any {
|
export function toJSONObject(rule: TokenStylingRule): any {
|
||||||
return {
|
return {
|
||||||
_selector: rule.selector.selectorString,
|
_selector: rule.selector.id,
|
||||||
_style: TokenStyle.toJSONObject(rule.style)
|
_style: TokenStyle.toJSONObject(rule.style)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -164,7 +164,7 @@ export namespace TokenStylingRule {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return r1 !== undefined && r2 !== undefined
|
return r1 !== undefined && r2 !== undefined
|
||||||
&& r1.selector && r2.selector && r1.selector.selectorString === r2.selector.selectorString
|
&& r1.selector && r2.selector && r1.selector.id === r2.selector.id
|
||||||
&& TokenStyle.equals(r1.style, r2.style);
|
&& TokenStyle.equals(r1.style, r2.style);
|
||||||
}
|
}
|
||||||
export function is(r: any): r is TokenStylingRule {
|
export function is(r: any): r is TokenStylingRule {
|
||||||
@@ -203,10 +203,11 @@ export interface ITokenClassificationRegistry {
|
|||||||
/**
|
/**
|
||||||
* Parses a token selector from a selector string.
|
* Parses a token selector from a selector string.
|
||||||
* @param selectorString selector string in the form (*|type)(.modifier)*
|
* @param selectorString selector string in the form (*|type)(.modifier)*
|
||||||
|
* @param language language to which the selector applies or undefined if the selector is for all languafe
|
||||||
* @returns the parsesd selector
|
* @returns the parsesd selector
|
||||||
* @throws an error if the string is not a valid selector
|
* @throws an error if the string is not a valid selector
|
||||||
*/
|
*/
|
||||||
parseTokenSelector(selectorString: string): TokenSelector;
|
parseTokenSelector(selectorString: string, language?: string): TokenSelector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a TokenStyle default to the registry.
|
* Register a TokenStyle default to the registry.
|
||||||
@@ -335,13 +336,13 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
|||||||
this.tokenStylingSchema.properties[`*.${id}`] = getStylingSchemeEntry(description, deprecationMessage);
|
this.tokenStylingSchema.properties[`*.${id}`] = getStylingSchemeEntry(description, deprecationMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public parseTokenSelector(selectorString: string): TokenSelector {
|
public parseTokenSelector(selectorString: string, language?: string): TokenSelector {
|
||||||
const selector = parseClassifierString(selectorString);
|
const selector = parseClassifierString(selectorString, language);
|
||||||
|
|
||||||
if (!selector.type) {
|
if (!selector.type) {
|
||||||
return {
|
return {
|
||||||
match: () => -1,
|
match: () => -1,
|
||||||
selectorString
|
id: '$invalid'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -352,7 +353,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
|||||||
if (selector.language !== language) {
|
if (selector.language !== language) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
score += 100;
|
score += 10;
|
||||||
}
|
}
|
||||||
if (selector.type !== TOKEN_TYPE_WILDCARD) {
|
if (selector.type !== TOKEN_TYPE_WILDCARD) {
|
||||||
const hierarchy = this.getTypeHierarchy(type);
|
const hierarchy = this.getTypeHierarchy(type);
|
||||||
@@ -370,7 +371,7 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
|||||||
}
|
}
|
||||||
return score + selector.modifiers.length * 100;
|
return score + selector.modifiers.length * 100;
|
||||||
},
|
},
|
||||||
selectorString
|
id: `${[selector.type, ...selector.modifiers.sort()].join('.')}${selector.language !== undefined ? ':' + selector.language : ''}`
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -379,8 +380,8 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public deregisterTokenStyleDefault(selector: TokenSelector): void {
|
public deregisterTokenStyleDefault(selector: TokenSelector): void {
|
||||||
const selectorString = selector.selectorString;
|
const selectorString = selector.id;
|
||||||
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => r.selector.selectorString !== selectorString);
|
this.tokenStylingDefaultRules = this.tokenStylingDefaultRules.filter(r => r.selector.id !== selectorString);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deregisterTokenType(id: string): void {
|
public deregisterTokenType(id: string): void {
|
||||||
@@ -442,9 +443,11 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
|||||||
const CHAR_LANGUAGE = TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR.charCodeAt(0);
|
const CHAR_LANGUAGE = TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR.charCodeAt(0);
|
||||||
const CHAR_MODIFIER = CLASSIFIER_MODIFIER_SEPARATOR.charCodeAt(0);
|
const CHAR_MODIFIER = CLASSIFIER_MODIFIER_SEPARATOR.charCodeAt(0);
|
||||||
|
|
||||||
export function parseClassifierString(s: string): { type: string, modifiers: string[], language: string | undefined; } {
|
export function parseClassifierString(s: string, defaultLanguage: string): { type: string, modifiers: string[], language: string; };
|
||||||
|
export function parseClassifierString(s: string, defaultLanguage?: string): { type: string, modifiers: string[], language: string | undefined; };
|
||||||
|
export function parseClassifierString(s: string, defaultLanguage: string | undefined): { type: string, modifiers: string[], language: string | undefined; } {
|
||||||
let k = s.length;
|
let k = s.length;
|
||||||
let language: string | undefined = undefined;
|
let language: string | undefined = defaultLanguage;
|
||||||
const modifiers = [];
|
const modifiers = [];
|
||||||
|
|
||||||
for (let i = k - 1; i >= 0; i--) {
|
for (let i = k - 1; i >= 0; i--) {
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
|
import { IUndoRedoService, IResourceUndoRedoElement, IWorkspaceUndoRedoElement, UndoRedoElementType, IUndoRedoElement, IPastFutureElements } from 'vs/platform/undoRedo/common/undoRedo';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { getComparisonKey as uriGetComparisonKey } from 'vs/base/common/resources';
|
|
||||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||||
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
|
||||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||||
@@ -14,6 +13,10 @@ import Severity from 'vs/base/common/severity';
|
|||||||
import { Schemas } from 'vs/base/common/network';
|
import { Schemas } from 'vs/base/common/network';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
|
||||||
|
function uriGetComparisonKey(resource: URI): string {
|
||||||
|
return resource.toString();
|
||||||
|
}
|
||||||
|
|
||||||
class ResourceStackElement {
|
class ResourceStackElement {
|
||||||
public readonly type = UndoRedoElementType.Resource;
|
public readonly type = UndoRedoElementType.Resource;
|
||||||
public readonly actual: IResourceUndoRedoElement;
|
public readonly actual: IResourceUndoRedoElement;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
|||||||
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
|
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
|
||||||
import { VSBuffer } from 'vs/base/common/buffer';
|
import { VSBuffer } from 'vs/base/common/buffer';
|
||||||
import { URI } from 'vs/base/common/uri';
|
import { URI } from 'vs/base/common/uri';
|
||||||
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync';
|
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources';
|
import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources';
|
||||||
import { CancelablePromise } from 'vs/base/common/async';
|
import { CancelablePromise } from 'vs/base/common/async';
|
||||||
@@ -144,6 +144,16 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getSyncPreview(): Promise<ISyncPreviewResult> {
|
||||||
|
if (!this.isEnabled()) {
|
||||||
|
return { hasLocalChanged: false, hasRemoteChanged: false };
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastSyncUserData = await this.getLastSyncUserData();
|
||||||
|
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
||||||
|
return this.generatePreview(remoteUserData, lastSyncUserData);
|
||||||
|
}
|
||||||
|
|
||||||
protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
|
protected async doSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
|
||||||
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
|
if (remoteUserData.syncData && remoteUserData.syncData.version > this.version) {
|
||||||
// current version is not compatible with cloud version
|
// current version is not compatible with cloud version
|
||||||
@@ -285,15 +295,14 @@ export abstract class AbstractSynchroniser extends Disposable {
|
|||||||
|
|
||||||
protected abstract readonly version: number;
|
protected abstract readonly version: number;
|
||||||
protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus>;
|
protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus>;
|
||||||
|
protected abstract generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<ISyncPreviewResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFileSyncPreviewResult {
|
export interface IFileSyncPreviewResult extends ISyncPreviewResult {
|
||||||
readonly fileContent: IFileContent | null;
|
readonly fileContent: IFileContent | null;
|
||||||
readonly remoteUserData: IRemoteUserData;
|
readonly remoteUserData: IRemoteUserData;
|
||||||
readonly lastSyncUserData: IRemoteUserData | null;
|
readonly lastSyncUserData: IRemoteUserData | null;
|
||||||
readonly content: string | null;
|
readonly content: string | null;
|
||||||
readonly hasLocalChanged: boolean;
|
|
||||||
readonly hasRemoteChanged: boolean;
|
|
||||||
readonly hasConflicts: boolean;
|
readonly hasConflicts: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
|
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle, ISyncPreviewResult } from 'vs/platform/userDataSync/common/userDataSync';
|
||||||
import { Event } from 'vs/base/common/event';
|
import { Event } from 'vs/base/common/event';
|
||||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||||
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||||
@@ -20,7 +20,7 @@ import { joinPath, dirname, basename } from 'vs/base/common/resources';
|
|||||||
import { format } from 'vs/base/common/jsonFormatter';
|
import { format } from 'vs/base/common/jsonFormatter';
|
||||||
import { applyEdits } from 'vs/base/common/jsonEdit';
|
import { applyEdits } from 'vs/base/common/jsonEdit';
|
||||||
|
|
||||||
interface ISyncPreviewResult {
|
interface IExtensionsSyncPreviewResult extends ISyncPreviewResult {
|
||||||
readonly localExtensions: ISyncExtension[];
|
readonly localExtensions: ISyncExtension[];
|
||||||
readonly remoteUserData: IRemoteUserData;
|
readonly remoteUserData: IRemoteUserData;
|
||||||
readonly lastSyncUserData: ILastSyncUserData | null;
|
readonly lastSyncUserData: ILastSyncUserData | null;
|
||||||
@@ -82,7 +82,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||||||
const localExtensions = await this.getLocalExtensions();
|
const localExtensions = await this.getLocalExtensions();
|
||||||
const remoteExtensions = this.parseExtensions(remoteUserData.syncData);
|
const remoteExtensions = this.parseExtensions(remoteUserData.syncData);
|
||||||
const { added, updated, remote, removed } = merge(localExtensions, remoteExtensions, localExtensions, [], this.getIgnoredExtensions());
|
const { added, updated, remote, removed } = merge(localExtensions, remoteExtensions, localExtensions, [], this.getIgnoredExtensions());
|
||||||
await this.apply({ added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData });
|
await this.apply({
|
||||||
|
added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData,
|
||||||
|
hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0,
|
||||||
|
hasRemoteChanged: remote !== null
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// No remote exists to pull
|
// No remote exists to pull
|
||||||
@@ -112,7 +116,11 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||||||
const { added, removed, updated, remote } = merge(localExtensions, null, null, [], this.getIgnoredExtensions());
|
const { added, removed, updated, remote } = merge(localExtensions, null, null, [], this.getIgnoredExtensions());
|
||||||
const lastSyncUserData = await this.getLastSyncUserData<ILastSyncUserData>();
|
const lastSyncUserData = await this.getLastSyncUserData<ILastSyncUserData>();
|
||||||
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
||||||
await this.apply({ added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData }, true);
|
await this.apply({
|
||||||
|
added, removed, updated, remote, remoteUserData, localExtensions, skippedExtensions: [], lastSyncUserData,
|
||||||
|
hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0,
|
||||||
|
hasRemoteChanged: remote !== null
|
||||||
|
}, true);
|
||||||
|
|
||||||
this.logService.info(`${this.syncResourceLogLabel}: Finished pushing extensions.`);
|
this.logService.info(`${this.syncResourceLogLabel}: Finished pushing extensions.`);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -163,12 +171,12 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<SyncStatus> {
|
protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<SyncStatus> {
|
||||||
const previewResult = await this.getPreview(remoteUserData, lastSyncUserData);
|
const previewResult = await this.generatePreview(remoteUserData, lastSyncUserData);
|
||||||
await this.apply(previewResult);
|
await this.apply(previewResult);
|
||||||
return SyncStatus.Idle;
|
return SyncStatus.Idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<ISyncPreviewResult> {
|
protected async generatePreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null): Promise<IExtensionsSyncPreviewResult> {
|
||||||
const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? this.parseExtensions(remoteUserData.syncData) : null;
|
const remoteExtensions: ISyncExtension[] | null = remoteUserData.syncData ? this.parseExtensions(remoteUserData.syncData) : null;
|
||||||
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? this.parseExtensions(lastSyncUserData.syncData!) : null;
|
const lastSyncExtensions: ISyncExtension[] | null = lastSyncUserData ? this.parseExtensions(lastSyncUserData.syncData!) : null;
|
||||||
const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : [];
|
const skippedExtensions: ISyncExtension[] = lastSyncUserData ? lastSyncUserData.skippedExtensions || [] : [];
|
||||||
@@ -183,22 +191,31 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
|||||||
|
|
||||||
const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, this.getIgnoredExtensions());
|
const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, this.getIgnoredExtensions());
|
||||||
|
|
||||||
return { added, removed, updated, remote, skippedExtensions, remoteUserData, localExtensions, lastSyncUserData };
|
return {
|
||||||
|
added,
|
||||||
|
removed,
|
||||||
|
updated,
|
||||||
|
remote,
|
||||||
|
skippedExtensions,
|
||||||
|
remoteUserData,
|
||||||
|
localExtensions,
|
||||||
|
lastSyncUserData,
|
||||||
|
hasLocalChanged: added.length > 0 || removed.length > 0 || updated.length > 0,
|
||||||
|
hasRemoteChanged: remote !== null
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private getIgnoredExtensions() {
|
private getIgnoredExtensions() {
|
||||||
return this.configurationService.getValue<string[]>('sync.ignoredExtensions') || [];
|
return this.configurationService.getValue<string[]>('sync.ignoredExtensions') || [];
|
||||||
}
|
}
|
||||||
|
|
||||||
private async apply({ added, removed, updated, remote, remoteUserData, skippedExtensions, lastSyncUserData, localExtensions }: ISyncPreviewResult, forcePush?: boolean): Promise<void> {
|
private async apply({ added, removed, updated, remote, remoteUserData, skippedExtensions, lastSyncUserData, localExtensions, hasLocalChanged, hasRemoteChanged }: IExtensionsSyncPreviewResult, forcePush?: boolean): Promise<void> {
|
||||||
|
|
||||||
const hasChanges = added.length || removed.length || updated.length || remote;
|
if (!hasLocalChanged && !hasRemoteChanged) {
|
||||||
|
|
||||||
if (!hasChanges) {
|
|
||||||
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`);
|
this.logService.info(`${this.syncResourceLogLabel}: No changes found during synchronizing extensions.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (added.length || removed.length || updated.length) {
|
if (hasLocalChanged) {
|
||||||
// back up all disabled or market place extensions
|
// back up all disabled or market place extensions
|
||||||
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
|
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
|
||||||
await this.backupLocal(JSON.stringify(backUpExtensions));
|
await this.backupLocal(JSON.stringify(backUpExtensions));
|
||||||
|
|||||||
@@ -13,17 +13,18 @@ import { ILogService } from 'vs/platform/log/common/log';
|
|||||||
export interface IMergeResult {
|
export interface IMergeResult {
|
||||||
local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> };
|
local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> };
|
||||||
remote: IStringDictionary<IStorageValue> | null;
|
remote: IStringDictionary<IStorageValue> | null;
|
||||||
|
skipped: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, storageKeys: ReadonlyArray<IStorageKey>, logService: ILogService): IMergeResult {
|
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, storageKeys: ReadonlyArray<IStorageKey>, previouslySkipped: string[], logService: ILogService): IMergeResult {
|
||||||
if (!remoteStorage) {
|
if (!remoteStorage) {
|
||||||
return { remote: localStorage, local: { added: {}, removed: [], updated: {} } };
|
return { remote: localStorage, local: { added: {}, removed: [], updated: {} }, skipped: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const localToRemote = compare(localStorage, remoteStorage);
|
const localToRemote = compare(localStorage, remoteStorage);
|
||||||
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
|
||||||
// No changes found between local and remote.
|
// No changes found between local and remote.
|
||||||
return { remote: null, local: { added: {}, removed: [], updated: {} } };
|
return { remote: null, local: { added: {}, removed: [], updated: {} }, skipped: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||||
@@ -31,17 +32,19 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
|
|||||||
|
|
||||||
const local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> } = { added: {}, removed: [], updated: {} };
|
const local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> } = { added: {}, removed: [], updated: {} };
|
||||||
const remote: IStringDictionary<IStorageValue> = objects.deepClone(remoteStorage);
|
const remote: IStringDictionary<IStorageValue> = objects.deepClone(remoteStorage);
|
||||||
|
const skipped: string[] = [];
|
||||||
|
|
||||||
// Added in remote
|
// Added in remote
|
||||||
for (const key of values(baseToRemote.added)) {
|
for (const key of values(baseToRemote.added)) {
|
||||||
const remoteValue = remoteStorage[key];
|
const remoteValue = remoteStorage[key];
|
||||||
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
||||||
if (!storageKey) {
|
if (!storageKey) {
|
||||||
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
|
skipped.push(key);
|
||||||
|
logService.info(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (storageKey.version !== remoteValue.version) {
|
if (storageKey.version !== remoteValue.version) {
|
||||||
logService.info(`GlobalState: Skipped updating ${key} in storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
|
logService.info(`GlobalState: Skipped adding ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const localValue = localStorage[key];
|
const localValue = localStorage[key];
|
||||||
@@ -60,11 +63,12 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
|
|||||||
const remoteValue = remoteStorage[key];
|
const remoteValue = remoteStorage[key];
|
||||||
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
||||||
if (!storageKey) {
|
if (!storageKey) {
|
||||||
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
|
skipped.push(key);
|
||||||
|
logService.info(`GlobalState: Skipped updating ${key} in local storage as is not registered.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (storageKey.version !== remoteValue.version) {
|
if (storageKey.version !== remoteValue.version) {
|
||||||
logService.info(`GlobalState: Skipped updating ${key} in storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
|
logService.info(`GlobalState: Skipped updating ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const localValue = localStorage[key];
|
const localValue = localStorage[key];
|
||||||
@@ -78,7 +82,7 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
|
|||||||
for (const key of values(baseToRemote.removed)) {
|
for (const key of values(baseToRemote.removed)) {
|
||||||
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
||||||
if (!storageKey) {
|
if (!storageKey) {
|
||||||
logService.info(`GlobalState: Skipped updating ${key} in storage. It is not registered to sync.`);
|
logService.info(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
local.removed.push(key);
|
local.removed.push(key);
|
||||||
@@ -99,6 +103,7 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
|
|||||||
const remoteValue = remote[key];
|
const remoteValue = remote[key];
|
||||||
const localValue = localStorage[key];
|
const localValue = localStorage[key];
|
||||||
if (localValue.version < remoteValue.version) {
|
if (localValue.version < remoteValue.version) {
|
||||||
|
logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${localValue.version}' and remote version '${remoteValue.version} are not same.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
remote[key] = localValue;
|
remote[key] = localValue;
|
||||||
@@ -106,18 +111,36 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
|
|||||||
|
|
||||||
// Removed in local
|
// Removed in local
|
||||||
for (const key of values(baseToLocal.removed)) {
|
for (const key of values(baseToLocal.removed)) {
|
||||||
|
// do not remove from remote if it is updated in remote
|
||||||
if (baseToRemote.updated.has(key)) {
|
if (baseToRemote.updated.has(key)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const remoteValue = remote[key];
|
|
||||||
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
|
||||||
if (storageKey && storageKey.version < remoteValue.version) {
|
// do not remove from remote if storage key is not found
|
||||||
|
if (!storageKey) {
|
||||||
|
skipped.push(key);
|
||||||
|
logService.info(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const remoteValue = remote[key];
|
||||||
|
// do not remove from remote if local data version is old
|
||||||
|
if (storageKey.version < remoteValue.version) {
|
||||||
|
logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add to local if it was skipped before
|
||||||
|
if (previouslySkipped.indexOf(key) !== -1) {
|
||||||
|
local.added[key] = remote[key];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
delete remote[key];
|
delete remote[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return { local, remote: areSame(remote, remoteStorage) ? null : remote };
|
return { local, remote: areSame(remote, remoteStorage) ? null : remote, skipped };
|
||||||
}
|
}
|
||||||
|
|
||||||
function compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
function compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user