diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..3f53300caa4 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Eric Amodio Eric Amodio diff --git a/build/lib/electron.ts b/build/lib/electron.ts index 90a25f4cac8..86c7afcf312 100644 --- a/build/lib/electron.ts +++ b/build/lib/electron.ts @@ -59,7 +59,7 @@ export const config = { darwinBundleDocumentType(["asp", "aspx", "cshtml", "htm", "html", "jshtm", "jsp", "phtml", "shtml"], 'resources/darwin/html.icns'), darwinBundleDocumentType(["jade"], 'resources/darwin/jade.icns'), darwinBundleDocumentType(["jav", "java"], 'resources/darwin/java.icns'), - darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs"], 'resources/darwin/javascript.icns'), + darwinBundleDocumentType(["js", "jscsrc", "jshintrc", "mjs", "cjs"], 'resources/darwin/javascript.icns'), darwinBundleDocumentType(["json"], 'resources/darwin/json.icns'), darwinBundleDocumentType(["less"], 'resources/darwin/less.icns'), darwinBundleDocumentType(["markdown", "md", "mdoc", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn"], 'resources/darwin/markdown.icns'), diff --git a/build/win32/code.iss b/build/win32/code.iss index 0e2ed70d64a..54bde39080a 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -182,6 +182,13 @@ Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc"; Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\cpp.ico"; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.cjs\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.cjs"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,JavaScript}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs"; ValueType: string; ValueName: "AppUserModelID"; ValueData: "{#AppUserId}"; Flags: uninsdeletekey; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\resources\app\resources\win32\javascript.ico"; Tasks: associatewithfiles +Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.cjs\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\{#ExeBasename}.exe"" ""%1"""; Tasks: associatewithfiles + Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: none; ValueName: "{#RegValueName}"; Flags: deletevalue uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\.clj\OpenWithProgids"; ValueType: string; ValueName: "{#RegValueName}.clj"; ValueData: ""; Flags: uninsdeletevalue; Tasks: associatewithfiles Root: {#SoftwareClassesRootKey}; Subkey: "Software\Classes\{#RegValueName}.clj"; ValueType: string; ValueName: ""; ValueData: "{cm:SourceFile,Clojure}"; Flags: uninsdeletekey; Tasks: associatewithfiles diff --git a/extensions/javascript/package.json b/extensions/javascript/package.json index 8d315c45835..b7303520549 100644 --- a/extensions/javascript/package.json +++ b/extensions/javascript/package.json @@ -32,6 +32,7 @@ ".js", ".es6", ".mjs", + ".cjs", ".pac" ], "filenames": [ diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 0e2578d4bdd..477fff23131 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -957,7 +957,7 @@ ] }, "fenced_code_block_js": { - "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", + "begin": "(^|\\G)(\\s*)(`{3,}|~{3,})\\s*(?i:(js|jsx|javascript|es6|mjs|cjs|\\{\\.js.+?\\})((\\s+|:|\\{)[^`~]*)?$)", "name": "markup.fenced_code.block.markdown", "end": "(^|\\G)(\\2|\\s{0,3})(\\3)\\s*$", "beginCaptures": { @@ -2623,4 +2623,4 @@ "name": "markup.inline.raw.string.markdown" } } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/features/preview.ts b/extensions/markdown-language-features/src/features/preview.ts index 2fbf35d8b20..ad8edc1c2ad 100644 --- a/extensions/markdown-language-features/src/features/preview.ts +++ b/extensions/markdown-language-features/src/features/preview.ts @@ -490,6 +490,9 @@ export class DynamicMarkdownPreview extends Disposable { } private async onDidClickPreview(line: number): Promise { + // fix #82457, find currently opened but unfocused source tab + await vscode.commands.executeCommand('markdown.showSource'); + for (const visibleEditor of vscode.window.visibleTextEditors) { if (this.isPreviewOf(visibleEditor.document.uri)) { const editor = await vscode.window.showTextDocument(visibleEditor.document, visibleEditor.viewColumn); diff --git a/extensions/package.json b/extensions/package.json index 8d6065560c0..d1030bbe6dd 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.7.3-insiders.20191118" + "typescript": "3.7.3-insiders.20191123" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/search-result/README.md b/extensions/search-result/README.md new file mode 100644 index 00000000000..a28a54db1b8 --- /dev/null +++ b/extensions/search-result/README.md @@ -0,0 +1,3 @@ +# Language Features for Search Result files + +**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled. diff --git a/extensions/search-result/extension.webpack.config.js b/extensions/search-result/extension.webpack.config.js new file mode 100644 index 00000000000..de88398eca0 --- /dev/null +++ b/extensions/search-result/extension.webpack.config.js @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check + +'use strict'; + +const withDefaults = require('../shared.webpack.config'); + +module.exports = withDefaults({ + context: __dirname, + resolve: { + mainFields: ['module', 'main'] + }, + entry: { + extension: './src/extension.ts', + } +}); diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json new file mode 100644 index 00000000000..bc7c6cf951c --- /dev/null +++ b/extensions/search-result/package.json @@ -0,0 +1,64 @@ +{ + "name": "search-result", + "displayName": "%displayName%", + "description": "%description%", + "version": "1.0.0", + "publisher": "vscode", + "license": "MIT", + "engines": { + "vscode": "^1.39.0" + }, + "categories": [ + "Programming Languages" + ], + "main": "./out/extension.js", + "activationEvents": [ + "*" + ], + "scripts": { + "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:search-result ./tsconfig.json" + }, + "contributes": { + "commands": [ + { + "command": "searchResult.rerunSearch", + "title": "%searchResult.rerunSearch.title%", + "category": "Search Result", + "icon": { + "light": "./src/media/refresh-light.svg", + "dark": "./src/media/refresh-dark.svg" + } + } + ], + "menus": { + "editor/title": [ + { + "command": "searchResult.rerunSearch", + "when": "editorLangId == search-result", + "group": "navigation" + } + ] + }, + "languages": [ + { + "id": "search-result", + "extensions": [ + ".code-search" + ], + "aliases": [ + "Search Result" + ] + } + ], + "grammars": [ + { + "language": "search-result", + "scopeName": "text.searchResult", + "path": "./syntaxes/searchResult.tmLanguage.json" + } + ] + }, + "devDependencies": { + "vscode": "^1.1.36" + } +} diff --git a/extensions/search-result/package.nls.json b/extensions/search-result/package.nls.json new file mode 100644 index 00000000000..694f6b61d80 --- /dev/null +++ b/extensions/search-result/package.nls.json @@ -0,0 +1,5 @@ +{ + "displayName": "Search Result", + "description": "Provides syntax highlighting and language features for tabbed search results.", + "searchResult.rerunSearch.title": "Search Again" +} diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts new file mode 100644 index 00000000000..aa4090cc50e --- /dev/null +++ b/extensions/search-result/src/extension.ts @@ -0,0 +1,159 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as pathUtils from 'path'; + +const FILE_LINE_REGEX = /^(\S.*):$/; +const RESULT_LINE_REGEX = /^(\s+)(\d+):(\s+)(.*)$/; +const LANGUAGE_SELECTOR = { language: 'search-result' }; + +let cachedLastParse: { version: number, parse: ParsedSearchResults } | undefined; + +export function activate() { + + vscode.commands.registerCommand('searchResult.rerunSearch', () => vscode.commands.executeCommand('search.action.rerunEditorSearch')); + + vscode.languages.registerCompletionItemProvider(LANGUAGE_SELECTOR, { + provideCompletionItems(document: vscode.TextDocument, position: vscode.Position): vscode.CompletionItem[] { + const line = document.lineAt(position.line); + if (line.text.indexOf('# Flags:') === -1) { return []; } + + return ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch'] + .filter(flag => line.text.indexOf(flag) === -1) + .map(flag => ({ label: flag, insertText: flag + ' ' })); + } + }); + + vscode.languages.registerDefinitionProvider(LANGUAGE_SELECTOR, { + provideDefinition(document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken): vscode.DefinitionLink[] { + const lineResult = parseSearchResults(document, token)[position.line]; + if (!lineResult) { return []; } + if (lineResult.type === 'file') { + // TODO: The multi-match peek UX isnt very smooth. + // return lineResult.allLocations.length > 1 ? lineResult.allLocations : [lineResult.location]; + return []; + } + + return [lineResult.location]; + } + }); + + vscode.languages.registerDocumentLinkProvider(LANGUAGE_SELECTOR, { + async provideDocumentLinks(document: vscode.TextDocument, token: vscode.CancellationToken): Promise { + return parseSearchResults(document, token) + .filter(({ type }) => type === 'file') + .map(({ location }) => ({ range: location.originSelectionRange!, target: location.targetUri })); + } + }); + + vscode.window.onDidChangeActiveTextEditor(e => { + if (e?.document.languageId === 'search-result') { + // Clear the parse whenever we open a new editor. + // Conservative because things like the URI might remain constant even if the contents change, and re-parsing even large files is relatively fast. + cachedLastParse = undefined; + } + }); +} + + +function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | undefined { + if (pathUtils.isAbsolute(path)) { return vscode.Uri.file(path); } + if (path.indexOf('~/') === 0) { + return vscode.Uri.file(pathUtils.join(process.env.HOME!, path.slice(2))); + } + + + if (vscode.workspace.workspaceFolders) { + const multiRootFormattedPath = /^(.*) • (.*)$/.exec(path); + if (multiRootFormattedPath) { + const [, workspaceName, workspacePath] = multiRootFormattedPath; + const folder = vscode.workspace.workspaceFolders.filter(wf => wf.name === workspaceName)[0]; + if (folder) { + return vscode.Uri.file(pathUtils.join(folder.uri.fsPath, workspacePath)); + } + } + + else if (vscode.workspace.workspaceFolders.length === 1) { + return vscode.Uri.file(pathUtils.join(vscode.workspace.workspaceFolders[0].uri.fsPath, path)); + } else if (resultsUri.scheme !== 'untitled') { + // We're in a multi-root workspace, but the path is not multi-root formatted + // Possibly a saved search from a single root session. Try checking if the search result document's URI is in a current workspace folder. + const prefixMatch = vscode.workspace.workspaceFolders.filter(wf => resultsUri.toString().startsWith(wf.uri.toString()))[0]; + if (prefixMatch) { return vscode.Uri.file(pathUtils.join(prefixMatch.uri.fsPath, path)); } + } + } + + console.error(`Unable to resolve path ${path}`); + return undefined; +} + +type ParsedSearchResults = Array< + { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[] } | + { type: 'result', location: vscode.LocationLink } +>; + +function parseSearchResults(document: vscode.TextDocument, token: vscode.CancellationToken): ParsedSearchResults { + + if (cachedLastParse && cachedLastParse.version === document.version) { + return cachedLastParse.parse; + } + + const lines = document.getText().split(/\r?\n/); + const links: ParsedSearchResults = []; + + let currentTarget: vscode.Uri | undefined = undefined; + let currentTargetLocations: vscode.LocationLink[] | undefined = undefined; + + for (let i = 0; i < lines.length; i++) { + if (token.isCancellationRequested) { return []; } + const line = lines[i]; + + const fileLine = FILE_LINE_REGEX.exec(line); + if (fileLine) { + const [, path] = fileLine; + + currentTarget = relativePathToUri(path, document.uri); + if (!currentTarget) { continue; } + currentTargetLocations = []; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(0, 0, 0, 1), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, 0, i, line.length), + }; + + + links[i] = { type: 'file', location, allLocations: currentTargetLocations }; + } + + if (!currentTarget) { continue; } + + const resultLine = RESULT_LINE_REGEX.exec(line); + if (resultLine) { + const [, indentation, _lineNumber, resultIndentation] = resultLine; + const lineNumber = +_lineNumber - 1; + const resultStart = (indentation + _lineNumber + ':' + resultIndentation).length; + + const location: vscode.LocationLink = { + targetRange: new vscode.Range(Math.max(lineNumber - 3, 0), 0, lineNumber + 3, line.length), + targetSelectionRange: new vscode.Range(lineNumber, 0, lineNumber, line.length), + targetUri: currentTarget, + originSelectionRange: new vscode.Range(i, resultStart, i, line.length), + }; + + currentTargetLocations?.push(location); + + links[i] = { type: 'result', location }; + } + } + + cachedLastParse = { + version: document.version, + parse: links + }; + + return links; +} diff --git a/extensions/search-result/src/media/refresh-dark.svg b/extensions/search-result/src/media/refresh-dark.svg new file mode 100644 index 00000000000..e1f05aadeeb --- /dev/null +++ b/extensions/search-result/src/media/refresh-dark.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/search-result/src/media/refresh-light.svg b/extensions/search-result/src/media/refresh-light.svg new file mode 100644 index 00000000000..9b1d9108409 --- /dev/null +++ b/extensions/search-result/src/media/refresh-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/extensions/search-result/src/typings/refs.d.ts b/extensions/search-result/src/typings/refs.d.ts new file mode 100644 index 00000000000..c9849d48e08 --- /dev/null +++ b/extensions/search-result/src/typings/refs.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json new file mode 100644 index 00000000000..4de2a40ba40 --- /dev/null +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -0,0 +1,18 @@ +{ + "name": "Search Results", + "scopeName": "text.searchResult", + "patterns": [ + { + "match": "^# (Query|Flags|Including|Excluding): .*$", + "name": "comment" + }, + { + "match": "^\\S.*:$", + "name": "string path.searchResult" + }, + { + "match": "^ \\d+", + "name": "constant.numeric lineNumber.searchResult" + } + ] +} diff --git a/extensions/search-result/tsconfig.json b/extensions/search-result/tsconfig.json new file mode 100644 index 00000000000..16ed233f6e8 --- /dev/null +++ b/extensions/search-result/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../shared.tsconfig.json", + "compilerOptions": { + "outDir": "./out", + }, + "include": [ + "src/**/*" + ] +} diff --git a/extensions/search-result/yarn.lock b/extensions/search-result/yarn.lock new file mode 100644 index 00000000000..4ff83ab98bb --- /dev/null +++ b/extensions/search-result/yarn.lock @@ -0,0 +1,602 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +agent-base@4, agent-base@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" + integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== + dependencies: + es6-promisify "^5.0.0" + +ajv@^6.5.5: + version "6.10.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.2.tgz#d3cea04d6b017b2894ad69040fec8b623eb4bd52" + integrity sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +asn1@~0.2.3: + version "0.2.4" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" + integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== + dependencies: + safer-buffer "~2.1.0" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= + +aws-sign2@~0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" + integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= + +aws4@^1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" + integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ== + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + +bcrypt-pbkdf@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" + integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= + dependencies: + tweetnacl "^0.14.3" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= + +combined-stream@^1.0.6, combined-stream@~1.0.6: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@2.15.1: + version "2.15.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" + integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +core-util-is@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +debug@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== + dependencies: + ms "2.0.0" + +debug@^3.1.0: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= + dependencies: + es6-promise "^4.0.3" + +escape-string-regexp@1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +extend@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +extsprintf@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" + integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= + +extsprintf@^1.2.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" + integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= + +form-data@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" + integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.6" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +glob@7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + integrity sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.2: + version "7.1.6" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" + integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + +har-schema@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" + integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= + +har-validator@~5.1.0: + version "5.1.3" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" + integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== + dependencies: + ajv "^6.5.5" + har-schema "^2.0.0" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +he@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" + integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= + +http-proxy-agent@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" + integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== + dependencies: + agent-base "4" + debug "3.1.0" + +http-signature@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" + integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= + dependencies: + assert-plus "^1.0.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +https-proxy-agent@^2.2.1: + version "2.2.4" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" + integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== + dependencies: + agent-base "^4.3.0" + debug "^3.1.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + +jsprim@^1.2.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" + integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.2.3" + verror "1.10.0" + +mime-db@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.40.0.tgz#a65057e998db090f732a68f6c276d387d4126c32" + integrity sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA== + +mime-types@^2.1.12, mime-types@~2.1.19: + version "2.1.24" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.24.tgz#b6f8d0b3e951efb77dedeca194cff6d16f676f81" + integrity sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ== + dependencies: + mime-db "1.40.0" + +minimatch@3.0.4, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== + dependencies: + brace-expansion "^1.1.7" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= + +mkdirp@0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + dependencies: + minimist "0.0.8" + +mocha@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" + integrity sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ== + dependencies: + browser-stdout "1.3.1" + commander "2.15.1" + debug "3.1.0" + diff "3.5.0" + escape-string-regexp "1.0.5" + glob "7.1.2" + growl "1.10.5" + he "1.1.1" + minimatch "3.0.4" + mkdirp "0.5.1" + supports-color "5.4.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +oauth-sign@~0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" + integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +performance-now@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" + integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= + +psl@^1.1.24: + version "1.4.0" + resolved "https://registry.yarnpkg.com/psl/-/psl-1.4.0.tgz#5dd26156cdb69fa1fdb8ab1991667d3f80ced7c2" + integrity sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw== + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +qs@~6.5.2: + version "6.5.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" + integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== + +querystringify@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + +request@^2.88.0: + version "2.88.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef" + integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg== + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.8.0" + caseless "~0.12.0" + combined-stream "~1.0.6" + extend "~3.0.2" + forever-agent "~0.6.1" + form-data "~2.3.2" + har-validator "~5.1.0" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.19" + oauth-sign "~0.9.0" + performance-now "^2.1.0" + qs "~6.5.2" + safe-buffer "^5.1.2" + tough-cookie "~2.4.3" + tunnel-agent "^0.6.0" + uuid "^3.3.2" + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + +safe-buffer@^5.0.1, safe-buffer@^5.1.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" + integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +source-map-support@^0.5.0: + version "0.5.16" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" + integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +sshpk@^1.7.0: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + +supports-color@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + integrity sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w== + dependencies: + has-flag "^3.0.0" + +tough-cookie@~2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781" + integrity sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ== + dependencies: + psl "^1.1.24" + punycode "^1.4.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= + dependencies: + safe-buffer "^5.0.1" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= + +uri-js@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + integrity sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ== + dependencies: + punycode "^2.1.0" + +url-parse@^1.4.4: + version "1.4.7" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278" + integrity sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg== + dependencies: + querystringify "^2.1.1" + requires-port "^1.0.0" + +uuid@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866" + integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ== + +verror@1.10.0: + version "1.10.0" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" + integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= + dependencies: + assert-plus "^1.0.0" + core-util-is "1.0.2" + extsprintf "^1.2.0" + +vscode-test@^0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/vscode-test/-/vscode-test-0.4.3.tgz#461ebf25fc4bc93d77d982aed556658a2e2b90b8" + integrity sha512-EkMGqBSefZH2MgW65nY05rdRSko15uvzq4VAPM5jVmwYuFQKE7eikKXNJDRxL+OITXHB6pI+a3XqqD32Y3KC5w== + dependencies: + http-proxy-agent "^2.1.0" + https-proxy-agent "^2.2.1" + +vscode@^1.1.36: + version "1.1.36" + resolved "https://registry.yarnpkg.com/vscode/-/vscode-1.1.36.tgz#5e1a0d1bf4977d0c7bc5159a9a13d5b104d4b1b6" + integrity sha512-cGFh9jmGLcTapCpPCKvn8aG/j9zVQ+0x5hzYJq5h5YyUXVGa1iamOaB2M2PZXoumQPES4qeAP1FwkI0b6tL4bQ== + dependencies: + glob "^7.1.2" + mocha "^5.2.0" + request "^2.88.0" + semver "^5.4.1" + source-map-support "^0.5.0" + url-parse "^1.4.4" + vscode-test "^0.4.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/extensions/typescript-basics/build/update-grammars.js b/extensions/typescript-basics/build/update-grammars.js index 527fa2c1d43..1839ad63674 100644 --- a/extensions/typescript-basics/build/update-grammars.js +++ b/extensions/typescript-basics/build/update-grammars.js @@ -33,7 +33,7 @@ function patchGrammar(grammar) { function adaptToJavaScript(grammar, replacementScope) { grammar.name = 'JavaScript (with React support)'; - grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs']; + grammar.fileTypes = ['.js', '.jsx', '.es6', '.mjs', '.cjs']; grammar.scopeName = `source${replacementScope}`; var fixScopeNames = function (rule) { diff --git a/extensions/typescript-language-features/src/features/completions.ts b/extensions/typescript-language-features/src/features/completions.ts index dde8a7f2f68..da13f97f3db 100644 --- a/extensions/typescript-language-features/src/features/completions.ts +++ b/extensions/typescript-language-features/src/features/completions.ts @@ -150,11 +150,11 @@ class MyCompletionItem extends vscode.CompletionItem { case PConst.Kind.keyword: return vscode.CompletionItemKind.Keyword; case PConst.Kind.const: - return vscode.CompletionItemKind.Constant; case PConst.Kind.let: case PConst.Kind.variable: case PConst.Kind.localVariable: case PConst.Kind.alias: + case PConst.Kind.parameter: return vscode.CompletionItemKind.Variable; case PConst.Kind.memberVariable: case PConst.Kind.memberGetAccessor: @@ -169,6 +169,8 @@ class MyCompletionItem extends vscode.CompletionItem { return vscode.CompletionItemKind.Method; case PConst.Kind.enum: return vscode.CompletionItemKind.Enum; + case PConst.Kind.enumMember: + return vscode.CompletionItemKind.EnumMember; case PConst.Kind.module: case PConst.Kind.externalModuleName: return vscode.CompletionItemKind.Module; diff --git a/extensions/typescript-language-features/src/protocol.const.ts b/extensions/typescript-language-features/src/protocol.const.ts index efded538f77..7a6a7d70235 100644 --- a/extensions/typescript-language-features/src/protocol.const.ts +++ b/extensions/typescript-language-features/src/protocol.const.ts @@ -12,6 +12,7 @@ export class Kind { public static readonly constructSignature = 'construct'; public static readonly directory = 'directory'; public static readonly enum = 'enum'; + public static readonly enumMember = 'enum member'; public static readonly externalModuleName = 'external module name'; public static readonly function = 'function'; public static readonly indexSignature = 'index'; @@ -69,4 +70,4 @@ export class DisplayPartKind { public static readonly propertyName = 'propertyName'; public static readonly punctuation = 'punctuation'; public static readonly text = 'text'; -} \ No newline at end of file +} diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index c77c86f7c9a..f45cb960dca 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -575,11 +575,15 @@ export default class TypeScriptServiceClient extends Disposable implements IType return undefined; } - const result = resource.fsPath; + let result = resource.fsPath; if (!result) { return undefined; } + if (resource.scheme === fileSchemes.file) { + result = path.normalize(result); + } + // Both \ and / must be escaped in regular expressions return result.replace(new RegExp('\\' + this.pathSeparator, 'g'), '/'); } diff --git a/extensions/yarn.lock b/extensions/yarn.lock index f1a8a66e077..f2e7ae95079 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.7.3-insiders.20191118: - version "3.7.3-insiders.20191118" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3-insiders.20191118.tgz#86d3b1de1859d70b0a081b708a223f8f3c6a4d46" - integrity sha512-r06UsJwJzLwgRkf3Or2T5tBnUmp8RD5gOHkC6ax9mLHu5r7voo1WAUpvG09R92GvdEQsgZXxOUhdEJWEoc/5sA== +typescript@3.7.3-insiders.20191123: + version "3.7.3-insiders.20191123" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3-insiders.20191123.tgz#f3bef33a2a3f6e02f11bcc0c20b6f0de526f17fd" + integrity sha512-b+tLx4D0a6SeuaCa7iehdgkRKHsS67FkioQWw+0REjVNOYZ+AqJ0NjlnomK1hEUvSzSNrH9Du+m+Yiv7JlVpSg== diff --git a/package.json b/package.json index 1a035661007..5ee09edfd02 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.41.0", - "distro": "2ec0dcb1b859bdfa9da42cb5243114591fee61af", + "distro": "30dcc7436405dfa68898d0f3843551c589fc9008", "author": { "name": "Microsoft Corporation" }, @@ -53,10 +53,10 @@ "vscode-ripgrep": "^1.5.7", "vscode-sqlite3": "4.0.9", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9", + "xterm-addon-webgl": "0.4.0-beta.11", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/package.json b/remote/package.json index 9823a4c0338..216a7c37015 100644 --- a/remote/package.json +++ b/remote/package.json @@ -20,10 +20,10 @@ "vscode-proxy-agent": "^0.5.2", "vscode-ripgrep": "^1.5.7", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9", + "xterm-addon-webgl": "0.4.0-beta.11", "yauzl": "^2.9.2", "yazl": "^2.4.3" }, diff --git a/remote/web/package.json b/remote/web/package.json index d2dd3026cb2..2a0519d282d 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -5,9 +5,9 @@ "onigasm-umd": "2.2.5", "semver-umd": "^5.5.3", "vscode-textmate": "4.4.0", - "xterm": "4.3.0-beta24", + "xterm": "4.3.0-beta.28", "xterm-addon-search": "0.4.0-beta4", "xterm-addon-web-links": "0.2.1", - "xterm-addon-webgl": "0.4.0-beta9" + "xterm-addon-webgl": "0.4.0-beta.11" } } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 6e26754170b..1e2d3f0de21 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -41,12 +41,12 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== diff --git a/remote/yarn.lock b/remote/yarn.lock index bc473844c5f..6b661b17e47 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -428,15 +428,15 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== yauzl@^2.9.2: version "2.10.0" diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index 1bf13b4ce11..c4b75d53cde 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -113,55 +113,28 @@ } .monaco-list-type-filter > .controls > * { + border: none; box-sizing: border-box; + -webkit-appearance: none; + -moz-appearance: none; + background: none; width: 16px; height: 16px; - margin: 0 0 0 2px; flex-shrink: 0; + margin: 0; + padding: 0; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; +} + +.monaco-list-type-filter > .controls > .filter:checked::before { + content: "\eb83" !important; /* codicon-list-filter */ } .monaco-list-type-filter > .controls > .filter { - -webkit-appearance: none; - -moz-appearance: none; - width: 16px; - height: 16px; - background: url("media/no-filter-light.svg"); - background-position: 50% 50%; - cursor: pointer; -} - -.monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-light.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-dark.svg"); -} - -.vs-dark .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter { - background-image: url("media/no-filter-hc.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .filter:checked { - background-image: url("media/filter-hc.svg"); -} - -.monaco-list-type-filter > .controls > .clear { - border: none; - background: url("media/close-light.svg"); - cursor: pointer; -} - -.vs-dark .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-dark.svg"); -} - -.hc-black .monaco-list-type-filter > .controls > .clear { - background-image: url("media/close-hc.svg"); + margin-left: 4px; } .monaco-list-type-filter-message { diff --git a/src/vs/base/browser/ui/list/media/close-dark.svg b/src/vs/base/browser/ui/list/media/close-dark.svg deleted file mode 100644 index 7305a8f099a..00000000000 --- a/src/vs/base/browser/ui/list/media/close-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-hc.svg b/src/vs/base/browser/ui/list/media/close-hc.svg deleted file mode 100644 index 7305a8f099a..00000000000 --- a/src/vs/base/browser/ui/list/media/close-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/close-light.svg b/src/vs/base/browser/ui/list/media/close-light.svg deleted file mode 100644 index ecddcd665b5..00000000000 --- a/src/vs/base/browser/ui/list/media/close-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-dark.svg b/src/vs/base/browser/ui/list/media/filter-dark.svg deleted file mode 100644 index 46c35f4374d..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-hc.svg b/src/vs/base/browser/ui/list/media/filter-hc.svg deleted file mode 100644 index d7b6bdd3923..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/filter-light.svg b/src/vs/base/browser/ui/list/media/filter-light.svg deleted file mode 100644 index 2550d80cb70..00000000000 --- a/src/vs/base/browser/ui/list/media/filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-dark.svg b/src/vs/base/browser/ui/list/media/no-filter-dark.svg deleted file mode 100644 index 6fc07d81a55..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-hc.svg b/src/vs/base/browser/ui/list/media/no-filter-hc.svg deleted file mode 100644 index 6fc07d81a55..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/list/media/no-filter-light.svg b/src/vs/base/browser/ui/list/media/no-filter-light.svg deleted file mode 100644 index 3608b15d29e..00000000000 --- a/src/vs/base/browser/ui/list/media/no-filter-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 52e88cc5c9b..af6d9965870 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -634,14 +634,14 @@ class TypeFilterController implements IDisposable { const controls = append(this.domNode, $('.controls')); this._filterOnType = !!tree.options.filterOnType; - this.filterOnTypeDomNode = append(controls, $('input.filter')); + this.filterOnTypeDomNode = append(controls, $('input.filter.codicon.codicon-list-selection')); this.filterOnTypeDomNode.type = 'checkbox'; this.filterOnTypeDomNode.checked = this._filterOnType; this.filterOnTypeDomNode.tabIndex = -1; this.updateFilterOnTypeTitle(); domEvent(this.filterOnTypeDomNode, 'input')(this.onDidChangeFilterOnType, this, this.disposables); - this.clearDomNode = append(controls, $('button.clear')); + this.clearDomNode = append(controls, $('button.clear.codicon.codicon-close')); this.clearDomNode.tabIndex = -1; this.clearDomNode.title = localize('clear', "Clear"); diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index 8d1cbd5b996..651c5fa22a4 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -206,43 +206,6 @@ export class MutableDisposable implements IDisposable { } } -/** - * Wrapper class that stores a disposable that is not currently "owned" by anyone. - * - * Example use cases: - * - * - Express that a function/method will take ownership of a disposable parameter. - * - Express that a function returns a disposable that the caller must explicitly take ownership of. - */ -export class UnownedDisposable extends Disposable { - private _hasBeenAcquired = false; - private _value?: T; - - public constructor(value: T) { - super(); - this._value = value; - } - - public acquire(): T { - if (this._hasBeenAcquired) { - throw new Error('This disposable has already been acquired'); - } - this._hasBeenAcquired = true; - const value = this._value!; - this._value = undefined; - return value; - } - - public dispose() { - super.dispose(); - if (!this._hasBeenAcquired) { - this._hasBeenAcquired = true; - this._value!.dispose(); - this._value = undefined; - } - } -} - export interface IReference extends IDisposable { readonly object: T; } diff --git a/src/vs/base/test/node/glob.test.ts b/src/vs/base/test/node/glob.test.ts index f2610a7ee5f..f7e8dfd1356 100644 --- a/src/vs/base/test/node/glob.test.ts +++ b/src/vs/base/test/node/glob.test.ts @@ -14,12 +14,12 @@ suite('Glob', () => { // let patterns = [ // '{**/*.cs,**/*.json,**/*.csproj,**/*.sln}', // '{**/*.cs,**/*.csproj,**/*.sln}', - // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '**/*.go', // '{**/*.ps,**/*.ps1}', // '{**/*.c,**/*.cpp,**/*.h}', // '{**/*.fsx,**/*.fsi,**/*.fs,**/*.ml,**/*.mli}', - // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs}', + // '{**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs}', // '{**/*.ts,**/*.tsx}', // '{**/*.php}', // '{**/*.php}', @@ -1015,4 +1015,4 @@ suite('Glob', () => { assertNoGlobMatch(p, '/DNXConsoleApp/foo/Program.cs'); } }); -}); \ No newline at end of file +}); diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index ead9bd31bfb..a33a4ec776f 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -34,12 +34,17 @@ class CommandOpener implements IOpener { // execute as command let args: any = []; try { - args = parse(target.query); - if (!Array.isArray(args)) { - args = [args]; + args = parse(decodeURIComponent(target.query)); + } catch { + // ignore and retry + try { + args = parse(target.query); + } catch { + // ignore error } - } catch (e) { - // ignore error + } + if (!Array.isArray(args)) { + args = [args]; } await this._commandService.executeCommand(target.path, ...args); return true; diff --git a/src/vs/editor/contrib/gotoError/gotoError.ts b/src/vs/editor/contrib/gotoError/gotoError.ts index 10cff5b6510..f9ac8b712e8 100644 --- a/src/vs/editor/contrib/gotoError/gotoError.ts +++ b/src/vs/editor/contrib/gotoError/gotoError.ts @@ -43,7 +43,7 @@ class MarkerModel { this._markers = []; this._nextIdx = -1; this._ignoreSelectionChange = false; - this._onCurrentMarkerChanged = new Emitter(); + this._onCurrentMarkerChanged = new Emitter(); this._onMarkerSetChanged = new Emitter(); this.setMarkers(markers); diff --git a/src/vs/editor/contrib/hover/hover.css b/src/vs/editor/contrib/hover/hover.css index 3e34dec5e19..684a3f64e5c 100644 --- a/src/vs/editor/contrib/hover/hover.css +++ b/src/vs/editor/contrib/hover/hover.css @@ -29,6 +29,10 @@ word-wrap: break-word; } +.monaco-editor-hover .markdown-hover > .hover-contents:not(.code-hover-contents) hr { + min-width: 100vw; +} + .monaco-editor-hover p, .monaco-editor-hover ul { margin: 8px 0; diff --git a/src/vs/editor/contrib/multicursor/multicursor.ts b/src/vs/editor/contrib/multicursor/multicursor.ts index a5341c5896b..b23cc0c09f8 100644 --- a/src/vs/editor/contrib/multicursor/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/multicursor.ts @@ -631,6 +631,12 @@ export class MultiCursorSelectionController extends Disposable implements IEdito this._setSelections(matches.map(m => new Selection(m.range.startLineNumber, m.range.startColumn, m.range.endLineNumber, m.range.endColumn))); } } + + public selectAllUsingSelections(selections: Selection[]): void { + if (selections.length > 0) { + this._setSelections(selections); + } + } } export abstract class MultiCursorSelectionControllerAction extends EditorAction { diff --git a/src/vs/platform/auth/electron-browser/authServer.ts b/src/vs/platform/auth/electron-browser/authServer.ts index b2f227114fd..dae70743ad9 100644 --- a/src/vs/platform/auth/electron-browser/authServer.ts +++ b/src/vs/platform/auth/electron-browser/authServer.ts @@ -8,6 +8,7 @@ import * as url from 'url'; import * as fs from 'fs'; import * as net from 'net'; import { getPathFromAmdModule } from 'vs/base/common/amd'; +import { assertIsDefined } from 'vs/base/common/types'; interface Deferred { resolve: (result: T | Promise) => void; @@ -25,7 +26,7 @@ export function createTerminateServer(server: http.Server) { }); }); return async () => { - const result = new Promise(resolve => server.close(resolve)); + const result = new Promise(resolve => server.close(resolve)); for (const id in sockets) { sockets[id].destroy(); } @@ -50,7 +51,7 @@ export async function startServer(server: http.Server): Promise { if (typeof address === 'string') { resolve(address); } else { - resolve(address.port.toString()); + resolve(assertIsDefined(address).port.toString()); } }); diff --git a/src/vs/platform/request/node/proxy.ts b/src/vs/platform/request/node/proxy.ts index 30b5bc29a9a..d71addc2248 100644 --- a/src/vs/platform/request/node/proxy.ts +++ b/src/vs/platform/request/node/proxy.ts @@ -41,10 +41,10 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = { host: proxyEndpoint.hostname || '', port: proxyEndpoint.port || (proxyEndpoint.protocol === 'https' ? '443' : '80'), auth: proxyEndpoint.auth, - rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true + rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true, }; return requestURL.protocol === 'http:' - ? new (await import('http-proxy-agent'))(opts) + ? new (await import('http-proxy-agent'))(opts as any as Url) : new (await import('https-proxy-agent'))(opts); } diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 4c3093b823d..864db693fd1 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -299,6 +299,14 @@ export const editorFindMatchBorder = registerColor('editor.findMatchBorder', { l export const editorFindMatchHighlightBorder = registerColor('editor.findMatchHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('findMatchHighlightBorder', "Border color of the other search matches.")); export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true); +/** + * Search Editor query match colors. + * + * Distinct from normal editor find match to allow for better differentiation + */ +export const searchEditorFindMatch = registerColor('searchEditor.findMatchBackground', { light: transparent(editorFindMatchHighlight, 0.5), dark: transparent(editorFindMatchHighlight, 0.5), hc: editorFindMatchHighlight }, nls.localize('searchEditor.queryMatch', "Color of the Search Editor query matches.")); +export const searchEditorFindMatchBorder = registerColor('searchEditor.findMatchBorder', { light: transparent(editorFindMatchHighlightBorder, 0.5), dark: transparent(editorFindMatchHighlightBorder, 0.5), hc: editorFindMatchHighlightBorder }, nls.localize('searchEditor.editorFindMatchBorder', "Border color of the Search Editor query matches.")); + /** * Editor hover */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 6dddd463989..d96918ef4f5 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1250,6 +1250,13 @@ declare module 'vscode' { */ save(): Thenable; + /** + * + * @param resource Resource being saved. + * @param targetResource Location to save to. + */ + saveAs(resource: Uri, targetResource: Uri): Thenable; + /** * Event triggered by extensions to signal to VS Code that an edit has occurred. * diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index 14c9efb3e55..4dd8f594288 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; import { startsWith } from 'vs/base/common/strings'; @@ -273,17 +273,23 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma const model = await this._customEditorService.models.loadOrCreate(webviewInput.getResource(), webviewInput.viewType); - model.onUndo(edits => { this._proxy.$undoEdits(handle, edits); }); - model.onRedo(edits => { this._proxy.$redoEdits(handle, edits); }); + model.onUndo(edits => { this._proxy.$undoEdits(handle, edits.map(x => x.data)); }); + model.onApplyEdit(edits => { + const editsToApply = edits.filter(x => x.source !== webviewInput).map(x => x.data); + if (editsToApply.length) { + this._proxy.$applyEdits(handle, editsToApply); + } + }); model.onWillSave(e => { e.waitUntil(this._proxy.$onSave(handle)); }); + model.onWillSaveAs(e => { e.waitUntil(this._proxy.$onSaveAs(handle, e.resource.toJSON(), e.targetResource.toJSON())); }); - webviewInput.onDispose(() => { + webviewInput.onDisposeWebview(() => { this._customEditorService.models.disposeModel(model); }); try { await this._proxy.$resolveWebviewEditor( - webviewInput.getResource(), + { resource: webviewInput.getResource(), edits: model.currentEdits }, handle, viewType, webviewInput.getTitle(), @@ -293,6 +299,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma } catch (error) { onUnexpectedError(error); webviewInput.webview.html = MainThreadWebviews.getDeserializationFailedContents(viewType); + return; } } })); @@ -308,7 +315,7 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma this._editorProviders.delete(viewType); } - public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: string): void { + public $onEdit(handle: extHostProtocol.WebviewPanelHandle, editData: any): void { const webview = this.getWebviewInput(handle); if (!(webview instanceof CustomFileEditorInput)) { throw new Error('Webview is not a webview editor'); @@ -319,18 +326,24 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma throw new Error('Could not find model for webview editor'); } - model.makeEdit(editData); + model.makeEdit({ source: webview, data: editData }); } private hookupWebviewEventDelegate(handle: extHostProtocol.WebviewPanelHandle, input: WebviewInput) { - input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri)); - input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message)); - input.onDispose(() => { + const disposables = new DisposableStore(); + + disposables.add(input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri))); + disposables.add(input.webview.onMessage((message: any) => { this._proxy.$onMessage(handle, message); })); + disposables.add(input.onDisposeWebview(() => { this._proxy.$onDidDisposeWebviewPanel(handle).finally(() => { this._webviewInputs.delete(handle); }); + })); + disposables.add(input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value))); + + input.onDispose(() => { + disposables.dispose(); }); - input.webview.onMissingCsp((extension: ExtensionIdentifier) => this._proxy.$onMissingCsp(handle, extension.value)); } private updateWebviewViewStates() { diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 1ad44491aa1..5ab449439fa 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -155,11 +155,11 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { if (!isPromiseCanceledError(err)) { return Promise.reject(err); } - return undefined; + return null; }); } - $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise { + $startTextSearch(pattern: IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise { const workspace = this._contextService.getWorkspace(); const folders = workspace.folders.map(folder => folder.uri); @@ -198,14 +198,14 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._searchService.fileSearch(query, token).then( result => { - return result.limitHit; + return !!result.limitHit; }, err => { if (!isPromiseCanceledError(err)) { return Promise.reject(err); } - return undefined; + return false; }); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5a735828101..cd622a40305 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -574,7 +574,7 @@ export interface MainThreadWebviewsShape extends IDisposable { $registerEditorProvider(extension: WebviewExtensionDescription, viewType: string, options: modes.IWebviewPanelOptions): void; $unregisterEditorProvider(viewType: string): void; - $onEdit(handle: WebviewPanelHandle, editJson: string): void; + $onEdit(handle: WebviewPanelHandle, editJson: any): void; } export interface WebviewPanelViewStateData { @@ -590,11 +590,15 @@ export interface ExtHostWebviewsShape { $onMissingCsp(handle: WebviewPanelHandle, extensionId: string): void; $onDidChangeWebviewPanelViewStates(newState: WebviewPanelViewStateData): void; $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; + $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; - $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $resolveWebviewEditor(input: { resource: UriComponents, edits: readonly any[] }, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; - $redoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; + $applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void; + $onSave(handle: WebviewPanelHandle): Promise; + $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise; } export interface MainThreadUrlsShape extends IDisposable { @@ -614,7 +618,7 @@ export interface ITextSearchComplete { export interface MainThreadWorkspaceShape extends IDisposable { $startFileSearch(includePattern: string | null, includeFolder: UriComponents | null, excludePatternOrDisregardExcludes: string | false | null, maxResults: number | null, token: CancellationToken): Promise; - $startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; + $startTextSearch(query: search.IPatternInfo, options: ITextQueryBuilderOptions, requestId: number, token: CancellationToken): Promise; $checkExists(folders: UriComponents[], includes: string[], token: CancellationToken): Promise; $saveAll(includeUntitled?: boolean): Promise; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents, name?: string; }[]): Promise; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 67e9ba8669e..f671ebd74bd 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -86,7 +86,7 @@ const newCommands: ApiCommand[] = [ new ApiCommand( 'vscode.prepareCallHierarchy', '_executePrepareCallHierarchy', 'Prepare call hierarchy at a position inside a document', [ApiCommandArgument.Uri, ApiCommandArgument.Position], - new ApiCommandResult('A CallHierarchyItem or undefined', v => typeConverters.CallHierarchyItem.to(v)) + new ApiCommandResult('A CallHierarchyItem or undefined', v => v.map(typeConverters.CallHierarchyItem.to)) ), new ApiCommand( 'vscode.provideIncomingCalls', '_executeProvideIncomingCalls', 'Compute incoming calls for an item', diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 75bc3851448..ea2d1da1b3d 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -378,7 +378,7 @@ export class ExtHostDebugServiceBase implements IExtHostDebugService, ExtHostDeb public async $substituteVariables(folderUri: UriComponents | undefined, config: IConfig): Promise { if (!this._variableResolver) { const [workspaceFolders, configProvider] = await Promise.all([this._workspaceService.getWorkspaceFolders2(), this._configurationService.getConfigProvider()]); - this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider); + this._variableResolver = this.createVariableResolver(workspaceFolders || [], this._editorsService, configProvider!); } let ws: IWorkspaceFolder | undefined; const folder = await this.getFolder(folderUri); diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index 8d089783698..aa41e81b660 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -135,7 +135,7 @@ export class ExtHostQuickOpen implements ExtHostQuickOpenShape { // ---- input - showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise { + showInput(options?: InputBoxOptions, token: CancellationToken = CancellationToken.None): Promise { // global validate fn used in callback below this._validateInput = options ? options.validateInput : undefined; diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 54cc945d6ae..5fa7306fdf9 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -419,7 +419,7 @@ class ExtHostTreeView extends Disposable { // check if an ancestor of extElement is already in the elements to update list let currentNode: TreeNode | undefined = elementNode; while (currentNode && currentNode.parent && !elementsToUpdate.has(currentNode.parent.item.handle)) { - const parentElement = this.elements.get(currentNode.parent.item.handle); + const parentElement: T | undefined = this.elements.get(currentNode.parent.item.handle); currentNode = parentElement ? this.nodes.get(parentElement) : undefined; } if (currentNode && !currentNode.parent) { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 08cfac73625..a1d98fee135 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -289,16 +289,23 @@ export namespace MarkdownString { if (!data) { return part; } + let changed = false; data = cloneAndChange(data, value => { if (URI.isUri(value)) { const key = `__uri_${Math.random().toString(16).slice(2, 8)}`; bucket[key] = value; + changed = true; return key; } else { return undefined; } }); - return encodeURIComponent(JSON.stringify(data)); + + if (!changed) { + return part; + } + + return JSON.stringify(data); } export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index 3cf879271d3..e59a0eebbe7 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -248,11 +248,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa } } - _undoEdits(edits: string[]): void { + _undoEdits(edits: readonly any[]): void { assertIsDefined(this._capabilities).editingCapability?.undoEdits(edits); } - _redoEdits(edits: string[]): void { + _redoEdits(edits: readonly any[]): void { assertIsDefined(this._capabilities).editingCapability?.applyEdits(edits); } @@ -260,6 +260,11 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa await assertIsDefined(this._capabilities).editingCapability?.save(); } + + async _onSaveAs(resource: vscode.Uri, targetResource: vscode.Uri): Promise { + await assertIsDefined(this._capabilities).editingCapability?.saveAs(resource, targetResource); + } + private assertNotDisposed() { if (this._isDisposed) { throw new Error('Webview is disposed'); @@ -427,7 +432,7 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { } async $resolveWebviewEditor( - resource: UriComponents, + input: { resource: UriComponents, edits: readonly any[] }, handle: WebviewPanelHandle, viewType: string, title: string, @@ -443,16 +448,21 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { const webview = new ExtHostWebview(handle, this._proxy, options, this.initData, this.workspace, extension, this._logService); const revivedPanel = new ExtHostWebviewEditor(handle, this._proxy, viewType, title, typeof position === 'number' && position >= 0 ? typeConverters.ViewColumn.to(position) : undefined, options, webview); this._webviewPanels.set(handle, revivedPanel); - const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(resource) }, revivedPanel); + const capabilities = await provider.resolveWebviewEditor({ resource: URI.revive(input.resource) }, revivedPanel); revivedPanel._setCapabilities(capabilities); + + // TODO: the first set of edits should likely be passed when resolving + if (input.edits.length) { + revivedPanel._redoEdits(input.edits); + } } - $undoEdits(handle: WebviewPanelHandle, edits: string[]): void { + $undoEdits(handle: WebviewPanelHandle, edits: readonly any[]): void { const panel = this.getWebviewPanel(handle); panel?._undoEdits(edits); } - $redoEdits(handle: WebviewPanelHandle, edits: string[]): void { + $applyEdits(handle: WebviewPanelHandle, edits: readonly any[]): void { const panel = this.getWebviewPanel(handle); panel?._redoEdits(edits); } @@ -462,6 +472,11 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { return panel?._onSave(); } + async $onSaveAs(handle: WebviewPanelHandle, resource: UriComponents, targetResource: UriComponents): Promise { + const panel = this.getWebviewPanel(handle); + return panel?._onSaveAs(URI.revive(resource), URI.revive(targetResource)); + } + private getWebviewPanel(handle: WebviewPanelHandle): ExtHostWebviewEditor | undefined { return this._webviewPanels.get(handle); } diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css index 51871f7106e..09bb59d2980 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css @@ -38,16 +38,6 @@ outline: 0 !important; /* activity bar indicates focus custom */ } -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-label.toggle-more { - mask: url('ellipsis-activity-bar.svg') no-repeat 50% 50%; - -webkit-mask: url('ellipsis-activity-bar.svg') no-repeat 50% 50%; -} - -.monaco-workbench .activitybar .global-activity .monaco-action-bar .action-label.update-activity { - mask: url('settings-activity-bar.svg') no-repeat 50% 50%; - -webkit-mask: url('settings-activity-bar.svg') no-repeat 50% 50%; -} - .monaco-workbench .activitybar > .content > .composite-bar { margin-bottom: auto; } diff --git a/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg b/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg deleted file mode 100644 index 6729ca3c90d..00000000000 --- a/src/vs/workbench/browser/parts/activitybar/media/ellipsis-activity-bar.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg b/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg deleted file mode 100644 index 5d5fbb8ea5e..00000000000 --- a/src/vs/workbench/browser/parts/activitybar/media/settings-activity-bar.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 70b32efb074..b7e65d14579 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -418,7 +418,7 @@ export class OpenToSideFromQuickOpenAction extends Action { updateClass(): void { const preferredDirection = preferredSideBySideGroupDirection(this.configurationService); - this.class = (preferredDirection === GroupDirection.RIGHT) ? 'quick-open-sidebyside-vertical' : 'quick-open-sidebyside-horizontal'; + this.class = (preferredDirection === GroupDirection.RIGHT) ? 'codicon-split-horizontal' : 'codicon-split-vertical'; } run(context: any): Promise { diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index c2f430cfefe..d3d6a4e6693 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -398,6 +398,11 @@ export interface IEditorInput extends IDisposable { */ saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise; + /** + * Handles when the input is replaced, such as by renaming its backing resource. + */ + handleMove?(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined; + /** * Reverts this input. */ diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts index cad37346668..ccfe8725195 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchy.ts @@ -172,7 +172,7 @@ CommandsRegistry.registerCommand('_executePrepareCallHierarchy', async (accessor _models.delete(key); } }); - return model.root; + return [model.root]; } finally { dispose(textModelReference); diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts index 29f6839f84b..a6b6117b2aa 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleWordWrap.ts @@ -211,6 +211,10 @@ class ToggleWordWrapController extends Disposable implements IEditorContribution // in the settings editor... return; } + if (this.editor.isSimpleWidget) { + // in a simple widget... + return; + } // Ensure correct word wrap settings const newModel = this.editor.getModel(); if (!newModel) { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 9d3653b9d8f..78cba9eadda 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -5,17 +5,21 @@ import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; import { isEqual } from 'vs/base/common/resources'; +import { assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { IEditorModel } from 'vs/platform/editor/common/editor'; +import { generateUuid } from 'vs/base/common/uuid'; +import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity } from 'vs/workbench/common/editor'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IWebviewWorkbenchService, LazilyResolvedWebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { @@ -28,11 +32,14 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { resource: URI, viewType: string, id: string, - webview: Lazy>, + webview: Lazy, @ILifecycleService lifecycleService: ILifecycleService, @IWebviewWorkbenchService webviewWorkbenchService: IWebviewWorkbenchService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @ILabelService private readonly labelService: ILabelService, @ICustomEditorService private readonly customEditorService: ICustomEditorService, + @IEditorService private readonly editorService: IEditorService, + @IFileDialogService private readonly fileDialogService: IFileDialogService, ) { super(id, viewType, '', webview, webviewWorkbenchService, lifecycleService); this._editorResource = resource; @@ -46,6 +53,10 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this._editorResource; } + public supportsSplitEditor() { + return true; + } + @memoize getName(): string { return basename(this.labelService.getUriLabel(this.getResource())); @@ -101,9 +112,32 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return this._model ? this._model.save(options) : Promise.resolve(false); } - public saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { - // TODO@matt implement properly (see TextEditorInput#saveAs()) - return this._model ? this._model.save(options) : Promise.resolve(false); + public async saveAs(groupId: GroupIdentifier, options?: ISaveOptions): Promise { + if (!this._model) { + return false; + } + + // Preserve view state by opening the editor first. In addition + // this allows the user to review the contents of the editor. + // let viewState: IEditorViewState | undefined = undefined; + // const editor = await this.editorService.openEditor(this, undefined, group); + // if (isTextEditor(editor)) { + // viewState = editor.getViewState(); + // } + + let dialogPath = this._editorResource; + // if (this._editorResource.scheme === Schemas.untitled) { + // dialogPath = this.suggestFileName(resource); + // } + + const target = await this.promptForPath(this._editorResource, dialogPath, options?.availableFileSystems); + if (!target) { + return false; // save cancelled + } + + await this._model.saveAs(this._editorResource, target, options); + + return true; } public revert(options?: IRevertOptions): Promise { @@ -113,6 +147,24 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { public async resolve(): Promise { this._model = await this.customEditorService.models.loadOrCreate(this.getResource(), this.viewType); this._register(this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + this._onDidChangeDirty.fire(); return await super.resolve(); } + + protected async promptForPath(resource: URI, defaultUri: URI, availableFileSystems?: readonly string[]): Promise { + + // Help user to find a name for the file by opening it first + await this.editorService.openEditor({ resource, options: { revealIfOpened: true, preserveFocus: true } }); + + return this.fileDialogService.pickFileToSave({});//this.getSaveDialogOptions(defaultUri, availableFileSystems)); + } + + public handleMove(groupId: GroupIdentifier, uri: URI, options?: ITextEditorOptions): IEditorInput | undefined { + const webview = assertIsDefined(this.takeOwnershipOfWebview()); + return this.instantiationService.createInstance(CustomFileEditorInput, + uri, + this.viewType, + generateUuid(), + new Lazy(() => webview)); + } } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts index 8739a05780a..866f96ef5f5 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInputFactory.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -48,7 +47,7 @@ export class CustomEditorInputFactory extends WebviewEditorInputFactory { location: data.extensionLocation, id: data.extensionId } : undefined, data.group); - return new UnownedDisposable(webviewInput.webview); + return webviewInput.webview; }); const customInput = this._instantiationService.createInstance(CustomFileEditorInput, URI.from((data as any).editorResource), data.viewType, id, webview); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 814c4d6b301..9e87a796443 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -6,7 +6,7 @@ import { coalesce, distinct, find, mergeSort } from 'vs/base/common/arrays'; import * as glob from 'vs/base/common/glob'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, UnownedDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { basename, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -201,7 +201,7 @@ export class CustomEditorService extends Disposable implements ICustomEditorServ ): CustomFileEditorInput { const id = generateUuid(); const webview = new Lazy(() => { - return new UnownedDisposable(this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {})); + return this.webviewService.createWebviewEditorOverlay(id, { customClasses: options?.customClasses }, {}); }); const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, webview); if (group) { diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 09b882531fb..9162ab0e3d2 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -38,7 +38,7 @@ export interface ICustomEditorService { promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise; } -export type CustomEditorEdit = unknown; +export type CustomEditorEdit = { source?: any, data: any }; export interface ICustomEditorModelManager { get(resource: URI, viewType: string): ICustomEditorModel | undefined; @@ -48,18 +48,33 @@ export interface ICustomEditorModelManager { disposeModel(model: ICustomEditorModel): void; } +export interface CustomEditorSaveEvent { + readonly resource: URI; + readonly waitUntil: (until: Promise) => void; +} + +export interface CustomEditorSaveAsEvent { + readonly resource: URI; + readonly targetResource: URI; + readonly waitUntil: (until: Promise) => void; +} + export interface ICustomEditorModel extends IWorkingCopy { readonly onUndo: Event; - readonly onRedo: Event; - readonly onWillSave: Event<{ waitUntil: (until: Promise) => void }>; + readonly onApplyEdit: Event; + readonly onWillSave: Event; + readonly onWillSaveAs: Event; + + readonly currentEdits: readonly CustomEditorEdit[]; undo(): void; redo(): void; revert(options?: IRevertOptions): Promise; save(options?: ISaveOptions): Promise; + saveAs(resource: URI, targetResource: URI, currentOptions?: ISaveOptions): Promise; - makeEdit(data: string): void; + makeEdit(edit: CustomEditorEdit): void; } export const enum CustomEditorPriority { diff --git a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts index ee49e9aaa14..1443db0b0ce 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditorModel.ts @@ -6,7 +6,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import { ICustomEditorModel, CustomEditorEdit } from 'vs/workbench/contrib/customEditor/common/customEditor'; +import { ICustomEditorModel, CustomEditorEdit, CustomEditorSaveAsEvent, CustomEditorSaveEvent } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; @@ -14,7 +14,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel private _currentEditIndex: number = -1; private _savePoint: number = -1; - private _edits: Array = []; + private _edits: Array = []; constructor( private readonly _resource: URI, @@ -44,16 +44,24 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel protected readonly _onUndo = this._register(new Emitter()); readonly onUndo = this._onUndo.event; - protected readonly _onRedo = this._register(new Emitter()); - readonly onRedo = this._onRedo.event; + protected readonly _onApplyEdit = this._register(new Emitter()); + readonly onApplyEdit = this._onApplyEdit.event; - protected readonly _onWillSave = this._register(new Emitter<{ waitUntil: (until: Promise) => void }>()); + protected readonly _onWillSave = this._register(new Emitter()); readonly onWillSave = this._onWillSave.event; - public makeEdit(data: string): void { - this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, data); + protected readonly _onWillSaveAs = this._register(new Emitter()); + readonly onWillSaveAs = this._onWillSaveAs.event; + + get currentEdits(): readonly CustomEditorEdit[] { + return this._edits.slice(0, Math.max(0, this._currentEditIndex + 1)); + } + + public makeEdit(edit: CustomEditorEdit): void { + this._edits.splice(this._currentEditIndex + 1, this._edits.length - this._currentEditIndex, edit.data); this._currentEditIndex = this._edits.length - 1; this.updateDirty(); + this._onApplyEdit.fire([edit]); } private updateDirty() { @@ -62,7 +70,10 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel public async save(_options?: ISaveOptions): Promise { const untils: Promise[] = []; - const handler = { waitUntil: (until: Promise) => untils.push(until) }; + const handler: CustomEditorSaveEvent = { + resource: this._resource, + waitUntil: (until: Promise) => untils.push(until) + }; try { this._onWillSave.fire(handler); @@ -77,6 +88,27 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel return true; } + public async saveAs(resource: URI, targetResource: URI, _options?: ISaveOptions): Promise { + const untils: Promise[] = []; + const handler: CustomEditorSaveAsEvent = { + resource, + targetResource, + waitUntil: (until: Promise) => untils.push(until) + }; + + try { + this._onWillSaveAs.fire(handler); + await Promise.all(untils); + } catch { + return false; + } + + this._savePoint = this._currentEditIndex; + this.updateDirty(); + + return true; + } + public async revert(_options?: IRevertOptions) { if (this._currentEditIndex === this._savePoint) { return true; @@ -87,7 +119,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel this._onUndo.fire(editsToUndo.reverse()); } else if (this._currentEditIndex < this._savePoint) { const editsToRedo = this._edits.slice(this._currentEditIndex, this._savePoint); - this._onRedo.fire(editsToRedo); + this._onApplyEdit.fire(editsToRedo); } this._currentEditIndex = this._savePoint; @@ -104,7 +136,7 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel const undoneEdit = this._edits[this._currentEditIndex]; --this._currentEditIndex; - this._onUndo.fire([undoneEdit]); + this._onUndo.fire([{ data: undoneEdit }]); this.updateDirty(); } @@ -117,7 +149,8 @@ export class CustomEditorModel extends Disposable implements ICustomEditorModel ++this._currentEditIndex; const redoneEdit = this._edits[this._currentEditIndex]; - this._onRedo.fire([redoneEdit]); + + this._onApplyEdit.fire([{ data: redoneEdit }]); this.updateDirty(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 24a3b0902e9..d7378c6a497 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -47,6 +47,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { withUndefinedAsNull } from 'vs/base/common/types'; const DEBUG_BREAKPOINTS_KEY = 'debug.breakpoint'; const DEBUG_FUNCTION_BREAKPOINTS_KEY = 'debug.functionbreakpoint'; @@ -807,7 +808,7 @@ export class DebugService implements IDebugService { return inactivePromise; } - return taskPromise; + return taskPromise.then(withUndefinedAsNull); }); return new Promise((c, e) => { diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 4d8c423dbce..71c102e3515 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -141,7 +141,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); this.$el.style.top = `${layoutService.getTitleBarOffset()}px`; - this.dragArea = dom.append(this.$el, dom.$('div.drag-area')); + this.dragArea = dom.append(this.$el, dom.$('div.drag-area.codicon.codicon-gripper')); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); this.debugToolBarMenu = menuService.createMenu(MenuId.DebugToolBar, contextKeyService); diff --git a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css index 4ac6feef480..56d48297c37 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugToolBar.css @@ -28,8 +28,10 @@ cursor: grab; height: 32px; width: 16px; - background: url('drag.svg') center center no-repeat; - background-size: 16px 16px; + opacity: 0.5; + display: flex; + align-items: center; + justify-content: center; } .monaco-workbench .debug-toolbar .drag-area.dragged { diff --git a/src/vs/workbench/contrib/debug/browser/media/drag.svg b/src/vs/workbench/contrib/debug/browser/media/drag.svg deleted file mode 100644 index b6b93f31fdf..00000000000 --- a/src/vs/workbench/contrib/debug/browser/media/drag.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index d3e7af804db..38c1765c955 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1253,6 +1253,10 @@ export class ReloadAction extends ExtensionAction { this.label = localize('reloadRequired', "Reload Required"); this.tooltip = localize('postUpdateTooltip', "Please reload Visual Studio Code to enable the updated extension."); } + } else { + this.enabled = true; + this.label = localize('reloadRequired', "Reload Required"); + this.tooltip = localize('postEnableTooltip', "Please reload Visual Studio Code to enable this extension."); } } } else { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index 5b2d45f343e..915b6cc7fae 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -1463,7 +1463,7 @@ suite('ExtensionsActions Test', () => { assert.equal(testObject.tooltip, 'Please reload Visual Studio Code to enable this extension.'); }); - test('Test ReloadAction is disabled when remote ui extension is installed in local server', async () => { + test('Test ReloadAction when ui extension is disabled on remote server and installed in local server', async () => { // multi server setup const gallery = aGalleryExtension('a'); const localExtensionManagementService = createExtensionManagementService([]); @@ -1478,7 +1478,7 @@ suite('ExtensionsActions Test', () => { const onDidChangeExtensionsEmitter: Emitter = new Emitter(); instantiationService.stub(IExtensionService, >{ - getExtensions: () => Promise.resolve([ExtensionsActions.toExtensionDescription(remoteExtension)]), + getExtensions: () => Promise.resolve([]), onDidChangeExtensions: onDidChangeExtensionsEmitter.event, canAddExtension: (extension) => false }); @@ -1495,7 +1495,8 @@ suite('ExtensionsActions Test', () => { const localExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a') }); onDidInstallEvent.fire({ identifier: localExtension.identifier, local: localExtension, operation: InstallOperation.Install }); - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal(testObject.tooltip, 'Please reload Visual Studio Code to enable this extension.'); }); test('Test ReloadAction for remote ui extension is disabled when it is installed and enabled in local server', async () => { @@ -1529,37 +1530,6 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test ReloadAction for local ui extension is disabled when it is installed and enabled in remote server', async () => { - // multi server setup - const gallery = aGalleryExtension('a'); - const localExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a') }); - const localExtensionManagementService = createExtensionManagementService([localExtension]); - const onDidInstallEvent = new Emitter(); - localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event; - const remoteExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file('pub.a').with({ scheme: Schemas.vscodeRemote }) }); - const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteExtension])); - instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); - instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); - const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); - instantiationService.set(IExtensionsWorkbenchService, workbenchService); - - const onDidChangeExtensionsEmitter: Emitter = new Emitter(); - instantiationService.stub(IExtensionService, >{ - getExtensions: () => Promise.resolve([ExtensionsActions.toExtensionDescription(remoteExtension)]), - onDidChangeExtensions: onDidChangeExtensionsEmitter.event, - canAddExtension: (extension) => false - }); - const testObject: ExtensionsActions.ReloadAction = instantiationService.createInstance(ExtensionsActions.ReloadAction); - instantiationService.createInstance(ExtensionContainers, [testObject]); - instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); - - await workbenchService.queryGallery(CancellationToken.None); - const extensions = await workbenchService.queryLocal(extensionManagementServerService.localExtensionManagementServer!); - testObject.extension = extensions[0]; - assert.ok(testObject.extension); - assert.ok(!testObject.enabled); - }); - test('Test remote install action is enabled for local workspace extension', async () => { // multi server setup const localWorkspaceExtension = aLocalExtension('a', { extensionKind: 'workspace' }, { location: URI.file(`pub.a`) }); @@ -1913,7 +1883,7 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote ui extension', async () => { + test('Test local install action is enabled for remote ui extension', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -1929,7 +1899,9 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); }); test('Test local install action when installing remote ui extension', async () => { @@ -1953,10 +1925,54 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + + onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery }); + assert.ok(testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + }); + + test('Test local install action when installing remote ui extension is finished', async () => { + // multi server setup + const localExtensionManagementService: IExtensionManagementService = createExtensionManagementService(); + const onInstallExtension = new Emitter(); + localExtensionManagementService.onInstallExtension = onInstallExtension.event; + const onDidInstallEvent = new Emitter(); + localExtensionManagementService.onDidInstallExtension = onDidInstallEvent.event; + const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); + const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, localExtensionManagementService, createExtensionManagementService([remoteUIExtension])); + instantiationService.stub(IExtensionManagementServerService, extensionManagementServerService); + instantiationService.stub(IExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); + const workbenchService: IExtensionsWorkbenchService = instantiationService.createInstance(ExtensionsWorkbenchService); + instantiationService.stub(IExtensionsWorkbenchService, workbenchService, 'open', undefined); + instantiationService.set(IExtensionsWorkbenchService, workbenchService); + + const gallery = aGalleryExtension('a', { identifier: remoteUIExtension.identifier }); + instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(gallery)); + const testObject: ExtensionsActions.InstallAction = instantiationService.createInstance(ExtensionsActions.LocalInstallAction); + instantiationService.createInstance(ExtensionContainers, [testObject]); + + const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); + await workbenchService.queryGallery(CancellationToken.None); + testObject.extension = extensions[0]; + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); + + onInstallExtension.fire({ identifier: remoteUIExtension.identifier, gallery }); + assert.ok(testObject.enabled); + assert.equal('Installing', testObject.label); + assert.equal('extension-action install installing', testObject.class); + + const installedExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`) }); + onDidInstallEvent.fire({ identifier: installedExtension.identifier, local: installedExtension, operation: InstallOperation.Install }); assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for disabled remote ui extension', async () => { + test('Test local install action is enabled for disabled remote ui extension', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -1973,7 +1989,9 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); + assert.equal('extension-action prominent install', testObject.class); }); test('Test local install action is disabled when extension is not set', async () => { @@ -2073,7 +2091,7 @@ suite('ExtensionsActions Test', () => { assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote UI extension if it uninstalled locally', async () => { + test('Test local install action is disabled for remoteUI extension if it is uninstalled locally', async () => { // multi server setup const extensionManagementService = instantiationService.get(IExtensionManagementService); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), extensionManagementService); @@ -2091,13 +2109,14 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); await workbenchService.queryGallery(CancellationToken.None); testObject.extension = extensions[0]; - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); + assert.equal('Install Locally', testObject.label); uninstallEvent.fire(remoteUIExtension.identifier); assert.ok(!testObject.enabled); }); - test('Test local install action is disabled for remote UI extension if it has gallery', async () => { + test('Test local install action is enabled for remote UI extension if it has gallery', async () => { // multi server setup const remoteUIExtension = aLocalExtension('a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); const extensionManagementServerService = aMultiExtensionManagementServerService(instantiationService, createExtensionManagementService(), createExtensionManagementService([remoteUIExtension])); @@ -2113,7 +2132,7 @@ suite('ExtensionsActions Test', () => { const extensions = await workbenchService.queryLocal(extensionManagementServerService.remoteExtensionManagementServer!); testObject.extension = extensions[0]; assert.ok(testObject.extension); - assert.ok(!testObject.enabled); + assert.ok(testObject.enabled); }); test('Test local install action is disabled for remote UI system extension', async () => { diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 3324f94e082..e12f352f072 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -206,7 +206,7 @@ suite('ExtensionsTipsService Test', () => { ...{ extensionTips: { 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', - 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx**/*.js,**/*.jsx,**/*.es6,**/.babelrc}', + 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs,**/.babelrc}', 'lukehoban.Go': '**/*.go' }, extensionImportantTips: { diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index ca9dba559e2..bcc39a73b37 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -27,7 +27,7 @@ import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor import { ResourceQueue, timeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { EditorActivation } from 'vs/platform/editor/common/editor'; +import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; export class FileEditorTracker extends Disposable implements IWorkbenchContribution { @@ -215,8 +215,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut private handleMovedFileInOpenedEditors(oldResource: URI, newResource: URI): void { this.editorGroupService.groups.forEach(group => { group.editors.forEach(editor => { - if (editor instanceof FileEditorInput) { - const resource = editor.getResource(); + const resource = editor.getResource(); + if (resource && (editor instanceof FileEditorInput || editor.handleMove)) { // Update Editor if file (or any parent of the input) got renamed or moved if (resources.isEqualOrParent(resource, oldResource)) { @@ -228,15 +228,27 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved } + const options: ITextEditorOptions = { + preserveFocus: true, + pinned: group.isPinned(editor), + index: group.getIndexOfEditor(editor), + inactive: !group.isActive(editor), + }; + + if (editor.handleMove) { + const replacement = editor.handleMove(group.id, reopenFileResource, options); + if (replacement) { + this.editorService.replaceEditors([{ editor, replacement }], group); + return; + } + } + this.editorService.replaceEditors([{ editor: { resource }, replacement: { resource: reopenFileResource, options: { - preserveFocus: true, - pinned: group.isPinned(editor), - index: group.getIndexOfEditor(editor), - inactive: !group.isActive(editor), + ...options, viewState: this.getViewStateFor(oldResource, group) } }, diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index aabb8afd4af..8040b6b0bb9 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -21,14 +21,12 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOS import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; -import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { WorkspaceFolderCountContext, IsWebContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ActiveEditorIsReadonlyContext, DirtyWorkingCopiesContext, ActiveEditorContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; -import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; // Contribute Global Actions const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; @@ -181,23 +179,17 @@ export function appendEditorTitleContextMenuItem(id: string, title: string, when } // Editor Title Menu for Conflict Resolution -appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), { - light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-light.svg`)), - dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/check-dark.svg`)) -}, -10, acceptLocalChangesCommand); -appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), { - light: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-light.svg`)), - dark: URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/files/browser/media/undo-dark.svg`)) -}, -9, revertLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.acceptLocalChanges', nls.localize('acceptLocalChanges', "Use your changes and overwrite file contents"), 'codicon-check', -10, acceptLocalChangesCommand); +appendSaveConflictEditorTitleAction('workbench.files.action.revertLocalChanges', nls.localize('revertLocalChanges', "Discard your changes and revert to file contents"), 'codicon-discard', -9, revertLocalChangesCommand); -function appendSaveConflictEditorTitleAction(id: string, title: string, iconLocation: { dark: URI; light?: URI; }, order: number, command: ICommandHandler): void { +function appendSaveConflictEditorTitleAction(id: string, title: string, iconClassName: string, order: number, command: ICommandHandler): void { // Command CommandsRegistry.registerCommand(id, command); // Action MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { id, title, iconLocation }, + command: { id, title, iconClassName }, when: ContextKeyExpr.equals(CONFLICT_RESOLUTION_CONTEXT, true), group: 'navigation', order diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 0e1d4b4801a..3bf6af89aa6 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -275,7 +275,7 @@ async function deleteFiles(textFileService: ITextFileService, dialogService: IDi }); }); - return servicePromise; + return servicePromise.then(undefined); }); }); } diff --git a/src/vs/workbench/contrib/files/browser/media/check-dark.svg b/src/vs/workbench/contrib/files/browser/media/check-dark.svg deleted file mode 100644 index 51674695e1f..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/check-light.svg b/src/vs/workbench/contrib/files/browser/media/check-light.svg deleted file mode 100644 index 7b1da6d7208..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg deleted file mode 100644 index 4862c55dbeb..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg deleted file mode 100644 index 05f920b29b6..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg b/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg deleted file mode 100644 index 6359b42e623..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/collapse-all-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css index 8933103c4d3..d20f338055d 100644 --- a/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css +++ b/src/vs/workbench/contrib/files/browser/media/explorerviewlet.css @@ -169,16 +169,3 @@ .hc-black .monaco-workbench .explorer-viewlet .editor-group { line-height: 20px; } - -/* TODO @misolori convert these to use icon font, for the debug viewlet */ -.monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-light.svg") 50% no-repeat; -} - -.vs-dark .monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-dark.svg") 50% no-repeat; -} - -.hc-black .monaco-workbench .explorer-action.collapse-explorer { - background: url("collapse-all-hc.svg") 50% no-repeat; -} diff --git a/src/vs/workbench/contrib/files/browser/media/fileactions.css b/src/vs/workbench/contrib/files/browser/media/fileactions.css index 890cd46cef9..a6320e959bf 100644 --- a/src/vs/workbench/contrib/files/browser/media/fileactions.css +++ b/src/vs/workbench/contrib/files/browser/media/fileactions.css @@ -3,41 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -/* Split editor vertical */ -.monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-light.svg"); -} - -.vs-dark .monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-dark.svg"); -} - -.hc-black .monaco-workbench .quick-open-sidebyside-vertical { - background-image: url("split-editor-vertical-hc.svg"); -} - -/* Split editor horizontal */ -.monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-light.svg"); -} - -.vs-dark .monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-dark.svg"); -} - -.hc-black .monaco-workbench .quick-open-sidebyside-horizontal { - background-image: url("split-editor-horizontal-hc.svg"); -} - -.monaco-workbench .file-editor-action.action-open-preview { - background: url("preview-light.svg") center center no-repeat; -} - -.vs-dark .monaco-workbench .file-editor-action.action-open-preview, -.hc-black .monaco-workbench .file-editor-action.action-open-preview { - background: url("preview-dark.svg") center center no-repeat; -} - .explorer-viewlet .explorer-open-editors .monaco-list .monaco-list-row.dirty:not(:hover) > .monaco-action-bar .codicon-close::before { content: "\ea71"; } diff --git a/src/vs/workbench/contrib/files/browser/media/preview-dark.svg b/src/vs/workbench/contrib/files/browser/media/preview-dark.svg deleted file mode 100644 index 1d59877196b..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/preview-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/preview-light.svg b/src/vs/workbench/contrib/files/browser/media/preview-light.svg deleted file mode 100644 index 3f1152fc3cd..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/preview-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg deleted file mode 100644 index 419c21be4f6..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg deleted file mode 100644 index 7565fd3c168..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg deleted file mode 100644 index 405291c14dd..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-horizontal-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg deleted file mode 100644 index 8c22a7c5bfe..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg deleted file mode 100644 index 82c19d0c8fc..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg b/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg deleted file mode 100644 index d5a2e9415b0..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/split-editor-vertical-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/files/browser/media/undo-dark.svg b/src/vs/workbench/contrib/files/browser/media/undo-dark.svg deleted file mode 100644 index de85d6ba679..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/undo-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/files/browser/media/undo-light.svg b/src/vs/workbench/contrib/files/browser/media/undo-light.svg deleted file mode 100644 index b70626957d0..00000000000 --- a/src/vs/workbench/contrib/files/browser/media/undo-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 82ac9658052..894f925ba71 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -216,14 +216,13 @@ overflow: hidden; text-overflow: ellipsis; line-height: 22px; - opacity: 0.9; flex-shrink: 1; } .settings-editor > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: none; line-height: 22px; - opacity: 0.7; + opacity: 0.8; margin-left: 3px; } @@ -231,17 +230,8 @@ display: block; } -.settings-editor > .settings-body .settings-toc-container .monaco-list-row .monaco-tl-twistie { - opacity: 0.9; -} - -.settings-editor > .settings-body .settings-toc-container .monaco-list-row.selected .monaco-tl-twistie { - opacity: 1; -} - .settings-editor > .settings-body .settings-toc-container .monaco-list-row.selected .settings-toc-entry { font-weight: bold; - opacity: 1; } .settings-editor > .settings-body .settings-tree-container { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 65b5fb097c1..abd4821d747 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -98,6 +98,7 @@ export class SettingsEditor2 extends BaseEditor { private headerContainer!: HTMLElement; private searchWidget!: SuggestEnabledInput; private countElement!: HTMLElement; + private controlsElement!: HTMLElement; private settingsTargetsWidget!: SettingsTargetsWidget; private settingsTreeContainer!: HTMLElement; @@ -301,7 +302,8 @@ export class SettingsEditor2 extends BaseEditor { this.layoutTrees(dimension); const innerWidth = Math.min(1000, dimension.width) - 24 * 2; // 24px padding on left and right; - const monacoWidth = innerWidth - 10 - this.countElement.clientWidth - 12; // minus padding inside inputbox, countElement width, extra padding before countElement + // minus padding inside inputbox, countElement width, controls width, extra padding before countElement + const monacoWidth = innerWidth - 10 - this.countElement.clientWidth - this.controlsElement.clientWidth - 12; this.searchWidget.layout({ height: 20, width: monacoWidth }); DOM.toggleClass(this.rootElement, 'mid-width', dimension.width < 1000 && dimension.width >= 600); @@ -375,6 +377,7 @@ export class SettingsEditor2 extends BaseEditor { clearSearchResults(): void { this.searchWidget.setValue(''); + this.focusSearch(); } clearSearchFilters(): void { @@ -387,17 +390,12 @@ export class SettingsEditor2 extends BaseEditor { this.searchWidget.setValue(query.trim()); } - clearSearch(): void { - this.clearSearchResults(); - this.focusSearch(); - } - private createHeader(parent: HTMLElement): void { this.headerContainer = DOM.append(parent, $('.settings-header')); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); - const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearch(); return Promise.resolve(null); }); + const clearInputAction = new Action(SETTINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Settings Search Input"), 'codicon-clear-all', false, () => { this.clearSearchResults(); return Promise.resolve(null); }); const searchBoxLabel = localize('SearchSettings.AriaLabel', "Search settings"); this.searchWidget = this._register(this.instantiationService.createInstance(SuggestEnabledInput, `${SettingsEditor2.ID}.searchbox`, searchContainer, { @@ -446,9 +444,9 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTargetsWidget.settingsTarget = ConfigurationTarget.USER_LOCAL; this.settingsTargetsWidget.onDidTargetChange(target => this.onDidSettingsTargetChange(target)); - const actionsContainer = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); + this.controlsElement = DOM.append(searchContainer, DOM.$('.settings-clear-widget')); - const actionBar = this._register(new ActionBar(actionsContainer, { + const actionBar = this._register(new ActionBar(this.controlsElement, { animated: false, actionViewItemProvider: (action: Action) => { return undefined; } })); @@ -541,7 +539,6 @@ export class SettingsEditor2 extends BaseEditor { this._register(DOM.addDisposableListener(clearSearch, DOM.EventType.CLICK, (e: MouseEvent) => { DOM.EventHelper.stop(e, false); this.clearSearchResults(); - this.focusSearch(); })); DOM.append(this.noResultsMessage, clearSearchContainer); @@ -555,7 +552,7 @@ export class SettingsEditor2 extends BaseEditor { this.createFocusSink( bodyContainer, e => { - if (DOM.findParentWithClass(e.relatedTarget, 'monaco-list')) { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop > 0) { const firstElement = this.settingsTree.firstVisibleElement; this.settingsTree.reveal(firstElement, 0.1); @@ -577,7 +574,7 @@ export class SettingsEditor2 extends BaseEditor { this.createFocusSink( bodyContainer, e => { - if (DOM.findParentWithClass(e.relatedTarget, 'monaco-list')) { + if (DOM.findParentWithClass(e.relatedTarget, 'settings-editor-tree')) { if (this.settingsTree.scrollTop < this.settingsTree.scrollHeight) { const lastElement = this.settingsTree.lastVisibleElement; this.settingsTree.reveal(lastElement, 0.9); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 592b3194e54..d52e1403ed8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1481,6 +1481,8 @@ export class SettingsTree extends ObjectTree { // applying an opacity to the link color. const fgWithOpacity = new Color(new RGBA(foregroundColor.rgba.r, foregroundColor.rgba.g, foregroundColor.rgba.b, 0.9)); collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-description { color: ${fgWithOpacity}; }`); + + collector.addRule(`.settings-editor > .settings-body .settings-toc-container .monaco-list-row:not(.selected) { color: ${fgWithOpacity}; }`); } const errorColor = theme.getColor(errorForeground); @@ -1516,6 +1518,8 @@ export class SettingsTree extends ObjectTree { } })); + this.getHTMLElement().classList.add('settings-editor-tree'); + this.disposables.add(attachStyler(themeService, { listBackground: editorBackground, listActiveSelectionBackground: editorBackground, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index eb1bbf865cd..cc60be57781 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -23,33 +23,33 @@ import { disposableTimeout } from 'vs/base/common/async'; import { isUndefinedOrNull } from 'vs/base/common/types'; const $ = DOM.$; -export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "(For settings editor preview) The foreground color for a section header or active title.")); +export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hc: '#ffffff' }, localize('headerForeground', "The foreground color for a section header or active title.")); export const modifiedItemIndicator = registerColor('settings.modifiedItemIndicator', { light: new Color(new RGBA(102, 175, 224)), dark: new Color(new RGBA(12, 125, 157)), hc: new Color(new RGBA(0, 73, 122)) -}, localize('modifiedItemForeground', "(For settings editor preview) The color of the modified setting indicator.")); +}, localize('modifiedItemForeground', "The color of the modified setting indicator.")); // Enum control colors -export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "(For settings editor preview) Settings editor dropdown background.")); -export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "(For settings editor preview) Settings editor dropdown foreground.")); -export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "(For settings editor preview) Settings editor dropdown border.")); -export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "(For settings editor preview) Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); +export const settingsSelectBackground = registerColor('settings.dropdownBackground', { dark: selectBackground, light: selectBackground, hc: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); +export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hc: selectForeground }, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); +export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hc: selectBorder }, localize('settingsDropdownBorder', "Settings editor dropdown border.")); +export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hc: editorWidgetBorder }, localize('settingsDropdownListBorder', "Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); // Bool control colors -export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: simpleCheckboxBackground, light: simpleCheckboxBackground, hc: simpleCheckboxBackground }, localize('settingsCheckboxBackground', "(For settings editor preview) Settings editor checkbox background.")); -export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: simpleCheckboxForeground, light: simpleCheckboxForeground, hc: simpleCheckboxForeground }, localize('settingsCheckboxForeground', "(For settings editor preview) Settings editor checkbox foreground.")); -export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "(For settings editor preview) Settings editor checkbox border.")); +export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: simpleCheckboxBackground, light: simpleCheckboxBackground, hc: simpleCheckboxBackground }, localize('settingsCheckboxBackground', "Settings editor checkbox background.")); +export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: simpleCheckboxForeground, light: simpleCheckboxForeground, hc: simpleCheckboxForeground }, localize('settingsCheckboxForeground', "Settings editor checkbox foreground.")); +export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: simpleCheckboxBorder, light: simpleCheckboxBorder, hc: simpleCheckboxBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); // Text control colors -export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "(For settings editor preview) Settings editor text input box background.")); -export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "(For settings editor preview) Settings editor text input box foreground.")); -export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "(For settings editor preview) Settings editor text input box border.")); +export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); +export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); +export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); // Number control colors -export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "(For settings editor preview) Settings editor number input box background.")); -export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "(For settings editor preview) Settings editor number input box foreground.")); -export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "(For settings editor preview) Settings editor number input box border.")); +export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hc: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); +export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hc: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); +export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hc: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const checkboxBackgroundColor = theme.getColor(settingsCheckboxBackground); diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index cb7b19f19f2..1120b48feca 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -10,7 +10,7 @@ import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTr import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Iterator } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; +import { editorBackground, transparent, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { SettingsTreeFilter } from 'vs/workbench/contrib/preferences/browser/settingsTree'; @@ -215,8 +215,8 @@ export class TOCTree extends ObjectTree { listFocusAndSelectionBackground: editorBackground, listFocusAndSelectionForeground: settingsHeaderForeground, listFocusBackground: editorBackground, - listFocusForeground: settingsHeaderForeground, - listHoverForeground: settingsHeaderForeground, + listFocusForeground: transparent(foreground, 0.9), + listHoverForeground: transparent(foreground, 0.9), listHoverBackground: editorBackground, listInactiveSelectionBackground: editorBackground, listInactiveSelectionForeground: settingsHeaderForeground, diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 30780a7a90f..a3cac4fbe01 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -1215,9 +1215,15 @@ class DirtyDiffItem { } } +interface IViewState { + readonly width: number; + readonly visibility: 'always' | 'hover'; +} + export class DirtyDiffWorkbenchController extends Disposable implements ext.IWorkbenchContribution, IModelRegistry { private enabled = false; + private viewState: IViewState = { width: 3, visibility: 'always' }; private models: ITextModel[] = []; private items: { [modelId: string]: DirtyDiffItem; } = Object.create(null); private readonly transientDisposables = this._register(new DisposableStore()); @@ -1262,15 +1268,20 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor width = 3; } - this.stylesheet.innerHTML = `.monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${width}px;}`; + this.setViewState({ ...this.viewState, width }); } private onDidChangeDiffVisibiltiyConfiguration(): void { - const visibility = this.configurationService.getValue('scm.diffDecorationsGutterVisibility'); + const visibility = this.configurationService.getValue<'always' | 'hover'>('scm.diffDecorationsGutterVisibility'); + this.setViewState({ ...this.viewState, visibility }); + } + private setViewState(state: IViewState): void { + this.viewState = state; this.stylesheet.innerHTML = ` + .monaco-editor .dirty-diff-modified,.monaco-editor .dirty-diff-added{border-left-width:${state.width}px;} .monaco-editor .dirty-diff-modified, .monaco-editor .dirty-diff-added, .monaco-editor .dirty-diff-deleted { - opacity: ${visibility === 'always' ? 1 : 0}; + opacity: ${state.visibility === 'always' ? 1 : 0}; } `; } diff --git a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css index 1c7d01f9e35..472114eedd5 100644 --- a/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css +++ b/src/vs/workbench/contrib/scm/browser/media/dirtydiffDecorator.css @@ -39,7 +39,7 @@ transition: height 80ms linear; } -.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-glyph:before { +.monaco-editor .margin-view-overlays .dirty-diff-glyph:hover::before { position: absolute; content: ''; height: 100%; @@ -47,7 +47,7 @@ left: -6px; } -.monaco-editor .margin-view-overlays > div:hover > .dirty-diff-deleted:after { +.monaco-editor .margin-view-overlays .dirty-diff-deleted:hover::after { bottom: 0; border-top-width: 0; border-bottom-width: 0; @@ -56,4 +56,4 @@ /* Hide glyph decorations when inside the inline diff editor */ .monaco-editor.modified-in-monaco-diff-editor .margin-view-overlays > div > .dirty-diff-glyph { display: none; -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 7cf58147ab2..fea95600589 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler'; import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler'; import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions'; -import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction } from 'vs/workbench/contrib/search/browser/searchActions'; +import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction, OpenResultsInEditorAction, RerunEditorSearchAction } from 'vs/workbench/contrib/search/browser/searchActions'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { SearchView, SearchViewPosition } from 'vs/workbench/contrib/search/browser/searchView'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; @@ -56,6 +56,7 @@ import { ISearchConfiguration, ISearchConfigurationProperties, PANEL_ID, VIEWLET import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); @@ -616,13 +617,40 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({ handler: toggleRegexCommand }, ToggleRegexKeybinding)); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: Constants.AddCursorsAtSearchResults, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and(Constants.SearchViewVisibleKey, Constants.FileMatchOrMatchFocusKey), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_L, + handler: (accessor, args: any) => { + const searchView = getSearchView(accessor.get(IViewletService), accessor.get(IPanelService)); + if (searchView) { + const tree: WorkbenchObjectTree = searchView.getControl(); + searchView.openEditorWithMultiCursor(tree.getFocus()[0]); + } + } +}); + registry.registerWorkbenchAction(SyncActionDescriptor.create(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...'); - registry.registerWorkbenchAction(SyncActionDescriptor.create(ToggleSearchOnTypeAction, ToggleSearchOnTypeAction.ID, ToggleSearchOnTypeAction.LABEL), 'Search: Toggle Search on Type', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category); registry.registerWorkbenchAction(SyncActionDescriptor.create(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL), 'Search: Clear Search Results', category); +registry.registerWorkbenchAction( + SyncActionDescriptor.create(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL, + { mac: { primary: KeyMod.CtrlCmd | KeyCode.Enter } }, + ContextKeyExpr.and(Constants.HasSearchResults, Constants.SearchViewFocusedKey, Constants.EnableSearchEditorPreview)), + 'Search: Open Results in Editor', category, + ContextKeyExpr.and(Constants.EnableSearchEditorPreview)); + +registry.registerWorkbenchAction( + SyncActionDescriptor.create(RerunEditorSearchAction, RerunEditorSearchAction.ID, RerunEditorSearchAction.LABEL, + { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.KEY_R }, + ContextKeyExpr.and(EditorContextKeys.languageId.isEqualTo('search-result'))), + 'Search Editor: Search Again', category, + ContextKeyExpr.and(EditorContextKeys.languageId.isEqualTo('search-result'))); + // Register Quick Open Handler Registry.as(QuickOpenExtensions.Quickopen).registerDefaultQuickOpenHandler( diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 8cb3ff896be..c228d3a1218 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -13,7 +13,7 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { ILabelService } from 'vs/platform/label/common/label'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { getSelectionKeyboardEvent, WorkbenchObjectTree } from 'vs/platform/list/browser/listService'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; @@ -23,12 +23,15 @@ import { FolderMatch, FileMatch, FileMatchOrMatch, FolderMatchWithResource, Matc import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ISearchConfiguration, VIEWLET_ID, PANEL_ID } from 'vs/workbench/services/search/common/search'; +import { ISearchConfiguration, VIEWLET_ID, PANEL_ID, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; import { ISearchHistoryService } from 'vs/workbench/contrib/search/common/searchHistoryService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet'; import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel'; import { ITreeNavigator } from 'vs/base/browser/ui/tree/tree'; +import { createEditorFromSearchResult, refreshActiveEditorSearch } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; export function isSearchViewFocused(viewletService: IViewletService, panelService: IPanelService): boolean { const searchView = getSearchView(viewletService, panelService); @@ -418,6 +421,63 @@ export class CancelSearchAction extends Action { } } +export class OpenResultsInEditorAction extends Action { + + static readonly ID: string = Constants.OpenInEditorCommandId; + static readonly LABEL = nls.localize('search.openResultsInEditor', "Open Results in Editor"); + + constructor(id: string, label: string, + @IViewletService private viewletService: IViewletService, + @IPanelService private panelService: IPanelService, + @ILabelService private labelService: ILabelService, + @IEditorService private editorService: IEditorService, + @IConfigurationService private configurationService: IConfigurationService + ) { + super(id, label, 'codicon-go-to-file'); + } + + get enabled(): boolean { + const searchView = getSearchView(this.viewletService, this.panelService); + return !!searchView && searchView.hasSearchResults(); + } + + update() { + this._setEnabled(this.enabled); + } + + async run() { + const searchView = getSearchView(this.viewletService, this.panelService); + if (searchView && this.configurationService.getValue('search').enableSearchEditorPreview) { + await createEditorFromSearchResult(searchView.searchResult, searchView.searchIncludePattern.getValue(), searchView.searchExcludePattern.getValue(), this.labelService, this.editorService); + } + } +} + +export class RerunEditorSearchAction extends Action { + + static readonly ID: string = Constants.RerunEditorSearchCommandId; + static readonly LABEL = nls.localize('search.rerunEditorSearch', "Search Again"); + + constructor(id: string, label: string, + @IInstantiationService private instantiationService: IInstantiationService, + @IEditorService private editorService: IEditorService, + @IConfigurationService private configurationService: IConfigurationService, + @IWorkspaceContextService private contextService: IWorkspaceContextService, + @ILabelService private labelService: ILabelService, + @IProgressService private progressService: IProgressService + ) { + super(id, label); + } + + async run() { + if (this.configurationService.getValue('search').enableSearchEditorPreview) { + await this.progressService.withProgress({ location: ProgressLocation.Window }, + () => refreshActiveEditorSearch(this.editorService, this.instantiationService, this.contextService, this.labelService, this.configurationService)); + } + } +} + + export class FocusNextSearchResultAction extends Action { static readonly ID = 'search.action.focusNextSearchResult'; static readonly LABEL = nls.localize('FocusNextSearchResult.label', "Focus Next Search Result"); diff --git a/src/vs/workbench/contrib/search/browser/searchEditor.ts b/src/vs/workbench/contrib/search/browser/searchEditor.ts new file mode 100644 index 00000000000..4f515b013b1 --- /dev/null +++ b/src/vs/workbench/contrib/search/browser/searchEditor.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Match, searchMatchComparer, FileMatch, SearchResult, SearchModel } from 'vs/workbench/contrib/search/common/searchModel'; +import { repeat } from 'vs/base/common/strings'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { coalesce, flatten } from 'vs/base/common/arrays'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { URI } from 'vs/base/common/uri'; +import { ITextQuery, IPatternInfo, ISearchConfigurationProperties } from 'vs/workbench/services/search/common/search'; +import * as network from 'vs/base/common/network'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; +import { getOutOfWorkspaceEditorResources } from 'vs/workbench/contrib/search/common/search'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { searchEditorFindMatch, searchEditorFindMatchBorder } from 'vs/platform/theme/common/colorRegistry'; + +// Using \r\n on Windows inserts an extra newline between results. +const lineDelimiter = '\n'; + +const translateRangeLines = + (n: number) => + (range: Range) => + new Range(range.startLineNumber + n, range.startColumn, range.endLineNumber + n, range.endColumn); + +const matchToSearchResultFormat = (match: Match): { line: string, ranges: Range[], lineNumber: string }[] => { + const getLinePrefix = (i: number) => `${match.range().startLineNumber + i}`; + + const fullMatchLines = match.fullPreviewLines(); + const largestPrefixSize = fullMatchLines.reduce((largest, _, i) => Math.max(getLinePrefix(i).length, largest), 0); + + + const results: { line: string, ranges: Range[], lineNumber: string }[] = []; + + fullMatchLines + .forEach((sourceLine, i) => { + const lineNumber = getLinePrefix(i); + const paddingStr = repeat(' ', largestPrefixSize - lineNumber.length); + const prefix = ` ${lineNumber}: ${paddingStr}`; + const prefixOffset = prefix.length; + + const line = (prefix + sourceLine); + + const rangeOnThisLine = ({ start, end }: { start?: number; end?: number; }) => new Range(1, (start ?? 1) + prefixOffset, 1, (end ?? sourceLine.length + 1) + prefixOffset); + + const matchRange = match.range(); + const matchIsSingleLine = matchRange.startLineNumber === matchRange.endLineNumber; + + let lineRange; + if (matchIsSingleLine) { lineRange = (rangeOnThisLine({ start: matchRange.startColumn, end: matchRange.endColumn })); } + else if (i === 0) { lineRange = (rangeOnThisLine({ start: matchRange.startColumn })); } + else if (i === fullMatchLines.length - 1) { lineRange = (rangeOnThisLine({ end: matchRange.endColumn })); } + else { lineRange = (rangeOnThisLine({})); } + + results.push({ lineNumber: lineNumber, line, ranges: [lineRange] }); + }); + + return results; +}; + +type SearchResultSerialization = { text: string[], matchRanges: Range[] }; +function fileMatchToSearchResultFormat(fileMatch: FileMatch, labelFormatter: (x: URI) => string): SearchResultSerialization { + const serializedMatches = flatten(fileMatch.matches() + .sort(searchMatchComparer) + .map(match => matchToSearchResultFormat(match))); + + const uriString = labelFormatter(fileMatch.resource); + let text: string[] = [`${uriString}:`]; + let matchRanges: Range[] = []; + + const targetLineNumberToOffset: Record = {}; + + const seenLines = new Set(); + serializedMatches.forEach(match => { + if (!seenLines.has(match.line)) { + targetLineNumberToOffset[match.lineNumber] = text.length; + seenLines.add(match.line); + text.push(match.line); + } + + matchRanges.push(...match.ranges.map(translateRangeLines(targetLineNumberToOffset[match.lineNumber]))); + }); + + + return { text, matchRanges }; +} + +const flattenSearchResultSerializations = (serializations: SearchResultSerialization[]): SearchResultSerialization => { + let text: string[] = []; + let matchRanges: Range[] = []; + + serializations.forEach(serialized => { + serialized.matchRanges.map(translateRangeLines(text.length)).forEach(range => matchRanges.push(range)); + serialized.text.forEach(line => text.push(line)); + text.push(''); // new line + }); + + return { text, matchRanges }; +}; + +const contentPatternToSearchResultHeader = (pattern: ITextQuery | null, includes: string, excludes: string): string[] => { + if (!pattern) { return []; } + + const removeNullFalseAndUndefined = (a: (T | null | false | undefined)[]) => a.filter(a => a !== false && a !== null && a !== undefined) as T[]; + + const escapeNewlines = (str: string) => str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n'); + + return removeNullFalseAndUndefined([ + `# Query: ${escapeNewlines(pattern.contentPattern.pattern)}`, + + (pattern.contentPattern.isCaseSensitive || pattern.contentPattern.isWordMatch || pattern.contentPattern.isRegExp || pattern.userDisabledExcludesAndIgnoreFiles) + && `# Flags: ${coalesce([ + pattern.contentPattern.isCaseSensitive && 'CaseSensitive', + pattern.contentPattern.isWordMatch && 'WordMatch', + pattern.contentPattern.isRegExp && 'RegExp', + pattern.userDisabledExcludesAndIgnoreFiles && 'IgnoreExcludeSettings' + ]).join(' ')}`, + includes ? `# Including: ${includes}` : undefined, + excludes ? `# Excluding: ${excludes}` : undefined, + '' + ]); +}; + +const searchHeaderToContentPattern = (header: string[]): { pattern: string, flags: { regex: boolean, wholeWord: boolean, caseSensitive: boolean, ignoreExcludes: boolean }, includes: string, excludes: string } => { + const query = { + pattern: '', + flags: { regex: false, caseSensitive: false, ignoreExcludes: false, wholeWord: false }, + includes: '', + excludes: '' + }; + + const unescapeNewlines = (str: string) => str.replace(/\\\\/g, '\\').replace(/\\n/g, '\n'); + const parseYML = /^# ([^:]*): (.*)$/; + for (const line of header) { + const parsed = parseYML.exec(line); + if (!parsed) { continue; } + const [, key, value] = parsed; + switch (key) { + case 'Query': query.pattern = unescapeNewlines(value); break; + case 'Including': query.includes = value; break; + case 'Excluding': query.excludes = value; break; + case 'Flags': { + query.flags = { + regex: value.indexOf('RegExp') !== -1, + caseSensitive: value.indexOf('CaseSensitive') !== -1, + ignoreExcludes: value.indexOf('IgnoreExcludeSettings') !== -1, + wholeWord: value.indexOf('WordMatch') !== -1 + }; + } + } + } + + return query; +}; + +const serializeSearchResultForEditor = (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelFormatter: (x: URI) => string): SearchResultSerialization => { + const header = contentPatternToSearchResultHeader(searchResult.query, rawIncludePattern, rawExcludePattern); + const allResults = + flattenSearchResultSerializations( + flatten(searchResult.folderMatches().sort(searchMatchComparer) + .map(folderMatch => folderMatch.matches().sort(searchMatchComparer) + .map(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); + + return { matchRanges: allResults.matchRanges.map(translateRangeLines(header.length)), text: header.concat(allResults.text) }; +}; + +export const refreshActiveEditorSearch = + async (editorService: IEditorService, instantiationService: IInstantiationService, contextService: IWorkspaceContextService, labelService: ILabelService, configurationService: IConfigurationService) => { + const model = editorService.activeTextEditorWidget?.getModel(); + if (!model) { return; } + + const textModel = model as ITextModel; + + const header = textModel.getValueInRange(new Range(1, 1, 5, 1)) + .split(lineDelimiter) + .filter(line => line.indexOf('# ') === 0); + + const contentPattern = searchHeaderToContentPattern(header); + + const content: IPatternInfo = { + pattern: contentPattern.pattern, + isRegExp: contentPattern.flags.regex, + isCaseSensitive: contentPattern.flags.caseSensitive, + isWordMatch: contentPattern.flags.wholeWord + }; + + const options: ITextQueryBuilderOptions = { + _reason: 'searchEditor', + extraFileResources: instantiationService.invokeFunction(getOutOfWorkspaceEditorResources), + maxResults: 10000, + disregardIgnoreFiles: contentPattern.flags.ignoreExcludes, + disregardExcludeSettings: contentPattern.flags.ignoreExcludes, + excludePattern: contentPattern.excludes, + includePattern: contentPattern.includes, + previewOptions: { + matchLines: 1, + charsPerLine: 1000 + }, + isSmartCase: configurationService.getValue('search').smartCase, + expandPatterns: true + }; + + const folderResources = contextService.getWorkspace().folders; + + let query: ITextQuery; + try { + const queryBuilder = instantiationService.createInstance(QueryBuilder); + query = queryBuilder.text(content, folderResources.map(folder => folder.uri), options); + } catch (err) { + return; + } + + const searchModel = instantiationService.createInstance(SearchModel); + await searchModel.search(query); + + const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); + const results = serializeSearchResultForEditor(searchModel.searchResult, '', '', labelFormatter); + + textModel.setValue(results.text.join(lineDelimiter)); + textModel.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + }; + + +export const createEditorFromSearchResult = + async (searchResult: SearchResult, rawIncludePattern: string, rawExcludePattern: string, labelService: ILabelService, editorService: IEditorService) => { + const searchTerm = searchResult.query?.contentPattern.pattern.replace(/[^\w-_. ]/g, '') || 'Search'; + + const labelFormatter = (uri: URI): string => labelService.getUriLabel(uri, { relative: true }); + + const results = serializeSearchResultForEditor(searchResult, rawIncludePattern, rawExcludePattern, labelFormatter); + + let possible = { + contents: results.text.join(lineDelimiter), + mode: 'search-result', + resource: URI.from({ scheme: network.Schemas.untitled, path: searchTerm }) + }; + + let id = 0; + while (editorService.getOpened(possible)) { + possible.resource = possible.resource.with({ path: searchTerm + '-' + ++id }); + } + + const editor = await editorService.openEditor(possible); + const control = editor?.getControl()!; + control.updateOptions({ lineNumbers: 'off' }); + + const model = control.getModel() as ITextModel; + + model.deltaDecorations([], results.matchRanges.map(range => ({ range, options: { className: 'searchEditorFindMatch', stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges } }))); + }; + +// theming +registerThemingParticipant((theme, collector) => { + collector.addRule(`.monaco-editor .searchEditorFindMatch { background-color: ${theme.getColor(searchEditorFindMatch)}; }`); + + const findMatchHighlightBorder = theme.getColor(searchEditorFindMatchBorder); + if (findMatchHighlightBorder) { + collector.addRule(`.monaco-editor .searchEditorFindMatch { border: 1px ${theme.type === 'hc' ? 'dotted' : 'solid'} ${findMatchHighlightBorder}; box-sizing: border-box; }`); + } +}); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index 2fc338d2dab..c814ae3a86b 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -20,7 +20,7 @@ import * as env from 'vs/base/common/platform'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchview'; -import { ICodeEditor, isCodeEditor, isDiffEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor, isDiffEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as nls from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -43,7 +43,7 @@ import { OpenFileFolderAction, OpenFolderAction } from 'vs/workbench/browser/act import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IEditor } from 'vs/workbench/common/editor'; import { ExcludePatternInputWidget, PatternInputWidget } from 'vs/workbench/contrib/search/browser/patternInputWidget'; -import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs } from 'vs/workbench/contrib/search/browser/searchActions'; +import { CancelSearchAction, ClearSearchResultsAction, CollapseDeepestExpandedLevelAction, RefreshAction, IFindInFilesArgs, OpenResultsInEditorAction, appendKeyBindingLabel } from 'vs/workbench/contrib/search/browser/searchActions'; import { FileMatchRenderer, FolderMatchRenderer, MatchRenderer, SearchAccessibilityProvider, SearchDelegate, SearchDND } from 'vs/workbench/contrib/search/browser/searchResultsView'; import { ISearchWidgetOptions, SearchWidget } from 'vs/workbench/contrib/search/browser/searchWidget'; import * as Constants from 'vs/workbench/contrib/search/common/constants'; @@ -61,7 +61,11 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Memento, MementoObject } from 'vs/workbench/common/memento'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { MultiCursorSelectionController } from 'vs/editor/contrib/multicursor/multicursor'; +import { Selection } from 'vs/editor/common/core/selection'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; +import { createEditorFromSearchResult } from 'vs/workbench/contrib/search/browser/searchEditor'; +import { ILabelService } from 'vs/platform/label/common/label'; const $ = dom.$; @@ -105,10 +109,11 @@ export class SearchView extends ViewletPane { private folderMatchFocused: IContextKey; private matchFocused: IContextKey; private hasSearchResultsKey: IContextKey; + private enableSearchEditorPreview: IContextKey; private state: SearchUIState = SearchUIState.Idle; - private actions: Array = []; + private actions: Array = []; private cancelAction: CancelSearchAction; private refreshAction: RefreshAction; private contextMenu: IMenu | null = null; @@ -161,6 +166,7 @@ export class SearchView extends ViewletPane { @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @IKeybindingService keybindingService: IKeybindingService, @IStorageService storageService: IStorageService, + @ILabelService private readonly labelService: ILabelService, @IOpenerService private readonly openerService: IOpenerService ) { super({ ...options, id: VIEW_ID, ariaHeaderLabel: nls.localize('searchView', "Search") }, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -178,6 +184,14 @@ export class SearchView extends ViewletPane { this.folderMatchFocused = Constants.FolderFocusKey.bindTo(contextKeyService); this.matchFocused = Constants.MatchFocusKey.bindTo(this.contextKeyService); this.hasSearchResultsKey = Constants.HasSearchResults.bindTo(this.contextKeyService); + this.enableSearchEditorPreview = Constants.EnableSearchEditorPreview.bindTo(this.contextKeyService); + + this.enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); + this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('search.previewSearchEditor')) { + this.enableSearchEditorPreview.set(this.searchConfig.enableSearchEditorPreview); + } + }); this.viewModel = this._register(this.searchWorkbenchService.searchModel); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); @@ -197,6 +211,13 @@ export class SearchView extends ViewletPane { this._register(this.instantiationService.createInstance(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL)), this._register(this.instantiationService.createInstance(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL)) ]; + + if (this.searchConfig.enableSearchEditorPreview) { + this.actions.push( + this._register(this.instantiationService.createInstance(OpenResultsInEditorAction, OpenResultsInEditorAction.ID, OpenResultsInEditorAction.LABEL)) + ); + } + this.refreshAction = this._register(this.instantiationService.createInstance(RefreshAction, RefreshAction.ID, RefreshAction.LABEL)); this.cancelAction = this._register(this.instantiationService.createInstance(CancelSearchAction, CancelSearchAction.ID, CancelSearchAction.LABEL)); } @@ -458,7 +479,7 @@ export class SearchView extends ViewletPane { } refreshTree(event?: IChangeEvent): void { - const collapseResults = this.configurationService.getValue('search').collapseResults; + const collapseResults = this.searchConfig.collapseResults; if (!event || event.added || event.removed) { // Refresh whole tree this.tree.setChildren(null, this.createResultIterator(collapseResults)); @@ -550,6 +571,7 @@ export class SearchView extends ViewletPane { progressComplete(); const messageEl = this.clearMessage(); dom.append(messageEl, $('p', undefined, afterReplaceAllMessage)); + this.reLayout(); }, (error) => { progressComplete(); errors.isPromiseCanceledError(error); @@ -892,7 +914,7 @@ export class SearchView extends ViewletPane { return; } - const actionsPosition = this.configurationService.getValue('search').actionsPosition; + const actionsPosition = this.searchConfig.actionsPosition; dom.toggleClass(this.getContainer(), SearchView.ACTIONS_RIGHT_CLASS_NAME, actionsPosition === 'right'); dom.toggleClass(this.getContainer(), SearchView.WIDE_CLASS_NAME, this.size.width >= SearchView.WIDE_VIEW_SIZE); @@ -1197,8 +1219,7 @@ export class SearchView extends ViewletPane { // Need the full match line to correctly calculate replace text, if this is a search/replace with regex group references ($1, $2, ...). // 10000 chars is enough to avoid sending huge amounts of text around, if you do a replace with a longer match, it may or may not resolve the group refs correctly. // https://github.com/Microsoft/vscode/issues/58374 - const charsPerLine = content.isRegExp ? 10000 : - 250; + const charsPerLine = content.isRegExp ? 10000 : 1000; const options: ITextQueryBuilderOptions = { _reason: 'searchView', @@ -1212,7 +1233,7 @@ export class SearchView extends ViewletPane { matchLines: 1, charsPerLine }, - isSmartCase: this.configurationService.getValue().search.smartCase, + isSmartCase: this.searchConfig.smartCase, expandPatterns: true }; const folderResources = this.contextService.getWorkspace().folders; @@ -1298,7 +1319,7 @@ export class SearchView extends ViewletPane { // Do final render, then expand if just 1 file with less than 50 matches this.onSearchResultsChanged(); - const collapseResults = this.configurationService.getValue('search').collapseResults; + const collapseResults = this.searchConfig.collapseResults; if (collapseResults !== 'alwaysCollapse' && this.viewModel.searchResult.matches().length === 1) { const onlyMatch = this.viewModel.searchResult.matches()[0]; if (onlyMatch.count() < 50) { @@ -1476,7 +1497,23 @@ export class SearchView extends ViewletPane { resultMsg += nls.localize('useIgnoresAndExcludesDisabled', " - exclude settings and ignore files are disabled"); } - dom.append(messageEl, $('p', undefined, resultMsg)); + if (this.searchConfig.enableSearchEditorPreview) { + dom.append(messageEl, $('span', undefined, resultMsg + ' - ')); + const span = dom.append(messageEl, $('span', undefined)); + const openInEditorLink = dom.append(span, $('a.pointer.prominent', undefined, nls.localize('openInEditor.message', "Open in editor"))); + + openInEditorLink.title = appendKeyBindingLabel( + nls.localize('openInEditor.tooltip', "Copy current search results to an editor"), + this.keybindingService.lookupKeybinding(Constants.OpenInEditorCommandId), this.keybindingService); + + this.messageDisposables.push(dom.addDisposableListener(openInEditorLink, dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, false); + createEditorFromSearchResult(this.searchResult, this.searchIncludePattern.getValue(), this.searchExcludePattern.getValue(), this.labelService, this.editorService); + })); + + } else { + dom.append(messageEl, $('p', undefined, resultMsg)); + } this.reLayout(); } else if (!msgWasHidden) { dom.hide(this.messagesElement); @@ -1567,6 +1604,38 @@ export class SearchView extends ViewletPane { }, errors.onUnexpectedError); } + openEditorWithMultiCursor(element: FileMatchOrMatch): Promise { + const resource = element instanceof Match ? element.parent().resource : (element).resource; + return this.editorService.openEditor({ + resource: resource, + options: { + preserveFocus: false, + pinned: true, + revealIfVisible: true + } + }).then(editor => { + if (editor) { + let fileMatch = null; + if (element instanceof FileMatch) { + fileMatch = element; + } + else if (element instanceof Match) { + fileMatch = element.parent(); + } + + if (fileMatch) { + const selections = fileMatch.matches().map(m => new Selection(m.range().startLineNumber, m.range().startColumn, m.range().endLineNumber, m.range().endColumn)); + const codeEditor = getCodeEditor(editor.getControl()); + if (codeEditor) { + let multiCursorController = MultiCursorSelectionController.get(codeEditor); + multiCursorController.selectAllUsingSelections(selections); + } + } + } + this.viewModel.searchResult.rangeHighlightDecorations.removeHighlightRange(); + }, errors.onUnexpectedError); + } + private getSelectionFrom(element: FileMatchOrMatch): any { let match: Match | null = null; if (element instanceof Match) { @@ -1625,6 +1694,10 @@ export class SearchView extends ViewletPane { ]; } + private get searchConfig(): ISearchConfigurationProperties { + return this.configurationService.getValue('search'); + } + private clearHistory(): void { this.searchWidget.clearHistory(); this.inputPatternExcludes.clearHistory(); diff --git a/src/vs/workbench/contrib/search/common/constants.ts b/src/vs/workbench/contrib/search/common/constants.ts index d2261e85097..483190b6498 100644 --- a/src/vs/workbench/contrib/search/common/constants.ts +++ b/src/vs/workbench/contrib/search/common/constants.ts @@ -15,6 +15,8 @@ export const RemoveActionId = 'search.action.remove'; export const CopyPathCommandId = 'search.action.copyPath'; export const CopyMatchCommandId = 'search.action.copyMatch'; export const CopyAllCommandId = 'search.action.copyAll'; +export const OpenInEditorCommandId = 'search.action.openInEditor'; +export const RerunEditorSearchCommandId = 'search.action.rerunEditorSearch'; export const ClearSearchHistoryCommandId = 'search.action.clearHistory'; export const FocusSearchListCommandID = 'search.action.focusSearchList'; export const ReplaceActionId = 'search.action.replace'; @@ -24,6 +26,7 @@ export const CloseReplaceWidgetActionId = 'closeReplaceInFilesWidget'; export const ToggleCaseSensitiveCommandId = 'toggleSearchCaseSensitive'; export const ToggleWholeWordCommandId = 'toggleSearchWholeWord'; export const ToggleRegexCommandId = 'toggleSearchRegex'; +export const AddCursorsAtSearchResults = 'addCursorsAtSearchResults'; export const RevealInSideBarForSearchResults = 'search.action.revealInSideBar'; export const ToggleSearchViewPositionCommandId = 'search.action.toggleSearchViewPosition'; @@ -37,6 +40,7 @@ export const PatternIncludesFocusedKey = new RawContextKey('patternIncl export const PatternExcludesFocusedKey = new RawContextKey('patternExcludesInputBoxFocus', false); export const ReplaceActiveKey = new RawContextKey('replaceActive', false); export const HasSearchResults = new RawContextKey('hasSearchResult', false); +export const EnableSearchEditorPreview = new RawContextKey('previewSearchEditor', false); export const FirstMatchFocusKey = new RawContextKey('firstMatchFocus', false); export const FileMatchOrMatchFocusKey = new RawContextKey('fileMatchOrMatchFocus', false); // This is actually, Match or File or Folder diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index cdd195c17e8..18ce0750f09 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -965,6 +965,12 @@ export class SearchModel extends Disposable { search(query: ITextQuery, onProgress?: (result: ISearchProgressItem) => void): Promise { this.cancelSearch(); + // Exclude Search Editor results unless explicity included + const searchEditorFilenameGlob = `**/*.code-search`; + if (!query.includePattern || !query.includePattern[searchEditorFilenameGlob]) { + query.excludePattern = { ...(query.excludePattern ?? {}), [searchEditorFilenameGlob]: true }; + } + this._searchQuery = query; if (!this.searchConfig.searchOnType) { this.searchResult.clear(); diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 987d0ad21a6..b77f7278b72 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -699,7 +699,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { + public run(task: Task | undefined, options?: ProblemMatcherRunOptions, runSource: TaskRunSource = TaskRunSource.System): Promise { if (!task) { throw new TaskError(Severity.Info, nls.localize('TaskServer.noTask', 'Task to execute is undefined'), TaskErrors.TaskNotFound); } diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index 374c2e6141d..21ed4fa7174 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -58,7 +58,7 @@ export interface ITaskService { configureAction(): Action; build(): Promise; runTest(): Promise; - run(task: Task | undefined, options?: ProblemMatcherRunOptions): Promise; + run(task: Task | undefined, options?: ProblemMatcherRunOptions): Promise; inTerminal(): boolean; isActive(): Promise; getActiveTasks(): Promise; diff --git a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts index ba3adbd5a48..648fa8617f9 100644 --- a/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/node/processTaskSystem.ts @@ -91,7 +91,7 @@ export class ProcessTaskSystem implements ITaskSystem { } public getBusyTasks(): Task[] { - return this.getActiveTasks(); + return []; } public run(task: Task): ITaskExecuteResult { diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 900c99707e3..661e638a8e6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand, RenameWithArgTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_PANEL_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -590,6 +590,7 @@ if (BrowserFeatures.clipboard.readText) { }] } })).register(); + (new CreateNewWithCwdTerminalCommand({ id: CreateNewWithCwdTerminalCommand.ID, precondition: undefined, @@ -611,6 +612,28 @@ if (BrowserFeatures.clipboard.readText) { } })).register(); +(new RenameWithArgTerminalCommand({ + id: RenameWithArgTerminalCommand.ID, + precondition: undefined, + description: { + description: RenameWithArgTerminalCommand.LABEL, + args: [{ + name: 'args', + schema: { + type: 'object', + required: ['name'], + properties: { + name: { + description: RenameWithArgTerminalCommand.NAME_ARG_LABEL, + type: 'string', + minLength: 1 + } + } + } + }] + } +})).register(); + setupTerminalCommands(); setupTerminalMenu(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 45973bb6d7b..d0d206b55e2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1055,6 +1055,31 @@ export class RenameTerminalAction extends Action { }); } } +export class RenameWithArgTerminalCommand extends Command { + public static readonly ID = TERMINAL_COMMAND_ID.RENAME_WITH_ARG; + public static readonly LABEL = nls.localize('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"); + public static readonly NAME_ARG_LABEL = nls.localize('workbench.action.terminal.renameWithArg.name', "The new name for the terminal"); + + public runCommand( + accessor: ServicesAccessor, + args?: { name?: string } + ): void { + const notificationService = accessor.get(INotificationService); + const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); + + if (!terminalInstance) { + notificationService.warn(nls.localize('workbench.action.terminal.renameWithArg.noTerminal', "No active terminal to rename")); + return; + } + + if (!args || !args.name) { + notificationService.warn(nls.localize('workbench.action.terminal.renameWithArg.noName', "No name argument provided")); + return; + } + + terminalInstance.setTitle(args.name, TitleEventSource.Api); + } +} export class FocusTerminalFindWidgetAction extends Action { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 77e3a5e70b7..8668c77070a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -472,7 +472,8 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { fastScrollModifier: 'alt', fastScrollSensitivity: editorOptions.fastScrollSensitivity, scrollSensitivity: editorOptions.mouseWheelScrollSensitivity, - rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType + rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType, + wordSeparator: ' ()[]{}\',:;"`' }); this._xterm = xterm; this._xtermCore = (xterm as any)._core as XTermCore; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 11d8cc113c8..2700e31e8ac 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -453,6 +453,7 @@ export const enum TERMINAL_COMMAND_ID { CLEAR_SELECTION = 'workbench.action.terminal.clearSelection', MANAGE_WORKSPACE_SHELL_PERMISSIONS = 'workbench.action.terminal.manageWorkspaceShellPermissions', RENAME = 'workbench.action.terminal.rename', + RENAME_WITH_ARG = 'workbench.action.terminal.renameWithArg', FIND_WIDGET_FOCUS = 'workbench.action.terminal.focusFindWidget', FIND_WIDGET_HIDE = 'workbench.action.terminal.hideFindWidget', QUICK_OPEN_TERM = 'workbench.action.quickOpenTerm', diff --git a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts index 945e6be9f4b..16b5b7e7d1d 100644 --- a/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts +++ b/src/vs/workbench/contrib/webview/browser/dynamicWebviewEditorOverlay.ts @@ -99,7 +99,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd if (this._options.tryRestoreScrollPosition) { webview.initialScrollProgress = this._initialScrollProgress; } - this._webview.value.mountTo(this.container); + webview.mountTo(this.container); // Forward events from inner webview to outer listeners this._webviewEvents.clear(); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 4577a20a046..eb930086188 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -6,12 +6,12 @@ import * as dom from 'vs/base/browser/dom'; import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { UnownedDisposable as Unowned } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { EditorInput, EditorModel, GroupIdentifier, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; +import { Emitter } from 'vs/base/common/event'; const WebviewPanelResourceScheme = 'webview-panel'; @@ -70,20 +70,31 @@ export class WebviewInput extends EditorInput { private _name: string; private _iconPath?: { light: URI, dark: URI }; private _group?: GroupIdentifier; + private readonly _webview: Lazy; + private _didSomeoneTakeMyWebview = false; + + private readonly _onDisposeWebview = this._register(new Emitter()); + readonly onDisposeWebview = this._onDisposeWebview.event; constructor( public readonly id: string, public readonly viewType: string, name: string, - webview: Lazy>, + webview: Lazy, @ILifecycleService private readonly lifecycleService: ILifecycleService, ) { super(); - this._name = name; + this._webview = webview; + } - this._webview = webview.map(value => this._register(value.acquire())); // The input owns this webview + dispose() { + super.dispose(); + if (!this._didSomeoneTakeMyWebview) { + this._webview?.rawValue?.dispose(); + this._onDisposeWebview.fire(); + } } public getTypeId(): string { @@ -119,7 +130,7 @@ export class WebviewInput extends EditorInput { } public get extension() { - return this._webview.getValue().extension; + return this.webview.extension; } public get iconPath() { @@ -150,4 +161,12 @@ export class WebviewInput extends EditorInput { public supportsSplitEditor() { return false; } + + protected takeOwnershipOfWebview(): WebviewEditorOverlay | undefined { + if (this._didSomeoneTakeMyWebview) { + return undefined; + } + this._didSomeoneTakeMyWebview = true; + return this.webview; + } } diff --git a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts index ee635fbe60a..67011891137 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewWorkbenchService.ts @@ -6,7 +6,7 @@ import { equals } from 'vs/base/common/arrays'; import { memoize } from 'vs/base/common/decorators'; import { Lazy } from 'vs/base/common/lazy'; -import { IDisposable, toDisposable, UnownedDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { values } from 'vs/base/common/map'; import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -108,7 +108,7 @@ export class LazilyResolvedWebviewEditorInput extends WebviewInput { id: string, viewType: string, name: string, - webview: Lazy>, + webview: Lazy, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, @ILifecycleService lifeCycleService: ILifecycleService, ) { @@ -161,7 +161,7 @@ export class WebviewEditorService implements IWebviewWorkbenchService { options: WebviewInputOptions, extension: WebviewExtensionDescription | undefined, ): WebviewInput { - const webview = new Lazy(() => new UnownedDisposable(this.createWebiew(id, extension, options))); + const webview = new Lazy(() => this.createWebiew(id, extension, options)); const webviewInput = this._instantiationService.createInstance(WebviewInput, id, viewType, title, webview); this._editorService.openEditor(webviewInput, { pinned: true, @@ -206,7 +206,7 @@ export class WebviewEditorService implements IWebviewWorkbenchService { const webview = new Lazy(() => { const webview = this.createWebiew(id, extension, options); webview.state = state; - return new UnownedDisposable(webview); + return webview; }); const webviewInput = this._instantiationService.createInstance(LazilyResolvedWebviewEditorInput, id, viewType, title, webview); diff --git a/src/vs/workbench/services/configuration/common/configurationEditingService.ts b/src/vs/workbench/services/configuration/common/configurationEditingService.ts index d0113b72bd7..55662338cdb 100644 --- a/src/vs/workbench/services/configuration/common/configurationEditingService.ts +++ b/src/vs/workbench/services/configuration/common/configurationEditingService.ts @@ -158,7 +158,7 @@ export class ConfigurationEditingService { writeConfiguration(target: EditableConfigurationTarget, value: IConfigurationValue, options: IConfigurationEditingOptions = {}): Promise { const operation = this.getConfigurationEditOperation(target, value, options.scopes || {}); return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(operation, options) // queue up writes to prevent race conditions - .then(() => null, + .then(() => { }, error => { if (!options.donotNotifyError) { this.onError(error, operation, options.scopes); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index c64ba121c87..839923c54bc 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -15,7 +15,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { isUndefinedOrNull } from 'vs/base/common/types'; import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { canExecuteOnUI } from 'vs/workbench/services/extensions/common/extensionsUtil'; +import { getExtensionKind } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -147,11 +147,9 @@ export class ExtensionEnablementService extends Disposable implements IExtension private _isDisabledByExtensionKind(extension: IExtension): boolean { if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) { - if (!canExecuteOnUI(extension.manifest, this.productService, this.configurationService)) { - // workspace extensions must run on the remote, but UI extensions can run on either side - const server = this.extensionManagementServerService.remoteExtensionManagementServer; - return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; - } + const extensionKind = getExtensionKind(extension.manifest, this.productService, this.configurationService); + const server = extensionKind[0] === 'ui' ? this.extensionManagementServerService.localExtensionManagementServer : this.extensionManagementServerService.remoteExtensionManagementServer; + return this.extensionManagementServerService.getExtensionManagementServer(extension.location) !== server; } return false; } diff --git a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts index 0e4e725ae64..ca0d803ed5d 100644 --- a/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts +++ b/src/vs/workbench/services/extensionManagement/test/electron-browser/extensionEnablementService.test.ts @@ -437,8 +437,8 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); - assert.ok(testObject.isEnabled(localWorkspaceExtension)); - assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.EnabledGlobally); + assert.ok(!testObject.isEnabled(localWorkspaceExtension)); + assert.deepEqual(testObject.getEnablementState(localWorkspaceExtension), EnablementState.DisabledByExtensionKind); }); test('test remote workspace extension is not disabled by kind', async () => { @@ -453,7 +453,7 @@ suite('ExtensionEnablementService Test', () => { instantiationService.stub(IExtensionManagementServerService, aMultiExtensionManagementServerService(instantiationService)); const localWorkspaceExtension = aLocalExtension2('pub.a', { extensionKind: 'ui' }, { location: URI.file(`pub.a`).with({ scheme: Schemas.vscodeRemote }) }); testObject = new TestExtensionEnablementService(instantiationService); - assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), true); + assert.equal(testObject.canChangeEnablement(localWorkspaceExtension), false); }); test('test canChangeEnablement return true for remote workspace extension', () => { diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 9955c682758..0f35c544319 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -27,6 +27,17 @@ interface ParsedExtHostArgs { uriTransformerPath?: string; } +// workaround for https://github.com/microsoft/vscode/issues/85490 +// remove --inspect-port=0 after start so that it doesn't trigger LSP debugging +(function removeInspectPort() { + for (let i = 0; i < process.execArgv.length; i++) { + if (process.execArgv[i] === '--inspect-port=0') { + process.execArgv.splice(i, 1); + i--; + } + } +})(); + const args = minimist(process.argv.slice(2), { string: [ 'uriTransformerPath' diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 57fd425f6e6..9c842761a68 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -331,6 +331,8 @@ export interface ISearchConfigurationProperties { collapseResults: 'auto' | 'alwaysCollapse' | 'alwaysExpand'; searchOnType: boolean; searchOnTypeDebouncePeriod: number; + enableSearchEditorPreview: boolean; + searchEditorPreviewForceAbsolutePaths: boolean; } export interface ISearchConfiguration extends IFilesConfiguration { diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index b9c252c2ca0..ddbe6ecb49e 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -391,6 +391,12 @@ export class SearchService extends Disposable implements ISearchService { return; } + // Skip search results + if (model.getModeId() === 'search-result' && !(query.includePattern && query.includePattern['**/*.code-search'])) { + // TODO: untitled search editors will be excluded from search even when include *.code-search is specified + return; + } + // Support untitled files if (resource.scheme === Schemas.untitled) { if (!this.untitledTextEditorService.exists(resource)) { diff --git a/src/vs/workbench/services/search/node/rawSearchService.ts b/src/vs/workbench/services/search/node/rawSearchService.ts index c0d0e6d6358..04fb0cf5e1f 100644 --- a/src/vs/workbench/services/search/node/rawSearchService.ts +++ b/src/vs/workbench/services/search/node/rawSearchService.ts @@ -383,7 +383,7 @@ export class SearchService implements IRawSearchService { cancel() { // Do nothing } - then(resolve: any, reject: any) { + then(resolve?: ((value: C) => TResult1 | Promise) | undefined | null, reject?: ((reason: any) => TResult2 | Promise) | undefined | null): Promise { return promise.then(resolve, reject); } catch(reject?: any) { diff --git a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts index 99d627ed559..f10a77afa63 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -885,16 +885,17 @@ suite('ExtHostLanguageFeatureCommands', function () { await rpcProtocol.sync(); - const root = await commands.executeCommand('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0)); + const root = await commands.executeCommand('vscode.prepareCallHierarchy', model.uri, new types.Position(0, 0)); - assert.ok(root); - assert.equal(root.name, 'ROOT'); + assert.ok(Array.isArray(root)); + assert.equal(root.length, 1); + assert.equal(root[0].name, 'ROOT'); - const incoming = await commands.executeCommand('vscode.provideIncomingCalls', root); + const incoming = await commands.executeCommand('vscode.provideIncomingCalls', root[0]); assert.equal(incoming.length, 1); assert.equal(incoming[0].from.name, 'INCOMING'); - const outgoing = await commands.executeCommand('vscode.provideOutgoingCalls', root); + const outgoing = await commands.executeCommand('vscode.provideOutgoingCalls', root[0]); assert.equal(outgoing.length, 1); assert.equal(outgoing[0].to.name, 'OUTGOING'); }); diff --git a/yarn.lock b/yarn.lock index fde045af196..a4b936375f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9369,15 +9369,15 @@ xterm-addon-web-links@0.2.1: resolved "https://registry.yarnpkg.com/xterm-addon-web-links/-/xterm-addon-web-links-0.2.1.tgz#6d1f2ce613e09870badf17615e7a1170a31542b2" integrity sha512-2KnHtiq0IG7hfwv3jw2/jQeH1RBk2d5CH4zvgwQe00rLofSJqSfgnJ7gwowxxpGHrpbPr6Lv4AmH/joaNw2+HQ== -xterm-addon-webgl@0.4.0-beta9: - version "0.4.0-beta9" - resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta9.tgz#3e004d5cd893ae678e2537b195ae0eed4d689df3" - integrity sha512-+lUsUAx4ATyetRuTuEorUpKD5NpDUUc5Z3chtYV8ECiTJYiDr0CfAxW9oa3tT8BVO7fOTdgxgJpQmsU4LGEm5A== +xterm-addon-webgl@0.4.0-beta.11: + version "0.4.0-beta.11" + resolved "https://registry.yarnpkg.com/xterm-addon-webgl/-/xterm-addon-webgl-0.4.0-beta.11.tgz#0e4a7242e2353cf74aba55e5a5bdc0b4ec87ad10" + integrity sha512-AteDxm1RFy1WnjY9r5iJSETozLebvUkR+jextdZk/ASsK21vsYK0DuVWwRI8afgiN2hUVhxcxuHEJUOV+CJDQA== -xterm@4.3.0-beta24: - version "4.3.0-beta24" - resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta24.tgz#a78684ac1d1bd263f3086ec4190d00cf9ce51fb9" - integrity sha512-N6slYV/c02hxTVgh21JvphBKkMTdvzljqFM01Inx9rriO4rVZEn39ZX/Sfer9Qm+vlgCvMtOm4pn0bCsK2OZdg== +xterm@4.3.0-beta.28: + version "4.3.0-beta.28" + resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.3.0-beta.28.tgz#80f7c4ba8f6ee3c953e6f33f8ce5aef08d5a8354" + integrity sha512-WWZ4XCvce5h+klL6ObwtMauJff/n2KGGOwJJkDbJhrAjVy2a77GKgAedJTDDFGgKJ6ix1d7puHtVSSKflIVaDQ== y18n@^3.2.1: version "3.2.1"