diff --git a/.yarnrc b/.yarnrc index 2c769cfba1..85baaa63a7 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "7.1.7" +target "6.1.6" runtime "electron" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2084e07940..02fb57f436 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,6 +17,6 @@ jobs: - job: macOS pool: - vmImage: macOS 10.13 + vmImage: macOS-latest steps: - template: build/azure-pipelines/darwin/continuous-build-darwin.yml diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 2eedaf8dce..a98b5f4f77 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -102,7 +102,7 @@ jobs: - job: macOS condition: and(succeeded(), eq(variables['VSCODE_COMPILE_ONLY'], 'false'), eq(variables['VSCODE_BUILD_MACOS'], 'true')) pool: - vmImage: macOS 10.13 + vmImage: macOS-latest dependsOn: - Compile steps: diff --git a/build/azure-pipelines/sql-product-build.yml b/build/azure-pipelines/sql-product-build.yml index 2576872ea4..0151b8ef42 100644 --- a/build/azure-pipelines/sql-product-build.yml +++ b/build/azure-pipelines/sql-product-build.yml @@ -15,7 +15,7 @@ jobs: - job: macOS condition: eq(variables['VSCODE_BUILD_MACOS'], 'true') pool: - vmImage: macOS 10.13 + vmImage: macOS-latest dependsOn: - Compile steps: diff --git a/build/builtin/browser-main.js b/build/builtin/browser-main.js index b7726c71cb..fb4a4e28f2 100644 --- a/build/builtin/browser-main.js +++ b/build/builtin/browser-main.js @@ -10,6 +10,7 @@ const os = require('os'); const { remote } = require('electron'); const dialog = remote.dialog; +const productJsonPath = path.join(__dirname, '..', '..', 'product.json'); const builtInExtensionsPath = path.join(__dirname, '..', 'builtInExtensions.json'); const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); @@ -51,6 +52,7 @@ function render(el, state) { } const ul = document.createElement('ul'); + const { quality } = readJson(productJsonPath); const { builtin, control } = state; for (const ext of builtin) { @@ -61,6 +63,10 @@ function render(el, state) { const name = document.createElement('code'); name.textContent = ext.name; + if (quality && ext.forQualities && !ext.forQualities.includes(quality)) { + name.textContent += ` (only on ${ext.forQualities.join(', ')})`; + } + li.appendChild(name); const form = document.createElement('form'); @@ -123,4 +129,4 @@ function main() { render(el, { builtin, control }); } -window.onload = main; \ No newline at end of file +window.onload = main; diff --git a/build/lib/extensions.js b/build/lib/extensions.js index 9b593b4119..455007e1b4 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -27,6 +27,7 @@ const util = require('./util'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const product = require('../../product.json'); function fromLocal(extensionPath) { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); const input = fs.existsSync(webpackFilename) @@ -185,8 +186,8 @@ const excludedExtensions = [ 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', - // {{SQL CARBON EDIT}} - 'integration-tests' + 'integration-tests', + 'ms.vscode.js-debug-nightly' ]; // {{SQL CARBON EDIT}} const externalExtensions = [ @@ -212,7 +213,8 @@ const rebuildExtensions = [ 'big-data-cluster', 'mssql' ]; -const builtInExtensions = require('../builtInExtensions.json'); +const builtInExtensions = require('../builtInExtensions.json') + .filter(({ forQualities }) => { var _a; return !product.quality || ((_a = forQualities === null || forQualities === void 0 ? void 0 : forQualities.includes) === null || _a === void 0 ? void 0 : _a.call(forQualities, product.quality)) !== false; }); function packageLocalExtensionsStream() { const localExtensionDescriptions = glob.sync('extensions/*/package.json') .map(manifestPath => { diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index a27027346f..7c3dc93875 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -27,6 +27,7 @@ const util = require('./util'); const root = path.dirname(path.dirname(__dirname)); const commit = util.getVersion(root); const sourceMappingURLBase = `https://ticino.blob.core.windows.net/sourcemaps/${commit}`; +const product = require('../../product.json'); function fromLocal(extensionPath: string): Stream { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); @@ -220,8 +221,8 @@ const excludedExtensions = [ 'vscode-test-resolver', 'ms-vscode.node-debug', 'ms-vscode.node-debug2', - // {{SQL CARBON EDIT}} - 'integration-tests' + 'integration-tests', // {{SQL CARBON EDIT}} + 'ms.vscode.js-debug-nightly' ]; // {{SQL CARBON EDIT}} @@ -254,10 +255,12 @@ interface IBuiltInExtension { name: string; version: string; repo: string; + forQualities?: ReadonlyArray; metadata: any; } -const builtInExtensions: IBuiltInExtension[] = require('../builtInExtensions.json'); +const builtInExtensions = (require('../builtInExtensions.json')) + .filter(({ forQualities }) => !product.quality || forQualities?.includes?.(product.quality) !== false); export function packageLocalExtensionsStream(): NodeJS.ReadWriteStream { const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) diff --git a/cgmanifest.json b/cgmanifest.json index e9dfd3d6ef..c102a04f70 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "e4745133a1d3745f066e068b8033c6a269b59caf" + "commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "78.0.3904.130" + "version": "76.0.3809.146" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "787378879acfb212ed4ff824bf9f767a24a5cb43a" + "commitHash": "64219741218aa87e259cf8257596073b8e747f0a" } }, "isOnlyProductionDependency": true, - "version": "12.8.1" + "version": "12.4.0" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "bef0dd868b7d6d32716c319664ed480f2ae17396" + "commitHash": "19c705ab80cd6fdccca3d65803ec2c4addb9540a" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "7.1.7" + "version": "6.1.6" }, { "component": { diff --git a/extensions/bat/language-configuration.json b/extensions/bat/language-configuration.json index e463954114..17bc92f6a9 100644 --- a/extensions/bat/language-configuration.json +++ b/extensions/bat/language-configuration.json @@ -17,6 +17,7 @@ ["{", "}"], ["[", "]"], ["(", ")"], + ["%", "%"], ["\"", "\""] ], "folding": { diff --git a/extensions/configuration-editing/schemas/devContainer.schema.json b/extensions/configuration-editing/schemas/devContainer.schema.json index c9c34af63f..292fe62993 100644 --- a/extensions/configuration-editing/schemas/devContainer.schema.json +++ b/extensions/configuration-editing/schemas/devContainer.schema.json @@ -24,6 +24,13 @@ "$ref": "vscode://schemas/settings/machine", "description": "Machine specific settings that should be copied into the container." }, + "forwardPorts": { + "type": "array", + "description": "Ports that are forwarded from the container to the local machine.", + "items": { + "type": "integer" + } + }, "remoteEnv": { "type": "object", "additionalProperties": { diff --git a/extensions/git/package.json b/extensions/git/package.json index 3e049ddd7e..2ba6515e96 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1194,7 +1194,7 @@ { "command": "git.openFile", "group": "navigation", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.openChange", @@ -1204,44 +1204,44 @@ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], "editor/context": [ { "command": "git.stageSelectedRanges", "group": "2_git@1", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.unstageSelectedRanges", "group": "2_git@2", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" }, { "command": "git.revertSelectedRanges", "group": "2_git@3", - "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^gitfs$|^file$/" + "when": "isInDiffRightEditor && config.git.enabled && gitOpenRepositoryCount != 0 && isInDiffEditor && resourceScheme =~ /^git$|^file$/" } ], "scm/change/title": [ { "command": "git.stageChange", - "when": "originalResourceScheme == gitfs" + "when": "originalResourceScheme == git" }, { "command": "git.revertChange", - "when": "originalResourceScheme == gitfs" + "when": "originalResourceScheme == git" } ] }, diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index c51ae898c0..d5d2cb0011 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -239,5 +239,10 @@ export class ApiImpl implements API { return toGitUri(uri, ref); } + getRepository(uri: Uri): Repository | null { + const result = this._model.getRepository(uri); + return result ? new ApiRepository(result) : null; + } + constructor(private _model: Model) { } } diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index d0fb3b9134..16ec429152 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -187,6 +187,7 @@ export interface API { readonly onDidCloseRepository: Event; toGitUri(uri: Uri, ref: string): Uri; + getRepository(uri: Uri): Repository | null; } export interface GitExtension { diff --git a/extensions/git/src/fileSystemProvider.ts b/extensions/git/src/fileSystemProvider.ts index 7bef4f4f5c..74c50982b4 100644 --- a/extensions/git/src/fileSystemProvider.ts +++ b/extensions/git/src/fileSystemProvider.ts @@ -47,9 +47,9 @@ export class GitFileSystemProvider implements FileSystemProvider { this.disposables.push( model.onDidChangeRepository(this.onDidChangeRepository, this), model.onDidChangeOriginalResource(this.onDidChangeOriginalResource, this), - workspace.registerFileSystemProvider('gitfs', this, { isReadonly: true, isCaseSensitive: true }), + workspace.registerFileSystemProvider('git', this, { isReadonly: true, isCaseSensitive: true }), workspace.registerResourceLabelFormatter({ - scheme: 'gitfs', + scheme: 'git', formatting: { label: '${path} (git)', separator: '/' diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 5c3344d814..560c4a5d01 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -392,7 +392,7 @@ export class Model { if (hint instanceof Uri) { let resourcePath: string; - if (hint.scheme === 'git' || hint.scheme === 'gitfs') { + if (hint.scheme === 'git') { resourcePath = fromGitUri(hint).path; } else { resourcePath = hint.fsPath; diff --git a/extensions/git/src/uri.ts b/extensions/git/src/uri.ts index a66877b07b..1731329fe0 100644 --- a/extensions/git/src/uri.ts +++ b/extensions/git/src/uri.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Uri } from 'vscode'; -import * as qs from 'querystring'; export interface GitUriParams { path: string; @@ -13,25 +12,11 @@ export interface GitUriParams { } export function isGitUri(uri: Uri): boolean { - return /^git(fs)?$/.test(uri.scheme); + return /^git$/.test(uri.scheme); } export function fromGitUri(uri: Uri): GitUriParams { - const result = qs.parse(uri.query) as any; - - if (!result) { - throw new Error('Invalid git URI: empty query'); - } - - if (typeof result.path !== 'string') { - throw new Error('Invalid git URI: missing path'); - } - - if (typeof result.ref !== 'string') { - throw new Error('Invalid git URI: missing ref'); - } - - return result; + return JSON.parse(uri.query); } export interface GitUriOptions { @@ -61,8 +46,8 @@ export function toGitUri(uri: Uri, ref: string, options: GitUriOptions = {}): Ur } return uri.with({ - scheme: 'gitfs', + scheme: 'git', path, - query: qs.stringify(params as any) + query: JSON.stringify(params) }); } diff --git a/extensions/git/test/colorize-results/COMMIT_EDITMSG.json b/extensions/git/test/colorize-results/COMMIT_EDITMSG.json index 508e3ee199..0cb3e0690b 100644 --- a/extensions/git/test/colorize-results/COMMIT_EDITMSG.json +++ b/extensions/git/test/colorize-results/COMMIT_EDITMSG.json @@ -235,9 +235,9 @@ "t": "text.git-commit meta.scope.metadata.git-commit comment.line.number-sign.git-commit markup.inserted.git-commit", "r": { "dark_plus": "markup.inserted: #B5CEA8", - "light_plus": "markup.inserted: #09885A", + "light_plus": "markup.inserted: #098658", "dark_vs": "markup.inserted: #B5CEA8", - "light_vs": "markup.inserted: #09885A", + "light_vs": "markup.inserted: #098658", "hc_black": "markup.inserted: #B5CEA8" } }, @@ -252,4 +252,4 @@ "hc_black": "comment: #7CA668" } } -] \ No newline at end of file +] diff --git a/extensions/git/test/colorize-results/example_diff.json b/extensions/git/test/colorize-results/example_diff.json index 07ec9f737b..85e08b9f13 100644 --- a/extensions/git/test/colorize-results/example_diff.json +++ b/extensions/git/test/colorize-results/example_diff.json @@ -147,9 +147,9 @@ "t": "source.diff markup.inserted.diff punctuation.definition.inserted.diff", "r": { "dark_plus": "markup.inserted: #B5CEA8", - "light_plus": "markup.inserted: #09885A", + "light_plus": "markup.inserted: #098658", "dark_vs": "markup.inserted: #B5CEA8", - "light_vs": "markup.inserted: #09885A", + "light_vs": "markup.inserted: #098658", "hc_black": "markup.inserted: #B5CEA8" } }, @@ -158,10 +158,10 @@ "t": "source.diff markup.inserted.diff", "r": { "dark_plus": "markup.inserted: #B5CEA8", - "light_plus": "markup.inserted: #09885A", + "light_plus": "markup.inserted: #098658", "dark_vs": "markup.inserted: #B5CEA8", - "light_vs": "markup.inserted: #09885A", + "light_vs": "markup.inserted: #098658", "hc_black": "markup.inserted: #B5CEA8" } } -] \ No newline at end of file +] diff --git a/extensions/git/test/colorize-results/git-rebase-todo.json b/extensions/git/test/colorize-results/git-rebase-todo.json index d21bab1709..38d8288296 100644 --- a/extensions/git/test/colorize-results/git-rebase-todo.json +++ b/extensions/git/test/colorize-results/git-rebase-todo.json @@ -26,9 +26,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -81,9 +81,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -136,9 +136,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -191,9 +191,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -246,9 +246,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -301,9 +301,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -356,9 +356,9 @@ "t": "text.git-rebase meta.commit-command.git-rebase constant.sha.git-rebase", "r": { "dark_plus": "constant.sha.git-rebase: #B5CEA8", - "light_plus": "constant.sha.git-rebase: #09885A", + "light_plus": "constant.sha.git-rebase: #098658", "dark_vs": "constant.sha.git-rebase: #B5CEA8", - "light_vs": "constant.sha.git-rebase: #09885A", + "light_vs": "constant.sha.git-rebase: #098658", "hc_black": "constant.sha.git-rebase: #B5CEA8" } }, @@ -538,4 +538,4 @@ "hc_black": "comment: #7CA668" } } -] \ No newline at end of file +] diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 9693e2f04e..3410b999d6 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -236,7 +236,7 @@ class Preview extends Disposable { } private async getResourcePath(webviewEditor: vscode.WebviewPanel, resource: vscode.Uri, version: string): Promise { - if (resource.scheme === 'gitfs') { + if (resource.scheme === 'git') { const stat = await vscode.workspace.fs.stat(resource); if (stat.size === 0) { return this.emptyPngDataUri; diff --git a/extensions/json-language-features/client/src/jsonMain.ts b/extensions/json-language-features/client/src/jsonMain.ts index f28b7d7e2a..ba4013e1be 100644 --- a/extensions/json-language-features/client/src/jsonMain.ts +++ b/extensions/json-language-features/client/src/jsonMain.ts @@ -117,7 +117,8 @@ export function activate(context: ExtensionContext) { documentSelector, initializationOptions: { handledSchemaProtocols: ['file'], // language server only loads file-URI. Fetching schemas with other protocols ('http'...) are made on the client. - provideFormatter: false // tell the server to not provide formatting capability and ignore the `json.format.enable` setting. + provideFormatter: false, // tell the server to not provide formatting capability and ignore the `json.format.enable` setting. + customCapabilities: { rangeFormatting: { editLimit: 1000 } } }, synchronize: { // Synchronize the setting section 'json' to the server diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index 09f7c6c164..dfceebce8e 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -111,12 +111,16 @@ }, "editor.suggest.insertMode": "replace" } - } + }, + "jsonValidation": [{ + "fileMatch": "*.schema.json", + "url": "http://json-schema.org/draft-07/schema#" + }] }, "dependencies": { "request-light": "^0.2.5", "vscode-extension-telemetry": "0.1.1", - "vscode-languageclient": "^6.0.0-next.3", + "vscode-languageclient": "^6.0.1", "vscode-nls": "^4.1.1" }, "devDependencies": { diff --git a/extensions/json-language-features/server/README.md b/extensions/json-language-features/server/README.md index 66fd8437d6..43cc04837d 100644 --- a/extensions/json-language-features/server/README.md +++ b/extensions/json-language-features/server/README.md @@ -46,6 +46,8 @@ The client can send the following initialization options to the server: - `provideFormatter: boolean | undefined`. If defined, the value defines whether the server provides the `documentRangeFormattingProvider` capability on initialization. If undefined, the setting `json.format.enable` is used to determine whether formatting is provided. The formatter will then be registered through dynamic registration. If the client does not support dynamic registration, no formatter will be available. - `handledSchemaProtocols`: The URI schemas handles by the server. See section `Schema configuration` below. +- `customCapabilities`: Additional non-LSP client capabilities: + - `rangeFormatting: { editLimit: x } }`: For performance reasons, limit the number of edits returned by the range formatter to `x`. ### Settings diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 64a9c5f5eb..0c26777f01 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -14,8 +14,8 @@ "dependencies": { "jsonc-parser": "^2.2.0", "request-light": "^0.2.5", - "vscode-json-languageservice": "^3.4.9", - "vscode-languageserver": "^6.0.0-next.3", + "vscode-json-languageservice": "^3.4.12", + "vscode-languageserver": "^6.0.1", "vscode-uri": "^2.1.1" }, "devDependencies": { diff --git a/extensions/json-language-features/server/src/jsonServerMain.ts b/extensions/json-language-features/server/src/jsonServerMain.ts index f1a91c6804..69bb484303 100644 --- a/extensions/json-language-features/server/src/jsonServerMain.ts +++ b/extensions/json-language-features/server/src/jsonServerMain.ts @@ -6,7 +6,7 @@ import { createConnection, IConnection, TextDocuments, InitializeParams, InitializeResult, NotificationType, RequestType, - DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind + DocumentRangeFormattingRequest, Disposable, ServerCapabilities, TextDocumentSyncKind, TextEdit } from 'vscode-languageserver'; import { xhr, XHRResponse, configure as configureHttpRequests, getErrorStatusDescription } from 'request-light'; @@ -16,7 +16,7 @@ import * as URL from 'url'; import { posix } from 'path'; import { setTimeout, clearTimeout } from 'timers'; import { formatError, runSafe, runSafeAsync } from './utils/runner'; -import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, SchemaRequestService, Diagnostic } from 'vscode-json-languageservice'; +import { TextDocument, JSONDocument, JSONSchema, getLanguageService, DocumentLanguageSettings, SchemaConfiguration, ClientCapabilities, SchemaRequestService, Diagnostic, Range, Position } from 'vscode-json-languageservice'; import { getLanguageModelCache } from './languageModelCache'; interface ISchemaAssociations { @@ -126,12 +126,13 @@ let hierarchicalDocumentSymbolSupport = false; let foldingRangeLimitDefault = Number.MAX_VALUE; let foldingRangeLimit = Number.MAX_VALUE; let resultLimit = Number.MAX_VALUE; +let formatterMaxNumberOfEdits = Number.MAX_VALUE; // After the server has started the client sends an initialize request. The server receives // in the passed params the rootPath of the workspace plus the client capabilities. connection.onInitialize((params: InitializeParams): InitializeResult => { - const handledProtocols = params.initializationOptions && params.initializationOptions['handledSchemaProtocols']; + const handledProtocols = params.initializationOptions?.handledSchemaProtocols; languageService = getLanguageService({ schemaRequestService: getSchemaRequestService(handledProtocols), @@ -153,9 +154,10 @@ connection.onInitialize((params: InitializeParams): InitializeResult => { } clientSnippetSupport = getClientCapability('textDocument.completion.completionItem.snippetSupport', false); - dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions.provideFormatter !== 'boolean'); + dynamicFormatterRegistration = getClientCapability('textDocument.rangeFormatting.dynamicRegistration', false) && (typeof params.initializationOptions?.provideFormatter !== 'boolean'); foldingRangeLimitDefault = getClientCapability('textDocument.foldingRange.rangeLimit', Number.MAX_VALUE); hierarchicalDocumentSymbolSupport = getClientCapability('textDocument.documentSymbol.hierarchicalDocumentSymbolSupport', false); + formatterMaxNumberOfEdits = params.initializationOptions?.customCapabilities?.rangeFormatting?.editLimit || Number.MAX_VALUE; const capabilities: ServerCapabilities = { textDocumentSync: TextDocumentSyncKind.Incremental, completionProvider: clientSnippetSupport ? { resolveProvider: true, triggerCharacters: ['"', ':'] } : undefined, @@ -445,7 +447,12 @@ connection.onDocumentRangeFormatting((formatParams, token) => { return runSafe(() => { const document = documents.get(formatParams.textDocument.uri); if (document) { - return languageService.format(document, formatParams.range, formatParams.options); + const edits = languageService.format(document, formatParams.range, formatParams.options); + if (edits.length > formatterMaxNumberOfEdits) { + const newText = TextDocument.applyEdits(document, edits); + return [TextEdit.replace(Range.create(Position.create(0, 0), document.positionAt(document.getText().length - 1)), newText)]; + } + return edits; } return []; }, [], `Error while formatting range for ${formatParams.textDocument.uri}`, token); diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 7ebc93341c..9c72cea8b0 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -80,57 +80,52 @@ request-light@^0.2.5: https-proxy-agent "^2.2.3" vscode-nls "^4.1.1" -vscode-json-languageservice@^3.4.9: - version "3.4.9" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.9.tgz#7ce485bb0f9a07b4d879c988baac9be2222909ad" - integrity sha512-4VCpZ9ooea/Zc/MTnj1ccc9C7rqcoinKVQLhLoi6jw6yueSf4y4tg/YIUiPPVMlEAG7ZCPS+NVmqxisQ+mOsSw== +vscode-json-languageservice@^3.4.12: + version "3.4.12" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.4.12.tgz#e7c96a1824896a624cc7bb14f46fbf9cb7e6c5a3" + integrity sha512-+tA0KPVM1pDfORZqsQen7bY5buBpQGDTVYEobm5MoGtXNeZY2Kn0iy5wIQqXveb28LRv/I5xKE87dmNJTEaijQ== dependencies: jsonc-parser "^2.2.0" - vscode-languageserver-textdocument "^1.0.0-next.4" - vscode-languageserver-types "^3.15.0-next.6" + vscode-languageserver-textdocument "^1.0.1-next.1" + vscode-languageserver-types "^3.15.0" vscode-nls "^4.1.1" - vscode-uri "^2.1.0" + vscode-uri "^2.1.1" -vscode-jsonrpc@^5.0.0-next.2: - version "5.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" - integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== +vscode-jsonrpc@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" + integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageserver-protocol@^3.15.0-next.10: - version "3.15.0-next.10" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.10.tgz#f1382f0c270ae5d0c2c7e552483285fb75810914" - integrity sha512-TmbBhKrBoYNX+/pQGwoXmy2qlOfjGBUhwUGIzQoWpj8qtDzYuLof8bi19rGLZ9sVuSHh3anvIyVpGJEqT0QODQ== +vscode-languageserver-protocol@^3.15.1: + version "3.15.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.1.tgz#7555e595f0058b9a166f14605ad039e97fab320a" + integrity sha512-wJAo06VM9ZBnRqslplDjfz6Tdive0O7z44yNxBFA3x0/YZkXBIL6I+9rwQ/9Y//0X0eCh12FQrj+KmEXf2L5eA== dependencies: - vscode-jsonrpc "^5.0.0-next.2" - vscode-languageserver-types "^3.15.0-next.6" + vscode-jsonrpc "^5.0.1" + vscode-languageserver-types "3.15.0" -vscode-languageserver-textdocument@^1.0.0-next.4: - version "1.0.0-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.0-next.4.tgz#8f7afdfe3e81411f57baaa29bb3214d1907160cd" - integrity sha512-LJ5WfoBO54nqinjlLJKnjoo2Im4bIvPJ8bFT7R0C84ZI36iK8M29ddslfe5jUeWNSTtCda7YuKdKsDIq38HpgA== +vscode-languageserver-textdocument@^1.0.1-next.1: + version "1.0.1-next.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1-next.1.tgz#c8f2f792c7c88d33ea8441ca04bfb8376896b671" + integrity sha512-Cmt0KsNxouns+d7/Kw/jWtWU9Z3h56z1qAA8utjDOEqrDcrTs2rDXv3EJRa99nuKM3wVf6DbWym1VqL9q71XPA== -vscode-languageserver-types@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.6.tgz#7a990d00c39ad4e744335afb4cc422a3e687ff25" - integrity sha512-+4jfvmZ26oFMSX6EgPRB75PWHoT8pzyWuSSWk0erC4hTzmJq2gWxVLh20bZutZjMmiivawvPshtM3XZhX2SttA== +vscode-languageserver-types@3.15.0, vscode-languageserver-types@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0.tgz#c45a23308ec0967135c483b759dfaf97978d9e0a" + integrity sha512-AXteNagMhBWnZ6gNN0UB4HTiD/7TajgfHl6jaM6O7qz3zDJw0H3Jf83w05phihnBRCML+K6Ockh8f8bL0OObPw== -vscode-languageserver@^6.0.0-next.3: - version "6.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.0-next.3.tgz#41e2fda6417939792f6a19fc19ecbb2f080e2072" - integrity sha512-Q6T+KwYuoXV9KRHD6x7RfTU13pV0xAX2BtcuvSC/LBCiVAnEIOe7jKZjzya+B9gDvSk4hpfvhPefy5IdQK1mpQ== +vscode-languageserver@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-6.0.1.tgz#4f499d245f1baf83bd607dd79c4c3fd19e8cefc0" + integrity sha512-Wk4I/Dn5KNARWockdCrYuuImJz6bpYG8n2G3Kk5AU6Xy9nWNHD6YjB9/Rd99p4goViZOyETM+hYE81LnEzQZUA== dependencies: - vscode-languageserver-protocol "^3.15.0-next.10" + vscode-languageserver-protocol "^3.15.1" vscode-nls@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.1.tgz#f9916b64e4947b20322defb1e676a495861f133c" integrity sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A== -vscode-uri@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.0.tgz#475a4269e63edbc13914b40c84bc1416e3398156" - integrity sha512-3voe44nOhb6OdKlpZShVsmVvY2vFQHMe6REP3Ky9RVJuPyM/XidsjH6HncCIDdSmbcF5YQHrTC/Q+Q2loJGkOw== - vscode-uri@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.1.tgz#5aa1803391b6ebdd17d047f51365cf62c38f6e90" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index 27e4f7cf88..608f3eaed5 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -120,31 +120,31 @@ vscode-extension-telemetry@0.1.1: dependencies: applicationinsights "1.0.8" -vscode-jsonrpc@^5.0.0-next.2: - version "5.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.0-next.2.tgz#a44bc03f67069e53f8d8beb88b96c0cacbfefbca" - integrity sha512-Q3/jabZUNviCG9hhF6hHWjhrABevPF9mv0aiE2j8BYCAP2k+aHTpjMyk+04MzaAqWYwXdQuZkLSbcYCCqbzJLg== +vscode-jsonrpc@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-5.0.1.tgz#9bab9c330d89f43fc8c1e8702b5c36e058a01794" + integrity sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A== -vscode-languageclient@^6.0.0-next.3: - version "6.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.0-next.3.tgz#41b701d963fc7affc01e9279532a747fcd4f3810" - integrity sha512-SuSaG9xjqkROm4Ie0jQig0CFDuU/WxHERegl3kRsFHDbhMSK4dH45ZeBY5zMWUgZ+LrIrEbwf8qWNlrTRBlUgg== +vscode-languageclient@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-6.0.1.tgz#acd138e0a19a40c5788365e882ae11c164d9a460" + integrity sha512-7yZaSHichTJEyOJykI2RLQEECf9MqNLoklzC/1OVi/M8ioIsWQ1+lkN1nTsUhd6+F7p9ar9dNmPiEhL0i5uUBA== dependencies: semver "^6.3.0" - vscode-languageserver-protocol "^3.15.0-next.10" + vscode-languageserver-protocol "^3.15.1" -vscode-languageserver-protocol@^3.15.0-next.10: - version "3.15.0-next.10" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.0-next.10.tgz#f1382f0c270ae5d0c2c7e552483285fb75810914" - integrity sha512-TmbBhKrBoYNX+/pQGwoXmy2qlOfjGBUhwUGIzQoWpj8qtDzYuLof8bi19rGLZ9sVuSHh3anvIyVpGJEqT0QODQ== +vscode-languageserver-protocol@^3.15.1: + version "3.15.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.15.1.tgz#7555e595f0058b9a166f14605ad039e97fab320a" + integrity sha512-wJAo06VM9ZBnRqslplDjfz6Tdive0O7z44yNxBFA3x0/YZkXBIL6I+9rwQ/9Y//0X0eCh12FQrj+KmEXf2L5eA== dependencies: - vscode-jsonrpc "^5.0.0-next.2" - vscode-languageserver-types "^3.15.0-next.6" + vscode-jsonrpc "^5.0.1" + vscode-languageserver-types "3.15.0" -vscode-languageserver-types@^3.15.0-next.6: - version "3.15.0-next.6" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0-next.6.tgz#7a990d00c39ad4e744335afb4cc422a3e687ff25" - integrity sha512-+4jfvmZ26oFMSX6EgPRB75PWHoT8pzyWuSSWk0erC4hTzmJq2gWxVLh20bZutZjMmiivawvPshtM3XZhX2SttA== +vscode-languageserver-types@3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.15.0.tgz#c45a23308ec0967135c483b759dfaf97978d9e0a" + integrity sha512-AXteNagMhBWnZ6gNN0UB4HTiD/7TajgfHl6jaM6O7qz3zDJw0H3Jf83w05phihnBRCML+K6Ockh8f8bL0OObPw== vscode-nls@^4.1.1: version "4.1.1" diff --git a/extensions/json/package.json b/extensions/json/package.json index e66955770b..8f34baf148 100644 --- a/extensions/json/package.json +++ b/extensions/json/package.json @@ -67,12 +67,6 @@ "scopeName": "source.json.comments", "path": "./syntaxes/JSONC.tmLanguage.json" } - ], - "jsonValidation": [ - { - "fileMatch": "*.schema.json", - "url": "http://json-schema.org/draft-07/schema#" - } ] } } diff --git a/extensions/json/test/colorize-results/test_json.json b/extensions/json/test/colorize-results/test_json.json index 75561c366d..fc3dec2172 100644 --- a/extensions/json/test/colorize-results/test_json.json +++ b/extensions/json/test/colorize-results/test_json.json @@ -279,9 +279,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -488,9 +488,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -686,9 +686,9 @@ "t": "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.array.json constant.numeric.json", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1165,4 +1165,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/powershell/test/colorize-results/test_ps1.json b/extensions/powershell/test/colorize-results/test_ps1.json index 67380ce77b..d12f004fb9 100644 --- a/extensions/powershell/test/colorize-results/test_ps1.json +++ b/extensions/powershell/test/colorize-results/test_ps1.json @@ -950,9 +950,9 @@ "t": "source.powershell meta.scriptblock.powershell interpolated.simple.source.powershell meta.attribute.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1522,9 +1522,9 @@ "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1599,9 +1599,9 @@ "t": "source.powershell meta.scriptblock.powershell meta.scriptblock.powershell meta.scriptblock.powershell interpolated.simple.source.powershell constant.numeric.integer.powershell", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2892,4 +2892,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/python/test/colorize-results/test_py.json b/extensions/python/test/colorize-results/test_py.json index 2c31265862..c61d2db761 100644 --- a/extensions/python/test/colorize-results/test_py.json +++ b/extensions/python/test/colorize-results/test_py.json @@ -169,9 +169,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -565,9 +565,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1137,9 +1137,9 @@ "t": "source.python meta.function.python meta.function.parameters.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1214,9 +1214,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1280,9 +1280,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1324,9 +1324,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1390,9 +1390,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1456,9 +1456,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1522,9 +1522,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1566,9 +1566,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1632,9 +1632,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1698,9 +1698,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1764,9 +1764,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1808,9 +1808,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1874,9 +1874,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1962,9 +1962,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2424,9 +2424,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2446,9 +2446,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2468,9 +2468,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2490,9 +2490,9 @@ "t": "source.python meta.function-call.python meta.function-call.arguments.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -2985,9 +2985,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3359,9 +3359,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3414,9 +3414,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3480,9 +3480,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3568,9 +3568,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3601,9 +3601,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3656,9 +3656,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3722,9 +3722,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3854,9 +3854,9 @@ "t": "source.python constant.numeric.float.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -3898,9 +3898,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -6252,9 +6252,9 @@ "t": "source.python constant.numeric.dec.python", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -6797,4 +6797,4 @@ "hc_black": "string: #CE9178" } } -] \ No newline at end of file +] diff --git a/extensions/r/test/colorize-results/test_r.json b/extensions/r/test/colorize-results/test_r.json index 2cba70d079..2d3e504f8d 100644 --- a/extensions/r/test/colorize-results/test_r.json +++ b/extensions/r/test/colorize-results/test_r.json @@ -554,9 +554,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -598,9 +598,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -631,9 +631,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -675,9 +675,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -708,9 +708,9 @@ "t": "source.r meta.function-call.r meta.function-call.arguments.r constant.numeric.float.decimal.r", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1044,4 +1044,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 40d59e2bcd..6a55968bda 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -25,46 +25,6 @@ "editor.lineNumbers": "off" } }, - "commands": [ - { - "command": "searchResult.rerunSearch", - "title": "%searchResult.rerunSearch.title%", - "category": "Search Result", - "icon": { - "light": "./src/media/refresh-light.svg", - "dark": "./src/media/refresh-dark.svg" - } - }, - { - "command": "searchResult.rerunSearchWithContext", - "title": "%searchResult.rerunSearchWithContext.title%", - "category": "Search Result", - "icon": { - "light": "./src/media/refresh-light.svg", - "dark": "./src/media/refresh-dark.svg" - } - } - ], - "menus": { - "commandPalette": [ - { - "command": "searchResult.rerunSearch", - "when": "false" - }, - { - "command": "searchResult.rerunSearchWithContext", - "when": "false" - } - ], - "editor/title": [ - { - "command": "searchResult.rerunSearch", - "when": "editorLangId == search-result", - "alt": "searchResult.rerunSearchWithContext", - "group": "navigation" - } - ] - }, "languages": [ { "id": "search-result", diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json index ce90d23c09..324fd97bcd 100644 --- a/extensions/search-result/package.nls.json +++ b/extensions/search-result/package.nls.json @@ -1,6 +1,4 @@ { "displayName": "Search Result", - "description": "Provides syntax highlighting and language features for tabbed search results.", - "searchResult.rerunSearch.title": "Search Again", - "searchResult.rerunSearchWithContext.title": "Search Again (With Context)" + "description": "Provides syntax highlighting and language features for tabbed search results." } diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 1ac0146401..295b620eee 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -34,8 +34,6 @@ export function activate(context: vscode.ExtensionContext) { } context.subscriptions.push( - vscode.commands.registerCommand('searchResult.rerunSearch', () => vscode.commands.executeCommand('search.action.rerunEditorSearch')), - vscode.commands.registerCommand('searchResult.rerunSearchWithContext', () => vscode.commands.executeCommand('search.action.rerunEditorSearchWithContext')), vscode.languages.registerDocumentSymbolProvider(SEARCH_RESULT_SELECTOR, { provideDocumentSymbols(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.DocumentSymbol[] { diff --git a/extensions/sql/test/colorize-results/test_sql.json b/extensions/sql/test/colorize-results/test_sql.json index da116cea9c..4bc7ff2815 100644 --- a/extensions/sql/test/colorize-results/test_sql.json +++ b/extensions/sql/test/colorize-results/test_sql.json @@ -136,9 +136,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -180,9 +180,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -213,9 +213,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -268,9 +268,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -290,9 +290,9 @@ "t": "source.sql constant.numeric.sql", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -318,4 +318,4 @@ "hc_black": "default: #FFFFFF" } } -] \ No newline at end of file +] diff --git a/extensions/theme-defaults/themes/light_vs.json b/extensions/theme-defaults/themes/light_vs.json index 5453787c4e..cbf19b2298 100644 --- a/extensions/theme-defaults/themes/light_vs.json +++ b/extensions/theme-defaults/themes/light_vs.json @@ -51,7 +51,7 @@ "keyword.operator.minus.exponent" ], "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -130,7 +130,7 @@ { "scope": "markup.inserted", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -185,7 +185,7 @@ { "scope": "meta.preprocessor.numeric", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -346,7 +346,7 @@ { "scope": "keyword.other.unit", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { @@ -367,7 +367,7 @@ { "scope": "constant.sha.git-rebase", "settings": { - "foreground": "#09885a" + "foreground": "#098658" } }, { diff --git a/extensions/vscode-account/src/AADHelper.ts b/extensions/vscode-account/src/AADHelper.ts index efc29ee51b..48a3136106 100644 --- a/extensions/vscode-account/src/AADHelper.ts +++ b/extensions/vscode-account/src/AADHelper.ts @@ -4,52 +4,131 @@ *--------------------------------------------------------------------------------------------*/ import * as crypto from 'crypto'; -import * as vscode from 'vscode'; import * as https from 'https'; import * as querystring from 'querystring'; -import { keychain } from './keychain'; -import { toBase64UrlEncoding } from './utils'; +import * as vscode from 'vscode'; import { createServer, startServer } from './authServer'; +import { keychain } from './keychain'; +import Logger from './logger'; +import { toBase64UrlEncoding } from './utils'; const redirectUrl = 'https://vscode-redirect.azurewebsites.net/'; const loginEndpointUrl = 'https://login.microsoftonline.com/'; const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const scope = 'https://management.core.windows.net/.default offline_access'; -const tenant = 'common'; +const tenant = 'organizations'; interface IToken { expiresIn: string; // How long access token is valid, in seconds accessToken: string; refreshToken: string; + + displayName: string; + scope: string; + sessionId: string; // The account id + the scope +} + +interface ITokenClaims { + tid: string; + email?: string; + unique_name?: string; + oid?: string; + altsecid?: string; + scp: string; +} + +interface IStoredSession { + id: string; + refreshToken: string; + scope: string; // Scopes are alphabetized and joined with a space } export const onDidChangeSessions = new vscode.EventEmitter(); export class AzureActiveDirectoryService { - private _token: IToken | undefined; - private _refreshTimeout: NodeJS.Timeout | undefined; + private _tokens: IToken[] = []; + private _refreshTimeouts: Map = new Map(); public async initialize(): Promise { - const existingRefreshToken = await keychain.getToken(); - if (existingRefreshToken) { - await this.refreshToken(existingRefreshToken); + const storedData = await keychain.getToken(); + if (storedData) { + try { + const sessions = this.parseStoredData(storedData); + const refreshes = sessions.map(async session => { + try { + await this.refreshToken(session.refreshToken, session.scope); + } catch (e) { + await this.logout(session.id); + } + }); + + await Promise.all(refreshes); + } catch (e) { + await this.clearSessions(); + } } this.pollForChange(); } + private parseStoredData(data: string): IStoredSession[] { + return JSON.parse(data); + } + + private async storeTokenData(): Promise { + const serializedData: IStoredSession[] = this._tokens.map(token => { + return { + id: token.sessionId, + refreshToken: token.refreshToken, + scope: token.scope + }; + }); + + await keychain.setToken(JSON.stringify(serializedData)); + } + private pollForChange() { setTimeout(async () => { - const refreshToken = await keychain.getToken(); - // Another window has logged in, generate access token for this instance. - if (refreshToken && !this._token) { - await this.refreshToken(refreshToken); - onDidChangeSessions.fire(); + let didChange = false; + const storedData = await keychain.getToken(); + if (storedData) { + try { + const sessions = this.parseStoredData(storedData); + let promises = sessions.map(async session => { + const matchesExisting = this._tokens.some(token => token.scope === session.scope && token.sessionId === session.id); + if (!matchesExisting) { + try { + await this.refreshToken(session.refreshToken, session.scope); + didChange = true; + } catch (e) { + await this.logout(session.id); + } + } + }); + + promises = promises.concat(this._tokens.map(async token => { + const matchesExisting = sessions.some(session => token.scope === session.scope && token.sessionId === session.id); + if (!matchesExisting) { + await this.logout(token.sessionId); + didChange = true; + } + })); + + await Promise.all(promises); + } catch (e) { + Logger.error(e.message); + // if data is improperly formatted, remove all of it and send change event + this.clearSessions(); + didChange = true; + } + } else { + if (this._tokens.length) { + // Log out all + await this.clearSessions(); + didChange = true; + } } - // Another window has logged out - if (!refreshToken && this._token) { - await this.logout(); + if (didChange) { onDidChangeSessions.fire(); } @@ -57,31 +136,30 @@ export class AzureActiveDirectoryService { }, 1000 * 30); } - private tokenToAccount(token: IToken): vscode.Session { + private convertToSession(token: IToken): vscode.Session { return { - id: '', + id: token.sessionId, accessToken: token.accessToken, - displayName: this.getDisplayNameFromToken(token.accessToken) + displayName: token.displayName, + scopes: token.scope.split(' ') }; } - private getDisplayNameFromToken(accessToken: string): string { - let displayName = 'user@example.com'; + private getTokenClaims(accessToken: string): ITokenClaims { try { - // TODO fixme - displayName = JSON.parse(atob(accessToken.split('.')[1])); + return JSON.parse(Buffer.from(accessToken.split('.')[1], 'base64').toString()); } catch (e) { - // Fall back to example display name + Logger.error(e.message); + throw new Error('Unable to read token claims'); } - - return displayName; } get sessions(): vscode.Session[] { - return this._token ? [this.tokenToAccount(this._token)] : []; + return this._tokens.map(token => this.convertToSession(token)); } - public async login(): Promise { + public async login(scope: string): Promise { + Logger.info('Logging in...'); const nonce = crypto.randomBytes(16).toString('base64'); const { server, redirectPromise, codePromise } = createServer(nonce); @@ -118,11 +196,13 @@ export class AzureActiveDirectoryService { if ('err' in codeRes) { throw codeRes.err; } - token = await this.exchangeCodeForToken(codeRes.code, codeVerifier); - this.setToken(token); + token = await this.exchangeCodeForToken(codeRes.code, codeVerifier, scope); + this.setToken(token, scope); + Logger.info('Login successful'); res.writeHead(302, { Location: '/' }); res.end(); } catch (err) { + Logger.error(err.message); res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unknown error')}` }); res.end(); } @@ -133,28 +213,48 @@ export class AzureActiveDirectoryService { } } - private async setToken(token: IToken): Promise { - this._token = token; - - if (this._refreshTimeout) { - clearTimeout(this._refreshTimeout); + private async setToken(token: IToken, scope: string): Promise { + const existingToken = this._tokens.findIndex(t => t.sessionId === token.sessionId); + if (existingToken) { + this._tokens.splice(existingToken, 1, token); + } else { + this._tokens.push(token); } - this._refreshTimeout = setTimeout(async () => { + const existingTimeout = this._refreshTimeouts.get(token.sessionId); + if (existingTimeout) { + clearTimeout(existingTimeout); + } + + this._refreshTimeouts.set(token.sessionId, setTimeout(async () => { try { - await this.refreshToken(token.refreshToken); + await this.refreshToken(token.refreshToken, scope); } catch (e) { - await this.logout(); + await this.logout(token.sessionId); } finally { onDidChangeSessions.fire(); } - }, 1000 * (parseInt(token.expiresIn) - 10)); + }, 1000 * (parseInt(token.expiresIn) - 10))); - await keychain.setToken(token.refreshToken); + this.storeTokenData(); } - private async exchangeCodeForToken(code: string, codeVerifier: string): Promise { + private getTokenFromResponse(buffer: Buffer[], scope: string): IToken { + const json = JSON.parse(Buffer.concat(buffer).toString()); + const claims = this.getTokenClaims(json.access_token); + return { + expiresIn: json.expires_in, + accessToken: json.access_token, + refreshToken: json.refresh_token, + scope, + sessionId: claims.tid + (claims.oid || claims.altsecid) + scope, + displayName: claims.email || claims.unique_name || 'user@example.com' + }; + } + + private async exchangeCodeForToken(code: string, codeVerifier: string, scope: string): Promise { return new Promise((resolve: (value: IToken) => void, reject) => { + Logger.info('Exchanging login code for token'); try { const postData = querystring.stringify({ grant_type: 'authorization_code', @@ -182,12 +282,7 @@ export class AzureActiveDirectoryService { }); result.on('end', () => { if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - resolve({ - expiresIn: json.expires_in, - accessToken: json.access_token, - refreshToken: json.refresh_token - }); + resolve(this.getTokenFromResponse(buffer, scope)); } else { reject(new Error('Unable to login.')); } @@ -202,13 +297,15 @@ export class AzureActiveDirectoryService { }); } catch (e) { + Logger.error(e.message); reject(e); } }); } - private async refreshToken(refreshToken: string): Promise { + private async refreshToken(refreshToken: string, scope: string): Promise { return new Promise((resolve: (value: IToken) => void, reject) => { + Logger.info('Refreshing token...'); const postData = querystring.stringify({ refresh_token: refreshToken, client_id: clientId, @@ -231,16 +328,12 @@ export class AzureActiveDirectoryService { }); result.on('end', async () => { if (result.statusCode === 200) { - const json = JSON.parse(Buffer.concat(buffer).toString()); - const token = { - expiresIn: json.expires_in, - accessToken: json.access_token, - refreshToken: json.refresh_token - }; - this.setToken(token); + const token = this.getTokenFromResponse(buffer, scope); + this.setToken(token, scope); + Logger.info('Token refresh success'); resolve(token); } else { - await this.logout(); + Logger.error('Refreshing token failed'); reject(new Error('Refreshing token failed.')); } }); @@ -250,16 +343,41 @@ export class AzureActiveDirectoryService { post.end(); post.on('error', err => { + Logger.error(err.message); reject(err); }); }); } - public async logout() { - delete this._token; - await keychain.deleteToken(); - if (this._refreshTimeout) { - clearTimeout(this._refreshTimeout); + public async logout(sessionId: string) { + Logger.info(`Logging out of session '${sessionId}'`); + const tokenIndex = this._tokens.findIndex(token => token.sessionId === sessionId); + if (tokenIndex > -1) { + this._tokens.splice(tokenIndex, 1); + } + + if (this._tokens.length === 0) { + await keychain.deleteToken(); + } else { + this.storeTokenData(); + } + + const timeout = this._refreshTimeouts.get(sessionId); + if (timeout) { + clearTimeout(timeout); + this._refreshTimeouts.delete(sessionId); } } + + public async clearSessions() { + Logger.info('Logging out of all sessions'); + this._tokens = []; + await keychain.deleteToken(); + + this._refreshTimeouts.forEach(timeout => { + clearTimeout(timeout); + }); + + this._refreshTimeouts.clear(); + } } diff --git a/extensions/vscode-account/src/extension.ts b/extensions/vscode-account/src/extension.ts index defce54576..3b0007ae49 100644 --- a/extensions/vscode-account/src/extension.ts +++ b/extensions/vscode-account/src/extension.ts @@ -14,12 +14,12 @@ export async function activate(context: vscode.ExtensionContext) { vscode.authentication.registerAuthenticationProvider({ id: 'MSA', - displayName: 'Microsoft Account', // TODO localize + displayName: 'Microsoft', onDidChangeSessions: onDidChangeSessions.event, getSessions: () => Promise.resolve(loginService.sessions), - login: async () => { + login: async (scopes: string[]) => { try { - await loginService.login(); + await loginService.login(scopes.sort().join(' ')); return loginService.sessions[0]!; } catch (e) { vscode.window.showErrorMessage(`Logging in failed: ${e}`); @@ -27,7 +27,7 @@ export async function activate(context: vscode.ExtensionContext) { } }, logout: async (id: string) => { - return loginService.logout(); + return loginService.logout(id); } }); diff --git a/extensions/vscode-account/src/logger.ts b/extensions/vscode-account/src/logger.ts new file mode 100644 index 0000000000..ec0699eab3 --- /dev/null +++ b/extensions/vscode-account/src/logger.ts @@ -0,0 +1,55 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; + +type LogLevel = 'Trace' | 'Info' | 'Error'; + +class Log { + private output: vscode.OutputChannel; + + constructor() { + this.output = vscode.window.createOutputChannel('Account'); + } + + private data2String(data: any): string { + if (data instanceof Error) { + return data.stack || data.message; + } + if (data.success === false && data.message) { + return data.message; + } + return data.toString(); + } + + public info(message: string, data?: any): void { + this.logLevel('Info', message, data); + } + + public error(message: string, data?: any): void { + this.logLevel('Error', message, data); + } + + public logLevel(level: LogLevel, message: string, data?: any): void { + this.output.appendLine(`[${level} - ${this.now()}] ${message}`); + if (data) { + this.output.appendLine(this.data2String(data)); + } + } + + private now(): string { + const now = new Date(); + return padLeft(now.getUTCHours() + '', 2, '0') + + ':' + padLeft(now.getMinutes() + '', 2, '0') + + ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds(); + } +} + +function padLeft(s: string, n: number, pad = ' ') { + return pad.repeat(Math.max(0, n - s.length)) + s; +} + +const Logger = new Log(); +export default Logger; diff --git a/extensions/vscode-account/src/vscode.proposed.d.ts b/extensions/vscode-account/src/vscode.proposed.d.ts index 891928bde3..d06d51c2c4 100644 --- a/extensions/vscode-account/src/vscode.proposed.d.ts +++ b/extensions/vscode-account/src/vscode.proposed.d.ts @@ -20,6 +20,7 @@ declare module 'vscode' { id: string; accessToken: string; displayName: string; + scopes: string[] } export interface AuthenticationProvider { @@ -35,7 +36,7 @@ declare module 'vscode' { /** * Prompts a user to login. */ - login(): Promise; + login(scopes: string[]): Promise; logout(sessionId: string): Promise; } @@ -48,13 +49,7 @@ declare module 'vscode' { export const onDidRegisterAuthenticationProvider: Event; export const onDidUnregisterAuthenticationProvider: Event; - /** - * Fires with the provider id that changed sessions. - */ - export const onDidChangeSessions: Event; - export function login(providerId: string): Promise; - export function logout(providerId: string, accountId: string): Promise; - export function getSessions(providerId: string): Promise | undefined>; + export const providers: ReadonlyArray; } // #region Ben - extension auth flow (desktop+web) diff --git a/extensions/yaml/test/colorize-results/issue-4008_yaml.json b/extensions/yaml/test/colorize-results/issue-4008_yaml.json index 10329df780..c16426c590 100644 --- a/extensions/yaml/test/colorize-results/issue-4008_yaml.json +++ b/extensions/yaml/test/colorize-results/issue-4008_yaml.json @@ -257,10 +257,10 @@ "t": "source.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } } -] \ No newline at end of file +] diff --git a/extensions/yaml/test/colorize-results/test_yaml.json b/extensions/yaml/test/colorize-results/test_yaml.json index 6c871b3220..258202824f 100644 --- a/extensions/yaml/test/colorize-results/test_yaml.json +++ b/extensions/yaml/test/colorize-results/test_yaml.json @@ -246,9 +246,9 @@ "t": "source.yaml constant.numeric.float.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -774,9 +774,9 @@ "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -906,9 +906,9 @@ "t": "source.yaml constant.numeric.integer.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", - "light_plus": "constant.numeric: #09885A", + "light_plus": "constant.numeric: #098658", "dark_vs": "constant.numeric: #B5CEA8", - "light_vs": "constant.numeric: #09885A", + "light_vs": "constant.numeric: #098658", "hc_black": "constant.numeric: #B5CEA8" } }, @@ -1132,4 +1132,4 @@ "hc_black": "string: #CE9178" } } -] \ No newline at end of file +] diff --git a/package.json b/package.json index 6ee54c7b56..1963abca5d 100644 --- a/package.json +++ b/package.json @@ -118,7 +118,7 @@ "coveralls": "^2.11.11", "cson-parser": "^1.3.3", "debounce": "^1.0.0", - "electron": "7.1.7", + "electron": "6.1.6", "eslint": "6.8.0", "eslint-plugin-jsdoc": "^19.1.0", "event-stream": "3.3.4", diff --git a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts index a4fc62a074..81178dcc4c 100644 --- a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts @@ -95,7 +95,7 @@ class MainThreadNotebookEditor extends Disposable { } public save(): Thenable { - return this.textFileService.save(this.uri); + return this.textFileService.save(this.uri).then(uri => !!uri); } public matches(input: NotebookInput): boolean { @@ -351,7 +351,7 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements let uriString = URI.revive(uri).toString(); let editor = this._notebookEditors.get(uriString); if (editor) { - return editor.save(); + return editor.save().then(uri => !!uri); } else { return Promise.resolve(false); } diff --git a/src/sql/workbench/browser/modal/optionsDialog.ts b/src/sql/workbench/browser/modal/optionsDialog.ts index 9c54e65b06..32897cbe27 100644 --- a/src/sql/workbench/browser/modal/optionsDialog.ts +++ b/src/sql/workbench/browser/modal/optionsDialog.ts @@ -34,6 +34,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; export class CategoryView extends ViewPane { @@ -45,9 +46,10 @@ export class CategoryView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); } // we want a fixed size, so when we render to will measure our content and set that to be our diff --git a/src/sql/workbench/browser/modelComponents/modelViewInput.ts b/src/sql/workbench/browser/modelComponents/modelViewInput.ts index f4fb921f5c..6e67f3b082 100644 --- a/src/sql/workbench/browser/modelComponents/modelViewInput.ts +++ b/src/sql/workbench/browser/modelComponents/modelViewInput.ts @@ -6,7 +6,7 @@ import * as azdata from 'azdata'; import { IEditorModel } from 'vs/platform/editor/common/editor'; -import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { EditorInput, EditorModel, IEditorInput } from 'vs/workbench/common/editor'; import * as DOM from 'vs/base/browser/dom'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -139,8 +139,8 @@ export class ModelViewInput extends EditorInput { /** * Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation. */ - save(): Promise { - return this._model.save(); + save(): Promise { + return this._model.save().then(saved => saved ? this : undefined); } public dispose(): void { diff --git a/src/sql/workbench/browser/modelComponents/queryTextEditor.ts b/src/sql/workbench/browser/modelComponents/queryTextEditor.ts index 686e010c78..d931398f6b 100644 --- a/src/sql/workbench/browser/modelComponents/queryTextEditor.ts +++ b/src/sql/workbench/browser/modelComponents/queryTextEditor.ts @@ -196,7 +196,7 @@ export class QueryTextEditor extends BaseTextEditor { this.refreshEditorConfiguration(); } - private refreshEditorConfiguration(configuration = this.configurationService.getValue(this.getResource())): void { + private refreshEditorConfiguration(configuration = this.textResourceConfigurationService.getValue(this.input.getResource())): void { if (!this.getControl()) { return; } diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index 0a0244e70b..87ef50d2a3 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -12,7 +12,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextAwareMenuEntryActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions } from 'vs/workbench/common/views'; +import { TreeItemCollapsibleState, ITreeViewDataProvider, TreeViewItemHandleArg, ITreeViewDescriptor, IViewsRegistry, ViewContainer, ITreeItemLabel, Extensions, IViewDescriptorService } from 'vs/workbench/common/views'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -27,7 +27,7 @@ import { URI } from 'vs/base/common/uri'; import { dirname, basename } from 'vs/base/common/resources'; import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; -import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; +import { WorkbenchAsyncDataTree, TreeResourceNavigator } from 'vs/platform/list/browser/listService'; import { localize } from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { editorFindMatchHighlight, editorFindMatchHighlightBorder, textLinkForeground, textCodeBlockBackground, focusBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -62,9 +62,10 @@ export class CustomTreeViewPanel extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView as ITreeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); @@ -443,7 +444,7 @@ export class CustomTreeView extends Disposable implements ITreeView { })); this.tree.setInput(this.root).then(() => this.updateContentAreas()); - const customTreeNavigator = new TreeResourceNavigator2(this.tree); + const customTreeNavigator = new TreeResourceNavigator(this.tree); this._register(customTreeNavigator); this._register(customTreeNavigator.onDidOpenResource(e => { if (!e.browserEvent) { diff --git a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts index 2d04717e21..5fa23cceb1 100644 --- a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts +++ b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts @@ -36,6 +36,7 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; class AccountPanel extends ViewPane { public index: number; @@ -48,9 +49,10 @@ class AccountPanel extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IThemeService private themeService: IThemeService, @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); } protected renderBody(container: HTMLElement): void { @@ -126,6 +128,7 @@ export class AccountDialog extends Modal { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IClipboardService clipboardService: IClipboardService, @ILogService logService: ILogService, + @IViewDescriptorService private viewDescriptorService: IViewDescriptorService, @ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService ) { super( @@ -296,7 +299,8 @@ export class AccountDialog extends Modal { this._configurationService, this._themeService, this.contextKeyService, - this._instantiationService + this._instantiationService, + this.viewDescriptorService ); attachPanelStyler(providerView, this._themeService); diff --git a/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts b/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts index 01f457469f..b1cc1abc28 100644 --- a/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts +++ b/src/sql/workbench/contrib/accounts/test/browser/accountDialogController.test.ts @@ -86,7 +86,7 @@ function createInstantiationService(addAccountFailureEmitter?: Emitter): .returns(() => undefined); // Create a mock account dialog - let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!); + let accountDialog = new AccountDialog(undefined!, undefined!, instantiationService.object, undefined!, undefined!, undefined!, undefined!, new MockContextKeyService(), undefined!, undefined!, undefined!, undefined!); let mockAccountDialog = TypeMoq.Mock.ofInstance(accountDialog); mockAccountDialog.setup(x => x.onAddAccountErrorEvent) .returns(() => { return addAccountFailureEmitter ? addAccountFailureEmitter.event : mockEvent.event; }); diff --git a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts index 567e3b5323..b5ada50808 100644 --- a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts +++ b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts @@ -393,7 +393,7 @@ suite('commandLineService tests', () => { querymodelService.setup(c => c.onRunQueryComplete).returns(() => Event.None); const instantiationService = new TestInstantiationService(); let uri = URI.file(args._[0]); - const untitledEditorInput = new UntitledTextEditorInput(uri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + const untitledEditorInput = new UntitledTextEditorInput(uri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); const queryInput = new UntitledQueryEditorInput(undefined, untitledEditorInput, undefined, connectionManagementService.object, querymodelService.object, configurationService.object, undefined); queryInput.state.connected = true; const editorService: TypeMoq.Mock = TypeMoq.Mock.ofType(TestEditorService, TypeMoq.MockBehavior.Strict); diff --git a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts index 258b30ee06..4c120582e3 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts @@ -20,6 +20,7 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; export class ConnectionViewletPanel extends ViewPane { @@ -38,9 +39,10 @@ export class ConnectionViewletPanel extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService configurationService: IConfigurationService, @IObjectExplorerService private readonly objectExplorerService: IObjectExplorerService, - @IContextKeyService contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService, ) { - super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); this._addServerAction = this.instantiationService.createInstance(AddServerAction, AddServerAction.ID, AddServerAction.LABEL); diff --git a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts index e195c848dc..835fd58c29 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts @@ -17,7 +17,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; import { ConnectionViewletPanel } from 'sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel'; -import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation } from 'vs/workbench/common/views'; +import { Extensions as ViewContainerExtensions, IViewDescriptor, IViewsRegistry, IViewContainersRegistry, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -103,9 +103,10 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer { @IExtensionService extensionService: IExtensionService, @IConfigurationService configurationService: IConfigurationService, @IMenuService private menuService: IMenuService, - @IContextKeyService private contextKeyService: IContextKeyService + @IContextKeyService private contextKeyService: IContextKeyService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); + super(VIEWLET_ID, `${VIEWLET_ID}.state`, { mergeViewWithContainerWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService, viewDescriptorService); } create(parent: HTMLElement): void { diff --git a/src/sql/workbench/contrib/editData/browser/editDataInput.ts b/src/sql/workbench/contrib/editData/browser/editDataInput.ts index 12c41764ab..05d1c65446 100644 --- a/src/sql/workbench/contrib/editData/browser/editDataInput.ts +++ b/src/sql/workbench/contrib/editData/browser/editDataInput.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, EditorModel, EncodingMode } from 'vs/workbench/common/editor'; +import { EditorInput, EditorModel, EncodingMode, IEditorInput } from 'vs/workbench/common/editor'; import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement'; import { IQueryModelService } from 'sql/platform/query/common/queryModel'; import { Event, Emitter } from 'vs/base/common/event'; @@ -107,7 +107,7 @@ export class EditDataInput extends EditorInput implements IConnectableInput { public get objectType(): string { return this._objectType; } public showResultsEditor(): void { this._showResultsEditor.fire(undefined); } public isDirty(): boolean { return false; } - public save(): Promise { return Promise.resolve(false); } + public save(): Promise { return Promise.resolve(undefined); } public getTypeId(): string { return EditDataInput.ID; } public setBootstrappedTrue(): void { this._hasBootstrapped = true; } public getResource(): URI { return this._uri; } @@ -220,7 +220,6 @@ export class EditDataInput extends EditorInput implements IConnectableInput { public get onDidModelChangeEncoding(): Event { return this._sql.onDidModelChangeEncoding; } public resolve(refresh?: boolean): Promise { return this._sql.resolve(); } public getEncoding(): string { return this._sql.getEncoding(); } - public suggestFileName(): string { return this._sql.suggestFileName(); } public getName(): string { return this._sql.getName(); } public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; } diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts index 119796e0ad..05bd5b2100 100644 --- a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts +++ b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EditorInput, EditorModel } from 'vs/workbench/common/editor'; +import { EditorInput, EditorModel, IRevertOptions, GroupIdentifier, IEditorInput } from 'vs/workbench/common/editor'; import { Emitter, Event } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; @@ -230,8 +230,8 @@ export abstract class NotebookInput extends EditorInput { return this._textInput; } - public revert(): Promise { - return this._textInput.revert(); + public revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + return this._textInput.revert(group, options); } public get notebookUri(): URI { @@ -283,11 +283,11 @@ export abstract class NotebookInput extends EditorInput { return this._standardKernels; } - save(groupId: number, options?: ITextFileSaveOptions): Promise { + save(groupId: number, options?: ITextFileSaveOptions): Promise { return this.textInput.save(groupId, options); } - saveAs(group: number, options?: ITextFileSaveOptions): Promise { + saveAs(group: number, options?: ITextFileSaveOptions): Promise { return this.textInput.saveAs(group, options); } diff --git a/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts b/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts index 17f4513a1e..0f81b7e8eb 100644 --- a/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts +++ b/src/sql/workbench/contrib/notebook/test/browser/notebookInput.test.ts @@ -48,7 +48,7 @@ suite('Notebook Input', function (): void { let untitledNotebookInput: UntitledNotebookInput; setup(() => { - untitledTextInput = new UntitledTextEditorInput(untitledUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + untitledTextInput = new UntitledTextEditorInput(untitledUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); untitledNotebookInput = new UntitledNotebookInput( testTitle, untitledUri, untitledTextInput, undefined, instantiationService, mockNotebookService.object, mockExtensionService.object); @@ -169,7 +169,7 @@ suite('Notebook Input', function (): void { assert.ok(untitledNotebookInput.matches(untitledNotebookInput), 'Input should match itself.'); let otherTestUri = URI.from({ scheme: Schemas.untitled, path: 'OtherTestPath' }); - let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + let otherTextInput = new UntitledTextEditorInput(otherTestUri, false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); let otherInput = instantiationService.createInstance(UntitledNotebookInput, 'OtherTestInput', otherTestUri, otherTextInput); assert.strictEqual(untitledNotebookInput.matches(otherInput), false, 'Input should not match different input.'); diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts index 4b71c61d8c..1815e8495f 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts @@ -109,6 +109,9 @@ suite('SQL Connection Tree Action tests', () => { }); const viewsService = new class implements IViewsService { + getActiveViewWithId(id: string): IView { + throw new Error('Method not implemented.'); + } _serviceBrand: undefined; openView(id: string, focus?: boolean): Promise { return Promise.resolve({ diff --git a/src/sql/workbench/contrib/query/common/queryEditorInput.ts b/src/sql/workbench/contrib/query/common/queryEditorInput.ts index fff8838fde..0d85989f0f 100644 --- a/src/sql/workbench/contrib/query/common/queryEditorInput.ts +++ b/src/sql/workbench/contrib/query/common/queryEditorInput.ts @@ -7,7 +7,7 @@ import { localize } from 'vs/nls'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; -import { EditorInput } from 'vs/workbench/common/editor'; +import { EditorInput, GroupIdentifier, IRevertOptions, ISaveOptions, IEditorInput } from 'vs/workbench/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileService } from 'vs/platform/files/common/files'; @@ -17,7 +17,6 @@ import { IQueryModelService } from 'sql/platform/query/common/queryModel'; import { ISelectionData, ExecutionPlanOptions } from 'azdata'; import { startsWith } from 'vs/base/common/strings'; -import { ITextFileSaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; const MAX_SIZE = 13; @@ -175,7 +174,9 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab // Description is shown beside the tab name in the combobox of open editors public getDescription(): string { return this._description; } public supportsSplitEditor(): boolean { return false; } - public revert(): Promise { return this._text.revert(); } + public revert(group: GroupIdentifier, options?: IRevertOptions): Promise { + return this._text.revert(group, options); + } public isReadonly(): boolean { return false; @@ -224,11 +225,11 @@ export abstract class QueryEditorInput extends EditorInput implements IConnectab } } - save(groupId: number, options?: ITextFileSaveOptions): Promise { - return this.text.save(groupId, options); + save(group: GroupIdentifier, options?: ISaveOptions): Promise { + return this.text.save(group, options); } - saveAs(group: number, options?: ITextFileSaveOptions): Promise { + saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise { return this.text.saveAs(group, options); } diff --git a/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts b/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts index ae379e36e7..df9189c443 100644 --- a/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts +++ b/src/sql/workbench/contrib/query/common/untitledQueryEditorInput.ts @@ -47,10 +47,6 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod return this.text.hasAssociatedFilePath; } - public suggestFileName(): string { - return this.text.suggestFileName(); - } - public setMode(mode: string): void { this.text.setMode(mode); } @@ -75,12 +71,4 @@ export class UntitledQueryEditorInput extends QueryEditorInput implements IEncod // Subclasses need to explicitly opt-in to being untitled. return true; } - - hasBackup(): boolean { - if (this.text) { - return this.text.hasBackup(); - } - - return false; - } } diff --git a/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts b/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts index 754c657ff0..d400e508c6 100644 --- a/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts +++ b/src/sql/workbench/contrib/query/test/browser/queryActions.test.ts @@ -70,7 +70,7 @@ suite('SQL QueryAction Tests', () => { connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService); connectionManagementService.setup(q => q.onDisconnect).returns(() => Event.None); const instantiationService = new TestInstantiationService(); - let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); // Setup a reusable mock QueryInput testQueryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object); testQueryInput.setup(x => x.uri).returns(() => testUri); @@ -175,7 +175,7 @@ suite('SQL QueryAction Tests', () => { queryModelService.setup(x => x.onRunQueryStart).returns(() => Event.None); queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None); const instantiationService = new TestInstantiationService(); - let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); // ... Mock "isSelectionEmpty" in QueryEditor let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Strict, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object); @@ -224,7 +224,7 @@ suite('SQL QueryAction Tests', () => { // ... Mock "getSelection" in QueryEditor const instantiationService = new TestInstantiationService(); - let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService, undefined, new LabelService(undefined, undefined), undefined, undefined); let queryInput = TypeMoq.Mock.ofType(UntitledQueryEditorInput, TypeMoq.MockBehavior.Loose, undefined, fileInput, undefined, connectionManagementService.object, queryModelService.object, configurationService.object); queryInput.setup(x => x.uri).returns(() => testUri); diff --git a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts index eb92c8b9d6..85923239e9 100644 --- a/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts +++ b/src/sql/workbench/contrib/query/test/browser/queryEditor.test.ts @@ -285,7 +285,7 @@ suite('SQL QueryEditor Tests', () => { return new RunQueryAction(undefined, undefined, undefined); }); - let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, undefined, undefined); + let fileInput = new UntitledTextEditorInput(URI.parse('file://testUri'), false, '', '', '', instantiationService.object, undefined, new LabelService(undefined, undefined), undefined, undefined); queryModelService = TypeMoq.Mock.ofType(TestQueryModelService, TypeMoq.MockBehavior.Strict); queryModelService.setup(x => x.disposeQuery(TypeMoq.It.isAny())); queryModelService.setup(x => x.onRunQueryComplete).returns(() => Event.None); diff --git a/src/sql/workbench/services/insights/browser/insightsDialogView.ts b/src/sql/workbench/services/insights/browser/insightsDialogView.ts index d0f6c9d136..9d448eadcc 100644 --- a/src/sql/workbench/services/insights/browser/insightsDialogView.ts +++ b/src/sql/workbench/services/insights/browser/insightsDialogView.ts @@ -43,6 +43,7 @@ import { ITextResourcePropertiesService } from 'vs/editor/common/services/textRe import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; const labelDisplay = nls.localize("insights.item", "Item"); const valueDisplay = nls.localize("insights.value", "Value"); @@ -63,9 +64,10 @@ class InsightTableView extends ViewPane { @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, - @IInstantiationService instantiationService: IInstantiationService + @IInstantiationService instantiationService: IInstantiationService, + @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, instantiationService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService); } protected renderBody(container: HTMLElement): void { diff --git a/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts b/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts index a70aa1bb12..d8659a56f5 100644 --- a/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts +++ b/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts @@ -25,6 +25,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; class TestEnvironmentService implements IWorkbenchEnvironmentService { + userDataSyncHome: URI; keybindingsSyncPreviewResource: URI; argvResource: URI; userDataSyncLogResource: URI; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index f4220b4766..5fb7fd0ebe 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -287,7 +287,7 @@ export function addDisposableGenericMouseUpListner(node: EventTarget, handler: ( export function addDisposableNonBubblingMouseOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'mouseout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.target); + let toElement: Node | null = (e.relatedTarget); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -302,7 +302,7 @@ export function addDisposableNonBubblingMouseOutListener(node: Element, handler: export function addDisposableNonBubblingPointerOutListener(node: Element, handler: (event: MouseEvent) => void): IDisposable { return addDisposableListener(node, 'pointerout', (e: MouseEvent) => { // Mouse out bubbles, so this is an attempt to ignore faux mouse outs coming from children elements - let toElement: Node | null = (e.relatedTarget || e.target); + let toElement: Node | null = (e.relatedTarget); while (toElement && toElement !== node) { toElement = toElement.parentNode; } @@ -628,11 +628,17 @@ export function getTopLeftOffset(element: HTMLElement): { left: number; top: num // Adapted from WinJS.Utilities.getPosition // and added borders to the mix - let offsetParent = element.offsetParent, top = element.offsetTop, left = element.offsetLeft; + let offsetParent = element.offsetParent; + let top = element.offsetTop; + let left = element.offsetLeft; - while ((element = element.parentNode) !== null && element !== document.body && element !== document.documentElement) { + while ( + (element = element.parentNode) !== null + && element !== document.body + && element !== document.documentElement + ) { top -= element.scrollTop; - let c = getComputedStyle(element); + const c = isShadowRoot(element) ? null : getComputedStyle(element); if (c) { left -= c.direction !== 'rtl' ? element.scrollLeft : -element.scrollLeft; } @@ -793,7 +799,7 @@ export function isAncestor(testChild: Node | null, testAncestor: Node | null): b } export function findParentWithClass(node: HTMLElement, clazz: string, stopAtClazzOrNode?: string | HTMLElement): HTMLElement | null { - while (node) { + while (node && node.nodeType === node.ELEMENT_NODE) { if (hasClass(node, clazz)) { return node; } @@ -820,6 +826,27 @@ export function hasParentWithClass(node: HTMLElement, clazz: string, stopAtClazz return !!findParentWithClass(node, clazz, stopAtClazzOrNode); } +export function isShadowRoot(node: Node): node is ShadowRoot { + return ( + node && !!(node).host && !!(node).mode + ); +} + +export function isInShadowDOM(domNode: Node): boolean { + return !!getShadowRoot(domNode); +} + +export function getShadowRoot(domNode: Node): ShadowRoot | null { + while (domNode.parentNode) { + if (domNode === document.body) { + // reached the body + return null; + } + domNode = domNode.parentNode; + } + return isShadowRoot(domNode) ? domNode : null; +} + export function createStyleSheet(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLStyleElement { let style = document.createElement('style'); style.type = 'text/css'; @@ -1167,7 +1194,7 @@ export function hide(...elements: HTMLElement[]): void { } function findParentWithAttribute(node: Node | null, attribute: string): HTMLElement | null { - while (node) { + while (node && node.nodeType === node.ELEMENT_NODE) { if (node instanceof HTMLElement && node.hasAttribute(attribute)) { return node; } diff --git a/src/vs/base/browser/fastDomNode.ts b/src/vs/base/browser/fastDomNode.ts index 202be357f3..8be66b6d90 100644 --- a/src/vs/base/browser/fastDomNode.ts +++ b/src/vs/base/browser/fastDomNode.ts @@ -25,6 +25,7 @@ export class FastDomNode { private _display: string; private _position: string; private _visibility: string; + private _backgroundColor: string; private _layerHint: boolean; private _contain: 'none' | 'strict' | 'content' | 'size' | 'layout' | 'style' | 'paint'; @@ -47,6 +48,7 @@ export class FastDomNode { this._display = ''; this._position = ''; this._visibility = ''; + this._backgroundColor = ''; this._layerHint = false; this._contain = 'none'; } @@ -200,6 +202,14 @@ export class FastDomNode { this.domNode.style.visibility = this._visibility; } + public setBackgroundColor(backgroundColor: string): void { + if (this._backgroundColor === backgroundColor) { + return; + } + this._backgroundColor = backgroundColor; + this.domNode.style.backgroundColor = this._backgroundColor; + } + public setLayerHinting(layerHint: boolean): void { if (this._layerHint === layerHint) { return; diff --git a/src/vs/base/browser/globalMouseMoveMonitor.ts b/src/vs/base/browser/globalMouseMoveMonitor.ts index c50e6e71ff..5265d2fb1b 100644 --- a/src/vs/base/browser/globalMouseMoveMonitor.ts +++ b/src/vs/base/browser/globalMouseMoveMonitor.ts @@ -75,6 +75,7 @@ export class GlobalMouseMoveMonitor implements I } public startMonitoring( + initialElement: HTMLElement, initialButtons: number, mouseMoveEventMerger: IEventMerger, mouseMoveCallback: IMouseMoveCallback, @@ -88,11 +89,18 @@ export class GlobalMouseMoveMonitor implements I this._mouseMoveCallback = mouseMoveCallback; this._onStopCallback = onStopCallback; - let windowChain = IframeUtils.getSameOriginWindowChain(); + const windowChain = IframeUtils.getSameOriginWindowChain(); const mouseMove = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointermove' : 'mousemove'; const mouseUp = platform.isIOS && BrowserFeatures.pointerEvents ? 'pointerup' : 'mouseup'; - for (const element of windowChain) { - this._hooks.add(dom.addDisposableThrottledListener(element.window.document, mouseMove, + + const listenTo: (Document | ShadowRoot)[] = windowChain.map(element => element.window.document); + const shadowRoot = dom.getShadowRoot(initialElement); + if (shadowRoot) { + listenTo.unshift(shadowRoot); + } + + for (const element of listenTo) { + this._hooks.add(dom.addDisposableThrottledListener(element, mouseMove, (data: R) => { if (data.buttons !== initialButtons) { // Buttons state has changed in the meantime @@ -103,7 +111,7 @@ export class GlobalMouseMoveMonitor implements I }, (lastEvent: R | null, currentEvent) => this._mouseMoveEventMerger!(lastEvent, currentEvent as MouseEvent) )); - this._hooks.add(dom.addDisposableListener(element.window.document, mouseUp, (e: MouseEvent) => this.stopMonitoring(true))); + this._hooks.add(dom.addDisposableListener(element, mouseUp, (e: MouseEvent) => this.stopMonitoring(true))); } if (IframeUtils.hasDifferentOriginAncestor()) { diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index f9daecb3e9..986a7808c0 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -5,7 +5,7 @@ @font-face { font-family: "codicon"; - src: url("./codicon.ttf?ed926e87ee4e27771159d875e877f74a") format("truetype"); + src: url("./codicon.ttf?be537a78617db0869caa4b4cc683a24a") format("truetype"); } .codicon[class*='codicon-'] { @@ -119,6 +119,7 @@ .codicon-github:before { content: "\ea84" } .codicon-terminal:before { content: "\ea85" } .codicon-console:before { content: "\ea85" } +.codicon-repl:before { content: "\ea85" } .codicon-zap:before { content: "\ea86" } .codicon-symbol-event:before { content: "\ea86" } .codicon-error:before { content: "\ea87" } @@ -410,4 +411,6 @@ .codicon-menu:before { content: "\eb94" } .codicon-expand-all:before { content: "\eb95" } .codicon-feedback:before { content: "\eb96" } +.codicon-group-by-ref-type:before { content: "\eb97" } +.codicon-ungroup-by-ref-type:before { content: "\eb98" } .codicon-debug-alt:before { content: "\f101" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf index aae19797fb..90ace76ff7 100644 Binary files a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf and b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf differ diff --git a/src/vs/base/browser/ui/iconLabel/iconLabel.ts b/src/vs/base/browser/ui/iconLabel/iconLabel.ts index a1eaa1ecd7..0a6c3392c9 100644 --- a/src/vs/base/browser/ui/iconLabel/iconLabel.ts +++ b/src/vs/base/browser/ui/iconLabel/iconLabel.ts @@ -92,8 +92,10 @@ class FastLabelNode { export class IconLabel extends Disposable { private domNode: FastLabelNode; - private descriptionContainer: FastLabelNode; + private nameNode: Label | LabelWithHighlights; + + private descriptionContainer: FastLabelNode; private descriptionNode: FastLabelNode | HighlightedLabel | undefined; private descriptionNodeFactory: () => FastLabelNode | HighlightedLabel; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index aae604189a..2bc82bb539 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -844,7 +844,7 @@ export class ListView implements ISpliceable, IDisposable { } // sanitize feedback list - feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort(); + feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort((a, b) => a - b); feedback = feedback[0] === -1 ? [-1] : feedback; if (equalsDragFeedback(this.currentDragFeedback, feedback)) { diff --git a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts index 9e6a493625..3575bad090 100644 --- a/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/abstractScrollbar.ts @@ -61,6 +61,7 @@ export abstract class AbstractScrollbar extends Widget { this._scrollable = opts.scrollable; this._scrollbarState = opts.scrollbarState; this._visibilityController = this._register(new ScrollbarVisibilityController(opts.visibility, 'visible scrollbar ' + opts.extraScrollbarClassName, 'invisible scrollbar ' + opts.extraScrollbarClassName)); + this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()); this._mouseMoveMonitor = this._register(new GlobalMouseMoveMonitor()); this._shouldRender = true; this.domNode = createFastDomNode(document.createElement('div')); @@ -216,13 +217,14 @@ export abstract class AbstractScrollbar extends Widget { } } - private _sliderMouseDown(e: ISimplifiedMouseEvent, onDragFinished: () => void): void { + private _sliderMouseDown(e: IMouseEvent, onDragFinished: () => void): void { const initialMousePosition = this._sliderMousePosition(e); const initialMouseOrthogonalPosition = this._sliderOrthogonalMousePosition(e); const initialScrollbarState = this._scrollbarState.clone(); this.slider.toggleClassName('active', true); this._mouseMoveMonitor.startMonitoring( + e.target, e.buttons, standardMouseMoveMerger, (mouseMoveData: IStandardMouseMoveEventData) => { diff --git a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts index de08891aa7..cc38e67f17 100644 --- a/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/horizontalScrollbar.ts @@ -13,13 +13,18 @@ import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from export class HorizontalScrollbar extends AbstractScrollbar { constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + const scrollDimensions = scrollable.getScrollDimensions(); + const scrollPosition = scrollable.getCurrentScrollPosition(); super({ lazyRender: options.lazyRender, host: host, scrollbarState: new ScrollbarState( (options.horizontalHasArrows ? options.arrowSize : 0), (options.horizontal === ScrollbarVisibility.Hidden ? 0 : options.horizontalScrollbarSize), - (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize) + (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), + scrollDimensions.width, + scrollDimensions.scrollWidth, + scrollPosition.scrollLeft ), visibility: options.horizontal, extraScrollbarClassName: 'horizontal', diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts index 7365ca8bb3..a5a16e8926 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarArrow.ts @@ -93,6 +93,7 @@ export class ScrollbarArrow extends Widget { this._mousedownScheduleRepeatTimer.cancelAndSet(scheduleRepeater, 200); this._mouseMoveMonitor.startMonitoring( + e.target, e.buttons, standardMouseMoveMerger, (mouseMoveData: IStandardMouseMoveEventData) => { diff --git a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts index 8213e3f42a..3c82b08ba8 100644 --- a/src/vs/base/browser/ui/scrollbar/scrollbarState.ts +++ b/src/vs/base/browser/ui/scrollbar/scrollbarState.ts @@ -62,14 +62,14 @@ export class ScrollbarState { private _computedSliderRatio: number; private _computedSliderPosition: number; - constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number) { + constructor(arrowSize: number, scrollbarSize: number, oppositeScrollbarSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) { this._scrollbarSize = Math.round(scrollbarSize); this._oppositeScrollbarSize = Math.round(oppositeScrollbarSize); this._arrowSize = Math.round(arrowSize); - this._visibleSize = 0; - this._scrollSize = 0; - this._scrollPosition = 0; + this._visibleSize = visibleSize; + this._scrollSize = scrollSize; + this._scrollPosition = scrollPosition; this._computedAvailableSize = 0; this._computedIsNeeded = false; @@ -81,11 +81,7 @@ export class ScrollbarState { } public clone(): ScrollbarState { - let r = new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize); - r.setVisibleSize(this._visibleSize); - r.setScrollSize(this._scrollSize); - r.setScrollPosition(this._scrollPosition); - return r; + return new ScrollbarState(this._arrowSize, this._scrollbarSize, this._oppositeScrollbarSize, this._visibleSize, this._scrollSize, this._scrollPosition); } public setVisibleSize(visibleSize: number): boolean { diff --git a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts index b59e7462ae..297ef8dab6 100644 --- a/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts +++ b/src/vs/base/browser/ui/scrollbar/verticalScrollbar.ts @@ -13,6 +13,8 @@ import { INewScrollPosition, ScrollEvent, Scrollable, ScrollbarVisibility } from export class VerticalScrollbar extends AbstractScrollbar { constructor(scrollable: Scrollable, options: ScrollableElementResolvedOptions, host: ScrollbarHost) { + const scrollDimensions = scrollable.getScrollDimensions(); + const scrollPosition = scrollable.getCurrentScrollPosition(); super({ lazyRender: options.lazyRender, host: host, @@ -20,7 +22,10 @@ export class VerticalScrollbar extends AbstractScrollbar { (options.verticalHasArrows ? options.arrowSize : 0), (options.vertical === ScrollbarVisibility.Hidden ? 0 : options.verticalScrollbarSize), // give priority to vertical scroll bar over horizontal and let it scroll all the way to the bottom - 0 + 0, + scrollDimensions.height, + scrollDimensions.scrollHeight, + scrollPosition.scrollTop ), visibility: options.vertical, extraScrollbarClassName: 'vertical', diff --git a/src/vs/base/common/amd.ts b/src/vs/base/common/amd.ts index 4f04a8c6a0..f8e89666ed 100644 --- a/src/vs/base/common/amd.ts +++ b/src/vs/base/common/amd.ts @@ -6,7 +6,11 @@ import { URI } from 'vs/base/common/uri'; export function getPathFromAmdModule(requirefn: typeof require, relativePath: string): string { - return URI.parse(requirefn.toUrl(relativePath)).fsPath; + return getUriFromAmdModule(requirefn, relativePath).fsPath; +} + +export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI { + return URI.parse(requirefn.toUrl(relativePath)); } /** diff --git a/src/vs/base/common/async.ts b/src/vs/base/common/async.ts index fe52899ca1..6aca3015e4 100644 --- a/src/vs/base/common/async.ts +++ b/src/vs/base/common/async.ts @@ -481,8 +481,9 @@ export class Queue extends Limiter { * A helper to organize queues per resource. The ResourceQueue makes sure to manage queues per resource * by disposing them once the queue is empty. */ -export class ResourceQueue { - private queues: Map> = new Map(); +export class ResourceQueue implements IDisposable { + + private readonly queues = new Map>(); queueFor(resource: URI): Queue { const key = resource.toString(); @@ -498,6 +499,11 @@ export class ResourceQueue { return this.queues.get(key)!; } + + dispose(): void { + this.queues.forEach(queue => queue.dispose()); + this.queues.clear(); + } } export class TimeoutTimer implements IDisposable { diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index c83d0bd2cc..0c16242740 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -148,6 +148,7 @@ export namespace Event { if (leading && !handle) { emitter.fire(output); + output = undefined; } clearTimeout(handle); diff --git a/src/vs/base/common/jsonEdit.ts b/src/vs/base/common/jsonEdit.ts index a106d88dfc..e8df6c3188 100644 --- a/src/vs/base/common/jsonEdit.ts +++ b/src/vs/base/common/jsonEdit.ts @@ -120,7 +120,7 @@ export function setProperty(text: string, originalPath: JSONPath, value: any, fo } } -function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] { +export function withFormatting(text: string, edit: Edit, formattingOptions: FormattingOptions): Edit[] { // apply the edit let newText = applyEdit(text, edit); diff --git a/src/vs/base/common/jsonFormatter.ts b/src/vs/base/common/jsonFormatter.ts index 5f23835921..215407249a 100644 --- a/src/vs/base/common/jsonFormatter.ts +++ b/src/vs/base/common/jsonFormatter.ts @@ -228,7 +228,7 @@ function computeIndentLevel(content: string, options: FormattingOptions): number return Math.floor(nChars / tabSize); } -function getEOL(options: FormattingOptions, text: string): string { +export function getEOL(options: FormattingOptions, text: string): string { for (let i = 0; i < text.length; i++) { const ch = text.charAt(i); if (ch === '\r') { @@ -245,4 +245,4 @@ function getEOL(options: FormattingOptions, text: string): string { export function isEOL(text: string, offset: number) { return '\r\n'.indexOf(text.charAt(offset)) !== -1; -} \ No newline at end of file +} diff --git a/src/vs/base/node/ports.ts b/src/vs/base/node/ports.ts index e41b8c4438..6a87c0bc5d 100644 --- a/src/vs/base/node/ports.ts +++ b/src/vs/base/node/ports.ts @@ -72,6 +72,49 @@ function doFindFreePort(startPort: number, giveUpAfter: number, clb: (port: numb client.connect(startPort, '127.0.0.1'); } +/** + * Uses listen instead of connect. Is faster, but if there is another listener on 0.0.0.0 then this will take 127.0.0.1 from that listener. + */ +export function findFreePortFaster(startPort: number, giveUpAfter: number, timeout: number): Promise { + let resolved: boolean = false; + let timeoutHandle: NodeJS.Timeout | undefined = undefined; + let countTried: number = 1; + const server = net.createServer({ pauseOnConnect: true }); + function doResolve(port: number, resolve: (port: number) => void) { + if (!resolved) { + resolved = true; + server.removeAllListeners(); + server.close(); + if (timeoutHandle) { + clearTimeout(timeoutHandle); + } + resolve(port); + } + } + return new Promise(resolve => { + timeoutHandle = setTimeout(() => { + doResolve(0, resolve); + }, timeout); + + server.on('listening', () => { + doResolve(startPort, resolve); + }); + server.on('error', err => { + if (err && ((err).code === 'EADDRINUSE' || (err).code === 'EACCES') && (countTried < giveUpAfter)) { + startPort++; + countTried++; + server.listen(startPort, '127.0.0.1'); + } else { + doResolve(0, resolve); + } + }); + server.on('close', () => { + doResolve(0, resolve); + }); + server.listen(startPort, '127.0.0.1'); + }); +} + function dispose(socket: net.Socket): void { try { socket.removeAllListeners('connect'); diff --git a/src/vs/base/parts/storage/test/node/storage.test.ts b/src/vs/base/parts/storage/test/node/storage.test.ts index 20f156e865..3d8b8397c3 100644 --- a/src/vs/base/parts/storage/test/node/storage.test.ts +++ b/src/vs/base/parts/storage/test/node/storage.test.ts @@ -550,7 +550,7 @@ suite('SQLite Storage Library', () => { let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); const items1 = new Map(); - items1.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); + items1.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); items1.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); items1.set('cpp.1.lastsessiondate', 'Fri Oct 05 2018'); items1.set('debug.actionswidgetposition', '0.6880952380952381'); @@ -641,7 +641,7 @@ suite('SQLite Storage Library', () => { let storage = new SQLiteStorageDatabase(join(storageDir, 'storage.db')); const items = new Map(); - items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#09885a"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#09885a"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#09885a"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#09885a"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#09885a"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); + items.set('colorthemedata', '{"id":"vs vscode-theme-defaults-themes-light_plus-json","label":"Light+ (default light)","settingsId":"Default Light+","selector":"vs.vscode-theme-defaults-themes-light_plus-json","themeTokenColors":[{"settings":{"foreground":"#000000ff","background":"#ffffffff"}},{"scope":["meta.embedded","source.groovy.embedded"],"settings":{"foreground":"#000000ff"}},{"scope":"emphasis","settings":{"fontStyle":"italic"}},{"scope":"strong","settings":{"fontStyle":"bold"}},{"scope":"meta.diff.header","settings":{"foreground":"#000080"}},{"scope":"comment","settings":{"foreground":"#008000"}},{"scope":"constant.language","settings":{"foreground":"#0000ff"}},{"scope":["constant.numeric"],"settings":{"foreground":"#098658"}},{"scope":"constant.regexp","settings":{"foreground":"#811f3f"}},{"name":"css tags in selectors, xml tags","scope":"entity.name.tag","settings":{"foreground":"#800000"}},{"scope":"entity.name.selector","settings":{"foreground":"#800000"}},{"scope":"entity.other.attribute-name","settings":{"foreground":"#ff0000"}},{"scope":["entity.other.attribute-name.class.css","entity.other.attribute-name.class.mixin.css","entity.other.attribute-name.id.css","entity.other.attribute-name.parent-selector.css","entity.other.attribute-name.pseudo-class.css","entity.other.attribute-name.pseudo-element.css","source.css.less entity.other.attribute-name.id","entity.other.attribute-name.attribute.scss","entity.other.attribute-name.scss"],"settings":{"foreground":"#800000"}},{"scope":"invalid","settings":{"foreground":"#cd3131"}},{"scope":"markup.underline","settings":{"fontStyle":"underline"}},{"scope":"markup.bold","settings":{"fontStyle":"bold","foreground":"#000080"}},{"scope":"markup.heading","settings":{"fontStyle":"bold","foreground":"#800000"}},{"scope":"markup.italic","settings":{"fontStyle":"italic"}},{"scope":"markup.inserted","settings":{"foreground":"#098658"}},{"scope":"markup.deleted","settings":{"foreground":"#a31515"}},{"scope":"markup.changed","settings":{"foreground":"#0451a5"}},{"scope":["punctuation.definition.quote.begin.markdown","punctuation.definition.list.begin.markdown"],"settings":{"foreground":"#0451a5"}},{"scope":"markup.inline.raw","settings":{"foreground":"#800000"}},{"name":"brackets of XML/HTML tags","scope":"punctuation.definition.tag","settings":{"foreground":"#800000"}},{"scope":"meta.preprocessor","settings":{"foreground":"#0000ff"}},{"scope":"meta.preprocessor.string","settings":{"foreground":"#a31515"}},{"scope":"meta.preprocessor.numeric","settings":{"foreground":"#098658"}},{"scope":"meta.structure.dictionary.key.python","settings":{"foreground":"#0451a5"}},{"scope":"storage","settings":{"foreground":"#0000ff"}},{"scope":"storage.type","settings":{"foreground":"#0000ff"}},{"scope":"storage.modifier","settings":{"foreground":"#0000ff"}},{"scope":"string","settings":{"foreground":"#a31515"}},{"scope":["string.comment.buffered.block.pug","string.quoted.pug","string.interpolated.pug","string.unquoted.plain.in.yaml","string.unquoted.plain.out.yaml","string.unquoted.block.yaml","string.quoted.single.yaml","string.quoted.double.xml","string.quoted.single.xml","string.unquoted.cdata.xml","string.quoted.double.html","string.quoted.single.html","string.unquoted.html","string.quoted.single.handlebars","string.quoted.double.handlebars"],"settings":{"foreground":"#0000ff"}},{"scope":"string.regexp","settings":{"foreground":"#811f3f"}},{"name":"String interpolation","scope":["punctuation.definition.template-expression.begin","punctuation.definition.template-expression.end","punctuation.section.embedded"],"settings":{"foreground":"#0000ff"}},{"name":"Reset JavaScript string interpolation expression","scope":["meta.template.expression"],"settings":{"foreground":"#000000"}},{"scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"scope":["support.type.vendored.property-name","support.type.property-name","variable.css","variable.scss","variable.other.less","source.coffee.embedded"],"settings":{"foreground":"#ff0000"}},{"scope":["support.type.property-name.json"],"settings":{"foreground":"#0451a5"}},{"scope":"keyword","settings":{"foreground":"#0000ff"}},{"scope":"keyword.control","settings":{"foreground":"#0000ff"}},{"scope":"keyword.operator","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.new","keyword.operator.expression","keyword.operator.cast","keyword.operator.sizeof","keyword.operator.instanceof","keyword.operator.logical.python"],"settings":{"foreground":"#0000ff"}},{"scope":"keyword.other.unit","settings":{"foreground":"#098658"}},{"scope":["punctuation.section.embedded.begin.php","punctuation.section.embedded.end.php"],"settings":{"foreground":"#800000"}},{"scope":"support.function.git-rebase","settings":{"foreground":"#0451a5"}},{"scope":"constant.sha.git-rebase","settings":{"foreground":"#098658"}},{"name":"coloring of the Java import and package identifiers","scope":["storage.modifier.import.java","variable.language.wildcard.java","storage.modifier.package.java"],"settings":{"foreground":"#000000"}},{"name":"this.self","scope":"variable.language","settings":{"foreground":"#0000ff"}},{"name":"Function declarations","scope":["entity.name.function","support.function","support.constant.handlebars"],"settings":{"foreground":"#795E26"}},{"name":"Types declaration and references","scope":["meta.return-type","support.class","support.type","entity.name.type","entity.name.class","storage.type.numeric.go","storage.type.byte.go","storage.type.boolean.go","storage.type.string.go","storage.type.uintptr.go","storage.type.error.go","storage.type.rune.go","storage.type.cs","storage.type.generic.cs","storage.type.modifier.cs","storage.type.variable.cs","storage.type.annotation.java","storage.type.generic.java","storage.type.java","storage.type.object.array.java","storage.type.primitive.array.java","storage.type.primitive.java","storage.type.token.java","storage.type.groovy","storage.type.annotation.groovy","storage.type.parameters.groovy","storage.type.generic.groovy","storage.type.object.array.groovy","storage.type.primitive.array.groovy","storage.type.primitive.groovy"],"settings":{"foreground":"#267f99"}},{"name":"Types declaration and references, TS grammar specific","scope":["meta.type.cast.expr","meta.type.new.expr","support.constant.math","support.constant.dom","support.constant.json","entity.other.inherited-class"],"settings":{"foreground":"#267f99"}},{"name":"Control flow keywords","scope":"keyword.control","settings":{"foreground":"#AF00DB"}},{"name":"Variable and parameter name","scope":["variable","meta.definition.variable.name","support.variable","entity.name.variable"],"settings":{"foreground":"#001080"}},{"name":"Object keys, TS grammar specific","scope":["meta.object-literal.key"],"settings":{"foreground":"#001080"}},{"name":"CSS property value","scope":["support.constant.property-value","support.constant.font-name","support.constant.media-type","support.constant.media","constant.other.color.rgb-value","constant.other.rgb-value","support.constant.color"],"settings":{"foreground":"#0451a5"}},{"name":"Regular expression groups","scope":["punctuation.definition.group.regexp","punctuation.definition.group.assertion.regexp","punctuation.definition.character-class.regexp","punctuation.character.set.begin.regexp","punctuation.character.set.end.regexp","keyword.operator.negation.regexp","support.other.parenthesis.regexp"],"settings":{"foreground":"#d16969"}},{"scope":["constant.character.character-class.regexp","constant.other.character-class.set.regexp","constant.other.character-class.regexp","constant.character.set.regexp"],"settings":{"foreground":"#811f3f"}},{"scope":"keyword.operator.quantifier.regexp","settings":{"foreground":"#000000"}},{"scope":["keyword.operator.or.regexp","keyword.control.anchor.regexp"],"settings":{"foreground":"#ff0000"}},{"scope":"constant.character","settings":{"foreground":"#0000ff"}},{"scope":"constant.character.escape","settings":{"foreground":"#ff0000"}},{"scope":"token.info-token","settings":{"foreground":"#316bcd"}},{"scope":"token.warn-token","settings":{"foreground":"#cd9731"}},{"scope":"token.error-token","settings":{"foreground":"#cd3131"}},{"scope":"token.debug-token","settings":{"foreground":"#800080"}}],"extensionData":{"extensionId":"vscode.theme-defaults","extensionPublisher":"vscode","extensionName":"theme-defaults","extensionIsBuiltin":true},"colorMap":{"editor.background":"#ffffff","editor.foreground":"#000000","editor.inactiveSelectionBackground":"#e5ebf1","editorIndentGuide.background":"#d3d3d3","editorIndentGuide.activeBackground":"#939393","editor.selectionHighlightBackground":"#add6ff4d","editorSuggestWidget.background":"#f3f3f3","activityBarBadge.background":"#007acc","sideBarTitle.foreground":"#6f6f6f","list.hoverBackground":"#e8e8e8","input.placeholderForeground":"#767676","settings.textInputBorder":"#cecece","settings.numberInputBorder":"#cecece"}}'); items.set('commandpalette.mru.cache', '{"usesLRU":true,"entries":[{"key":"revealFileInOS","value":3},{"key":"extension.openInGitHub","value":4},{"key":"workbench.extensions.action.openExtensionsFolder","value":11},{"key":"workbench.action.showRuntimeExtensions","value":14},{"key":"workbench.action.toggleTabsVisibility","value":15},{"key":"extension.liveServerPreview.open","value":16},{"key":"workbench.action.openIssueReporter","value":18},{"key":"workbench.action.openProcessExplorer","value":19},{"key":"workbench.action.toggleSharedProcess","value":20},{"key":"workbench.action.configureLocale","value":21},{"key":"workbench.action.appPerf","value":22},{"key":"workbench.action.reportPerformanceIssueUsingReporter","value":23},{"key":"workbench.action.openGlobalKeybindings","value":25},{"key":"workbench.action.output.toggleOutput","value":27},{"key":"extension.sayHello","value":29}]}'); let uuid = generateUuid(); diff --git a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts index addca1f742..75673ae740 100644 --- a/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts +++ b/src/vs/base/test/browser/ui/scrollbar/scrollbarState.test.ts @@ -8,10 +8,7 @@ import { ScrollbarState } from 'vs/base/browser/ui/scrollbar/scrollbarState'; suite('ScrollbarState', () => { test('inflates slider size', () => { - let actual = new ScrollbarState(0, 14, 0); - actual.setVisibleSize(339); - actual.setScrollSize(42423); - actual.setScrollPosition(32787); + let actual = new ScrollbarState(0, 14, 0, 339, 42423, 32787); assert.equal(actual.getArrowSize(), 0); assert.equal(actual.getScrollPosition(), 32787); @@ -34,10 +31,7 @@ suite('ScrollbarState', () => { }); test('inflates slider size with arrows', () => { - let actual = new ScrollbarState(12, 14, 0); - actual.setVisibleSize(339); - actual.setScrollSize(42423); - actual.setScrollPosition(32787); + let actual = new ScrollbarState(12, 14, 0, 339, 42423, 32787); assert.equal(actual.getArrowSize(), 12); assert.equal(actual.getScrollPosition(), 32787); diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index b480564a15..b71ac81b5a 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -237,6 +237,20 @@ suite('Event', function () { assert.equal(calls, 2); }); + test('Debounce Event - leading reset', async function () { + const emitter = new Emitter(); + let debounced = Event.debounce(emitter.event, (l, e) => l ? l + 1 : 1, 0, /*leading=*/true); + + let calls: number[] = []; + debounced((e) => calls.push(e)); + + emitter.fire(1); + emitter.fire(1); + + await timeout(1); + assert.deepEqual(calls, [1, 1]); + }); + test('Emitter - In Order Delivery', function () { const a = new Emitter(); const listener2Events: string[] = []; diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 872b639574..380e8a5c06 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -12,8 +12,8 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannel, GlobalExtensionEnablementServiceClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -113,6 +113,9 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat disposables.add(logService); logService.info('main', JSON.stringify(configuration)); + const mainProcessService = new MainProcessService(server, mainRouter); + services.set(IMainProcessService, mainProcessService); + const configurationService = new ConfigurationService(environmentService.settingsResource); disposables.add(configurationService); await configurationService.initialize(); @@ -124,8 +127,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(ILoggerService, new SyncDescriptor(LoggerService)); - const mainProcessService = new MainProcessService(server, mainRouter); - services.set(IMainProcessService, mainProcessService); const electronService = createChannelSender(mainProcessService.getChannel('electron'), { context: configuration.windowId }); services.set(IElectronService, electronService); @@ -184,6 +185,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IUserDataAuthTokenService, new SyncDescriptor(UserDataAuthTokenService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter))); + services.set(IGlobalExtensionEnablementService, new GlobalExtensionEnablementServiceClient(server.getChannel('globalExtensionEnablement', activeWindowRouter))); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(ISettingsSyncService, new SyncDescriptor(SettingsSynchroniser)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 443142a980..c715f4d517 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -171,7 +171,7 @@ export class CodeApplication extends Disposable { app.on('web-contents-created', (_event: Event, contents) => { contents.on('will-attach-webview', (event: Event, webPreferences, params) => { - const isValidWebviewSource = (source: string | undefined): boolean => { + const isValidWebviewSource = (source: string): boolean => { if (!source) { return false; } @@ -191,12 +191,11 @@ export class CodeApplication extends Disposable { webPreferences.nodeIntegration = false; // Verify URLs being loaded - // https://github.com/electron/electron/issues/21553 - if (isValidWebviewSource(params.src) && isValidWebviewSource((webPreferences as { preloadURL: string }).preloadURL)) { + if (isValidWebviewSource(params.src) && isValidWebviewSource(webPreferences.preloadURL)) { return; } - delete (webPreferences as { preloadURL: string }).preloadURL; // https://github.com/electron/electron/issues/21553 + delete webPreferences.preloadUrl; // Otherwise prevent loading this.logService.error('webContents#web-contents-created: Prevented webview attach'); @@ -498,27 +497,27 @@ export class CodeApplication extends Disposable { this.logService.info(`Tracing: waiting for windows to get ready...`); let recordingStopped = false; - const stopRecording = async (timeout: boolean) => { + const stopRecording = (timeout: boolean) => { if (recordingStopped) { return; } recordingStopped = true; // only once - const path = await contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`)); - - if (!timeout) { - if (this.dialogMainService) { - this.dialogMainService.showMessageBox({ - type: 'info', - message: localize('trace.message', "Successfully created trace."), - detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), - buttons: [localize('trace.ok', "Ok")] - }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); + contentTracing.stopRecording(join(homedir(), `${product.applicationName}-${Math.random().toString(16).slice(-4)}.trace.txt`), path => { + if (!timeout) { + if (this.dialogMainService) { + this.dialogMainService.showMessageBox({ + type: 'info', + message: localize('trace.message', "Successfully created trace."), + detail: localize('trace.detail', "Please create an issue and manually attach the following file:\n{0}", path), + buttons: [localize('trace.ok', "Ok")] + }, withNullAsUndefined(BrowserWindow.getFocusedWindow())); + } + } else { + this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); } - } else { - this.logService.info(`Tracing: data recorded (after 30s timeout) to ${path}`); - } + }); }; // Wait up to 30s before creating the trace anyways diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index be27392a3f..b8929ca792 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import * as nls from 'vs/nls'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; 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 } from 'electron'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -347,9 +347,9 @@ export class CodeWindow extends Disposable implements ICodeWindow { }); this._win.webContents.session.webRequest.onHeadersReceived(null!, (details, callback) => { - const responseHeaders = details.responseHeaders as Record; + const responseHeaders = details.responseHeaders as { [key: string]: string[] }; - const contentType = (responseHeaders['content-type'] || responseHeaders['Content-Type']); + const contentType: string[] = (responseHeaders['content-type'] || responseHeaders['Content-Type']); if (contentType && Array.isArray(contentType) && contentType.some(x => x.toLowerCase().indexOf('image/svg') >= 0)) { return callback({ cancel: true }); } @@ -441,7 +441,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; this._win.webContents.session.webRequest.onBeforeSendHeaders({ urls }, (details, cb) => - this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record }))); + this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as { [key: string]: string | undefined } }))); } private onWindowError(error: WindowError): void { @@ -648,7 +648,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { if (windowConfig?.autoDetectHighContrast === false) { autoDetectHighContrast = false; } - windowConfiguration.highContrast = isWindows && autoDetectHighContrast && nativeTheme.shouldUseInvertedColorScheme; + windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme(); windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related @@ -1007,22 +1007,22 @@ export class CodeWindow extends Disposable implements ICodeWindow { switch (visibility) { case ('default'): this._win.setMenuBarVisibility(!isFullscreen); - this._win.autoHideMenuBar = isFullscreen; + this._win.setAutoHideMenuBar(isFullscreen); break; case ('visible'): this._win.setMenuBarVisibility(true); - this._win.autoHideMenuBar = false; + this._win.setAutoHideMenuBar(false); break; case ('toggle'): this._win.setMenuBarVisibility(false); - this._win.autoHideMenuBar = true; + this._win.setAutoHideMenuBar(true); break; case ('hidden'): this._win.setMenuBarVisibility(false); - this._win.autoHideMenuBar = false; + this._win.setAutoHideMenuBar(false); break; } } diff --git a/src/vs/editor/browser/config/configuration.ts b/src/vs/editor/browser/config/configuration.ts index 8aa15fa6b9..2447f36936 100644 --- a/src/vs/editor/browser/config/configuration.ts +++ b/src/vs/editor/browser/config/configuration.ts @@ -14,7 +14,7 @@ import { CommonEditorConfiguration, IEnvConfiguration } from 'vs/editor/common/c import { EditorOption, IEditorConstructionOptions, EditorFontLigatures } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo, FontInfo } from 'vs/editor/common/config/fontInfo'; import { IDimension } from 'vs/editor/common/editorCommon'; -import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; class CSSBasedConfigurationCache { @@ -87,6 +87,7 @@ export interface ISerializedFontInfo { readonly typicalFullwidthCharacterWidth: number; readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; + middotWidth: number; readonly maxDigitWidth: number; } @@ -159,6 +160,7 @@ class CSSBasedConfiguration extends Disposable { const savedFontInfo = savedFontInfos[i]; // compatibility with older versions of VS Code which did not store this... savedFontInfo.fontFeatureSettings = savedFontInfo.fontFeatureSettings || EditorFontLigatures.OFF; + savedFontInfo.middotWidth = savedFontInfo.middotWidth || savedFontInfo.spaceWidth; const fontInfo = new FontInfo(savedFontInfo, false); this._writeToCache(fontInfo, fontInfo); } @@ -183,6 +185,7 @@ class CSSBasedConfiguration extends Disposable { typicalFullwidthCharacterWidth: Math.max(readConfig.typicalFullwidthCharacterWidth, 5), canUseHalfwidthRightwardsArrow: readConfig.canUseHalfwidthRightwardsArrow, spaceWidth: Math.max(readConfig.spaceWidth, 5), + middotWidth: Math.max(readConfig.middotWidth, 5), maxDigitWidth: Math.max(readConfig.maxDigitWidth, 5), }, false); } @@ -223,7 +226,8 @@ class CSSBasedConfiguration extends Disposable { const rightwardsArrow = this.createRequest('→', CharWidthRequestType.Regular, all, monospace); const halfwidthRightwardsArrow = this.createRequest('ï¿«', CharWidthRequestType.Regular, all, null); - this.createRequest('·', CharWidthRequestType.Regular, all, monospace); + // middle dot character + const middot = this.createRequest('·', CharWidthRequestType.Regular, all, monospace); // monospace test: some characters this.createRequest('|', CharWidthRequestType.Regular, all, monospace); @@ -289,6 +293,7 @@ class CSSBasedConfiguration extends Disposable { typicalFullwidthCharacterWidth: typicalFullwidthCharacter.width, canUseHalfwidthRightwardsArrow: canUseHalfwidthRightwardsArrow, spaceWidth: space.width, + middotWidth: middot.width, maxDigitWidth: maxDigitWidth }, canTrustBrowserZoomLevel); } @@ -333,7 +338,7 @@ export class Configuration extends CommonEditorConfiguration { } this._register(browser.onDidChangeZoomLevel(_ => this._recomputeOptions())); - this._register(this.accessibilityService.onDidChangeAccessibilitySupport(() => this._recomputeOptions())); + this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._recomputeOptions())); this._recomputeOptions(); } @@ -374,7 +379,7 @@ export class Configuration extends CommonEditorConfiguration { emptySelectionClipboard: browser.isWebKit || browser.isFirefox, pixelRatio: browser.getPixelRatio(), zoomLevel: browser.getZoomLevel(), - accessibilitySupport: this.accessibilityService.getAccessibilitySupport() + accessibilitySupport: this.accessibilityService.isScreenReaderOptimized() ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled }; } diff --git a/src/vs/editor/browser/controller/mouseHandler.ts b/src/vs/editor/browser/controller/mouseHandler.ts index 4beed992e3..b501b22230 100644 --- a/src/vs/editor/browser/controller/mouseHandler.ts +++ b/src/vs/editor/browser/controller/mouseHandler.ts @@ -363,6 +363,7 @@ class MouseDownOperation extends Disposable { this._isActive = true; this._mouseMoveMonitor.startMonitoring( + e.target, e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), @@ -387,6 +388,7 @@ class MouseDownOperation extends Disposable { if (!this._isActive) { this._isActive = true; this._mouseMoveMonitor.startMonitoring( + e.target, e.buttons, createMouseMoveEventMerger(null), (e) => this._onMouseDownThenMove(e), diff --git a/src/vs/editor/browser/controller/mouseTarget.ts b/src/vs/editor/browser/controller/mouseTarget.ts index 37eed5b0e0..73e67f1852 100644 --- a/src/vs/editor/browser/controller/mouseTarget.ts +++ b/src/vs/editor/browser/controller/mouseTarget.ts @@ -17,6 +17,7 @@ import { HorizontalPosition } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; import { IViewModel } from 'vs/editor/common/viewModel/viewModel'; import { CursorColumns } from 'vs/editor/common/controller/cursorCommon'; +import * as dom from 'vs/base/browser/dom'; export interface IViewZoneData { viewZoneId: string; @@ -835,8 +836,17 @@ export class MouseTargetFactory { } private static _actualDoHitTestWithCaretRangeFromPoint(ctx: HitTestContext, coords: ClientCoordinates): IHitTestResult { - - const range: Range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + const shadowRoot = dom.getShadowRoot(ctx.viewDomNode); + let range: Range; + if (shadowRoot) { + if (typeof shadowRoot.caretRangeFromPoint === 'undefined') { + range = shadowCaretRangeFromPoint(shadowRoot, coords.clientX, coords.clientY); + } else { + range = shadowRoot.caretRangeFromPoint(coords.clientX, coords.clientY); + } + } else { + range = document.caretRangeFromPoint(coords.clientX, coords.clientY); + } if (!range || !range.startContainer) { return { @@ -1009,3 +1019,94 @@ export class MouseTargetFactory { }; } } + +export function shadowCaretRangeFromPoint(shadowRoot: ShadowRoot, x: number, y: number): Range { + const range = document.createRange(); + + // Get the element under the point + let el: Element | null = shadowRoot.elementFromPoint(x, y); + + if (el !== null) { + // Get the last child of the element until its firstChild is a text node + // This assumes that the pointer is on the right of the line, out of the tokens + // and that we want to get the offset of the last token of the line + while (el && el.firstChild && el.firstChild.nodeType !== el.firstChild.TEXT_NODE) { + el = el.lastChild; + } + + // Grab its rect + const rect = el.getBoundingClientRect(); + + // And its font + const font = window.getComputedStyle(el, null).getPropertyValue('font'); + + // And also its txt content + const text = (el as any).innerText; + + // Position the pixel cursor at the left of the element + let pixelCursor = rect.left; + let offset = 0; + let step: number; + + // If the point is on the right of the box put the cursor after the last character + if (x > rect.left + rect.width) { + offset = text.length; + } else { + const charWidthReader = CharWidthReader.getInstance(); + // Goes through all the characters of the innerText, and checks if the x of the point + // belongs to the character. + for (let i = 0; i < text.length + 1; i++) { + // The step is half the width of the character + step = charWidthReader.getCharWidth(text.charAt(i), font) / 2; + // Move to the center of the character + pixelCursor += step; + // If the x of the point is smaller that the position of the cursor, the point is over that character + if (x < pixelCursor) { + offset = i; + break; + } + // Move between the current character and the next + pixelCursor += step; + } + } + + // Creates a range with the text node of the element and set the offset found + range.setStart(el.firstChild!, offset); + range.setEnd(el.firstChild!, offset); + } + + return range; +} + +class CharWidthReader { + private static _INSTANCE: CharWidthReader | null = null; + + public static getInstance(): CharWidthReader { + if (!CharWidthReader._INSTANCE) { + CharWidthReader._INSTANCE = new CharWidthReader(); + } + return CharWidthReader._INSTANCE; + } + + private readonly _cache: { [cacheKey: string]: number; }; + private readonly _canvas: HTMLCanvasElement; + + private constructor() { + this._cache = {}; + this._canvas = document.createElement('canvas'); + } + + public getCharWidth(char: string, font: string): number { + const cacheKey = char + font; + if (this._cache[cacheKey]) { + return this._cache[cacheKey]; + } + + const context = this._canvas.getContext('2d')!; + context.font = font; + const metrics = context.measureText(char); + const width = metrics.width; + this._cache[cacheKey] = width; + return width; + } +} diff --git a/src/vs/editor/browser/controller/textAreaHandler.ts b/src/vs/editor/browser/controller/textAreaHandler.ts index 7bcfd73eb5..94cacfb550 100644 --- a/src/vs/editor/browser/controller/textAreaHandler.ts +++ b/src/vs/editor/browser/controller/textAreaHandler.ts @@ -17,7 +17,7 @@ import { ViewController } from 'vs/editor/browser/view/viewController'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; import { LineNumbersOverlay } from 'vs/editor/browser/viewParts/lineNumbers/lineNumbers'; import { Margin } from 'vs/editor/browser/viewParts/margin/margin'; -import { RenderLineNumbersType, EditorOption, IComputedEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { RenderLineNumbersType, EditorOption, IComputedEditorOptions, EditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; import { WordCharacterClass, getMapForWordSeparators } from 'vs/editor/common/controller/wordCharacterClassifier'; import { Position } from 'vs/editor/common/core/position'; @@ -62,8 +62,8 @@ export class TextAreaHandler extends ViewPart { private _scrollLeft: number; private _scrollTop: number; - private _accessibilitySupport: AccessibilitySupport; - private _accessibilityPageSize: number; + private _accessibilitySupport!: AccessibilitySupport; + private _accessibilityPageSize!: number; private _contentLeft: number; private _contentWidth: number; private _contentHeight: number; @@ -77,6 +77,7 @@ export class TextAreaHandler extends ViewPart { */ private _visibleTextArea: VisibleTextAreaData | null; private _selections: Selection[]; + private _modelSelections: Selection[]; /** * The position at which the textarea was rendered. @@ -99,8 +100,7 @@ export class TextAreaHandler extends ViewPart { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); - this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); + this._setAccessibilityOptions(options); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._contentHeight = layoutInfo.height; @@ -111,6 +111,7 @@ export class TextAreaHandler extends ViewPart { this._visibleTextArea = null; this._selections = [new Selection(1, 1, 1, 1)]; + this._modelSelections = [new Selection(1, 1, 1, 1)]; this._lastRenderPosition = null; // Text Area (The focus will always be in the textarea when the cursor is blinking) @@ -149,24 +150,30 @@ export class TextAreaHandler extends ViewPart { const textAreaInputHost: ITextAreaInputHost = { getDataToCopy: (generateHTML: boolean): ClipboardDataToCopy => { - const rawTextToCopy = this._context.model.getPlainTextToCopy(this._selections, this._emptySelectionClipboard, platform.isWindows); + const rawTextToCopy = this._context.model.getPlainTextToCopy(this._modelSelections, this._emptySelectionClipboard, platform.isWindows); const newLineCharacter = this._context.model.getEOL(); - const isFromEmptySelection = (this._emptySelectionClipboard && this._selections.length === 1 && this._selections[0].isEmpty()); + const isFromEmptySelection = (this._emptySelectionClipboard && this._modelSelections.length === 1 && this._modelSelections[0].isEmpty()); const multicursorText = (Array.isArray(rawTextToCopy) ? rawTextToCopy : null); const text = (Array.isArray(rawTextToCopy) ? rawTextToCopy.join(newLineCharacter) : rawTextToCopy); let html: string | null | undefined = undefined; + let mode: string | null = null; if (generateHTML) { if (CopyOptions.forceCopyWithSyntaxHighlighting || (this._copyWithSyntaxHighlighting && text.length < 65536)) { - html = this._context.model.getHTMLToCopy(this._selections, this._emptySelectionClipboard); + const richText = this._context.model.getRichTextToCopy(this._modelSelections, this._emptySelectionClipboard); + if (richText) { + html = richText.html; + mode = richText.mode; + } } } return { isFromEmptySelection, multicursorText, text, - html + html, + mode }; }, @@ -220,11 +227,13 @@ export class TextAreaHandler extends ViewPart { this._register(this._textAreaInput.onPaste((e: IPasteData) => { let pasteOnNewLine = false; let multicursorText: string[] | null = null; + let mode: string | null = null; if (e.metadata) { pasteOnNewLine = (this._emptySelectionClipboard && !!e.metadata.isFromEmptySelection); multicursorText = (typeof e.metadata.multicursorText !== 'undefined' ? e.metadata.multicursorText : null); + mode = e.metadata.mode; } - this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText); + this._viewController.paste('keyboard', e.text, pasteOnNewLine, multicursorText, mode); })); this._register(this._textAreaInput.onCut(() => { @@ -344,14 +353,24 @@ export class TextAreaHandler extends ViewPart { return options.get(EditorOption.ariaLabel); } + private _setAccessibilityOptions(options: IComputedEditorOptions): void { + this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); + const accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); + if (this._accessibilitySupport === AccessibilitySupport.Enabled && accessibilityPageSize === EditorOptions.accessibilityPageSize.defaultValue) { + // If a screen reader is attached and the default value is not set we shuold automatically increase the page size to 1000 for a better experience + this._accessibilityPageSize = 1000; + } else { + this._accessibilityPageSize = accessibilityPageSize; + } + } + // --- begin event handlers public onConfigurationChanged(e: viewEvents.ViewConfigurationChangedEvent): boolean { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); - this._accessibilitySupport = options.get(EditorOption.accessibilitySupport); - this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); + this._setAccessibilityOptions(options); this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._contentHeight = layoutInfo.height; @@ -377,6 +396,7 @@ export class TextAreaHandler extends ViewPart { } public onCursorStateChanged(e: viewEvents.ViewCursorStateChangedEvent): boolean { this._selections = e.selections.slice(0); + this._modelSelections = e.modelSelections.slice(0); this._textAreaInput.writeScreenReaderContent('selection changed'); return true; } diff --git a/src/vs/editor/browser/controller/textAreaInput.ts b/src/vs/editor/browser/controller/textAreaInput.ts index 818d2b2add..e3f98ab2e0 100644 --- a/src/vs/editor/browser/controller/textAreaInput.ts +++ b/src/vs/editor/browser/controller/textAreaInput.ts @@ -41,12 +41,14 @@ export interface ClipboardDataToCopy { multicursorText: string[] | null | undefined; text: string; html: string | null | undefined; + mode: string | null; } export interface ClipboardStoredMetadata { version: 1; isFromEmptySelection: boolean | undefined; multicursorText: string[] | null | undefined; + mode: string | null; } export interface ITextAreaInputHost { @@ -550,7 +552,8 @@ export class TextAreaInput extends Disposable { const storedMetadata: ClipboardStoredMetadata = { version: 1, isFromEmptySelection: dataToCopy.isFromEmptySelection, - multicursorText: dataToCopy.multicursorText + multicursorText: dataToCopy.multicursorText, + mode: dataToCopy.mode }; InMemoryClipboardMetadataManager.INSTANCE.set( // When writing "LINE\r\n" to the clipboard and then pasting, diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index f08ee077cc..d95fcd3644 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -308,6 +308,14 @@ export interface IPartialEditorMouseEvent { readonly target: IMouseTarget | null; } +/** + * A paste event originating from the editor. + */ +export interface IPasteEvent { + readonly range: Range; + readonly mode: string | null; +} + /** * An overview ruler * @internal @@ -416,11 +424,11 @@ export interface ICodeEditor extends editorCommon.IEditor { /** * An event emitted after composition has started. */ - onCompositionStart(listener: () => void): IDisposable; + onDidCompositionStart(listener: () => void): IDisposable; /** * An event emitted after composition has ended. */ - onCompositionEnd(listener: () => void): IDisposable; + onDidCompositionEnd(listener: () => void): IDisposable; /** * An event emitted when editing failed because the editor is read-only. * @event @@ -431,7 +439,7 @@ export interface ICodeEditor extends editorCommon.IEditor { * An event emitted when users paste text in the editor. * @event */ - onDidPaste(listener: (range: Range) => void): IDisposable; + onDidPaste(listener: (e: IPasteEvent) => void): IDisposable; /** * An event emitted on a "mouseup". * @event @@ -723,6 +731,11 @@ export interface ICodeEditor extends editorCommon.IEditor { */ getTelemetryData(): { [key: string]: any } | undefined; + /** + * Returns the editor's container dom node + */ + getContainerDomNode(): HTMLElement; + /** * Returns the editor's dom node */ diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 5baaf1fedc..fbf1cd5ce5 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -182,7 +182,13 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { this._keydownListener = null; } - public startMonitoring(initialButtons: number, merger: EditorMouseEventMerger, mouseMoveCallback: (e: EditorMouseEvent) => void, onStopCallback: () => void): void { + public startMonitoring( + initialElement: HTMLElement, + initialButtons: number, + merger: EditorMouseEventMerger, + mouseMoveCallback: (e: EditorMouseEvent) => void, + onStopCallback: () => void + ): void { // Add a <> keydown event listener that will cancel the monitoring // if something other than a modifier key is pressed @@ -199,7 +205,7 @@ export class GlobalEditorMouseMoveMonitor extends Disposable { return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode)); }; - this._globalMouseMoveMonitor.startMonitoring(initialButtons, myMerger, mouseMoveCallback, () => { + this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, () => { this._keydownListener!.dispose(); onStopCallback(); }); diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 945cd8b2d7..5d10b96410 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -89,7 +89,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return editorWithWidgetFocus; } - abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + abstract registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; abstract removeDecorationType(key: string): void; abstract resolveDecorationOptions(decorationTypeKey: string | undefined, writable: boolean): IModelDecorationOptions; @@ -120,6 +120,16 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return this._transientWatchers[uri].get(key); } + public getTransientModelProperties(model: ITextModel): [string, any][] | undefined { + const uri = model.uri.toString(); + + if (!this._transientWatchers.hasOwnProperty(uri)) { + return undefined; + } + + return this._transientWatchers[uri].keys().map(key => [key, this._transientWatchers[uri].get(key)]); + } + _removeWatcher(w: ModelTransientSettingWatcher): void { delete this._transientWatchers[w.uri]; } @@ -145,4 +155,8 @@ export class ModelTransientSettingWatcher { public get(key: string): any { return this._values[key]; } + + public keys(): string[] { + return Object.keys(this._values); + } } diff --git a/src/vs/editor/browser/services/codeEditorService.ts b/src/vs/editor/browser/services/codeEditorService.ts index edf9a1081a..611a6a3480 100644 --- a/src/vs/editor/browser/services/codeEditorService.ts +++ b/src/vs/editor/browser/services/codeEditorService.ts @@ -37,12 +37,13 @@ export interface ICodeEditorService { */ getFocusedCodeEditor(): ICodeEditor | null; - registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void; + registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void; removeDecorationType(key: string): void; resolveDecorationOptions(typeKey: string, writable: boolean): IModelDecorationOptions; setTransientModelProperty(model: ITextModel, key: string, value: any): void; getTransientModelProperty(model: ITextModel, key: string): any; + getTransientModelProperties(model: ITextModel): [string, any][] | undefined; getActiveCodeEditor(): ICodeEditor | null; openCodeEditor(input: IResourceInput, source: ICodeEditor | null, sideBySide?: boolean): Promise; diff --git a/src/vs/editor/browser/services/codeEditorServiceImpl.ts b/src/vs/editor/browser/services/codeEditorServiceImpl.ts index 1840d47596..38029fdbba 100644 --- a/src/vs/editor/browser/services/codeEditorServiceImpl.ts +++ b/src/vs/editor/browser/services/codeEditorServiceImpl.ts @@ -14,31 +14,101 @@ import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, Overview import { IResourceInput } from 'vs/platform/editor/common/editor'; import { ITheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService'; +class RefCountedStyleSheet { + + private readonly _parent: CodeEditorServiceImpl; + private readonly _editorId: string; + public readonly styleSheet: HTMLStyleElement; + private _refCount: number; + + constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) { + this._parent = parent; + this._editorId = editorId; + this.styleSheet = styleSheet; + this._refCount = 0; + } + + public ref(): void { + this._refCount++; + } + + public unref(): void { + this._refCount--; + if (this._refCount === 0) { + this.styleSheet.parentNode?.removeChild(this.styleSheet); + this._parent._removeEditorStyleSheets(this._editorId); + } + } +} + +class GlobalStyleSheet { + public readonly styleSheet: HTMLStyleElement; + + constructor(styleSheet: HTMLStyleElement) { + this.styleSheet = styleSheet; + } + + public ref(): void { + } + + public unref(): void { + } +} + export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService { - private readonly _styleSheet: HTMLStyleElement; + private _globalStyleSheet: GlobalStyleSheet | null; private readonly _decorationOptionProviders = new Map(); + private readonly _editorStyleSheets = new Map(); private readonly _themeService: IThemeService; - constructor(@IThemeService themeService: IThemeService, styleSheet = dom.createStyleSheet()) { + constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) { super(); - this._styleSheet = styleSheet; + this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null; this._themeService = themeService; } - public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string): void { + private _getOrCreateGlobalStyleSheet(): GlobalStyleSheet { + if (!this._globalStyleSheet) { + this._globalStyleSheet = new GlobalStyleSheet(dom.createStyleSheet()); + } + return this._globalStyleSheet; + } + + private _getOrCreateStyleSheet(editor: ICodeEditor | undefined): GlobalStyleSheet | RefCountedStyleSheet { + if (!editor) { + return this._getOrCreateGlobalStyleSheet(); + } + const domNode = editor.getContainerDomNode(); + if (!dom.isInShadowDOM(domNode)) { + return this._getOrCreateGlobalStyleSheet(); + } + const editorId = editor.getId(); + if (!this._editorStyleSheets.has(editorId)) { + const refCountedStyleSheet = new RefCountedStyleSheet(this, editorId, dom.createStyleSheet(domNode)); + this._editorStyleSheets.set(editorId, refCountedStyleSheet); + } + return this._editorStyleSheets.get(editorId)!; + } + + _removeEditorStyleSheets(editorId: string): void { + this._editorStyleSheets.delete(editorId); + } + + public registerDecorationType(key: string, options: IDecorationRenderOptions, parentTypeKey?: string, editor?: ICodeEditor): void { let provider = this._decorationOptionProviders.get(key); if (!provider) { + const styleSheet = this._getOrCreateStyleSheet(editor); const providerArgs: ProviderArguments = { - styleSheet: this._styleSheet, + styleSheet: styleSheet.styleSheet, key: key, parentTypeKey: parentTypeKey, options: options || Object.create(null) }; if (!parentTypeKey) { - provider = new DecorationTypeOptionsProvider(this._themeService, providerArgs); + provider = new DecorationTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } else { - provider = new DecorationSubTypeOptionsProvider(this._themeService, providerArgs); + provider = new DecorationSubTypeOptionsProvider(this._themeService, styleSheet, providerArgs); } this._decorationOptionProviders.set(key, provider); } @@ -76,13 +146,16 @@ interface IModelDecorationOptionsProvider extends IDisposable { class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvider { + private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; private readonly _parentTypeKey: string | undefined; private _beforeContentRules: DecorationCSSRules | null; private _afterContentRules: DecorationCSSRules | null; - constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) { + this._styleSheet = styleSheet; + this._styleSheet.ref(); this._parentTypeKey = providerArgs.parentTypeKey; this.refCount = 0; @@ -110,6 +183,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide this._afterContentRules.dispose(); this._afterContentRules = null; } + this._styleSheet.unref(); } } @@ -124,6 +198,7 @@ interface ProviderArguments { class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { private readonly _disposables = new DisposableStore(); + private readonly _styleSheet: GlobalStyleSheet | RefCountedStyleSheet; public refCount: number; public className: string | undefined; @@ -136,7 +211,9 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { public overviewRuler: IModelDecorationOverviewRulerOptions | undefined; public stickiness: TrackedRangeStickiness | undefined; - constructor(themeService: IThemeService, providerArgs: ProviderArguments) { + constructor(themeService: IThemeService, styleSheet: GlobalStyleSheet | RefCountedStyleSheet, providerArgs: ProviderArguments) { + this._styleSheet = styleSheet; + this._styleSheet.ref(); this.refCount = 0; const createCSSRules = (type: ModelDecorationCSSRuleType) => { @@ -202,6 +279,7 @@ class DecorationTypeOptionsProvider implements IModelDecorationOptionsProvider { public dispose(): void { this._disposables.dispose(); + this._styleSheet.unref(); } } diff --git a/src/vs/editor/browser/view/viewController.ts b/src/vs/editor/browser/view/viewController.ts index c25248f8e9..e6f7f4ca0e 100644 --- a/src/vs/editor/browser/view/viewController.ts +++ b/src/vs/editor/browser/view/viewController.ts @@ -37,7 +37,7 @@ export interface IMouseDispatchData { export interface ICommandDelegate { executeEditorCommand(editorCommand: CoreEditorCommand, args: any): void; - paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void; + paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void; type(source: string, text: string): void; replacePreviousChar(source: string, text: string, replaceCharCnt: number): void; compositionStart(source: string): void; @@ -69,8 +69,8 @@ export class ViewController { this.commandDelegate.executeEditorCommand(editorCommand, args); } - public paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null): void { - this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText); + public paste(source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null): void { + this.commandDelegate.paste(source, text, pasteOnNewLine, multicursorText, mode); } public type(source: string, text: string): void { diff --git a/src/vs/editor/browser/view/viewImpl.ts b/src/vs/editor/browser/view/viewImpl.ts index 7f6f30e4a4..77c6b2d307 100644 --- a/src/vs/editor/browser/view/viewImpl.ts +++ b/src/vs/editor/browser/view/viewImpl.ts @@ -38,6 +38,7 @@ import { ViewCursors } from 'vs/editor/browser/viewParts/viewCursors/viewCursors import { ViewZones } from 'vs/editor/browser/viewParts/viewZones/viewZones'; import { Cursor } from 'vs/editor/common/controller/cursor'; import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; import { IConfiguration } from 'vs/editor/common/editorCommon'; import { RenderingContext } from 'vs/editor/common/view/renderingContext'; import { ViewContext } from 'vs/editor/common/view/viewContext'; @@ -525,10 +526,15 @@ export class View extends ViewEventHandler { } public layoutContentWidget(widgetData: IContentWidgetData): void { - const newPosition = widgetData.position ? widgetData.position.position : null; - const newRange = widgetData.position ? widgetData.position.range || null : null; + let newRange = widgetData.position ? widgetData.position.range || null : null; + if (newRange === null) { + const newPosition = widgetData.position ? widgetData.position.position : null; + if (newPosition !== null) { + newRange = new Range(newPosition.lineNumber, newPosition.column, newPosition.lineNumber, newPosition.column); + } + } const newPreference = widgetData.position ? widgetData.position.preference : null; - this.contentWidgets.setWidgetPosition(widgetData.widget, newPosition, newRange, newPreference); + this.contentWidgets.setWidgetPosition(widgetData.widget, newRange, newPreference); this._scheduleRender(); } diff --git a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts index 4e0993c54f..ae516cf568 100644 --- a/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts +++ b/src/vs/editor/browser/viewParts/contentWidgets/contentWidgets.ts @@ -7,7 +7,6 @@ import * as dom from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { ContentWidgetPositionPreference, IContentWidget } from 'vs/editor/browser/editorBrowser'; import { PartFingerprint, PartFingerprints, ViewPart } from 'vs/editor/browser/view/viewPart'; -import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Constants } from 'vs/base/common/uint'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/common/view/renderingContext'; @@ -112,9 +111,9 @@ export class ViewContentWidgets extends ViewPart { this.setShouldRender(); } - public setWidgetPosition(widget: IContentWidget, position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { + public setWidgetPosition(widget: IContentWidget, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { const myWidget = this._widgets[widget.getId()]; - myWidget.setPosition(position, range, preference); + myWidget.setPosition(range, preference); this.setShouldRender(); } @@ -187,8 +186,6 @@ class Widget { private _contentLeft: number; private _lineHeight: number; - private _position: IPosition | null; - private _viewPosition: Position | null; private _range: IRange | null; private _viewRange: Range | null; private _preference: ContentWidgetPositionPreference[] | null; @@ -217,9 +214,7 @@ class Widget { this._contentLeft = layoutInfo.contentLeft; this._lineHeight = options.get(EditorOption.lineHeight); - this._position = null; this._range = null; - this._viewPosition = null; this._viewRange = null; this._preference = []; this._cachedDomNodeClientWidth = -1; @@ -246,26 +241,19 @@ class Widget { } public onLineMappingChanged(e: viewEvents.ViewLineMappingChangedEvent): void { - this._setPosition(this._position, this._range); + this._setPosition(this._range); } - private _setPosition(position: IPosition | null, range: IRange | null): void { - this._position = position; + private _setPosition(range: IRange | null): void { this._range = range; - this._viewPosition = null; this._viewRange = null; - if (this._position) { - // Do not trust that widgets give a valid position - const validModelPosition = this._context.model.validateModelPosition(this._position); - if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelPosition)) { - this._viewPosition = this._context.model.coordinatesConverter.convertModelPositionToViewPosition(validModelPosition); - } - } if (this._range) { // Do not trust that widgets give a valid position const validModelRange = this._context.model.validateModelRange(this._range); - this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange); + if (this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getStartPosition()) || this._context.model.coordinatesConverter.modelPositionIsVisible(validModelRange.getEndPosition())) { + this._viewRange = this._context.model.coordinatesConverter.convertModelRangeToViewRange(validModelRange); + } } } @@ -277,8 +265,8 @@ class Widget { ); } - public setPosition(position: IPosition | null, range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { - this._setPosition(position, range); + public setPosition(range: IRange | null, preference: ContentWidgetPositionPreference[] | null): void { + this._setPosition(range); this._preference = preference; this._cachedDomNodeClientWidth = -1; this._cachedDomNodeClientHeight = -1; @@ -327,60 +315,65 @@ class Widget { }; } - private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null { - const aboveLeft0 = topLeft.left - ctx.scrollLeft; - const belowLeft0 = bottomLeft.left - ctx.scrollLeft; + private _layoutHorizontalSegmentInPage(windowSize: dom.Dimension, domNodePosition: dom.IDomNodePagePosition, left: number, width: number): [number, number] { + const MIN_LIMIT = (width <= domNodePosition.width - 20 ? domNodePosition.left : 0); + const MAX_LIMIT = (width <= domNodePosition.width - 20 ? domNodePosition.left + domNodePosition.width - 20 : windowSize.width - 20); - let aboveTop = topLeft.top - height; - let belowTop = bottomLeft.top + this._lineHeight; - let aboveLeft = aboveLeft0 + this._contentLeft; - let belowLeft = belowLeft0 + this._contentLeft; + let absoluteLeft = domNodePosition.left + left - dom.StandardWindow.scrollX; + + if (absoluteLeft + width > MAX_LIMIT) { + const delta = absoluteLeft - (MAX_LIMIT - width); + absoluteLeft -= delta; + left -= delta; + } + + if (absoluteLeft < MIN_LIMIT) { + const delta = absoluteLeft - MIN_LIMIT; + absoluteLeft -= delta; + left -= delta; + } + + return [left, absoluteLeft]; + } + + private _layoutBoxInPage(topLeft: Coordinate, bottomLeft: Coordinate, width: number, height: number, ctx: RenderingContext): IBoxLayoutResult | null { + const aboveTop = topLeft.top - height; + const belowTop = bottomLeft.top + this._lineHeight; const domNodePosition = dom.getDomNodePagePosition(this._viewDomNode.domNode); const absoluteAboveTop = domNodePosition.top + aboveTop - dom.StandardWindow.scrollY; const absoluteBelowTop = domNodePosition.top + belowTop - dom.StandardWindow.scrollY; - let absoluteAboveLeft = domNodePosition.left + aboveLeft - dom.StandardWindow.scrollX; - let absoluteBelowLeft = domNodePosition.left + belowLeft - dom.StandardWindow.scrollX; - const INNER_WIDTH = window.innerWidth || document.documentElement!.clientWidth || document.body.clientWidth; - const INNER_HEIGHT = window.innerHeight || document.documentElement!.clientHeight || document.body.clientHeight; + const windowSize = dom.getClientArea(document.body); + const [aboveLeft, absoluteAboveLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, topLeft.left - ctx.scrollLeft + this._contentLeft, width); + const [belowLeft, absoluteBelowLeft] = this._layoutHorizontalSegmentInPage(windowSize, domNodePosition, bottomLeft.left - ctx.scrollLeft + this._contentLeft, width); - // Leave some clearance to the bottom + // Leave some clearance to the top/bottom const TOP_PADDING = 22; const BOTTOM_PADDING = 22; - const fitsAbove = (absoluteAboveTop >= TOP_PADDING), - fitsBelow = (absoluteBelowTop + height <= INNER_HEIGHT - BOTTOM_PADDING); - - if (absoluteAboveLeft + width + 20 > INNER_WIDTH) { - const delta = absoluteAboveLeft - (INNER_WIDTH - width - 20); - absoluteAboveLeft -= delta; - aboveLeft -= delta; - } - if (absoluteBelowLeft + width + 20 > INNER_WIDTH) { - const delta = absoluteBelowLeft - (INNER_WIDTH - width - 20); - absoluteBelowLeft -= delta; - belowLeft -= delta; - } - if (absoluteAboveLeft < 0) { - const delta = absoluteAboveLeft; - absoluteAboveLeft -= delta; - aboveLeft -= delta; - } - if (absoluteBelowLeft < 0) { - const delta = absoluteBelowLeft; - absoluteBelowLeft -= delta; - belowLeft -= delta; - } + const fitsAbove = (absoluteAboveTop >= TOP_PADDING); + const fitsBelow = (absoluteBelowTop + height <= windowSize.height - BOTTOM_PADDING); if (this._fixedOverflowWidgets) { - aboveTop = absoluteAboveTop; - belowTop = absoluteBelowTop; - aboveLeft = absoluteAboveLeft; - belowLeft = absoluteBelowLeft; + return { + fitsAbove, + aboveTop: Math.max(absoluteAboveTop, TOP_PADDING), + aboveLeft: absoluteAboveLeft, + fitsBelow, + belowTop: absoluteBelowTop, + belowLeft: absoluteBelowLeft + }; } - return { fitsAbove, aboveTop: Math.max(aboveTop, TOP_PADDING), aboveLeft, fitsBelow, belowTop, belowLeft }; + return { + fitsAbove, + aboveTop: Math.max(aboveTop, TOP_PADDING), + aboveLeft, + fitsBelow, + belowTop, + belowLeft + }; } private _prepareRenderWidgetAtExactPositionOverflowing(topLeft: Coordinate): Coordinate { @@ -391,45 +384,45 @@ class Widget { * Compute `this._topLeft` */ private _getTopAndBottomLeft(ctx: RenderingContext): [Coordinate, Coordinate] | [null, null] { - if (!this._viewPosition) { + if (!this._viewRange) { return [null, null]; } - const visibleRangeForPosition = ctx.visibleRangeForPosition(this._viewPosition); - if (!visibleRangeForPosition) { + const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false); + if (!visibleRangesForRange || visibleRangesForRange.length === 0) { return [null, null]; } - const topForPosition = ctx.getVerticalOffsetForLineNumber(this._viewPosition.lineNumber) - ctx.scrollTop; - const topLeft = new Coordinate(topForPosition, visibleRangeForPosition.left); - - let largestLineNumber = this._viewPosition.lineNumber; - let smallestLeft = visibleRangeForPosition.left; - - if (this._viewRange) { - const visibleRangesForRange = ctx.linesVisibleRangesForRange(this._viewRange, false); - if (visibleRangesForRange && visibleRangesForRange.length > 0) { - for (let i = visibleRangesForRange.length - 1; i >= 0; i--) { - const visibleRangesForLine = visibleRangesForRange[i]; - if (visibleRangesForLine.lineNumber >= largestLineNumber) { - if (visibleRangesForLine.lineNumber > largestLineNumber) { - largestLineNumber = visibleRangesForLine.lineNumber; - smallestLeft = Constants.MAX_SAFE_SMALL_INTEGER; - } - for (let j = 0, lenJ = visibleRangesForLine.ranges.length; j < lenJ; j++) { - const visibleRange = visibleRangesForLine.ranges[j]; - - if (visibleRange.left < smallestLeft) { - smallestLeft = visibleRange.left; - } - } - } - } + let firstLine = visibleRangesForRange[0]; + let lastLine = visibleRangesForRange[0]; + for (const visibleRangesForLine of visibleRangesForRange) { + if (visibleRangesForLine.lineNumber < firstLine.lineNumber) { + firstLine = visibleRangesForLine; + } + if (visibleRangesForLine.lineNumber > lastLine.lineNumber) { + lastLine = visibleRangesForLine; } } - const topForBottomLine = ctx.getVerticalOffsetForLineNumber(largestLineNumber) - ctx.scrollTop; - const bottomLeft = new Coordinate(topForBottomLine, smallestLeft); + let firstLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//firstLine.Constants.MAX_SAFE_SMALL_INTEGER; + for (const visibleRange of firstLine.ranges) { + if (visibleRange.left < firstLineMinLeft) { + firstLineMinLeft = visibleRange.left; + } + } + + let lastLineMinLeft = Constants.MAX_SAFE_SMALL_INTEGER;//lastLine.Constants.MAX_SAFE_SMALL_INTEGER; + for (const visibleRange of lastLine.ranges) { + if (visibleRange.left < lastLineMinLeft) { + lastLineMinLeft = visibleRange.left; + } + } + + const topForPosition = ctx.getVerticalOffsetForLineNumber(firstLine.lineNumber) - ctx.scrollTop; + const topLeft = new Coordinate(topForPosition, firstLineMinLeft); + + const topForBottomLine = ctx.getVerticalOffsetForLineNumber(lastLine.lineNumber) - ctx.scrollTop; + const bottomLeft = new Coordinate(topForBottomLine, lastLineMinLeft); return [topLeft, bottomLeft]; } @@ -491,11 +484,11 @@ class Widget { * On this first pass, we ensure that the content widget (if it is in the viewport) has the max width set correctly. */ public onBeforeRender(viewportData: ViewportData): void { - if (!this._viewPosition || !this._preference) { + if (!this._viewRange || !this._preference) { return; } - if (this._viewPosition.lineNumber < viewportData.startLineNumber || this._viewPosition.lineNumber > viewportData.endLineNumber) { + if (this._viewRange.endLineNumber < viewportData.startLineNumber || this._viewRange.startLineNumber > viewportData.endLineNumber) { // Outside of viewport return; } diff --git a/src/vs/editor/browser/viewParts/lines/viewLine.ts b/src/vs/editor/browser/viewParts/lines/viewLine.ts index 009918f17c..4defe1e100 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLine.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLine.ts @@ -73,6 +73,7 @@ export class ViewLineOptions { public readonly renderWhitespace: 'none' | 'boundary' | 'selection' | 'all'; public readonly renderControlCharacters: boolean; public readonly spaceWidth: number; + public readonly middotWidth: number; public readonly useMonospaceOptimizations: boolean; public readonly canUseHalfwidthRightwardsArrow: boolean; public readonly lineHeight: number; @@ -86,6 +87,7 @@ export class ViewLineOptions { this.renderWhitespace = options.get(EditorOption.renderWhitespace); this.renderControlCharacters = options.get(EditorOption.renderControlCharacters); this.spaceWidth = fontInfo.spaceWidth; + this.middotWidth = fontInfo.middotWidth; this.useMonospaceOptimizations = ( fontInfo.isMonospace && !options.get(EditorOption.disableMonospaceOptimizations) @@ -102,6 +104,7 @@ export class ViewLineOptions { && this.renderWhitespace === other.renderWhitespace && this.renderControlCharacters === other.renderControlCharacters && this.spaceWidth === other.spaceWidth + && this.middotWidth === other.middotWidth && this.useMonospaceOptimizations === other.useMonospaceOptimizations && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.lineHeight === other.lineHeight @@ -215,6 +218,7 @@ export class ViewLine implements IVisibleLine { lineData.tabSize, lineData.startVisibleColumn, options.spaceWidth, + options.middotWidth, options.stopRenderingLineAfter, options.renderWhitespace, options.renderControlCharacters, diff --git a/src/vs/editor/browser/viewParts/minimap/minimap.ts b/src/vs/editor/browser/viewParts/minimap/minimap.ts index d85f3e03ea..fbaf98ef90 100644 --- a/src/vs/editor/browser/viewParts/minimap/minimap.ts +++ b/src/vs/editor/browser/viewParts/minimap/minimap.ts @@ -126,8 +126,8 @@ class MinimapOptions { this.minimapWidth = layoutInfo.minimapWidth; this.minimapHeight = layoutInfo.height; - this.canvasInnerWidth = Math.max(1, Math.floor(pixelRatio * this.minimapWidth)); - this.canvasInnerHeight = Math.max(1, Math.floor(pixelRatio * this.minimapHeight)); + this.canvasInnerWidth = Math.floor(pixelRatio * this.minimapWidth); + this.canvasInnerHeight = Math.floor(pixelRatio * this.minimapHeight); this.canvasOuterWidth = this.canvasInnerWidth / pixelRatio; this.canvasOuterHeight = this.canvasInnerHeight / pixelRatio; @@ -555,6 +555,7 @@ export class Minimap extends ViewPart { this._slider.toggleClassName('active', true); this._sliderMouseMoveMonitor.startMonitoring( + e.target, e.buttons, standardMouseMoveMerger, (mouseMoveData: IStandardMouseMoveEventData) => { @@ -656,16 +657,18 @@ export class Minimap extends ViewPart { this._slider.setWidth(this._options.minimapWidth); } - private _getBuffer(): ImageData { + private _getBuffer(): ImageData | null { if (!this._buffers) { - this._buffers = new MinimapBuffers( - this._canvas.domNode.getContext('2d')!, - this._options.canvasInnerWidth, - this._options.canvasInnerHeight, - this._tokensColorTracker.getColor(ColorId.DefaultBackground) - ); + if (this._options.canvasInnerWidth > 0 && this._options.canvasInnerHeight > 0) { + this._buffers = new MinimapBuffers( + this._canvas.domNode.getContext('2d')!, + this._options.canvasInnerWidth, + this._options.canvasInnerHeight, + this._tokensColorTracker.getColor(ColorId.DefaultBackground) + ); + } } - return this._buffers!.getBuffer(); + return this._buffers ? this._buffers.getBuffer() : null; } private _onOptionsMaybeChanged(): boolean { @@ -905,7 +908,7 @@ export class Minimap extends ViewPart { canvasContext.fillRect(x, y, width, height); } - private renderLines(layout: MinimapLayout): RenderData { + private renderLines(layout: MinimapLayout): RenderData | null { const renderMinimap = this._options.renderMinimap; const charRenderer = this._options.charRenderer(); const startLineNumber = layout.startLineNumber; @@ -922,6 +925,10 @@ export class Minimap extends ViewPart { // Oh well!! We need to repaint some lines... const imageData = this._getBuffer(); + if (!imageData) { + // 0 width or 0 height canvas, nothing to do + return null; + } // Render untouched lines by using last rendered data. let [_dirtyY1, _dirtyY2, needed] = Minimap._renderUntouchedLines( diff --git a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts index 148cd44427..fe2a89b63c 100644 --- a/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts +++ b/src/vs/editor/browser/viewParts/overviewRuler/decorationsOverviewRuler.ts @@ -74,8 +74,14 @@ class Settings { this.right = position.right; this.domWidth = position.width; this.domHeight = position.height; - this.canvasWidth = (this.domWidth * this.pixelRatio) | 0; - this.canvasHeight = (this.domHeight * this.pixelRatio) | 0; + if (this.overviewRulerLanes === 0) { + // overview ruler is off + this.canvasWidth = 0; + this.canvasHeight = 0; + } else { + this.canvasWidth = (this.domWidth * this.pixelRatio) | 0; + this.canvasHeight = (this.domHeight * this.pixelRatio) | 0; + } const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes); this.x = x; @@ -303,6 +309,11 @@ export class DecorationsOverviewRuler extends ViewPart { } private _render(): void { + if (this._settings.overviewRulerLanes === 0) { + // overview ruler is off + this._domNode.setBackgroundColor(this._settings.backgroundColor ? this._settings.backgroundColor : ''); + return; + } const canvasWidth = this._settings.canvasWidth; const canvasHeight = this._settings.canvasHeight; const lineHeight = this._settings.lineHeight; diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index d0630d55b2..ff0586eb8e 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -22,7 +22,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { ICommandDelegate } from 'vs/editor/browser/view/viewController'; import { IContentWidgetData, IOverlayWidgetData, View } from 'vs/editor/browser/view/viewImpl'; import { ViewOutgoingEvents } from 'vs/editor/browser/view/viewOutgoingEvents'; -import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions } from 'vs/editor/common/config/editorOptions'; +import { ConfigurationChangedEvent, EditorLayoutInfo, IEditorOptions, EditorOption, IComputedEditorOptions, FindComputedEditorOptionValueById, IEditorConstructionOptions, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; import { Cursor, CursorStateChangedEvent } from 'vs/editor/common/controller/cursor'; import { CursorColumns, ICursors } from 'vs/editor/common/controller/cursorCommon'; import { ICursorPositionChangedEvent, ICursorSelectionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; @@ -156,13 +156,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _onDidType: Emitter = this._register(new Emitter()); public readonly onDidType = this._onDidType.event; - private readonly _onCompositionStart: Emitter = this._register(new Emitter()); - public readonly onCompositionStart = this._onCompositionStart.event; + private readonly _onDidCompositionStart: Emitter = this._register(new Emitter()); + public readonly onDidCompositionStart = this._onDidCompositionStart.event; - private readonly _onCompositionEnd: Emitter = this._register(new Emitter()); - public readonly onCompositionEnd = this._onCompositionEnd.event; + private readonly _onDidCompositionEnd: Emitter = this._register(new Emitter()); + public readonly onDidCompositionEnd = this._onDidCompositionEnd.event; - private readonly _onDidPaste: Emitter = this._register(new Emitter()); + private readonly _onDidPaste: Emitter = this._register(new Emitter()); public readonly onDidPaste = this._onDidPaste.event; private readonly _onMouseUp: Emitter = this._register(new Emitter()); @@ -950,19 +950,15 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE const endPosition = this._modelData.cursor.getSelection().getStartPosition(); if (source === 'keyboard') { this._onDidPaste.fire( - new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column) + { + range: new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column), + mode: payload.mode + } ); } return; } - if (handlerId === editorCommon.Handler.CompositionStart) { - this._onCompositionStart.fire(); - } - if (handlerId === editorCommon.Handler.CompositionEnd) { - this._onCompositionEnd.fire(); - } - const action = this.getAction(handlerId); if (action) { Promise.resolve(action.run()).then(undefined, onUnexpectedError); @@ -978,6 +974,13 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } this._modelData.cursor.trigger(source, handlerId, payload); + + if (handlerId === editorCommon.Handler.CompositionStart) { + this._onDidCompositionStart.fire(); + } + if (handlerId === editorCommon.Handler.CompositionEnd) { + this._onDidCompositionEnd.fire(); + } } private _triggerEditorCommand(source: string, handlerId: string, payload: any): boolean { @@ -1061,7 +1064,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE if (!this._modelData) { return null; } - return this._modelData.model.getLineDecorations(lineNumber, this._id, this._configuration.options.get(EditorOption.readOnly)); + return this._modelData.model.getLineDecorations(lineNumber, this._id, filterValidationDecorations(this._configuration.options)); } public deltaDecorations(oldDecorations: string[], newDecorations: IModelDeltaDecoration[]): string[] { @@ -1165,6 +1168,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE return this._modelData.view.createOverviewRuler(cssClassName); } + public getContainerDomNode(): HTMLElement { + return this._domElement; + } + public getDomNode(): HTMLElement | null { if (!this._modelData || !this._modelData.hasRealView) { return null; @@ -1441,8 +1448,8 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { editorCommand.runCoreEditorCommand(cursor, args); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null) => { - this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText }); + paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { + this.trigger(source, editorCommon.Handler.Paste, { text, pasteOnNewLine, multicursorText, mode }); }, type: (source: string, text: string) => { this.trigger(source, editorCommon.Handler.Type, { text }); @@ -1465,11 +1472,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE executeEditorCommand: (editorCommand: CoreEditorCommand, args: any): void => { editorCommand.runCoreEditorCommand(cursor, args); }, - paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null) => { + paste: (source: string, text: string, pasteOnNewLine: boolean, multicursorText: string[] | null, mode: string | null) => { this._commandService.executeCommand(editorCommon.Handler.Paste, { text: text, pasteOnNewLine: pasteOnNewLine, - multicursorText: multicursorText + multicursorText: multicursorText, + mode }); }, type: (source: string, text: string) => { @@ -1548,7 +1556,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } private _registerDecorationType(key: string, options: editorCommon.IDecorationRenderOptions, parentTypeKey?: string): void { - this._codeEditorService.registerDecorationType(key, options, parentTypeKey); + this._codeEditorService.registerDecorationType(key, options, parentTypeKey, this); } private _removeDecorationType(key: string): void { diff --git a/src/vs/editor/browser/widget/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditorWidget.ts index d8943af24c..42306aa635 100644 --- a/src/vs/editor/browser/widget/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditorWidget.ts @@ -2164,6 +2164,7 @@ class InlineViewZonesComputer extends ViewZonesComputer { tabSize, 0, fontInfo.spaceWidth, + fontInfo.middotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), diff --git a/src/vs/editor/browser/widget/diffReview.ts b/src/vs/editor/browser/widget/diffReview.ts index 5856e375a3..7779d8ea5d 100644 --- a/src/vs/editor/browser/widget/diffReview.ts +++ b/src/vs/editor/browser/widget/diffReview.ts @@ -782,6 +782,7 @@ export class DiffReview extends Disposable { tabSize, 0, fontInfo.spaceWidth, + fontInfo.middotWidth, options.get(EditorOption.stopRenderingLineAfter), options.get(EditorOption.renderWhitespace), options.get(EditorOption.renderControlCharacters), diff --git a/src/vs/editor/common/config/commonEditorConfig.ts b/src/vs/editor/common/config/commonEditorConfig.ts index 69f2fe58b4..9512311777 100644 --- a/src/vs/editor/common/config/commonEditorConfig.ts +++ b/src/vs/editor/common/config/commonEditorConfig.ts @@ -432,7 +432,7 @@ export const editorConfigurationBaseNode = Object.freeze({ order: 5, type: 'object', title: nls.localize('editorConfigurationTitle', "Editor"), - scope: ConfigurationScope.RESOURCE_LANGUAGE, + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, }); const configurationRegistry = Registry.as(Extensions.Configuration); diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 108dcf0ace..f4961be23b 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -135,6 +135,11 @@ export interface IEditorOptions { * Defaults to false. */ readOnly?: boolean; + /** + * Should the editor render validation decorations. + * Defaults to editable. + */ + renderValidationDecorations?: 'editable' | 'on' | 'off'; /** * Control the behavior and rendering of the scrollbars. */ @@ -295,6 +300,10 @@ export interface IEditorOptions { * Enable inline color decorators and color picker rendering. */ colorDecorators?: boolean; + /** + * Control the behaviour of comments in the editor. + */ + comments?: IEditorCommentsOptions; /** * Enable custom contextmenu. * Defaults to true. @@ -545,7 +554,7 @@ export interface IEditorOptions { * Controls whether to focus the inline editor in the peek widget by default. * Defaults to false. */ - peekWidgetFocusInlineEditor?: boolean; + peekWidgetDefaultFocus?: 'tree' | 'editor'; } export interface IEditorConstructionOptions extends IEditorOptions { @@ -998,6 +1007,52 @@ class EditorAccessibilitySupport extends BaseEditorOption>; + +class EditorComments extends BaseEditorOption { + + constructor() { + const defaults: EditorCommentsOptions = { + insertSpace: true, + }; + super( + EditorOption.comments, 'comments', defaults, + { + 'editor.comments.insertSpace': { + type: 'boolean', + default: defaults.insertSpace, + description: nls.localize('comments.insertSpace', "Controls whether a space character is inserted when commenting.") + }, + } + ); + } + + public validate(_input: any): EditorCommentsOptions { + if (typeof _input !== 'object') { + return this.defaultValue; + } + const input = _input as IEditorCommentsOptions; + return { + insertSpace: EditorBooleanOption.boolean(input.insertSpace, this.defaultValue.insertSpace), + }; + } +} + +//#endregion + //#region cursorBlinking /** @@ -2290,6 +2345,21 @@ class EditorRenderLineNumbersOption extends BaseEditorOption { @@ -3066,6 +3136,7 @@ export const enum EditorOption { autoSurround, codeLens, colorDecorators, + comments, contextmenu, copyWithSyntaxHighlighting, cursorBlinking, @@ -3117,7 +3188,7 @@ export const enum EditorOption { overviewRulerBorder, overviewRulerLanes, parameterHints, - peekWidgetFocusInlineEditor, + peekWidgetDefaultFocus, quickSuggestions, quickSuggestionsDelay, readOnly, @@ -3125,6 +3196,7 @@ export const enum EditorOption { renderIndentGuides, renderFinalNewline, renderLineHighlight, + renderValidationDecorations, renderWhitespace, revealHorizontalRightPadding, roundedSelection, @@ -3169,6 +3241,7 @@ export const enum EditorOption { * WORKAROUND: TS emits "any" for complex editor options values (anything except string, bool, enum, etc. ends up being "any") * @monacodtsreplace * /accessibilitySupport, any/accessibilitySupport, AccessibilitySupport/ + * /comments, any/comments, EditorCommentsOptions/ * /find, any/find, EditorFindOptions/ * /fontInfo, any/fontInfo, FontInfo/ * /gotoLocation, any/gotoLocation, GoToLocationOptions/ @@ -3285,6 +3358,7 @@ export const EditorOptions = { EditorOption.colorDecorators, 'colorDecorators', true, { description: nls.localize('colorDecorators', "Controls whether the editor should render the inline color decorators and color picker.") } )), + comments: register(new EditorComments()), contextmenu: register(new EditorBooleanOption( EditorOption.contextmenu, 'contextmenu', true, )), @@ -3494,9 +3568,17 @@ export const EditorOptions = { 3, 0, 3 )), parameterHints: register(new EditorParameterHints()), - peekWidgetFocusInlineEditor: register(new EditorBooleanOption( - EditorOption.peekWidgetFocusInlineEditor, 'peekWidgetFocusInlineEditor', false, - { description: nls.localize('peekWidgetFocusInlineEditor', "Controls whether to focus the inline editor in the peek widget by default.") } + peekWidgetDefaultFocus: register(new EditorStringEnumOption( + EditorOption.peekWidgetDefaultFocus, 'peekWidgetDefaultFocus', + 'tree' as 'tree' | 'editor', + ['tree', 'editor'] as const, + { + enumDescriptions: [ + nls.localize('peekWidgetDefaultFocus.tree', "Focus the tree when openeing peek"), + nls.localize('peekWidgetDefaultFocus.editor', "Focus the editor when opening peek") + ], + description: nls.localize('peekWidgetDefaultFocus', "Controls whether to focus the inline editor or the tree in the peek widget.") + } )), quickSuggestions: register(new EditorQuickSuggestions()), quickSuggestionsDelay: register(new EditorIntOption( @@ -3533,6 +3615,11 @@ export const EditorOptions = { description: nls.localize('renderLineHighlight', "Controls how the editor should render the current line highlight.") } )), + renderValidationDecorations: register(new EditorStringEnumOption( + EditorOption.renderValidationDecorations, 'renderValidationDecorations', + 'editable' as 'editable' | 'on' | 'off', + ['editable', 'on', 'off'] as const + )), renderWhitespace: register(new EditorStringEnumOption( EditorOption.renderWhitespace, 'renderWhitespace', 'none' as 'none' | 'boundary' | 'selection' | 'all', diff --git a/src/vs/editor/common/config/fontInfo.ts b/src/vs/editor/common/config/fontInfo.ts index 9b39823b94..b8b6311e63 100644 --- a/src/vs/editor/common/config/fontInfo.ts +++ b/src/vs/editor/common/config/fontInfo.ts @@ -134,6 +134,7 @@ export class FontInfo extends BareFontInfo { readonly typicalFullwidthCharacterWidth: number; readonly canUseHalfwidthRightwardsArrow: boolean; readonly spaceWidth: number; + readonly middotWidth: number; readonly maxDigitWidth: number; /** @@ -152,6 +153,7 @@ export class FontInfo extends BareFontInfo { typicalFullwidthCharacterWidth: number; canUseHalfwidthRightwardsArrow: boolean; spaceWidth: number; + middotWidth: number; maxDigitWidth: number; }, isTrusted: boolean) { super(opts); @@ -161,6 +163,7 @@ export class FontInfo extends BareFontInfo { this.typicalFullwidthCharacterWidth = opts.typicalFullwidthCharacterWidth; this.canUseHalfwidthRightwardsArrow = opts.canUseHalfwidthRightwardsArrow; this.spaceWidth = opts.spaceWidth; + this.middotWidth = opts.middotWidth; this.maxDigitWidth = opts.maxDigitWidth; } @@ -179,6 +182,7 @@ export class FontInfo extends BareFontInfo { && this.typicalFullwidthCharacterWidth === other.typicalFullwidthCharacterWidth && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.spaceWidth === other.spaceWidth + && this.middotWidth === other.middotWidth && this.maxDigitWidth === other.maxDigitWidth ); } diff --git a/src/vs/editor/common/controller/cursor.ts b/src/vs/editor/common/controller/cursor.ts index 02275fbbd6..c8dc0ea75f 100644 --- a/src/vs/editor/common/controller/cursor.ts +++ b/src/vs/editor/common/controller/cursor.ts @@ -545,7 +545,7 @@ export class Cursor extends viewEvents.ViewEventEmitter implements ICursors { // Let the view get the event first. try { const eventsCollector = this._beginEmit(); - eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections)); + eventsCollector.emit(new viewEvents.ViewCursorStateChangedEvent(viewSelections, selections)); } finally { this._endEmit(); } diff --git a/src/vs/editor/common/controller/cursorTypeOperations.ts b/src/vs/editor/common/controller/cursorTypeOperations.ts index 17068f4e16..62b9ad4554 100644 --- a/src/vs/editor/common/controller/cursorTypeOperations.ts +++ b/src/vs/editor/common/controller/cursorTypeOperations.ts @@ -492,15 +492,20 @@ export class TypeOperations { }); } + private static _autoClosingPairIsSymmetric(autoClosingPair: StandardAutoClosingPairConditional): boolean { + const { open, close } = autoClosingPair; + return (open.indexOf(close) >= 0 || close.indexOf(open) >= 0); + } + private static _isBeforeClosingBrace(config: CursorConfiguration, autoClosingPair: StandardAutoClosingPairConditional, characterAfter: string) { const otherAutoClosingPairs = config.autoClosingPairsClose2.get(characterAfter); if (!otherAutoClosingPairs) { return false; } - const thisBraceIsSymmetric = (autoClosingPair.open === autoClosingPair.close); + const thisBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(autoClosingPair); for (const otherAutoClosingPair of otherAutoClosingPairs) { - const otherBraceIsSymmetric = (otherAutoClosingPair.open === otherAutoClosingPair.close); + const otherBraceIsSymmetric = TypeOperations._autoClosingPairIsSymmetric(otherAutoClosingPair); if (!thisBraceIsSymmetric && otherBraceIsSymmetric) { continue; } diff --git a/src/vs/editor/common/model/indentationGuesser.ts b/src/vs/editor/common/model/indentationGuesser.ts index 9561ebf108..001acba580 100644 --- a/src/vs/editor/common/model/indentationGuesser.ts +++ b/src/vs/editor/common/model/indentationGuesser.ts @@ -72,11 +72,12 @@ function spacesDiff(a: string, aLength: number, b: string, bLength: number, resu if (spacesDiff > 0 && 0 <= bSpacesCnt - 1 && bSpacesCnt - 1 < a.length && bSpacesCnt < b.length) { if (b.charCodeAt(bSpacesCnt) !== CharCode.Space && a.charCodeAt(bSpacesCnt - 1) === CharCode.Space) { - // This looks like an alignment desire: e.g. - // const a = b + c, - // d = b - c; - - result.looksLikeAlignment = true; + if (a.charCodeAt(a.length - 1) === CharCode.Comma) { + // This looks like an alignment desire: e.g. + // const a = b + c, + // d = b - c; + result.looksLikeAlignment = true; + } } } return; diff --git a/src/vs/editor/common/model/textModel.ts b/src/vs/editor/common/model/textModel.ts index d33b96bae7..d65b4a0058 100644 --- a/src/vs/editor/common/model/textModel.ts +++ b/src/vs/editor/common/model/textModel.ts @@ -1947,6 +1947,7 @@ export class TextModel extends Disposable implements model.ITextModel { private _matchBracket(position: Position): [Range, Range] | null { const lineNumber = position.lineNumber; const lineTokens = this._getLineTokens(lineNumber); + const tokenCount = lineTokens.getCount(); const lineText = this._buffer.getLineContent(lineNumber); const tokenIndex = lineTokens.findTokenIndexAtOffset(position.column - 1); @@ -1959,6 +1960,15 @@ export class TextModel extends Disposable implements model.ITextModel { if (currentModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(tokenIndex))) { // limit search to not go before `maxBracketLength` let searchStartOffset = Math.max(0, position.column - 1 - currentModeBrackets.maxBracketLength); + for (let i = tokenIndex - 1; i >= 0; i--) { + const tokenEndOffset = lineTokens.getEndOffset(i); + if (tokenEndOffset <= searchStartOffset) { + break; + } + if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) { + searchStartOffset = tokenEndOffset; + } + } // limit search to not go after `maxBracketLength` const searchEndOffset = Math.min(lineText.length, position.column - 1 + currentModeBrackets.maxBracketLength); @@ -1998,7 +2008,16 @@ export class TextModel extends Disposable implements model.ITextModel { if (prevModeBrackets && !ignoreBracketsInToken(lineTokens.getStandardTokenType(prevTokenIndex))) { // limit search in case previous token is very large, there's no need to go beyond `maxBracketLength` const searchStartOffset = Math.max(0, position.column - 1 - prevModeBrackets.maxBracketLength); - const searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength); + let searchEndOffset = Math.min(lineText.length, position.column - 1 + prevModeBrackets.maxBracketLength); + for (let i = prevTokenIndex + 1; i < tokenCount; i++) { + const tokenStartOffset = lineTokens.getStartOffset(i); + if (tokenStartOffset >= searchEndOffset) { + break; + } + if (ignoreBracketsInToken(lineTokens.getStandardTokenType(i))) { + searchEndOffset = tokenStartOffset; + } + } const foundBracket = BracketsUtils.findPrevBracketInRange(prevModeBrackets.reversedRegex, lineNumber, lineText, searchStartOffset, searchEndOffset); // check that we didn't hit a bracket too far away from position diff --git a/src/vs/editor/common/model/tokensStore.ts b/src/vs/editor/common/model/tokensStore.ts index cfb66060aa..5f80d4252b 100644 --- a/src/vs/editor/common/model/tokensStore.ts +++ b/src/vs/editor/common/model/tokensStore.ts @@ -804,7 +804,7 @@ export class TokensStore2 { aIndex++; } - const aMetadata = aTokens.getMetadata(aIndex - 1 > 0 ? aIndex - 1 : aIndex); + const aMetadata = aTokens.getMetadata(Math.min(Math.max(0, aIndex - 1), aLen - 1)); const languageId = TokenMetadata.getLanguageId(aMetadata); const tokenType = TokenMetadata.getTokenType(aMetadata); diff --git a/src/vs/editor/common/modes.ts b/src/vs/editor/common/modes.ts index 67a2ac73d0..56042681e7 100644 --- a/src/vs/editor/common/modes.ts +++ b/src/vs/editor/common/modes.ts @@ -369,6 +369,28 @@ export let completionKindFromString: { }; })(); +export interface CompletionItemLabel { + /** + * The function or variable. Rendered leftmost. + */ + name: string; + + /** + * The signature without the return type. Render after `name`. + */ + signature?: string; + + /** + * The fully qualified name, like package name or file path. Rendered after `signature`. + */ + qualifier?: string; + + /** + * The return-type of a function or type of a property/variable. Rendered rightmost. + */ + type?: string; +} + export const enum CompletionItemTag { Deprecated = 1 } @@ -396,7 +418,7 @@ export interface CompletionItem { * this is also the text that is inserted when selecting * this completion. */ - label: string; + label: string | CompletionItemLabel; /** * The kind of this completion item. Based on the kind * an icon is chosen by the editor. @@ -481,7 +503,6 @@ export interface CompletionItem { export interface CompletionList { suggestions: CompletionItem[]; incomplete?: boolean; - isDetailsResolved?: boolean; dispose?(): void; } @@ -1257,20 +1278,36 @@ export namespace WorkspaceTextEdit { * @internal */ export function is(thing: any): thing is WorkspaceTextEdit { - return isObject(thing) && (thing).resource && Array.isArray((thing).edits); + return isObject(thing) && URI.isUri((thing).resource) && isObject((thing).edit); } } +export interface WorkspaceEditMetadata { + needsConfirmation: boolean; + label: string; + description?: string; + iconPath?: { id: string } | { light: URI, dark: URI }; +} + +export interface WorkspaceFileEditOptions { + overwrite?: boolean; + ignoreIfNotExists?: boolean; + ignoreIfExists?: boolean; + recursive?: boolean; +} + export interface WorkspaceFileEdit { oldUri?: URI; newUri?: URI; - options?: { overwrite?: boolean, ignoreIfNotExists?: boolean, ignoreIfExists?: boolean, recursive?: boolean }; + options?: WorkspaceFileEditOptions; + metadata?: WorkspaceEditMetadata; } export interface WorkspaceTextEdit { resource: URI; + edit: TextEdit; modelVersionId?: number; - edits: TextEdit[]; + metadata?: WorkspaceEditMetadata; } export interface WorkspaceEdit { diff --git a/src/vs/editor/common/modes/linkComputer.ts b/src/vs/editor/common/modes/linkComputer.ts index 6c427272d7..a446446b8d 100644 --- a/src/vs/editor/common/modes/linkComputer.ts +++ b/src/vs/editor/common/modes/linkComputer.ts @@ -268,6 +268,10 @@ export class LinkComputer { // `*` terminates a link if the link began with `*` chClass = (linkBeginChCode === CharCode.Asterisk) ? CharacterClass.ForceTermination : CharacterClass.None; break; + case CharCode.Pipe: + // `|` terminates a link if the link began with `|` + chClass = (linkBeginChCode === CharCode.Pipe) ? CharacterClass.ForceTermination : CharacterClass.None; + break; default: chClass = classifier.get(chCode); } diff --git a/src/vs/editor/common/modes/supports.ts b/src/vs/editor/common/modes/supports.ts index d1e1011f00..e99037e0b6 100644 --- a/src/vs/editor/common/modes/supports.ts +++ b/src/vs/editor/common/modes/supports.ts @@ -62,6 +62,11 @@ export class ScopedLineTokens { return actualLineContent.substring(this.firstCharOffset, this._lastCharOffset); } + public getActualLineContentBefore(offset: number): string { + const actualLineContent = this._actual.getLineContent(); + return actualLineContent.substring(0, this.firstCharOffset + offset); + } + public getTokenCount(): number { return this._lastTokenIndex - this._firstTokenIndex; } diff --git a/src/vs/editor/common/modes/supports/electricCharacter.ts b/src/vs/editor/common/modes/supports/electricCharacter.ts index b1de661514..26595d78d5 100644 --- a/src/vs/editor/common/modes/supports/electricCharacter.ts +++ b/src/vs/editor/common/modes/supports/electricCharacter.ts @@ -49,28 +49,27 @@ export class BracketElectricCharacterSupport { return null; } - let tokenIndex = context.findTokenIndexAtOffset(column - 1); + const tokenIndex = context.findTokenIndexAtOffset(column - 1); if (ignoreBracketsInToken(context.getStandardTokenType(tokenIndex))) { return null; } - let reversedBracketRegex = this._richEditBrackets.reversedRegex; - let text = context.getLineContent().substring(0, column - 1) + character; + const reversedBracketRegex = this._richEditBrackets.reversedRegex; + const text = context.getLineContent().substring(0, column - 1) + character; - let r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length); + const r = BracketsUtils.findPrevBracketInRange(reversedBracketRegex, 1, text, 0, text.length); if (!r) { return null; } - let bracketText = text.substring(r.startColumn - 1, r.endColumn - 1); - bracketText = bracketText.toLowerCase(); + const bracketText = text.substring(r.startColumn - 1, r.endColumn - 1).toLowerCase(); - let isOpen = this._richEditBrackets.textIsOpenBracket[bracketText]; + const isOpen = this._richEditBrackets.textIsOpenBracket[bracketText]; if (isOpen) { return null; } - let textBeforeBracket = text.substring(0, r.startColumn - 1); + const textBeforeBracket = context.getActualLineContentBefore(r.startColumn - 1); if (!/^\s*$/.test(textBeforeBracket)) { // There is other text on the line before the bracket return null; diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index f1bc5504b1..f63805f3f4 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -147,12 +147,10 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor let ret = Range.lift(rawMarker); - if (rawMarker.severity === MarkerSeverity.Hint) { - if (!rawMarker.tags || rawMarker.tags.indexOf(MarkerTag.Unnecessary) === -1) { - // * never render hints on multiple lines - // * make enough space for three dots - ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2); - } + if (rawMarker.severity === MarkerSeverity.Hint && !this._hasMarkerTag(rawMarker, MarkerTag.Unnecessary) && !this._hasMarkerTag(rawMarker, MarkerTag.Deprecated)) { + // * never render hints on multiple lines + // * make enough space for three dots + ret = ret.setEndPosition(ret.startLineNumber, ret.startColumn + 2); } ret = model.validateRange(ret); @@ -188,7 +186,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor private _createDecorationOption(marker: IMarker): IModelDecorationOptions { - let className: string; + let className: string | undefined; let color: ThemeColor | undefined = undefined; let zIndex: number; let inlineClassName: string | undefined = undefined; @@ -196,7 +194,9 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor switch (marker.severity) { case MarkerSeverity.Hint: - if (marker.tags && marker.tags.indexOf(MarkerTag.Unnecessary) >= 0) { + if (this._hasMarkerTag(marker, MarkerTag.Deprecated)) { + className = undefined; + } else if (this._hasMarkerTag(marker, MarkerTag.Unnecessary)) { className = ClassName.EditorUnnecessaryDecoration; } else { className = ClassName.EditorHintDecoration; @@ -251,4 +251,11 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor inlineClassName, }; } + + private _hasMarkerTag(marker: IMarker, tag: MarkerTag): boolean { + if (marker.tags) { + return marker.tags.indexOf(tag) >= 0; + } + return false; + } } diff --git a/src/vs/editor/common/standalone/standaloneEnums.ts b/src/vs/editor/common/standalone/standaloneEnums.ts index 9404a4beee..da44ff3fa5 100644 --- a/src/vs/editor/common/standalone/standaloneEnums.ts +++ b/src/vs/editor/common/standalone/standaloneEnums.ts @@ -178,101 +178,103 @@ export enum EditorOption { autoSurround = 10, codeLens = 11, colorDecorators = 12, - contextmenu = 13, - copyWithSyntaxHighlighting = 14, - cursorBlinking = 15, - cursorSmoothCaretAnimation = 16, - cursorStyle = 17, - cursorSurroundingLines = 18, - cursorSurroundingLinesStyle = 19, - cursorWidth = 20, - disableLayerHinting = 21, - disableMonospaceOptimizations = 22, - dragAndDrop = 23, - emptySelectionClipboard = 24, - extraEditorClassName = 25, - fastScrollSensitivity = 26, - find = 27, - fixedOverflowWidgets = 28, - folding = 29, - foldingStrategy = 30, - foldingHighlight = 31, - fontFamily = 32, - fontInfo = 33, - fontLigatures = 34, - fontSize = 35, - fontWeight = 36, - formatOnPaste = 37, - formatOnType = 38, - glyphMargin = 39, - gotoLocation = 40, - hideCursorInOverviewRuler = 41, - highlightActiveIndentGuide = 42, - hover = 43, - inDiffEditor = 44, - letterSpacing = 45, - lightbulb = 46, - lineDecorationsWidth = 47, - lineHeight = 48, - lineNumbers = 49, - lineNumbersMinChars = 50, - links = 51, - matchBrackets = 52, - minimap = 53, - mouseStyle = 54, - mouseWheelScrollSensitivity = 55, - mouseWheelZoom = 56, - multiCursorMergeOverlapping = 57, - multiCursorModifier = 58, - multiCursorPaste = 59, - occurrencesHighlight = 60, - overviewRulerBorder = 61, - overviewRulerLanes = 62, - parameterHints = 63, - peekWidgetFocusInlineEditor = 64, - quickSuggestions = 65, - quickSuggestionsDelay = 66, - readOnly = 67, - renderControlCharacters = 68, - renderIndentGuides = 69, - renderFinalNewline = 70, - renderLineHighlight = 71, - renderWhitespace = 72, - revealHorizontalRightPadding = 73, - roundedSelection = 74, - rulers = 75, - scrollbar = 76, - scrollBeyondLastColumn = 77, - scrollBeyondLastLine = 78, - selectionClipboard = 79, - selectionHighlight = 80, - selectOnLineNumbers = 81, - semanticHighlighting = 82, - showFoldingControls = 83, - showUnused = 84, - snippetSuggestions = 85, - smoothScrolling = 86, - stopRenderingLineAfter = 87, - suggest = 88, - suggestFontSize = 89, - suggestLineHeight = 90, - suggestOnTriggerCharacters = 91, - suggestSelection = 92, - tabCompletion = 93, - useTabStops = 94, - wordSeparators = 95, - wordWrap = 96, - wordWrapBreakAfterCharacters = 97, - wordWrapBreakBeforeCharacters = 98, - wordWrapColumn = 99, - wordWrapMinified = 100, - wrappingIndent = 101, - wrappingAlgorithm = 102, - editorClassName = 103, - pixelRatio = 104, - tabFocusMode = 105, - layoutInfo = 106, - wrappingInfo = 107 + comments = 13, + contextmenu = 14, + copyWithSyntaxHighlighting = 15, + cursorBlinking = 16, + cursorSmoothCaretAnimation = 17, + cursorStyle = 18, + cursorSurroundingLines = 19, + cursorSurroundingLinesStyle = 20, + cursorWidth = 21, + disableLayerHinting = 22, + disableMonospaceOptimizations = 23, + dragAndDrop = 24, + emptySelectionClipboard = 25, + extraEditorClassName = 26, + fastScrollSensitivity = 27, + find = 28, + fixedOverflowWidgets = 29, + folding = 30, + foldingStrategy = 31, + foldingHighlight = 32, + fontFamily = 33, + fontInfo = 34, + fontLigatures = 35, + fontSize = 36, + fontWeight = 37, + formatOnPaste = 38, + formatOnType = 39, + glyphMargin = 40, + gotoLocation = 41, + hideCursorInOverviewRuler = 42, + highlightActiveIndentGuide = 43, + hover = 44, + inDiffEditor = 45, + letterSpacing = 46, + lightbulb = 47, + lineDecorationsWidth = 48, + lineHeight = 49, + lineNumbers = 50, + lineNumbersMinChars = 51, + links = 52, + matchBrackets = 53, + minimap = 54, + mouseStyle = 55, + mouseWheelScrollSensitivity = 56, + mouseWheelZoom = 57, + multiCursorMergeOverlapping = 58, + multiCursorModifier = 59, + multiCursorPaste = 60, + occurrencesHighlight = 61, + overviewRulerBorder = 62, + overviewRulerLanes = 63, + parameterHints = 64, + peekWidgetDefaultFocus = 65, + quickSuggestions = 66, + quickSuggestionsDelay = 67, + readOnly = 68, + renderControlCharacters = 69, + renderIndentGuides = 70, + renderFinalNewline = 71, + renderLineHighlight = 72, + renderValidationDecorations = 73, + renderWhitespace = 74, + revealHorizontalRightPadding = 75, + roundedSelection = 76, + rulers = 77, + scrollbar = 78, + scrollBeyondLastColumn = 79, + scrollBeyondLastLine = 80, + selectionClipboard = 81, + selectionHighlight = 82, + selectOnLineNumbers = 83, + semanticHighlighting = 84, + showFoldingControls = 85, + showUnused = 86, + snippetSuggestions = 87, + smoothScrolling = 88, + stopRenderingLineAfter = 89, + suggest = 90, + suggestFontSize = 91, + suggestLineHeight = 92, + suggestOnTriggerCharacters = 93, + suggestSelection = 94, + tabCompletion = 95, + useTabStops = 96, + wordSeparators = 97, + wordWrap = 98, + wordWrapBreakAfterCharacters = 99, + wordWrapBreakBeforeCharacters = 100, + wordWrapColumn = 101, + wordWrapMinified = 102, + wrappingIndent = 103, + wrappingAlgorithm = 104, + editorClassName = 105, + pixelRatio = 106, + tabFocusMode = 107, + layoutInfo = 108, + wrappingInfo = 109 } /** diff --git a/src/vs/editor/common/view/viewEvents.ts b/src/vs/editor/common/view/viewEvents.ts index f02b5ac556..7715c163fd 100644 --- a/src/vs/editor/common/view/viewEvents.ts +++ b/src/vs/editor/common/view/viewEvents.ts @@ -69,13 +69,12 @@ export class ViewCursorStateChangedEvent { public readonly type = ViewEventType.ViewCursorStateChanged; - /** - * The primary selection is always at index 0. - */ public readonly selections: Selection[]; + public readonly modelSelections: Selection[]; - constructor(selections: Selection[]) { + constructor(selections: Selection[], modelSelections: Selection[]) { this.selections = selections; + this.modelSelections = modelSelections; } } diff --git a/src/vs/editor/common/viewLayout/viewLineRenderer.ts b/src/vs/editor/common/viewLayout/viewLineRenderer.ts index 1addc868a1..4f4f76bce4 100644 --- a/src/vs/editor/common/viewLayout/viewLineRenderer.ts +++ b/src/vs/editor/common/viewLayout/viewLineRenderer.ts @@ -68,6 +68,7 @@ export class RenderLineInput { public readonly tabSize: number; public readonly startVisibleColumn: number; public readonly spaceWidth: number; + public readonly middotWidth: number; public readonly stopRenderingLineAfter: number; public readonly renderWhitespace: RenderWhitespace; public readonly renderControlCharacters: boolean; @@ -92,6 +93,7 @@ export class RenderLineInput { tabSize: number, startVisibleColumn: number, spaceWidth: number, + middotWidth: number, stopRenderingLineAfter: number, renderWhitespace: 'none' | 'boundary' | 'selection' | 'all', renderControlCharacters: boolean, @@ -110,6 +112,7 @@ export class RenderLineInput { this.tabSize = tabSize; this.startVisibleColumn = startVisibleColumn; this.spaceWidth = spaceWidth; + this.middotWidth = middotWidth; this.stopRenderingLineAfter = stopRenderingLineAfter; this.renderWhitespace = ( renderWhitespace === 'all' @@ -380,6 +383,7 @@ class ResolvedRenderLineInput { public readonly startVisibleColumn: number, public readonly containsRTL: boolean, public readonly spaceWidth: number, + public readonly middotWidth: number, public readonly renderWhitespace: RenderWhitespace, public readonly renderControlCharacters: boolean, ) { @@ -439,6 +443,7 @@ function resolveRenderLineInput(input: RenderLineInput): ResolvedRenderLineInput input.startVisibleColumn, input.containsRTL, input.spaceWidth, + input.middotWidth, input.renderWhitespace, input.renderControlCharacters ); @@ -734,9 +739,13 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render const startVisibleColumn = input.startVisibleColumn; const containsRTL = input.containsRTL; const spaceWidth = input.spaceWidth; + const middotWidth = input.middotWidth; const renderWhitespace = input.renderWhitespace; const renderControlCharacters = input.renderControlCharacters; + // use U+2E31 - WORD SEPARATOR MIDDLE DOT or U+00B7 - MIDDLE DOT + const spaceRenderWhitespaceCharacter = (middotWidth > spaceWidth ? 0x2E31 : 0xB7); + const characterMapping = new CharacterMapping(len + 1, parts.length); let charIndex = 0; @@ -808,7 +817,7 @@ function _renderLine(input: ResolvedRenderLineInput, sb: IStringBuilder): Render } else { // must be CharCode.Space charWidth = 1; - sb.write1(0xB7); // · + sb.write1(spaceRenderWhitespaceCharacter); // · or word separator middle dot } charOffsetInPart += charWidth; diff --git a/src/vs/editor/common/viewModel/viewModel.ts b/src/vs/editor/common/viewModel/viewModel.ts index 40820d7323..ac08c4318a 100644 --- a/src/vs/editor/common/viewModel/viewModel.ts +++ b/src/vs/editor/common/viewModel/viewModel.ts @@ -138,8 +138,8 @@ export interface IViewModel { deduceModelPositionRelativeToViewPosition(viewAnchorPosition: Position, deltaOffset: number, lineFeedCnt: number): Position; getEOL(): string; - getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[]; - getHTMLToCopy(ranges: Range[], emptySelectionClipboard: boolean): string | null; + getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[]; + getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null; } export class MinimapLinesRenderingData { diff --git a/src/vs/editor/common/viewModel/viewModelDecorations.ts b/src/vs/editor/common/viewModel/viewModelDecorations.ts index 54c1a9da27..0a4b86bae6 100644 --- a/src/vs/editor/common/viewModel/viewModelDecorations.ts +++ b/src/vs/editor/common/viewModel/viewModelDecorations.ts @@ -10,7 +10,7 @@ import * as editorCommon from 'vs/editor/common/editorCommon'; import { IModelDecoration, ITextModel } from 'vs/editor/common/model'; import { IViewModelLinesCollection } from 'vs/editor/common/viewModel/splitLinesCollection'; import { ICoordinatesConverter, InlineDecoration, InlineDecorationType, ViewModelDecoration } from 'vs/editor/common/viewModel/viewModel'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; export interface IDecorationsViewportData { /** @@ -104,7 +104,7 @@ export class ViewModelDecorations implements IDisposable { } private _getDecorationsViewportData(viewportRange: Range): IDecorationsViewportData { - const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, this.configuration.options.get(EditorOption.readOnly)); + const modelDecorations = this._linesCollection.getDecorationsInRange(viewportRange, this.editorId, filterValidationDecorations(this.configuration.options)); const startLineNumber = viewportRange.startLineNumber; const endLineNumber = viewportRange.endLineNumber; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 54b5155a77..bd2c8439a6 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -6,7 +6,7 @@ import { Color } from 'vs/base/common/color'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; -import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; +import { ConfigurationChangedEvent, EDITOR_FONT_DEFAULTS, EditorOption, filterValidationDecorations } from 'vs/editor/common/config/editorOptions'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IConfiguration, IViewState } from 'vs/editor/common/editorCommon'; @@ -596,7 +596,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } public getAllOverviewRulerDecorations(theme: ITheme): IOverviewRulerDecorations { - return this.lines.getAllOverviewRulerDecorations(this.editorId, this.configuration.options.get(EditorOption.readOnly), theme); + return this.lines.getAllOverviewRulerDecorations(this.editorId, filterValidationDecorations(this.configuration.options), theme); } public invalidateOverviewRulerColorCache(): void { @@ -656,15 +656,15 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return this.model.getEOL(); } - public getPlainTextToCopy(ranges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] { + public getPlainTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean, forceCRLF: boolean): string | string[] { const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL(); - ranges = ranges.slice(0); - ranges.sort(Range.compareRangesUsingStarts); + modelRanges = modelRanges.slice(0); + modelRanges.sort(Range.compareRangesUsingStarts); let hasEmptyRange = false; let hasNonEmptyRange = false; - for (const range of ranges) { + for (const range of modelRanges) { if (range.isEmpty()) { hasEmptyRange = true; } else { @@ -678,10 +678,7 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel return ''; } - const modelLineNumbers = ranges.map((r) => { - const viewLineStart = new Position(r.startLineNumber, 1); - return this.coordinatesConverter.convertViewPositionToModelPosition(viewLineStart).lineNumber; - }); + const modelLineNumbers = modelRanges.map((r) => r.startLineNumber); let result = ''; for (let i = 0; i < modelLineNumbers.length; i++) { @@ -697,14 +694,14 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel // mixed empty selections and non-empty selections let result: string[] = []; let prevModelLineNumber = 0; - for (const range of ranges) { - const modelLineNumber = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(range.startLineNumber, 1)).lineNumber; - if (range.isEmpty()) { + for (const modelRange of modelRanges) { + const modelLineNumber = modelRange.startLineNumber; + if (modelRange.isEmpty()) { if (modelLineNumber !== prevModelLineNumber) { result.push(this.model.getLineContent(modelLineNumber)); } } else { - result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); + result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); } prevModelLineNumber = modelLineNumber; } @@ -712,31 +709,32 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel } let result: string[] = []; - for (const range of ranges) { - if (!range.isEmpty()) { - result.push(this.getValueInRange(range, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); + for (const modelRange of modelRanges) { + if (!modelRange.isEmpty()) { + result.push(this.model.getValueInRange(modelRange, forceCRLF ? EndOfLinePreference.CRLF : EndOfLinePreference.TextDefined)); } } return result.length === 1 ? result[0] : result; } - public getHTMLToCopy(viewRanges: Range[], emptySelectionClipboard: boolean): string | null { - if (this.model.getLanguageIdentifier().id === LanguageId.PlainText) { + public getRichTextToCopy(modelRanges: Range[], emptySelectionClipboard: boolean): { html: string, mode: string } | null { + const languageId = this.model.getLanguageIdentifier(); + if (languageId.id === LanguageId.PlainText) { return null; } - if (viewRanges.length !== 1) { + if (modelRanges.length !== 1) { // no multiple selection support at this time return null; } - let range = this.coordinatesConverter.convertViewRangeToModelRange(viewRanges[0]); + let range = modelRanges[0]; if (range.isEmpty()) { if (!emptySelectionClipboard) { // nothing to copy return null; } - let lineNumber = range.startLineNumber; + const lineNumber = range.startLineNumber; range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber)); } @@ -744,19 +742,22 @@ export class ViewModel extends viewEvents.ViewEventEmitter implements IViewModel const colorMap = this._getColorMap(); const fontFamily = fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily ? fontInfo.fontFamily : `'${fontInfo.fontFamily}', ${EDITOR_FONT_DEFAULTS.fontFamily}`; - return ( - `
` - + this._getHTMLToCopy(range, colorMap) - + '
' - ); + return { + mode: languageId.language, + html: ( + `
` + + this._getHTMLToCopy(range, colorMap) + + '
' + ) + }; } private _getHTMLToCopy(modelRange: Range, colorMap: string[]): string { diff --git a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts index b04705e853..e63f5428d6 100644 --- a/src/vs/editor/contrib/codeAction/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/lightBulbWidget.ts @@ -110,7 +110,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { // showings until mouse is released this.hide(); const monitor = new GlobalMouseMoveMonitor(); - monitor.startMonitoring(e.buttons, standardMouseMoveMerger, () => { }, () => { + monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, () => { }, () => { monitor.dispose(); }); })); diff --git a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts index f52ecb2b0a..46a5f1c614 100644 --- a/src/vs/editor/contrib/codeAction/test/codeAction.test.ts +++ b/src/vs/editor/contrib/codeAction/test/codeAction.test.ts @@ -220,6 +220,43 @@ suite('CodeAction', () => { } }); + test('getCodeActions no invoke a provider that has been excluded #84602', async function () { + const baseType = CodeActionKind.Refactor; + const subType = CodeActionKind.Refactor.append('sub'); + + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', staticCodeActionProvider( + { title: 'a', kind: baseType.value } + ))); + + let didInvoke = false; + disposables.add(modes.CodeActionProviderRegistry.register('fooLang', new class implements modes.CodeActionProvider { + + providedCodeActionKinds = [subType.value]; + + provideCodeActions(): modes.ProviderResult { + didInvoke = true; + return { + actions: [ + { title: 'x', kind: subType.value } + ], + dispose: () => { } + }; + } + })); + + { + const { validActions: actions } = await getCodeActions(model, new Range(1, 1, 2, 1), { + type: modes.CodeActionTriggerType.Auto, filter: { + include: baseType, + excludes: [subType], + } + }, CancellationToken.None); + assert.strictEqual(didInvoke, false); + assert.equal(actions.length, 1); + assert.strictEqual(actions[0].title, 'a'); + } + }); + test('getCodeActions should not invoke code action providers filtered out by providedCodeActionKinds', async function () { let wasInvoked = false; const provider = new class implements modes.CodeActionProvider { diff --git a/src/vs/editor/contrib/codeAction/types.ts b/src/vs/editor/contrib/codeAction/types.ts index e889f0af43..5a20507a40 100644 --- a/src/vs/editor/contrib/codeAction/types.ts +++ b/src/vs/editor/contrib/codeAction/types.ts @@ -58,6 +58,12 @@ export function mayIncludeActionsOfKind(filter: CodeActionFilter, providedKind: return false; } + if (filter.excludes) { + if (filter.excludes.some(exclude => excludesAction(providedKind, exclude, filter.include))) { + return false; + } + } + // Don't return source actions unless they are explicitly requested if (!filter.includeSourceActions && CodeActionKind.Source.contains(providedKind)) { return false; @@ -77,10 +83,7 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo } if (filter.excludes) { - if (actionKind && filter.excludes.some(exclude => { - // Excludes are overwritten by includes - return exclude.contains(actionKind) && (!filter.include || !filter.include.contains(actionKind)); - })) { + if (actionKind && filter.excludes.some(exclude => excludesAction(actionKind, exclude, filter.include))) { return false; } } @@ -101,6 +104,17 @@ export function filtersAction(filter: CodeActionFilter, action: CodeAction): boo return true; } +function excludesAction(providedKind: CodeActionKind, exclude: CodeActionKind, include: CodeActionKind | undefined): boolean { + if (!exclude.contains(providedKind)) { + return false; + } + if (include && exclude.contains(include)) { + // The include is more specific, don't filter out + return false; + } + return true; +} + export interface CodeActionTrigger { readonly type: CodeActionTriggerType; readonly filter?: CodeActionFilter; diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 7c52af14e6..5e300b45a5 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -18,7 +18,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ICodeLensCache } from 'vs/editor/contrib/codelens/codeLensCache'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { createStyleSheet } from 'vs/base/browser/dom'; +import * as dom from 'vs/base/browser/dom'; import { hash } from 'vs/base/common/hash'; export class CodeLensContribution implements IEditorContribution { @@ -65,7 +65,11 @@ export class CodeLensContribution implements IEditorContribution { this._onModelChange(); this._styleClassName = hash(this._editor.getId()).toString(16); - this._styleElement = createStyleSheet(); + this._styleElement = dom.createStyleSheet( + dom.isInShadowDOM(this._editor.getContainerDomNode()) + ? this._editor.getContainerDomNode() + : undefined + ); this._updateLensStyle(); } @@ -81,7 +85,13 @@ export class CodeLensContribution implements IEditorContribution { const fontInfo = options.get(EditorOption.fontInfo); const lineHeight = options.get(EditorOption.lineHeight); - const newStyle = `.monaco-editor .codelens-decoration.${this._styleClassName} { height: ${Math.round(lineHeight * 1.1)}px; line-height: ${lineHeight}px; font-size: ${Math.round(fontInfo.fontSize * 0.9)}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;}`; + + const height = Math.round(lineHeight * 1.1); + const fontSize = Math.round(fontInfo.fontSize * 0.9); + const newStyle = ` + .monaco-editor .codelens-decoration.${this._styleClassName} { height: ${height}px; line-height: ${lineHeight}px; font-size: ${fontSize}px; padding-right: ${Math.round(fontInfo.fontSize * 0.45)}px;} + .monaco-editor .codelens-decoration.${this._styleClassName} > a > .codicon { line-height: ${lineHeight}px; font-size: ${fontSize}px; } + `; this._styleElement.innerHTML = newStyle; } diff --git a/src/vs/editor/contrib/codelens/codelensWidget.css b/src/vs/editor/contrib/codelens/codelensWidget.css index df30d79470..b5f2e1c0db 100644 --- a/src/vs/editor/contrib/codelens/codelensWidget.css +++ b/src/vs/editor/contrib/codelens/codelensWidget.css @@ -27,9 +27,8 @@ } .monaco-editor .codelens-decoration .codicon { - line-height: inherit; - font-size: 110%; - vertical-align: inherit; + vertical-align: middle; + color: currentColor !important; } .monaco-editor .codelens-decoration > a:hover .codicon::before { diff --git a/src/vs/editor/contrib/colorPicker/colorDetector.ts b/src/vs/editor/contrib/colorPicker/colorDetector.ts index d361de9ed7..e6d11697d6 100644 --- a/src/vs/editor/contrib/colorPicker/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/colorDetector.ts @@ -192,7 +192,7 @@ export class ColorDetector extends Disposable implements IEditorContribution { border: 'solid 0.1em #eee' } } - }); + }, undefined, this._editor); } newDecorationsTypes[key] = true; diff --git a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts index 4af6e98a26..e05d43aee2 100644 --- a/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/colorPickerWidget.ts @@ -163,7 +163,7 @@ class SaturationBox extends Disposable { this.onDidChangePosition(e.offsetX, e.offsetY); } - this.monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); + this.monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangePosition(event.posx - origin.left, event.posy - origin.top), () => null); const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => { this._onColorFlushed.fire(); @@ -270,7 +270,7 @@ abstract class Strip extends Disposable { this.onDidChangeTop(e.offsetY); } - monitor.startMonitoring(e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); + monitor.startMonitoring(e.target, e.buttons, standardMouseMoveMerger, event => this.onDidChangeTop(event.posy - origin.top), () => null); const mouseUpListener = dom.addDisposableGenericMouseUpListner(document, () => { this._onColorFlushed.fire(); diff --git a/src/vs/editor/contrib/comment/blockCommentCommand.ts b/src/vs/editor/contrib/comment/blockCommentCommand.ts index 36d5edeb48..dc6fe03a21 100644 --- a/src/vs/editor/contrib/comment/blockCommentCommand.ts +++ b/src/vs/editor/contrib/comment/blockCommentCommand.ts @@ -15,10 +15,12 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo export class BlockCommentCommand implements ICommand { private readonly _selection: Selection; + private readonly _insertSpace: boolean; private _usedEndToken: string | null; - constructor(selection: Selection) { + constructor(selection: Selection, insertSpace: boolean) { this._selection = selection; + this._insertSpace = insertSpace; this._usedEndToken = null; } @@ -53,7 +55,7 @@ export class BlockCommentCommand implements ICommand { return true; } - private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, model: ITextModel, builder: IEditOperationBuilder): void { + private _createOperationsForBlockComment(selection: Range, startToken: string, endToken: string, insertSpace: boolean, model: ITextModel, builder: IEditOperationBuilder): void { const startLineNumber = selection.startLineNumber; const startColumn = selection.startColumn; const endLineNumber = selection.endLineNumber; @@ -91,25 +93,21 @@ export class BlockCommentCommand implements ICommand { if (startTokenIndex !== -1 && endTokenIndex !== -1) { // Consider spaces as part of the comment tokens - if (startTokenIndex + startToken.length < startLineText.length) { - if (startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { - // Pretend the start token contains a trailing space - startToken = startToken + ' '; - } + if (insertSpace && startTokenIndex + startToken.length < startLineText.length && startLineText.charCodeAt(startTokenIndex + startToken.length) === CharCode.Space) { + // Pretend the start token contains a trailing space + startToken = startToken + ' '; } - if (endTokenIndex > 0) { - if (endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) { - // Pretend the end token contains a leading space - endToken = ' ' + endToken; - endTokenIndex -= 1; - } + if (insertSpace && endTokenIndex > 0 && endLineText.charCodeAt(endTokenIndex - 1) === CharCode.Space) { + // Pretend the end token contains a leading space + endToken = ' ' + endToken; + endTokenIndex -= 1; } ops = BlockCommentCommand._createRemoveBlockCommentOperations( new Range(startLineNumber, startTokenIndex + startToken.length + 1, endLineNumber, endTokenIndex + 1), startToken, endToken ); } else { - ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken); + ops = BlockCommentCommand._createAddBlockCommentOperations(selection, startToken, endToken, this._insertSpace); this._usedEndToken = ops.length === 1 ? endToken : null; } @@ -144,15 +142,15 @@ export class BlockCommentCommand implements ICommand { return res; } - public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string): IIdentifiedSingleEditOperation[] { + public static _createAddBlockCommentOperations(r: Range, startToken: string, endToken: string, insertSpace: boolean): IIdentifiedSingleEditOperation[] { let res: IIdentifiedSingleEditOperation[] = []; if (!Range.isEmpty(r)) { // Insert block comment start - res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + ' ')); + res.push(EditOperation.insert(new Position(r.startLineNumber, r.startColumn), startToken + (insertSpace ? ' ' : ''))); // Insert block comment end - res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), ' ' + endToken)); + res.push(EditOperation.insert(new Position(r.endLineNumber, r.endColumn), (insertSpace ? ' ' : '') + endToken)); } else { // Insert both continuously res.push(EditOperation.replace(new Range( @@ -176,7 +174,7 @@ export class BlockCommentCommand implements ICommand { return; } - this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, model, builder); + this._createOperationsForBlockComment(this._selection, config.blockCommentStartToken, config.blockCommentEndToken, this._insertSpace, model, builder); } public computeCursorState(model: ITextModel, helper: ICursorStateComputerData): Selection { diff --git a/src/vs/editor/contrib/comment/comment.ts b/src/vs/editor/contrib/comment/comment.ts index a11f92dd17..12d9c8fa1a 100644 --- a/src/vs/editor/contrib/comment/comment.ts +++ b/src/vs/editor/contrib/comment/comment.ts @@ -12,6 +12,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { BlockCommentCommand } from 'vs/editor/contrib/comment/blockCommentCommand'; import { LineCommentCommand, Type } from 'vs/editor/contrib/comment/lineCommentCommand'; // import { MenuId } from 'vs/platform/actions/common/actions'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; abstract class CommentLineAction extends EditorAction { @@ -28,13 +29,14 @@ abstract class CommentLineAction extends EditorAction { return; } - let model = editor.getModel(); - let commands: ICommand[] = []; - let selections = editor.getSelections(); - let opts = model.getOptions(); + const model = editor.getModel(); + const commands: ICommand[] = []; + const selections = editor.getSelections(); + const modelOptions = model.getOptions(); + const commentsOptions = editor.getOption(EditorOption.comments); for (const selection of selections) { - commands.push(new LineCommentCommand(selection, opts.tabSize, this._type)); + commands.push(new LineCommentCommand(selection, modelOptions.tabSize, this._type, commentsOptions.insertSpace)); } editor.pushUndoStop(); @@ -126,10 +128,11 @@ class BlockCommentAction extends EditorAction { return; } - let commands: ICommand[] = []; - let selections = editor.getSelections(); + const commentsOptions = editor.getOption(EditorOption.comments); + const commands: ICommand[] = []; + const selections = editor.getSelections(); for (const selection of selections) { - commands.push(new BlockCommentCommand(selection)); + commands.push(new BlockCommentCommand(selection, commentsOptions.insertSpace)); } editor.pushUndoStop(); diff --git a/src/vs/editor/contrib/comment/lineCommentCommand.ts b/src/vs/editor/contrib/comment/lineCommentCommand.ts index 78055731bc..6f3ea597b2 100644 --- a/src/vs/editor/contrib/comment/lineCommentCommand.ts +++ b/src/vs/editor/contrib/comment/lineCommentCommand.ts @@ -50,17 +50,19 @@ export const enum Type { export class LineCommentCommand implements ICommand { private readonly _selection: Selection; + private readonly _tabSize: number; + private readonly _type: Type; + private readonly _insertSpace: boolean; private _selectionId: string | null; private _deltaColumn: number; private _moveEndPositionDown: boolean; - private readonly _tabSize: number; - private readonly _type: Type; - constructor(selection: Selection, tabSize: number, type: Type) { + constructor(selection: Selection, tabSize: number, type: Type, insertSpace: boolean) { this._selection = selection; - this._selectionId = null; this._tabSize = tabSize; this._type = type; + this._insertSpace = insertSpace; + this._selectionId = null; this._deltaColumn = 0; this._moveEndPositionDown = false; } @@ -98,7 +100,7 @@ export class LineCommentCommand implements ICommand { * Analyze lines and decide which lines are relevant and what the toggle should do. * Also, build up several offsets and lengths useful in the generation of editor operations. */ - public static _analyzeLines(type: Type, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData { + public static _analyzeLines(type: Type, insertSpace: boolean, model: ISimpleModel, lines: ILinePreflightData[], startLineNumber: number): IPreflightData { let onlyWhitespaceLines = true; let shouldRemoveComments: boolean; @@ -145,7 +147,8 @@ export class LineCommentCommand implements ICommand { } } - if (shouldRemoveComments) { + if (shouldRemoveComments && insertSpace) { + // Remove a following space if present const commentStrEndOffset = lineContentStartOffset + lineData.commentStrLength; if (commentStrEndOffset < lineContent.length && lineContent.charCodeAt(commentStrEndOffset) === CharCode.Space) { lineData.commentStrLength += 1; @@ -173,7 +176,7 @@ export class LineCommentCommand implements ICommand { /** * Analyze all lines and decide exactly what to do => not supported | insert line comments | remove line comments */ - public static _gatherPreflightData(type: Type, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData { + public static _gatherPreflightData(type: Type, insertSpace: boolean, model: ITextModel, startLineNumber: number, endLineNumber: number): IPreflightData { const lines = LineCommentCommand._gatherPreflightCommentStrings(model, startLineNumber, endLineNumber); if (lines === null) { return { @@ -181,7 +184,7 @@ export class LineCommentCommand implements ICommand { }; } - return LineCommentCommand._analyzeLines(type, model, lines, startLineNumber); + return LineCommentCommand._analyzeLines(type, insertSpace, model, lines, startLineNumber); } /** @@ -195,7 +198,7 @@ export class LineCommentCommand implements ICommand { ops = LineCommentCommand._createRemoveLineCommentsOperations(data.lines, s.startLineNumber); } else { LineCommentCommand._normalizeInsertionPoint(model, data.lines, s.startLineNumber, this._tabSize); - ops = LineCommentCommand._createAddLineCommentsOperations(data.lines, s.startLineNumber); + ops = this._createAddLineCommentsOperations(data.lines, s.startLineNumber); } const cursorPosition = new Position(s.positionLineNumber, s.positionColumn); @@ -288,11 +291,17 @@ export class LineCommentCommand implements ICommand { firstNonWhitespaceIndex = lineContent.length; } ops = BlockCommentCommand._createAddBlockCommentOperations( - new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), startToken, endToken + new Range(s.startLineNumber, firstNonWhitespaceIndex + 1, s.startLineNumber, lineContent.length + 1), + startToken, + endToken, + this._insertSpace ); } else { ops = BlockCommentCommand._createAddBlockCommentOperations( - new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), startToken, endToken + new Range(s.startLineNumber, model.getLineFirstNonWhitespaceColumn(s.startLineNumber), s.endLineNumber, model.getLineMaxColumn(s.endLineNumber)), + startToken, + endToken, + this._insertSpace ); } @@ -317,7 +326,7 @@ export class LineCommentCommand implements ICommand { s = s.setEndPosition(s.endLineNumber - 1, model.getLineMaxColumn(s.endLineNumber - 1)); } - const data = LineCommentCommand._gatherPreflightData(this._type, model, s.startLineNumber, s.endLineNumber); + const data = LineCommentCommand._gatherPreflightData(this._type, this._insertSpace, model, s.startLineNumber, s.endLineNumber); if (data.supported) { return this._executeLineComments(model, builder, data, s); } @@ -365,8 +374,10 @@ export class LineCommentCommand implements ICommand { /** * Generate edit operations in the add line comment case */ - public static _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { + private _createAddLineCommentsOperations(lines: ILinePreflightData[], startLineNumber: number): IIdentifiedSingleEditOperation[] { let res: IIdentifiedSingleEditOperation[] = []; + const afterCommentStr = this._insertSpace ? ' ' : ''; + for (let i = 0, len = lines.length; i < len; i++) { const lineData = lines[i]; @@ -375,7 +386,7 @@ export class LineCommentCommand implements ICommand { continue; } - res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + ' ')); + res.push(EditOperation.insert(new Position(startLineNumber + i, lineData.commentStrOffset + 1), lineData.commentStr + afterCommentStr)); } return res; diff --git a/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts index d4efa01b12..40686125fd 100644 --- a/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/blockCommentCommand.test.ts @@ -9,7 +9,7 @@ import { CommentMode } from 'vs/editor/test/common/commentMode'; function testBlockCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, true), expectedLines, expectedSelection); mode.dispose(); } @@ -468,4 +468,45 @@ suite('Editor Contrib - Block Comment Command', () => { new Selection(1, 1, 1, 1) ); }); + + test('', () => { + }); + + test('insertSpace false', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + 'some text' + ], + new Selection(1, 1, 1, 5), + [ + '<0some0> text' + ], + new Selection(1, 3, 1, 7) + ); + }); + + test('insertSpace false does not remove space', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#', blockComment: ['<0', '0>'] }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new BlockCommentCommand(sel, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + '<0 some 0> text' + ], + new Selection(1, 4, 1, 8), + [ + ' some text' + ], + new Selection(1, 1, 1, 7) + ); + }); }); diff --git a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts index 0444170f8e..c24374cced 100644 --- a/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts +++ b/src/vs/editor/contrib/comment/test/lineCommentCommand.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { Selection } from 'vs/editor/common/core/selection'; import { TokenizationResult2 } from 'vs/editor/common/core/token'; @@ -18,13 +19,13 @@ suite('Editor Contrib - Line Comment Command', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); mode.dispose(); } function testAddLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '!@#', blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.ForceAdd, true), expectedLines, expectedSelection); mode.dispose(); } @@ -46,7 +47,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('case insensitive', function () { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: 'rem' }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); mode.dispose(); } @@ -85,7 +86,7 @@ suite('Editor Contrib - Line Comment Command', () => { test('_analyzeLines', () => { let r: IPreflightData; - r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([ '\t\t', ' ', ' c', @@ -116,7 +117,7 @@ suite('Editor Contrib - Line Comment Command', () => { assert.equal(r.lines[3].commentStrOffset, 2); - r = LineCommentCommand._analyzeLines(Type.Toggle, createSimpleModel([ + r = LineCommentCommand._analyzeLines(Type.Toggle, true, createSimpleModel([ '\t\t', ' rem ', ' !@# c', @@ -626,13 +627,51 @@ suite('Editor Contrib - Line Comment Command', () => { new Selection(2, 11, 1, 1) ); }); + + test('insertSpace false', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#' }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + 'some text' + ], + new Selection(1, 1, 1, 1), + [ + '!@#some text' + ], + new Selection(1, 4, 1, 4) + ); + }); + + test('insertSpace false does not remove space', () => { + function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { + let mode = new CommentMode({ lineComment: '!@#' }); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, false), expectedLines, expectedSelection); + mode.dispose(); + } + + testLineCommentCommand( + [ + '!@# some text' + ], + new Selection(1, 1, 1, 1), + [ + ' some text' + ], + new Selection(1, 1, 1, 1) + ); + }); }); suite('Editor Contrib - Line Comment As Block Comment', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: '', blockComment: ['(', ')'] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); mode.dispose(); } @@ -743,7 +782,7 @@ suite('Editor Contrib - Line Comment As Block Comment', () => { suite('Editor Contrib - Line Comment As Block Comment 2', () => { function testLineCommentCommand(lines: string[], selection: Selection, expectedLines: string[], expectedSelection: Selection): void { let mode = new CommentMode({ lineComment: null, blockComment: [''] }); - testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle), expectedLines, expectedSelection); + testCommand(lines, mode.getLanguageIdentifier(), selection, (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection); mode.dispose(); } @@ -984,7 +1023,7 @@ suite('Editor Contrib - Line Comment in mixed modes', () => { lines, outerMode.getLanguageIdentifier(), selection, - (sel) => new LineCommentCommand(sel, 4, Type.Toggle), + (sel) => new LineCommentCommand(sel, 4, Type.Toggle, true), expectedLines, expectedSelection, true diff --git a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts index 9919172166..c72ee3a5e2 100644 --- a/src/vs/editor/contrib/cursorUndo/cursorUndo.ts +++ b/src/vs/editor/contrib/cursorUndo/cursorUndo.ts @@ -9,7 +9,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction, ServicesAccessor, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -35,6 +35,14 @@ class CursorState { } } +class StackElement { + constructor( + public readonly cursorState: CursorState, + public readonly scrollTop: number, + public readonly scrollLeft: number + ) { } +} + export class CursorUndoRedoController extends Disposable implements IEditorContribution { public static readonly ID = 'editor.contrib.cursorUndoRedoController'; @@ -46,8 +54,8 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr private readonly _editor: ICodeEditor; private _isCursorUndoRedo: boolean; - private _undoStack: CursorState[]; - private _redoStack: CursorState[]; + private _undoStack: StackElement[]; + private _redoStack: StackElement[]; constructor(editor: ICodeEditor) { super(); @@ -76,9 +84,9 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } const prevState = new CursorState(e.oldSelections); - const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].equals(prevState)); + const isEqualToLastUndoStack = (this._undoStack.length > 0 && this._undoStack[this._undoStack.length - 1].cursorState.equals(prevState)); if (!isEqualToLastUndoStack) { - this._undoStack.push(prevState); + this._undoStack.push(new StackElement(prevState, editor.getScrollTop(), editor.getScrollLeft())); this._redoStack = []; if (this._undoStack.length > 50) { // keep the cursor undo stack bounded @@ -93,7 +101,7 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } - this._redoStack.push(new CursorState(this._editor.getSelections())); + this._redoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft())); this._applyState(this._undoStack.pop()!); } @@ -102,14 +110,17 @@ export class CursorUndoRedoController extends Disposable implements IEditorContr return; } - this._undoStack.push(new CursorState(this._editor.getSelections())); + this._undoStack.push(new StackElement(new CursorState(this._editor.getSelections()), this._editor.getScrollTop(), this._editor.getScrollLeft())); this._applyState(this._redoStack.pop()!); } - private _applyState(state: CursorState): void { + private _applyState(stackElement: StackElement): void { this._isCursorUndoRedo = true; - this._editor.setSelections(state.selections); - this._editor.revealRangeInCenterIfOutsideViewport(state.selections[0], ScrollType.Smooth); + this._editor.setSelections(stackElement.cursorState.selections); + this._editor.setScrollPosition({ + scrollTop: stackElement.scrollTop, + scrollLeft: stackElement.scrollLeft + }); this._isCursorUndoRedo = false; } } diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 8dc2a18262..30afe93381 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -191,7 +191,7 @@ class FormatOnPaste implements IEditorContribution { return; } - this._callOnModel.add(this.editor.onDidPaste(range => this._trigger(range))); + this._callOnModel.add(this.editor.onDidPaste(({ range }) => this._trigger(range))); } private _trigger(range: Range): void { diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 8786f8400e..7fe02c5bd1 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -27,6 +27,7 @@ import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Action } from 'vs/base/common/actions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { isEqual } from 'vs/base/common/resources'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; class MarkerModel { @@ -209,7 +210,8 @@ export class MarkerController implements IEditorContribution { @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IThemeService private readonly _themeService: IThemeService, @ICodeEditorService private readonly _editorService: ICodeEditorService, - @IKeybindingService private readonly _keybindingService: IKeybindingService + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IOpenerService private readonly _openerService: IOpenerService ) { this._editor = editor; this._widgetVisible = CONTEXT_MARKERS_NAVIGATION_VISIBLE.bindTo(this._contextKeyService); @@ -243,7 +245,7 @@ export class MarkerController implements IEditorContribution { new Action(NextMarkerAction.ID, NextMarkerAction.LABEL + (nextMarkerKeybinding ? ` (${nextMarkerKeybinding.getLabel()})` : ''), 'show-next-problem codicon-chevron-down', this._model.canNavigate(), async () => { if (this._model) { this._model.move(true, true); } }), new Action(PrevMarkerAction.ID, PrevMarkerAction.LABEL + (prevMarkerKeybinding ? ` (${prevMarkerKeybinding.getLabel()})` : ''), 'show-previous-problem codicon-chevron-up', this._model.canNavigate(), async () => { if (this._model) { this._model.move(false, true); } }) ]; - this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService); + this._widget = new MarkerNavigationWidget(this._editor, actions, this._themeService, this._openerService); this._widgetVisible.set(true); this._widget.onDidClose(() => this.closeMarkersNavigation(), this, this._disposeOnClose); diff --git a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts index b33bfe4720..832247fb8a 100644 --- a/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/gotoErrorWidget.ts @@ -26,6 +26,7 @@ import { IAction } from 'vs/base/common/actions'; import { IActionBarOptions, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; class MessageWidget { @@ -39,7 +40,14 @@ class MessageWidget { private readonly _relatedDiagnostics = new WeakMap(); private readonly _disposables: DisposableStore = new DisposableStore(); - constructor(parent: HTMLElement, editor: ICodeEditor, onRelatedInformation: (related: IRelatedInformation) => void) { + private _codeLink?: HTMLElement; + + constructor( + parent: HTMLElement, + editor: ICodeEditor, + onRelatedInformation: (related: IRelatedInformation) => void, + private readonly _openerService: IOpenerService, + ) { this._editor = editor; const domNode = document.createElement('div'); @@ -81,12 +89,20 @@ class MessageWidget { } update({ source, message, relatedInformation, code }: IMarker): void { + let sourceAndCodeLength = (source?.length || 0) + '()'.length; + if (code) { + if (typeof code === 'string') { + sourceAndCodeLength += code.length; + } else { + sourceAndCodeLength += code.value.length; + } + } const lines = message.split(/\r\n|\r|\n/g); this._lines = lines.length; this._longestLineLength = 0; for (const line of lines) { - this._longestLineLength = Math.max(line.length, this._longestLineLength); + this._longestLineLength = Math.max(line.length + sourceAndCodeLength, this._longestLineLength); } dom.clearNode(this._messageBlock); @@ -111,10 +127,25 @@ class MessageWidget { detailsElement.appendChild(sourceElement); } if (code) { - const codeElement = document.createElement('span'); - codeElement.innerText = `(${code})`; - dom.addClass(codeElement, 'code'); - detailsElement.appendChild(codeElement); + if (typeof code === 'string') { + const codeElement = document.createElement('span'); + codeElement.innerText = `(${code})`; + dom.addClass(codeElement, 'code'); + detailsElement.appendChild(codeElement); + } else { + this._codeLink = dom.$('a.code-link'); + this._codeLink.setAttribute('href', `${code.link.toString()}`); + + this._codeLink.onclick = (e) => { + this._openerService.open(code.link); + e.preventDefault(); + e.stopPropagation(); + }; + + const codeElement = dom.append(this._codeLink, dom.$('span')); + codeElement.innerText = code.value; + detailsElement.appendChild(this._codeLink); + } } } @@ -180,7 +211,8 @@ export class MarkerNavigationWidget extends PeekViewWidget { constructor( editor: ICodeEditor, private readonly actions: ReadonlyArray, - private readonly _themeService: IThemeService + private readonly _themeService: IThemeService, + private readonly _openerService: IOpenerService ) { super(editor, { showArrow: true, showFrame: true, isAccessible: true }); this._severity = MarkerSeverity.Warning; @@ -250,7 +282,7 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._container = document.createElement('div'); container.appendChild(this._container); - this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related)); + this._message = new MessageWidget(this._container, this.editor, related => this._onDidSelectRelatedInformation.fire(related), this._openerService); this._disposables.add(this._message); } @@ -329,8 +361,9 @@ export const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationI export const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: '#2D2D30', light: Color.white, hc: '#0C141F' }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.')); registerThemingParticipant((theme, collector) => { - const link = theme.getColor(textLinkForeground); - if (link) { - collector.addRule(`.monaco-editor .marker-widget a { color: ${link}; }`); + const linkFg = theme.getColor(textLinkForeground); + if (linkFg) { + collector.addRule(`.monaco-editor .marker-widget a { color: ${linkFg}; }`); + collector.addRule(`.monaco-editor .marker-widget a.code-link span:hover { color: ${linkFg}; }`); } }); diff --git a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css index 016634b514..0b68c48a91 100644 --- a/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css +++ b/src/vs/editor/contrib/gotoError/media/gotoErrorWidget.css @@ -45,10 +45,27 @@ } .monaco-editor .marker-widget .descriptioncontainer .message .source, -.monaco-editor .marker-widget .descriptioncontainer .message .code { +.monaco-editor .marker-widget .descriptioncontainer .message span.code { opacity: 0.6; } +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link { + opacity: 0.6; + color: inherit; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:before { + content: '('; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link:after { + content: ')'; +} +.monaco-editor .marker-widget .descriptioncontainer .message a.code-link > span { + text-decoration: underline; + /** Hack to force underline to show **/ + border-bottom: 1px solid transparent; + text-underline-position: under; +} + .monaco-editor .marker-widget .descriptioncontainer .filename { cursor: pointer; } diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index a5a30a336f..2a32c3b2b7 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -165,7 +165,7 @@ export abstract class ReferencesController implements IEditorContribution { let selection = this._model.nearestReference(uri, pos); if (selection) { return this._widget.setSelection(selection).then(() => { - if (this._widget && this._editor.getOption(EditorOption.peekWidgetFocusInlineEditor)) { + if (this._widget && this._editor.getOption(EditorOption.peekWidgetDefaultFocus) === 'editor') { this._widget.focusOnPreviewEditor(); } }); diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css index 4984d83707..0c3fe7ec48 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/editor/contrib/hover/hover.css @@ -110,3 +110,20 @@ font-size: inherit; vertical-align: middle; } + +.monaco-editor-hover .hover-contents a.code-link:before { + content: '('; +} +.monaco-editor-hover .hover-contents a.code-link:after { + content: ')'; +} + +.monaco-editor-hover .hover-contents a.code-link { + color: inherit; +} +.monaco-editor-hover .hover-contents a.code-link > span { + text-decoration: underline; + /** Hack to force underline to show **/ + border-bottom: 1px solid transparent; + text-underline-position: under; +} diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 0c4fda743e..04556904f3 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -22,7 +22,7 @@ import { getHover } from 'vs/editor/contrib/hover/getHover'; import { HoverOperation, HoverStartMode, IHoverComputer } from 'vs/editor/contrib/hover/hoverOperation'; import { ContentHoverWidget } from 'vs/editor/contrib/hover/hoverWidgets'; import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { coalesce, isNonEmptyArray, asArray } from 'vs/base/common/arrays'; import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; import { basename } from 'vs/base/common/resources'; @@ -39,6 +39,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IIdentifiedSingleEditOperation } from 'vs/editor/common/model'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Constants } from 'vs/base/common/uint'; +import { textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; const $ = dom.$; @@ -204,6 +205,8 @@ export class ModesContentHoverWidget extends ContentHoverWidget { private _shouldFocus: boolean; private _colorPicker: ColorPickerWidget | null; + private _codeLink?: HTMLElement; + private readonly renderDisposable = this._register(new MutableDisposable()); constructor( @@ -500,10 +503,35 @@ export class ModesContentHoverWidget extends ContentHoverWidget { messageElement.innerText = message; if (source || code) { - const detailsElement = dom.append(markerElement, $('span')); - detailsElement.style.opacity = '0.6'; - detailsElement.style.paddingLeft = '6px'; - detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`; + if (typeof code === 'string') { + const detailsElement = dom.append(markerElement, $('span')); + detailsElement.style.opacity = '0.6'; + detailsElement.style.paddingLeft = '6px'; + detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`; + } else { + if (code) { + const sourceAndCodeElement = $('span'); + if (source) { + const sourceElement = dom.append(sourceAndCodeElement, $('span')); + sourceElement.innerText = source; + } + this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link')); + this._codeLink.setAttribute('href', code.link.toString()); + + this._codeLink.onclick = (e) => { + this._openerService.open(code.link); + e.preventDefault(); + e.stopPropagation(); + }; + + const codeElement = dom.append(this._codeLink, $('span')); + codeElement.innerText = code.value; + + const detailsElement = dom.append(markerElement, sourceAndCodeElement); + detailsElement.style.opacity = '0.6'; + detailsElement.style.paddingLeft = '6px'; + } + } } if (isNonEmptyArray(relatedInformation)) { @@ -648,3 +676,11 @@ function hoverContentsEquals(first: HoverPart[], second: HoverPart[]): boolean { } return true; } + +registerThemingParticipant((theme, collector) => { + const linkFg = theme.getColor(textLinkForeground); + if (linkFg) { + collector.addRule(`.monaco-editor-hover .hover-contents a.code-link span:hover { color: ${linkFg}; }`); + } +}); + diff --git a/src/vs/editor/contrib/indentation/indentation.ts b/src/vs/editor/contrib/indentation/indentation.ts index 2be294e28b..644a171e51 100644 --- a/src/vs/editor/contrib/indentation/indentation.ts +++ b/src/vs/editor/contrib/indentation/indentation.ts @@ -451,7 +451,7 @@ export class AutoIndentOnPaste implements IEditorContribution { return; } - this.callOnModel.add(this.editor.onDidPaste((range: Range) => { + this.callOnModel.add(this.editor.onDidPaste(({ range }) => { this.trigger(range); })); } diff --git a/src/vs/editor/contrib/peekView/peekView.ts b/src/vs/editor/contrib/peekView/peekView.ts index 6030a54d9a..c210eb85b8 100644 --- a/src/vs/editor/contrib/peekView/peekView.ts +++ b/src/vs/editor/contrib/peekView/peekView.ts @@ -260,7 +260,7 @@ export abstract class PeekViewWidget extends ZoneWidget { export const peekViewTitleBackground = registerColor('peekViewTitle.background', { dark: '#1E1E1E', light: '#FFFFFF', hc: '#0C141F' }, nls.localize('peekViewTitleBackground', 'Background color of the peek view title area.')); export const peekViewTitleForeground = registerColor('peekViewTitleLabel.foreground', { dark: '#FFFFFF', light: '#333333', hc: '#FFFFFF' }, nls.localize('peekViewTitleForeground', 'Color of the peek view title.')); -export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#6c6c6cb3', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.')); +export const peekViewTitleInfoForeground = registerColor('peekViewTitleDescription.foreground', { dark: '#ccccccb3', light: '#616161e6', hc: '#FFFFFF99' }, nls.localize('peekViewTitleInfoForeground', 'Color of the peek view title info.')); export const peekViewBorder = registerColor('peekView.border', { dark: '#007acc', light: '#007acc', hc: contrastBorder }, nls.localize('peekViewBorder', 'Color of the peek view borders and arrow.')); export const peekViewResultsBackground = registerColor('peekViewResult.background', { dark: '#252526', light: '#F3F3F3', hc: Color.black }, nls.localize('peekViewResultsBackground', 'Background color of the peek view result list.')); diff --git a/src/vs/editor/contrib/rename/rename.ts b/src/vs/editor/contrib/rename/rename.ts index 8f47c0b36c..2d0897bf63 100644 --- a/src/vs/editor/contrib/rename/rename.ts +++ b/src/vs/editor/contrib/rename/rename.ts @@ -341,8 +341,8 @@ Registry.as(Extensions.Configuration).registerConfigurat id: 'editor', properties: { 'editor.rename.enablePreview': { - scope: ConfigurationScope.RESOURCE_LANGUAGE, - description: nls.localize('enablePreview', "Enabe/disable the ability to preview changes before renaming"), + scope: ConfigurationScope.LANGUAGE_OVERRIDABLE, + description: nls.localize('enablePreview', "Enable/disable the ability to preview changes before renaming"), default: true, type: 'boolean' } diff --git a/src/vs/editor/contrib/rename/renameInputField.css b/src/vs/editor/contrib/rename/renameInputField.css index b4dac18ae4..6ec7d6d10c 100644 --- a/src/vs/editor/contrib/rename/renameInputField.css +++ b/src/vs/editor/contrib/rename/renameInputField.css @@ -9,12 +9,12 @@ } .monaco-editor .rename-box.preview { - padding: 4px; + padding: 3px 3px 0 3px; } .monaco-editor .rename-box .rename-input { - padding: 4px; - width: calc(100% - 8px); + padding: 3px; + width: calc(100% - 6px); } .monaco-editor .rename-box .rename-label { diff --git a/src/vs/editor/contrib/suggest/completionModel.ts b/src/vs/editor/contrib/suggest/completionModel.ts index 8f756495de..1c69c86a2c 100644 --- a/src/vs/editor/contrib/suggest/completionModel.ts +++ b/src/vs/editor/contrib/suggest/completionModel.ts @@ -192,6 +192,7 @@ export class CompletionModel { } } + const textLabel = typeof item.completion.label === 'string' ? item.completion.label : item.completion.label.name; if (wordPos >= wordLen) { // the wordPos at which scoring starts is the whole word // and therefore the same rules as not having a word apply @@ -206,19 +207,19 @@ export class CompletionModel { if (!match) { continue; // NO match } - if (compareIgnoreCase(item.completion.filterText, item.completion.label) === 0) { + if (compareIgnoreCase(item.completion.filterText, textLabel) === 0) { // filterText and label are actually the same -> use good highlights item.score = match; } else { // re-run the scorer on the label in the hope of a result BUT use the rank // of the filterText-match - item.score = anyScore(word, wordLow, wordPos, item.completion.label, item.labelLow, 0); + item.score = anyScore(word, wordLow, wordPos, textLabel, item.labelLow, 0); item.score[0] = match[0]; // use score from filterText } } else { // by default match `word` against the `label` - let match = scoreFn(word, wordLow, wordPos, item.completion.label, item.labelLow, 0, false); + let match = scoreFn(word, wordLow, wordPos, textLabel, item.labelLow, 0, false); if (!match) { continue; // NO match } diff --git a/src/vs/editor/contrib/suggest/media/suggest.css b/src/vs/editor/contrib/suggest/media/suggest.css index ca5abb77d6..e5c418b354 100644 --- a/src/vs/editor/contrib/suggest/media/suggest.css +++ b/src/vs/editor/contrib/suggest/media/suggest.css @@ -99,20 +99,25 @@ overflow: hidden; text-overflow: ellipsis; white-space: pre; + justify-content: space-between; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right { + display: flex; } .monaco-editor .suggest-widget:not(.frozen) .monaco-highlighted-label .highlight { font-weight: bold; } -/** Icon styles **/ +/** ReadMore Icon styles **/ .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore::before { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore::before { color: inherit; - opacity: 0.6; + opacity: 1; font-size: 14px; - margin-left: 4px; cursor: pointer; } @@ -123,39 +128,110 @@ } .monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .codicon-close:hover, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore:hover { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore:hover { opacity: 1; } +/** signature, qualifier, type/details opacity **/ +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .signature-label, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label, +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label { + opacity: 0.7; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .qualifier-label { + margin-left: 4px; + opacity: 0.4; + font-size: 90%; + text-overflow: ellipsis; + overflow: hidden; + line-height: 17px; + align-self: center; +} + /** Type Info and icon next to the label in the focused completion item **/ -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label { margin-left: 0.8em; - flex: 1; - text-align: right; overflow: hidden; text-overflow: ellipsis; - opacity: 0.7; white-space: nowrap; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label > .monaco-tokenized-source { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label > .monaco-tokenized-source { display: inline; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .readMore, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .type-label, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .type-label, -.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row.focused > .contents > .main > .readMore { +/** Details: if using CompletionItem#details, show on focus **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .details-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label { display: none; } -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .readMore, -.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .type-label { +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right > .details-label { display: inline; } +/** Details: if using CompletionItemLabel#details, always show **/ + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right.always-show-details > .details-label, +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row.focused > .contents > .main > .right.always-show-details > .details-label { + display: inline; +} + +/** Ellipsis on hover **/ +.monaco-editor .suggest-widget:not(.docs-side) .monaco-list .monaco-list-row:hover > .contents > .main > .right.can-expand-details > .details-label { + width: calc(100% - 26px); +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left { + flex-shrink: 1; + overflow: hidden; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .left > .monaco-icon-label { + flex-shrink: 0; +} +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right { + overflow: hidden; + margin-left: 16px; + flex-shrink: 0; + max-width: 45%; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { + display: inline-block; + position: absolute; + right: 10px; + width: 18px; + height: 18px; + visibility: hidden; +} + +/** Do NOT display ReadMore when docs is side/below **/ +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore, +.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { + display: none !important; +} + +/** Do NOT display ReadMore when using plain CompletionItemLabel (details/documentation might not be resolved) **/ +.monaco-editor .suggest-widget .monaco-list .monaco-list-row > .contents > .main > .right:not(.always-show-details) > .readMore { + display: none; +} +/** Focused item can show ReadMore, but can't when docs is side/below **/ +.monaco-editor .suggest-widget .monaco-list .monaco-list-row.focused > .contents > .main > .right:not(.always-show-details) > .readMore { + display: inline-block; +} + +.monaco-editor .suggest-widget.docs-side .monaco-list .monaco-list-row > .contents > .main > .right > .readMore, +.monaco-editor .suggest-widget.docs-below .monaco-list .monaco-list-row > .contents > .main > .right > .readMore { + display: none; +} + +.monaco-editor .suggest-widget .monaco-list .monaco-list-row:hover > .contents > .main > .right > .readMore { + visibility: visible; +} + /** Styles for each row in the list **/ .monaco-editor .suggest-widget .monaco-list .monaco-list-row .monaco-icon-label.deprecated { diff --git a/src/vs/editor/contrib/suggest/suggest.ts b/src/vs/editor/contrib/suggest/suggest.ts index f89288f1c3..f4bb3fd65b 100644 --- a/src/vs/editor/contrib/suggest/suggest.ts +++ b/src/vs/editor/contrib/suggest/suggest.ts @@ -37,6 +37,9 @@ export class CompletionItem { readonly editInsertEnd: IPosition; readonly editReplaceEnd: IPosition; + // + readonly textLabel: string; + // perf readonly labelLow: string; readonly sortTextLow?: string; @@ -48,9 +51,6 @@ export class CompletionItem { idx?: number; word?: string; - // - readonly isDetailsResolved: boolean; - constructor( readonly position: IPosition, readonly completion: modes.CompletionItem, @@ -58,8 +58,13 @@ export class CompletionItem { readonly provider: modes.CompletionItemProvider, model: ITextModel ) { + this.textLabel = typeof completion.label === 'string' + ? completion.label + : completion.label.name; + // ensure lower-variants (perf) - this.labelLow = completion.label.toLowerCase(); + this.labelLow = this.textLabel.toLowerCase(); + this.sortTextLow = completion.sortText && completion.sortText.toLowerCase(); this.filterTextLow = completion.filterText && completion.filterText.toLowerCase(); @@ -74,8 +79,6 @@ export class CompletionItem { this.editReplaceEnd = new Position(completion.range.replace.endLineNumber, completion.range.replace.endColumn); } - this.isDetailsResolved = container.isDetailsResolved || typeof provider.resolveCompletionItem === 'undefined'; - // create the suggestion resolver const { resolveCompletionItem } = provider; if (typeof resolveCompletionItem !== 'function') { @@ -189,7 +192,7 @@ export function provideSuggestionItems( } // fill in default sortText when missing if (!suggestion.sortText) { - suggestion.sortText = suggestion.label; + suggestion.sortText = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; } allSuggestions.push(new CompletionItem(position, suggestion, container, provider, model)); diff --git a/src/vs/editor/contrib/suggest/suggestController.ts b/src/vs/editor/contrib/suggest/suggestController.ts index 82d0c851ac..9a706b214c 100644 --- a/src/vs/editor/contrib/suggest/suggestController.ts +++ b/src/vs/editor/contrib/suggest/suggestController.ts @@ -341,8 +341,9 @@ export class SuggestController implements IEditorContribution { } private _alertCompletionItem({ completion: suggestion }: CompletionItem): void { + const textLabel = typeof suggestion.label === 'string' ? suggestion.label : suggestion.label.name; if (isNonEmptyArray(suggestion.additionalTextEdits)) { - let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", suggestion.label, suggestion.additionalTextEdits.length); + let msg = nls.localize('arai.alert.snippet', "Accepting '{0}' made {1} additional edits", textLabel, suggestion.additionalTextEdits.length); alert(msg); } } diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 8a626d9eff..1779c97d7e 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -142,10 +142,10 @@ export class SuggestModel implements IDisposable { })); let editorIsComposing = false; - this._toDispose.add(this._editor.onCompositionStart(() => { + this._toDispose.add(this._editor.onDidCompositionStart(() => { editorIsComposing = true; })); - this._toDispose.add(this._editor.onCompositionEnd(() => { + this._toDispose.add(this._editor.onDidCompositionEnd(() => { // refilter when composition ends editorIsComposing = false; this._refilterCompletionItems(); @@ -233,7 +233,7 @@ export class SuggestModel implements IDisposable { }; this._triggerCharacterListener.add(this._editor.onDidType(checkTriggerCharacter)); - this._triggerCharacterListener.add(this._editor.onCompositionEnd(checkTriggerCharacter)); + this._triggerCharacterListener.add(this._editor.onDidCompositionEnd(checkTriggerCharacter)); } // --- trigger/retrigger/cancel suggest @@ -286,9 +286,7 @@ export class SuggestModel implements IDisposable { ) { // Early exit if nothing needs to be done! // Leave some form of early exit check here if you wish to continue being a cursor position change listener ;) - if (this._state !== State.Idle) { - this.cancel(); - } + this.cancel(); return; } diff --git a/src/vs/editor/contrib/suggest/suggestWidget.ts b/src/vs/editor/contrib/suggest/suggestWidget.ts index 09f1461740..ae5a36dc2d 100644 --- a/src/vs/editor/contrib/suggest/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/suggestWidget.ts @@ -46,11 +46,25 @@ const expandSuggestionDocsByDefault = false; interface ISuggestionTemplateData { root: HTMLElement; + + /** + * Flexbox + * < ------- left ------- > < -------- right -------- > + *