diff --git a/build/package.json b/build/package.json index 2893570167..07773220ce 100644 --- a/build/package.json +++ b/build/package.json @@ -47,7 +47,7 @@ "service-downloader": "github:anthonydresser/service-downloader#0.1.7", "terser": "4.3.8", "tslint": "^5.9.1", - "typescript": "3.7.2", + "typescript": "3.7.3", "vsce": "1.48.0", "vscode-telemetry-extractor": "^1.5.4", "xml2js": "^0.4.17" diff --git a/build/yarn.lock b/build/yarn.lock index e39467f9f3..0715af3591 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -3806,10 +3806,10 @@ typed-rest-client@^0.9.0: tunnel "0.0.4" underscore "1.8.3" -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" + integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== typescript@^3.0.1: version "3.5.3" diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 560c4a5d01..5c3344d814 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -392,7 +392,7 @@ export class Model { if (hint instanceof Uri) { let resourcePath: string; - if (hint.scheme === 'git') { + if (hint.scheme === 'git' || hint.scheme === 'gitfs') { resourcePath = fromGitUri(hint).path; } else { resourcePath = hint.fsPath; diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index 477fff2313..1babbfd546 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/e3091a421bdcad527018c897652ded47585cbd12", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/8fbbc11a6bb917f287bbe21d0573454020599547", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2623,4 +2623,4 @@ "name": "markup.inline.raw.string.markdown" } } -} +} \ No newline at end of file diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index bcc21a57aa..8503fb393e 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -346,7 +346,7 @@ "mocha-junit-reporter": "^1.17.0", "mocha-multi-reporters": "^1.1.7", "ts-loader": "^6.2.1", - "typescript": "^3.7.2", + "typescript": "^3.7.3", "vscode": "^1.1.10", "webpack": "^4.41.2", "webpack-cli": "^3.3.0" diff --git a/extensions/markdown-language-features/yarn.lock b/extensions/markdown-language-features/yarn.lock index 956c457152..8ec43a6d0e 100644 --- a/extensions/markdown-language-features/yarn.lock +++ b/extensions/markdown-language-features/yarn.lock @@ -4528,10 +4528,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@^3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" + integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== uc.micro@^1.0.1: version "1.0.3" diff --git a/extensions/search-result/package.json b/extensions/search-result/package.json index 5e7482b603..7442c05b8e 100644 --- a/extensions/search-result/package.json +++ b/extensions/search-result/package.json @@ -16,6 +16,7 @@ "*" ], "scripts": { + "generate-grammar": "node ./syntaxes/generateTMLanguage.js", "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:search-result ./tsconfig.json" }, "contributes": { diff --git a/extensions/search-result/src/extension.ts b/extensions/search-result/src/extension.ts index 54421536d4..1ac0146401 100644 --- a/extensions/search-result/src/extension.ts +++ b/extensions/search-result/src/extension.ts @@ -12,9 +12,27 @@ const SEARCH_RESULT_SELECTOR = { language: 'search-result' }; const DIRECTIVES = ['# Query:', '# Flags:', '# Including:', '# Excluding:', '# ContextLines:']; const FLAGS = ['RegExp', 'CaseSensitive', 'IgnoreExcludeSettings', 'WordMatch']; -let cachedLastParse: { version: number, parse: ParsedSearchResults } | undefined; +let cachedLastParse: { version: number, parse: ParsedSearchResults, uri: vscode.Uri } | undefined; +let documentChangeListener: vscode.Disposable | undefined; + export function activate(context: vscode.ExtensionContext) { + + const contextLineDecorations = vscode.window.createTextEditorDecorationType({ opacity: '0.7' }); + const matchLineDecorations = vscode.window.createTextEditorDecorationType({ fontWeight: 'bold' }); + + const decorate = (editor: vscode.TextEditor) => { + const parsed = parseSearchResults(editor.document).filter(isResultLine); + const contextRanges = parsed.filter(line => line.isContext).map(line => line.prefixRange); + const matchRanges = parsed.filter(line => !line.isContext).map(line => line.prefixRange); + editor.setDecorations(contextLineDecorations, contextRanges); + editor.setDecorations(matchLineDecorations, matchRanges); + }; + + if (vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.languageId === 'search-result') { + decorate(vscode.window.activeTextEditor); + } + context.subscriptions.push( vscode.commands.registerCommand('searchResult.rerunSearch', () => vscode.commands.executeCommand('search.action.rerunEditorSearch')), vscode.commands.registerCommand('searchResult.rerunSearchWithContext', () => vscode.commands.executeCommand('search.action.rerunEditorSearchWithContext')), @@ -84,15 +102,24 @@ export function activate(context: vscode.ExtensionContext) { } }), - vscode.window.onDidChangeActiveTextEditor(e => { - if (e?.document.languageId === 'search-result') { + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor?.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; + + documentChangeListener?.dispose(); + documentChangeListener = vscode.workspace.onDidChangeTextDocument(doc => { + if (doc.document.uri === editor.document.uri) { + decorate(editor); + } + }); + + decorate(editor); } }), - { dispose() { cachedLastParse = undefined; } } + { dispose() { cachedLastParse = undefined; documentChangeListener?.dispose(); } } ); } @@ -129,14 +156,15 @@ function relativePathToUri(path: string, resultsUri: vscode.Uri): vscode.Uri | u } type ParsedSearchFileLine = { type: 'file', location: vscode.LocationLink, allLocations: vscode.LocationLink[], path: string }; -type ParsedSearchResultLine = { type: 'result', location: vscode.LocationLink }; +type ParsedSearchResultLine = { type: 'result', location: vscode.LocationLink, isContext: boolean, prefixRange: vscode.Range }; type ParsedSearchResults = Array; const isFileLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchFileLine => line.type === 'file'; +const isResultLine = (line: ParsedSearchResultLine | ParsedSearchFileLine): line is ParsedSearchResultLine => line.type === 'result'; -function parseSearchResults(document: vscode.TextDocument, token: vscode.CancellationToken): ParsedSearchResults { +function parseSearchResults(document: vscode.TextDocument, token?: vscode.CancellationToken): ParsedSearchResults { - if (cachedLastParse && cachedLastParse.version === document.version) { + if (cachedLastParse && cachedLastParse.uri === document.uri && cachedLastParse.version === document.version) { return cachedLastParse.parse; } @@ -147,7 +175,8 @@ function parseSearchResults(document: vscode.TextDocument, token: vscode.Cancell let currentTargetLocations: vscode.LocationLink[] | undefined = undefined; for (let i = 0; i < lines.length; i++) { - if (token.isCancellationRequested) { return []; } + // TODO: This is probably always false, given we're pegging the thread... + if (token?.isCancellationRequested) { return []; } const line = lines[i]; const fileLine = FILE_LINE_REGEX.exec(line); @@ -186,13 +215,14 @@ function parseSearchResults(document: vscode.TextDocument, token: vscode.Cancell currentTargetLocations?.push(location); - links[i] = { type: 'result', location }; + links[i] = { type: 'result', location, isContext: seperator === ' ', prefixRange: new vscode.Range(i, 0, i, metadataOffset) }; } } cachedLastParse = { version: document.version, - parse: links + parse: links, + uri: document.uri }; return links; diff --git a/extensions/search-result/syntaxes/generateTMLanguage.js b/extensions/search-result/syntaxes/generateTMLanguage.js new file mode 100644 index 0000000000..1024585089 --- /dev/null +++ b/extensions/search-result/syntaxes/generateTMLanguage.js @@ -0,0 +1,243 @@ +// @ts-check + +const mappings = [ + ['bat', 'source.batchfile'], + ['c', 'source.c'], + ['cc', 'source.cpp'], + ['clj', 'source.clojure'], + ['coffee', 'source.coffee'], + ['cpp', 'source.cpp'], + ['cs', 'source.cs'], + ['cshtml', 'text.html.cshtml'], + ['css', 'source.css'], + ['dart', 'source.dart'], + ['diff', 'source.diff'], + ['dockerfile', 'source.dockerfile', '(?:dockerfile|Dockerfile)'], + ['fs', 'source.fsharp'], + ['go', 'source.go'], + ['groovy', 'source.groovy'], + ['h', 'source.objc'], + ['handlebars', 'text.html.handlebars'], + ['hbs', 'text.html.handlebars'], + ['hlsl', 'source.hlsl'], + ['hpp', 'source.objcpp'], + ['html', 'text.html.basic'], + ['ini', 'source.ini'], + ['java', 'source.java'], + ['js', 'source.js'], + ['json', 'source.json.comments'], + ['jsx', 'source.js.jsx'], + ['less', 'source.css.less'], + ['log', 'text.log'], + ['lua', 'source.lua'], + ['m', 'source.objc'], + ['makefile', 'source.makefile', '(?:makefile|Makefile)(?:\\..*)?'], + ['md', 'text.html.markdown'], + ['mm', 'source.objcpp'], + ['p6', 'source.perl.6'], + ['perl', 'source.perl'], + ['php', 'source.php'], + ['pl', 'source.perl'], + ['ps1', 'source.powershell'], + ['pug', 'text.pug'], + ['py', 'source.python'], + ['r', 'source.r'], + ['rb', 'source.ruby'], + ['rs', 'source.rust'], + ['scala', 'source.scala'], + ['scss', 'source.css.scss'], + ['sh', 'source.shell'], + ['sql', 'source.sql'], + ['swift', 'source.swift'], + ['ts', 'source.ts'], + ['tsx', 'source.tsx'], + ['vb', 'source.asp.vb.net'], + ['xml', 'text.xml'], + ['yaml', 'source.yaml'], +]; + +const scopes = { + root: 'text.searchResult', + header: { + meta: 'meta.header.search keyword.operator.word.search', + key: 'entity.other.attribute-name', + value: 'entity.other.attribute-value string.unquoted', + flags: { + keyword: 'keyword.other', + }, + contextLines: { + number: 'constant.numeric.integer', + invalid: 'invalid.illegal', + }, + query: { + escape: 'constant.character.escape', + invalid: 'invalid.illegal', + } + }, + resultBlock: { + meta: 'meta.resultBlock.search', + path: { + meta: 'string meta.path.search', + dirname: 'meta.path.dirname.search', + basename: 'meta.path.basename.search', + colon: 'punctuation.separator', + }, + result: { + meta: 'meta.resultLine.search', + metaSingleLine: 'meta.resultLine.singleLine.search', + metaMultiLine: 'meta.resultLine.multiLine.search', + prefix: { + meta: 'constant.numeric.integer meta.resultLinePrefix.search', + metaContext: 'meta.resultLinePrefix.contextLinePrefix.search', + metaMatch: 'meta.resultLinePrefix.matchLinePrefix.search', + lineNumber: 'meta.resultLinePrefix.lineNumber.search', + colon: 'punctuation.separator', + } + } + } +}; + +const repository = {}; +mappings.forEach(([ext, scope, regexp]) => + repository[ext] = { + name: scopes.resultBlock.meta, + begin: `^(?!\\s)(.*?)([^\\\\\\/\\n]*${regexp || `\\.${ext}`})(:)$`, + end: "^(?!\\s)", + beginCaptures: { + "0": { name: scopes.resultBlock.path.meta }, + "1": { name: scopes.resultBlock.path.dirname }, + "2": { name: scopes.resultBlock.path.basename }, + "3": { name: scopes.resultBlock.path.colon }, + }, + patterns: [ + { + name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaMultiLine].join(' '), + begin: "^ ((\\d+) )", + while: "^ ((\\d+)(:))|((\\d+) )", + beginCaptures: { + "0": { name: scopes.resultBlock.result.prefix.meta }, + "1": { name: scopes.resultBlock.result.prefix.metaContext }, + "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + }, + whileCaptures: { + "0": { name: scopes.resultBlock.result.prefix.meta }, + "1": { name: scopes.resultBlock.result.prefix.metaMatch }, + "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + "3": { name: scopes.resultBlock.result.prefix.colon }, + + "4": { name: scopes.resultBlock.result.prefix.metaContext }, + "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + }, + patterns: [{ include: scope }] + }, + { + begin: "^ ((\\d+)(:))", + while: "(?=not)possible", + name: [scopes.resultBlock.result.meta, scopes.resultBlock.result.metaSingleLine].join(' '), + beginCaptures: { + "0": { name: scopes.resultBlock.result.prefix.meta }, + "1": { name: scopes.resultBlock.result.prefix.metaMatch }, + "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + "3": { name: scopes.resultBlock.result.prefix.colon }, + }, + patterns: [{ include: scope }] + } + ] + }); + +const header = [ + { + begin: "^(# Query): ", + end: "\n", + name: scopes.header.meta, + beginCaptures: { "1": { name: scopes.header.key }, }, + patterns: [ + { + match: '(\\\\n)|(\\\\\\\\)', + name: [scopes.header.value, scopes.header.query.escape].join(' ') + }, + { + match: '\\\\.|\\\\$', + name: [scopes.header.value, scopes.header.query.invalid].join(' ') + }, + { + match: '[^\\\\\\\n]+', + name: [scopes.header.value].join(' ') + }, + ] + }, + { + begin: "^(# Flags): ", + end: "\n", + name: scopes.header.meta, + beginCaptures: { "1": { name: scopes.header.key }, }, + patterns: [ + { + match: '(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)', + name: [scopes.header.value, 'keyword.other'].join(' ') + }, + { match: '.' }, + ] + }, + { + begin: "^(# ContextLines): ", + end: "\n", + name: scopes.header.meta, + beginCaptures: { "1": { name: scopes.header.key }, }, + patterns: [ + { + match: '\\d', + name: [scopes.header.value, scopes.header.contextLines.number].join(' ') + }, + { match: '.', name: scopes.header.contextLines.invalid }, + ] + }, + { + match: "^(# (?:Including|Excluding)): (.*)$", + name: scopes.header.meta, + captures: { + "1": { name: scopes.header.key }, + "2": { name: scopes.header.value } + } + }, +]; + +const plainText = [ + { + match: "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$", + name: [scopes.resultBlock.meta, scopes.resultBlock.path.meta].join(' '), + captures: { + "1": { name: scopes.resultBlock.path.dirname }, + "2": { name: scopes.resultBlock.path.basename }, + "3": { name: scopes.resultBlock.path.colon } + } + }, + { + match: "^ ((\\d+)(:))|((\\d+)( ))(.*)", + name: [scopes.resultBlock.meta, scopes.resultBlock.result.meta].join(' '), + captures: { + "1": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaMatch].join(' ') }, + "2": { name: scopes.resultBlock.result.prefix.lineNumber }, + "3": { name: scopes.resultBlock.result.prefix.colon }, + + "4": { name: [scopes.resultBlock.result.prefix.meta, scopes.resultBlock.result.prefix.metaContext].join(' ') }, + "5": { name: scopes.resultBlock.result.prefix.lineNumber }, + } + } +]; + +const tmLanguage = { + "information_for_contributors": "This file is generated from ./generateTMLanguage.js.", + name: "Search Results", + scopeName: scopes.root, + patterns: [ + ...header, + ...mappings.map(([ext]) => ({ include: `#${ext}` })), + ...plainText + ], + repository +}; + +require('fs').writeFileSync( + require('path').join(__dirname, './searchResult.tmLanguage.json'), + JSON.stringify(tmLanguage, null, 2)); diff --git a/extensions/search-result/syntaxes/searchResult.tmLanguage.json b/extensions/search-result/syntaxes/searchResult.tmLanguage.json index d16ecb8c97..e16ca1cb95 100644 --- a/extensions/search-result/syntaxes/searchResult.tmLanguage.json +++ b/extensions/search-result/syntaxes/searchResult.tmLanguage.json @@ -1,18 +1,4837 @@ { - "name": "Search Results", - "scopeName": "text.searchResult", - "patterns": [ - { - "match": "^# (Query|Flags|Including|Excluding|ContextLines): .*$", - "name": "comment" - }, - { - "match": "^\\S.*:$", - "name": "string path.searchResult" - }, - { - "match": "^ \\d+", - "name": "constant.numeric lineNumber.searchResult" - } - ] -} + "information_for_contributors": "This file is generated from ./generateTMLanguage.js.", + "name": "Search Results", + "scopeName": "text.searchResult", + "patterns": [ + { + "begin": "^(# Query): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "(\\\\n)|(\\\\\\\\)", + "name": "entity.other.attribute-value string.unquoted constant.character.escape" + }, + { + "match": "\\\\.|\\\\$", + "name": "entity.other.attribute-value string.unquoted invalid.illegal" + }, + { + "match": "[^\\\\\\\n]+", + "name": "entity.other.attribute-value string.unquoted" + } + ] + }, + { + "begin": "^(# Flags): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "(RegExp|CaseSensitive|IgnoreExcludeSettings|WordMatch)", + "name": "entity.other.attribute-value string.unquoted keyword.other" + }, + { + "match": "." + } + ] + }, + { + "begin": "^(# ContextLines): ", + "end": "\n", + "name": "meta.header.search keyword.operator.word.search", + "beginCaptures": { + "1": { + "name": "entity.other.attribute-name" + } + }, + "patterns": [ + { + "match": "\\d", + "name": "entity.other.attribute-value string.unquoted constant.numeric.integer" + }, + { + "match": ".", + "name": "invalid.illegal" + } + ] + }, + { + "match": "^(# (?:Including|Excluding)): (.*)$", + "name": "meta.header.search keyword.operator.word.search", + "captures": { + "1": { + "name": "entity.other.attribute-name" + }, + "2": { + "name": "entity.other.attribute-value string.unquoted" + } + } + }, + { + "include": "#bat" + }, + { + "include": "#c" + }, + { + "include": "#cc" + }, + { + "include": "#clj" + }, + { + "include": "#coffee" + }, + { + "include": "#cpp" + }, + { + "include": "#cs" + }, + { + "include": "#cshtml" + }, + { + "include": "#css" + }, + { + "include": "#dart" + }, + { + "include": "#diff" + }, + { + "include": "#dockerfile" + }, + { + "include": "#fs" + }, + { + "include": "#go" + }, + { + "include": "#groovy" + }, + { + "include": "#h" + }, + { + "include": "#handlebars" + }, + { + "include": "#hbs" + }, + { + "include": "#hlsl" + }, + { + "include": "#hpp" + }, + { + "include": "#html" + }, + { + "include": "#ini" + }, + { + "include": "#java" + }, + { + "include": "#js" + }, + { + "include": "#json" + }, + { + "include": "#jsx" + }, + { + "include": "#less" + }, + { + "include": "#log" + }, + { + "include": "#lua" + }, + { + "include": "#m" + }, + { + "include": "#makefile" + }, + { + "include": "#md" + }, + { + "include": "#mm" + }, + { + "include": "#p6" + }, + { + "include": "#perl" + }, + { + "include": "#php" + }, + { + "include": "#pl" + }, + { + "include": "#ps1" + }, + { + "include": "#pug" + }, + { + "include": "#py" + }, + { + "include": "#r" + }, + { + "include": "#rb" + }, + { + "include": "#rs" + }, + { + "include": "#scala" + }, + { + "include": "#scss" + }, + { + "include": "#sh" + }, + { + "include": "#sql" + }, + { + "include": "#swift" + }, + { + "include": "#ts" + }, + { + "include": "#tsx" + }, + { + "include": "#vb" + }, + { + "include": "#xml" + }, + { + "include": "#yaml" + }, + { + "match": "^(?!\\s)(.*?)([^\\\\\\/\\n]*)(:)$", + "name": "meta.resultBlock.search string meta.path.search", + "captures": { + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + } + }, + { + "match": "^ ((\\d+)(:))|((\\d+)( ))(.*)", + "name": "meta.resultBlock.search meta.resultLine.search", + "captures": { + "1": { + "name": "constant.numeric.integer meta.resultLinePrefix.search meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "constant.numeric.integer meta.resultLinePrefix.search meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + } + } + ], + "repository": { + "bat": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.bat)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.batchfile" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.batchfile" + } + ] + } + ] + }, + "c": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.c)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.c" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.c" + } + ] + } + ] + }, + "cc": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cc)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.cpp" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.cpp" + } + ] + } + ] + }, + "clj": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.clj)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.clojure" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.clojure" + } + ] + } + ] + }, + "coffee": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.coffee)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.coffee" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.coffee" + } + ] + } + ] + }, + "cpp": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cpp)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.cpp" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.cpp" + } + ] + } + ] + }, + "cs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cs)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.cs" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.cs" + } + ] + } + ] + }, + "cshtml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.cshtml)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.html.cshtml" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.html.cshtml" + } + ] + } + ] + }, + "css": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.css)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.css" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.css" + } + ] + } + ] + }, + "dart": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.dart)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.dart" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.dart" + } + ] + } + ] + }, + "diff": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.diff)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.diff" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.diff" + } + ] + } + ] + }, + "dockerfile": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:dockerfile|Dockerfile))(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.dockerfile" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.dockerfile" + } + ] + } + ] + }, + "fs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.fs)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.fsharp" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.fsharp" + } + ] + } + ] + }, + "go": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.go)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.go" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.go" + } + ] + } + ] + }, + "groovy": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.groovy)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.groovy" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.groovy" + } + ] + } + ] + }, + "h": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.h)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.objc" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.objc" + } + ] + } + ] + }, + "handlebars": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.handlebars)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.html.handlebars" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.html.handlebars" + } + ] + } + ] + }, + "hbs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hbs)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.html.handlebars" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.html.handlebars" + } + ] + } + ] + }, + "hlsl": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hlsl)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.hlsl" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.hlsl" + } + ] + } + ] + }, + "hpp": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.hpp)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.objcpp" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.objcpp" + } + ] + } + ] + }, + "html": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.html)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.html.basic" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.html.basic" + } + ] + } + ] + }, + "ini": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ini)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.ini" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.ini" + } + ] + } + ] + }, + "java": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.java)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.java" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.java" + } + ] + } + ] + }, + "js": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.js)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.js" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.js" + } + ] + } + ] + }, + "json": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.json)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.json.comments" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.json.comments" + } + ] + } + ] + }, + "jsx": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.jsx)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.js.jsx" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.js.jsx" + } + ] + } + ] + }, + "less": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.less)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.css.less" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.css.less" + } + ] + } + ] + }, + "log": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.log)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.log" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.log" + } + ] + } + ] + }, + "lua": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.lua)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.lua" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.lua" + } + ] + } + ] + }, + "m": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.m)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.objc" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.objc" + } + ] + } + ] + }, + "makefile": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*(?:makefile|Makefile)(?:\\..*)?)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.makefile" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.makefile" + } + ] + } + ] + }, + "md": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.md)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.html.markdown" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.html.markdown" + } + ] + } + ] + }, + "mm": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.mm)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.objcpp" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.objcpp" + } + ] + } + ] + }, + "p6": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.p6)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.perl.6" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.perl.6" + } + ] + } + ] + }, + "perl": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.perl)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + } + ] + }, + "php": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.php)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.php" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.php" + } + ] + } + ] + }, + "pl": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pl)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.perl" + } + ] + } + ] + }, + "ps1": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ps1)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.powershell" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.powershell" + } + ] + } + ] + }, + "pug": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.pug)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.pug" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.pug" + } + ] + } + ] + }, + "py": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.py)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.python" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.python" + } + ] + } + ] + }, + "r": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.r)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.r" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.r" + } + ] + } + ] + }, + "rb": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.rb)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.ruby" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.ruby" + } + ] + } + ] + }, + "rs": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.rs)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.rust" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.rust" + } + ] + } + ] + }, + "scala": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.scala)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.scala" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.scala" + } + ] + } + ] + }, + "scss": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.scss)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.css.scss" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.css.scss" + } + ] + } + ] + }, + "sh": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.sh)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.shell" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.shell" + } + ] + } + ] + }, + "sql": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.sql)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.sql" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.sql" + } + ] + } + ] + }, + "swift": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.swift)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.swift" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.swift" + } + ] + } + ] + }, + "ts": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.ts)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.ts" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.ts" + } + ] + } + ] + }, + "tsx": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.tsx)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.tsx" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.tsx" + } + ] + } + ] + }, + "vb": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.vb)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.asp.vb.net" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.asp.vb.net" + } + ] + } + ] + }, + "xml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.xml)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "text.xml" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "text.xml" + } + ] + } + ] + }, + "yaml": { + "name": "meta.resultBlock.search", + "begin": "^(?!\\s)(.*?)([^\\\\\\/\\n]*\\.yaml)(:)$", + "end": "^(?!\\s)", + "beginCaptures": { + "0": { + "name": "string meta.path.search" + }, + "1": { + "name": "meta.path.dirname.search" + }, + "2": { + "name": "meta.path.basename.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "name": "meta.resultLine.search meta.resultLine.multiLine.search", + "begin": "^ ((\\d+) )", + "while": "^ ((\\d+)(:))|((\\d+) )", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "whileCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + }, + "4": { + "name": "meta.resultLinePrefix.contextLinePrefix.search" + }, + "5": { + "name": "meta.resultLinePrefix.lineNumber.search" + } + }, + "patterns": [ + { + "include": "source.yaml" + } + ] + }, + { + "begin": "^ ((\\d+)(:))", + "while": "(?=not)possible", + "name": "meta.resultLine.search meta.resultLine.singleLine.search", + "beginCaptures": { + "0": { + "name": "constant.numeric.integer meta.resultLinePrefix.search" + }, + "1": { + "name": "meta.resultLinePrefix.matchLinePrefix.search" + }, + "2": { + "name": "meta.resultLinePrefix.lineNumber.search" + }, + "3": { + "name": "punctuation.separator" + } + }, + "patterns": [ + { + "include": "source.yaml" + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/extensions/theme-defaults/themes/hc_black.json b/extensions/theme-defaults/themes/hc_black.json index f76d7bb960..73309c34b4 100644 --- a/extensions/theme-defaults/themes/hc_black.json +++ b/extensions/theme-defaults/themes/hc_black.json @@ -114,6 +114,13 @@ "settings": { "foreground": "#CE9178" } + }, + { + "name": "HC Search Editor context line override", + "scope": "meta.resultLinePrefix.contextLinePrefix.search", + "settings": { + "foreground": "#CBEDCB", + } } ] } diff --git a/package.json b/package.json index 13d13770b9..7993654e87 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azuredatastudio", "version": "1.14.0", - "distro": "0fd359ecaf4b9ee6948b976a69a36a2179deaefd", + "distro": "70b195f4ddcfa2e6540928240456ed949ffb6ae0", "author": { "name": "Microsoft Corporation" }, @@ -65,7 +65,7 @@ "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", "sanitize-html": "^1.19.1", - "semver-umd": "^5.5.3", + "semver-umd": "^5.5.5", "slickgrid": "github:anthonydresser/SlickGrid#2.3.32", "spdlog": "^0.11.1", "sudo-prompt": "9.1.1", @@ -99,7 +99,6 @@ "@types/node": "^10.12.12", "@types/plotly.js": "^1.44.9", "@types/sanitize-html": "^1.18.2", - "@types/semver": "^5.5.0", "@types/sinon": "^1.16.36", "@types/webpack": "^4.4.10", "@types/windows-foreground-love": "^0.3.0", @@ -114,6 +113,7 @@ "coveralls": "^2.11.11", "cson-parser": "^1.3.3", "debounce": "^1.0.0", + "electron": "6.1.5", "event-stream": "3.3.4", "express": "^4.13.1", "fancy-log": "^1.3.3", @@ -169,7 +169,7 @@ "tslint": "^5.16.0", "tslint-microsoft-contrib": "^6.0.0", "typemoq": "^0.3.2", - "typescript": "3.7.2", + "typescript": "3.7.3", "typescript-formatter": "7.1.0", "vinyl": "^2.0.0", "vinyl-fs": "^3.0.0", diff --git a/remote/package.json b/remote/package.json index 891ae131f6..e61f399623 100644 --- a/remote/package.json +++ b/remote/package.json @@ -13,7 +13,7 @@ "native-watchdog": "1.3.0", "node-pty": "^0.10.0-beta2", "onigasm-umd": "2.2.5", - "semver-umd": "^5.5.3", + "semver-umd": "^5.5.5", "spdlog": "^0.11.1", "vscode-minimist": "^1.2.2", "vscode-nsfw": "1.2.8", diff --git a/remote/web/package.json b/remote/web/package.json index 0aed404aab..745747611d 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -3,7 +3,7 @@ "version": "0.0.0", "dependencies": { "onigasm-umd": "2.2.5", - "semver-umd": "^5.5.3", + "semver-umd": "^5.5.5", "vscode-textmate": "4.4.0", "xterm": "4.3.0-beta.28.vscode.1", "xterm-addon-search": "0.4.0-beta4", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 18078da8d1..b11746cd8f 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -19,10 +19,10 @@ oniguruma@^7.2.0: dependencies: nan "^2.14.0" -semver-umd@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" - integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== +semver-umd@^5.5.5: + version "5.5.5" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" + integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== vscode-textmate@4.4.0: version "4.4.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index a45c4d94ff..ef13c7644d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -317,10 +317,10 @@ readdirp@~3.2.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -semver-umd@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" - integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== +semver-umd@^5.5.5: + version "5.5.5" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" + integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== semver@^5.3.0: version "5.6.0" diff --git a/scripts/code-web.js b/scripts/code-web.js index a7370520c9..26fb980e3d 100755 --- a/scripts/code-web.js +++ b/scripts/code-web.js @@ -62,6 +62,17 @@ const server = http.createServer((req, res) => { // favicon return serveFile(req, res, path.join(APP_ROOT, 'resources', 'win32', 'code.ico')); } + if (pathname === '/manifest.json') { + // manifest + res.writeHead(200, { 'Content-Type': 'application/json' }); + return res.end(JSON.stringify({ + "name": "Code Web - OSS", + "short_name": "Code Web - OSS", + "start_url": "/", + "lang": "en-US", + "display": "standalone" + })); + } if (/^\/static\//.test(pathname)) { // static requests return handleStatic(req, res, parsedUrl); diff --git a/src/sql/workbench/browser/modal/optionsDialog.ts b/src/sql/workbench/browser/modal/optionsDialog.ts index 3860a8d673..6615e1cd70 100644 --- a/src/sql/workbench/browser/modal/optionsDialog.ts +++ b/src/sql/workbench/browser/modal/optionsDialog.ts @@ -33,14 +33,14 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -export class CategoryView extends ViewletPane { +export class CategoryView extends ViewPane { constructor( private contentElement: HTMLElement, private size: number, - options: IViewletPaneOptions, + options: IViewPaneOptions, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, diff --git a/src/sql/workbench/browser/parts/views/customView.ts b/src/sql/workbench/browser/parts/views/customView.ts index 44b09f2f3c..7c9d0b105a 100644 --- a/src/sql/workbench/browser/parts/views/customView.ts +++ b/src/sql/workbench/browser/parts/views/customView.ts @@ -49,9 +49,9 @@ import { IOEShimService } from 'sql/workbench/contrib/objectExplorer/browser/obj import { NodeContextKey } from 'sql/workbench/contrib/dataExplorer/browser/nodeContext'; import { UserCancelledConnectionError } from 'sql/base/common/errors'; import { firstIndex } from 'vs/base/common/arrays'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -export class CustomTreeViewPanel extends ViewletPane { +export class CustomTreeViewPanel extends ViewPane { private treeView: ITreeView; @@ -63,7 +63,7 @@ export class CustomTreeViewPanel extends ViewletPane { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView as ITreeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); diff --git a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts index ef991f0b48..c1be54cb4c 100644 --- a/src/sql/workbench/contrib/accounts/browser/accountDialog.ts +++ b/src/sql/workbench/contrib/accounts/browser/accountDialog.ts @@ -35,14 +35,14 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -class AccountPanel extends ViewletPane { +class AccountPanel extends ViewPane { public index: number; private accountList: List; constructor( - private options: IViewletPaneOptions, + private options: IViewPaneOptions, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, diff --git a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts index a85167f3c3..04332b98ea 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts @@ -19,9 +19,9 @@ import { import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ITree } from 'vs/base/parts/tree/browser/tree'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -export class ConnectionViewletPanel extends ViewletPane { +export class ConnectionViewletPanel extends ViewPane { public static readonly ID = 'dataExplorer.servers'; @@ -40,7 +40,7 @@ export class ConnectionViewletPanel extends ViewletPane { @IObjectExplorerService private readonly objectExplorerService: IObjectExplorerService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); this._addServerAction = this.instantiationService.createInstance(AddServerAction, AddServerAction.ID, AddServerAction.LABEL); diff --git a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts index 03acfb6559..1453a5f1d9 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts @@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -23,10 +23,10 @@ import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/la import { Registry } from 'vs/platform/registry/common/platform'; import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; +import { ShowViewletAction, Viewlet } from 'vs/workbench/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPaneContainer, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; export const VIEWLET_ID = 'workbench.view.connections'; @@ -72,7 +72,23 @@ export class DataExplorerViewletViewsContribution implements IWorkbenchContribut } } -export class DataExplorerViewlet extends ViewContainerViewlet { +export class DataExplorerViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(DataExplorerViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class DataExplorerViewPaneContainer extends ViewPaneContainer { private root: HTMLElement; private dataSourcesBox: HTMLElement; @@ -90,7 +106,7 @@ export class DataExplorerViewlet extends ViewContainerViewlet { @IMenuService private menuService: IMenuService, @IContextKeyService private contextKeyService: IContextKeyService ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(VIEWLET_ID, `${VIEWLET_ID}.state`, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); } create(parent: HTMLElement): void { @@ -134,13 +150,13 @@ export class DataExplorerViewlet extends ViewContainerViewlet { return actions; } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { + protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { const addedViews = super.onDidAddViews(added); return addedViews; } - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane { - let viewletPanel = this.instantiationService.createInstance(viewDescriptor.ctorDescriptor.ctor, options) as ViewletPane; + protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane { + let viewletPanel = this.instantiationService.createInstance(viewDescriptor.ctorDescriptor.ctor, options) as ViewPane; this._register(viewletPanel); return viewletPanel; } diff --git a/src/sql/workbench/contrib/dataExplorer/test/browser/dataExplorerViewlet.test.ts b/src/sql/workbench/contrib/dataExplorer/test/browser/dataExplorerViewlet.test.ts deleted file mode 100644 index 641efbdf35..0000000000 --- a/src/sql/workbench/contrib/dataExplorer/test/browser/dataExplorerViewlet.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as assert from 'assert'; -import * as Platform from 'vs/platform/registry/common/platform'; -import { ViewletDescriptor, Extensions, Viewlet, ViewletRegistry } from 'vs/workbench/browser/viewlet'; -import * as Types from 'vs/base/common/types'; - -suite('Data Explorer Viewlet', () => { - - class DataExplorerTestViewlet extends Viewlet { - - constructor() { - super('dataExplorer', null, null, null, null, null); - } - - public layout(dimension: any): void { - throw new Error('Method not implemented.'); - } - } - - test('ViewletDescriptor API', function () { - let d = ViewletDescriptor.create(DataExplorerTestViewlet, 'id', 'name', 'class', 1); - assert.strictEqual(d.id, 'id'); - assert.strictEqual(d.name, 'name'); - assert.strictEqual(d.cssClass, 'class'); - assert.strictEqual(d.order, 1); - }); - - test('Editor Aware ViewletDescriptor API', function () { - let d = ViewletDescriptor.create(DataExplorerTestViewlet, 'id', 'name', 'class', 5); - assert.strictEqual(d.id, 'id'); - assert.strictEqual(d.name, 'name'); - - d = ViewletDescriptor.create(DataExplorerTestViewlet, 'id', 'name', 'class', 5); - assert.strictEqual(d.id, 'id'); - assert.strictEqual(d.name, 'name'); - }); - - test('Data Explorer Viewlet extension point and registration', function () { - assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).registerViewlet)); - assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).getViewlet)); - assert(Types.isFunction(Platform.Registry.as(Extensions.Viewlets).getViewlets)); - - let oldCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length; - let d = ViewletDescriptor.create(DataExplorerTestViewlet, 'dataExplorer-test-id', 'name'); - Platform.Registry.as(Extensions.Viewlets).registerViewlet(d); - let retrieved = Platform.Registry.as(Extensions.Viewlets).getViewlet('dataExplorer-test-id'); - assert(d === retrieved); - let newCount = Platform.Registry.as(Extensions.Viewlets).getViewlets().length; - assert.equal(oldCount + 1, newCount); - }); -}); diff --git a/src/sql/workbench/contrib/extensions/browser/extensionsActions.ts b/src/sql/workbench/contrib/extensions/browser/extensionsActions.ts index e5c4136eaa..f73c9a06d4 100644 --- a/src/sql/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/sql/workbench/contrib/extensions/browser/extensionsActions.ts @@ -6,10 +6,11 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsWorkbenchService, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { CancellationToken } from 'vs/base/common/cancellation'; import { PagedModel } from 'vs/base/common/paging'; +import { ExtensionsViewlet, ExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; function getScenarioID(scenarioType: string) { return 'workbench.extensions.action.show' + scenarioType; @@ -25,9 +26,8 @@ export class ShowRecommendedExtensionsByScenarioAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@' + this.scenarioType); + .then((viewlet: ExtensionsViewlet) => { + (viewlet.getViewPaneContainer() as ExtensionsViewPaneContainer).search('@' + this.scenarioType); viewlet.focus(); }); } @@ -51,9 +51,8 @@ export class InstallRecommendedExtensionsByScenarioAction extends Action { run(): Promise { if (!this.recommendations.length) { return Promise.resolve(); } return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) - .then(viewlet => { - viewlet.search('@' + this.scenarioType); + .then((viewlet: ExtensionsViewlet) => { + (viewlet.getViewPaneContainer() as ExtensionsViewPaneContainer).search('@' + this.scenarioType); viewlet.focus(); const names = this.recommendations.map(({ extensionId }) => extensionId); return this.extensionWorkbenchService.queryGallery({ names, source: 'install-' + this.scenarioType }, CancellationToken.None).then(pager => { diff --git a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts index 80345c308d..2bff812ca8 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebook.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebook.component.ts @@ -39,7 +39,7 @@ import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHos import { IConnectionDialogService } from 'sql/workbench/services/connection/common/connectionDialogService'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { CellMagicMapper } from 'sql/workbench/contrib/notebook/browser/models/cellMagicMapper'; -import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { CellModel } from 'sql/workbench/contrib/notebook/browser/models/cell'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -55,6 +55,7 @@ import { isUndefinedOrNull } from 'vs/base/common/types'; import { IBootstrapParams } from 'sql/workbench/services/bootstrap/common/bootstrapParams'; import { getErrorMessage } from 'vs/base/common/errors'; import { find, firstIndex } from 'vs/base/common/arrays'; +import { ExtensionsViewlet, ExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; export const NOTEBOOK_SELECTOR: string = 'notebook-component'; @@ -343,8 +344,8 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe private async openExtensionGallery(): Promise { try { - let viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true) as IExtensionsViewlet; - viewlet.search('sql-vnext'); + let viewlet = await this.viewletService.openViewlet(VIEWLET_ID, true) as ExtensionsViewlet; + (viewlet.getViewPaneContainer() as ExtensionsViewPaneContainer).search('sql-vnext'); viewlet.focus(); } catch (error) { this.notificationService.error(error.message); diff --git a/src/sql/workbench/contrib/query/common/resultSerializer.ts b/src/sql/workbench/contrib/query/common/resultSerializer.ts index 6d33e31ef2..60760374f5 100644 --- a/src/sql/workbench/contrib/query/common/resultSerializer.ts +++ b/src/sql/workbench/contrib/query/common/resultSerializer.ts @@ -21,10 +21,11 @@ import { getBaseLabel } from 'vs/base/common/labels'; import { ShowFileInFolderAction, OpenFileInFolderAction } from 'sql/workbench/common/workspaceActions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { getRootPath, resolveCurrentDirectory, resolveFilePath } from 'sql/platform/common/pathUtilities'; -import { IOutputService, IOutputChannelRegistry, IOutputChannel, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService, IOutputChannel } from 'vs/workbench/contrib/output/common/output'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFileDialogService, FileFilter } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; let prevSavePath: string; diff --git a/src/sql/workbench/services/insights/browser/insightsDialogView.ts b/src/sql/workbench/services/insights/browser/insightsDialogView.ts index bf6843dae3..3b2aab8478 100644 --- a/src/sql/workbench/services/insights/browser/insightsDialogView.ts +++ b/src/sql/workbench/services/insights/browser/insightsDialogView.ts @@ -41,14 +41,14 @@ import { IInsightsConfigDetails } from 'sql/platform/dashboard/browser/insightRe import { TaskRegistry } from 'sql/platform/tasks/browser/tasksRegistry'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; const labelDisplay = nls.localize("insights.item", "Item"); const valueDisplay = nls.localize("insights.value", "Value"); const iconClass = 'codicon'; -class InsightTableView extends ViewletPane { +class InsightTableView extends ViewPane { private _table: Table; public get table(): Table { return this._table; @@ -58,7 +58,7 @@ class InsightTableView extends ViewletPane { private columns: Slick.Column[], private data: IDisposableDataProvider | Array, private tableOptions: Slick.GridOptions, - options: IViewletPaneOptions, + options: IViewPaneOptions, @IKeybindingService keybindingService: IKeybindingService, @IContextMenuService contextMenuService: IContextMenuService, @IConfigurationService configurationService: IConfigurationService, diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts deleted file mode 100644 index 369817dcd1..0000000000 --- a/src/typings/electron.d.ts +++ /dev/null @@ -1,10730 +0,0 @@ -// Type definitions for Electron 6.1.5 -// Project: http://electronjs.org/ -// Definitions by: The Electron Team -// Definitions: https://github.com/electron/electron-typescript-definitions - -/// - -type GlobalEvent = Event; - -declare namespace Electron { - // TODO: Replace this declaration with NodeJS.EventEmitter - class EventEmitter { - addListener(event: string, listener: Function): this; - on(event: string, listener: Function): this; - once(event: string, listener: Function): this; - removeListener(event: string, listener: Function): this; - removeAllListeners(event?: string): this; - setMaxListeners(n: number): this; - getMaxListeners(): number; - listeners(event: string): Function[]; - emit(event: string, ...args: any[]): boolean; - listenerCount(type: string): number; - prependListener(event: string, listener: Function): this; - prependOnceListener(event: string, listener: Function): this; - eventNames(): Array<(string | symbol)>; - } - - class Accelerator extends String { - - } - - interface CommonInterface { - clipboard: Clipboard; - crashReporter: CrashReporter; - nativeImage: typeof NativeImage; - shell: Shell; - } - - interface MainInterface extends CommonInterface { - app: App; - autoUpdater: AutoUpdater; - BrowserView: typeof BrowserView; - BrowserWindow: typeof BrowserWindow; - ClientRequest: typeof ClientRequest; - contentTracing: ContentTracing; - Cookies: typeof Cookies; - Debugger: typeof Debugger; - dialog: Dialog; - DownloadItem: typeof DownloadItem; - globalShortcut: GlobalShortcut; - inAppPurchase: InAppPurchase; - IncomingMessage: typeof IncomingMessage; - ipcMain: IpcMain; - Menu: typeof Menu; - MenuItem: typeof MenuItem; - net: Net; - netLog: NetLog; - Notification: typeof Notification; - powerMonitor: PowerMonitor; - powerSaveBlocker: PowerSaveBlocker; - protocol: Protocol; - screen: Screen; - session: typeof Session; - systemPreferences: SystemPreferences; - TouchBar: typeof TouchBar; - Tray: typeof Tray; - webContents: typeof WebContents; - WebRequest: typeof WebRequest; - } - - interface RendererInterface extends CommonInterface { - BrowserWindowProxy: typeof BrowserWindowProxy; - contextBridge: ContextBridge; - desktopCapturer: DesktopCapturer; - ipcRenderer: IpcRenderer; - remote: Remote; - webFrame: WebFrame; - webviewTag: WebviewTag; - } - - interface AllElectron extends MainInterface, RendererInterface { } - - const app: App; - const autoUpdater: AutoUpdater; - const clipboard: Clipboard; - const contentTracing: ContentTracing; - const contextBridge: ContextBridge; - const crashReporter: CrashReporter; - const desktopCapturer: DesktopCapturer; - const dialog: Dialog; - const globalShortcut: GlobalShortcut; - const inAppPurchase: InAppPurchase; - const ipcMain: IpcMain; - const ipcRenderer: IpcRenderer; - type nativeImage = NativeImage; - const nativeImage: typeof NativeImage; - const net: Net; - const netLog: NetLog; - const powerMonitor: PowerMonitor; - const powerSaveBlocker: PowerSaveBlocker; - const protocol: Protocol; - // const remote: Remote; ### VSCODE CHANGE (we do not want to use remote) - const screen: Screen; - type session = Session; - const session: typeof Session; - const shell: Shell; - const systemPreferences: SystemPreferences; - type webContents = WebContents; - const webContents: typeof WebContents; - const webFrame: WebFrame; - const webviewTag: WebviewTag; - - interface App extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/app - - /** - * Emitted when Chrome's accessibility support changes. This event fires when - * assistive technologies, such as screen readers, are enabled or disabled. See - * https://www.chromium.org/developers/design-documents/accessibility for more - * details. - */ - on(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - once(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - addListener(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - removeListener(event: 'accessibility-support-changed', listener: (event: Event, - /** - * `true` when Chrome's accessibility support is enabled, `false` otherwise. - */ - accessibilitySupportEnabled: boolean) => void): this; - /** - * Emitted when the application is activated. Various actions can trigger this - * event, such as launching the application for the first time, attempting to - * re-launch the application when it's already running, or clicking on the - * application's dock or taskbar icon. - */ - on(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - once(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - addListener(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - removeListener(event: 'activate', listener: (event: Event, - hasVisibleWindows: boolean) => void): this; - /** - * Emitted during Handoff after an activity from this device was successfully - * resumed on another one. - */ - on(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - once(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - addListener(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - removeListener(event: 'activity-was-continued', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - /** - * Emitted before the application starts closing its windows. Calling - * event.preventDefault() will prevent the default behavior, which is terminating - * the application. Note: If application quit was initiated by - * autoUpdater.quitAndInstall(), then before-quit is emitted after emitting close - * event on all windows and closing them. Note: On Windows, this event will not be - * emitted if the app is closed due to a shutdown/restart of the system or a user - * logout. - */ - on(event: 'before-quit', listener: (event: Event) => void): this; - once(event: 'before-quit', listener: (event: Event) => void): this; - addListener(event: 'before-quit', listener: (event: Event) => void): this; - removeListener(event: 'before-quit', listener: (event: Event) => void): this; - /** - * Emitted when a browserWindow gets blurred. - */ - on(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-blur', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when a new browserWindow is created. - */ - on(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-created', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when a browserWindow gets focused. - */ - on(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - once(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - addListener(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - removeListener(event: 'browser-window-focus', listener: (event: Event, - window: BrowserWindow) => void): this; - /** - * Emitted when failed to verify the certificate for url, to trust the certificate - * you should prevent the default behavior with event.preventDefault() and call - * callback(true). - */ - on(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - once(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - addListener(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - removeListener(event: 'certificate-error', listener: (event: Event, - webContents: WebContents, - url: string, - /** - * The error code - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - /** - * Emitted during Handoff when an activity from a different device wants to be - * resumed. You should call event.preventDefault() if you want to handle this - * event. A user activity can be continued only in an app that has the same - * developer Team ID as the activity's source app and that supports the activity's - * type. Supported activity types are specified in the app's Info.plist under the - * NSUserActivityTypes key. - */ - on(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - once(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - addListener(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - removeListener(event: 'continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity on another device. - */ - userInfo: any) => void): this; - /** - * Emitted during Handoff when an activity from a different device fails to be - * resumed. - */ - on(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - once(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - addListener(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - removeListener(event: 'continue-activity-error', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * A string with the error's localized description. - */ - error: string) => void): this; - /** - * Emitted when desktopCapturer.getSources() is called in the renderer process of - * webContents. Calling event.preventDefault() will make it return empty sources. - */ - on(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when the gpu process crashes or is killed. - */ - on(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - once(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - addListener(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - removeListener(event: 'gpu-process-crashed', listener: (event: Event, - killed: boolean) => void): this; - /** - * Emitted when webContents wants to do basic auth. The default behavior is to - * cancel all authentications. To override this you should prevent the default - * behavior with event.preventDefault() and call callback(username, password) with - * the credentials. - */ - on(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (event: Event, - webContents: WebContents, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when the user clicks the native macOS new tab button. The new tab button - * is only visible if the current BrowserWindow has a tabbingIdentifier - */ - on(event: 'new-window-for-tab', listener: (event: Event) => void): this; - once(event: 'new-window-for-tab', listener: (event: Event) => void): this; - addListener(event: 'new-window-for-tab', listener: (event: Event) => void): this; - removeListener(event: 'new-window-for-tab', listener: (event: Event) => void): this; - /** - * Emitted when the user wants to open a file with the application. The open-file - * event is usually emitted when the application is already open and the OS wants - * to reuse the application to open the file. open-file is also emitted when a file - * is dropped onto the dock and the application is not yet running. Make sure to - * listen for the open-file event very early in your application startup to handle - * this case (even before the ready event is emitted). You should call - * event.preventDefault() if you want to handle this event. On Windows, you have to - * parse process.argv (in the main process) to get the filepath. - */ - on(event: 'open-file', listener: (event: Event, - path: string) => void): this; - once(event: 'open-file', listener: (event: Event, - path: string) => void): this; - addListener(event: 'open-file', listener: (event: Event, - path: string) => void): this; - removeListener(event: 'open-file', listener: (event: Event, - path: string) => void): this; - /** - * Emitted when the user wants to open a URL with the application. Your - * application's Info.plist file must define the url scheme within the - * CFBundleURLTypes key, and set NSPrincipalClass to AtomApplication. You should - * call event.preventDefault() if you want to handle this event. - */ - on(event: 'open-url', listener: (event: Event, - url: string) => void): this; - once(event: 'open-url', listener: (event: Event, - url: string) => void): this; - addListener(event: 'open-url', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'open-url', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when the application is quitting. Note: On Windows, this event will not - * be emitted if the app is closed due to a shutdown/restart of the system or a - * user logout. - */ - on(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - once(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - addListener(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - removeListener(event: 'quit', listener: (event: Event, - exitCode: number) => void): this; - /** - * Emitted when Electron has finished initializing. On macOS, launchInfo holds the - * userInfo of the NSUserNotification that was used to open the application, if it - * was launched from Notification Center. You can call app.isReady() to check if - * this event has already fired. - */ - on(event: 'ready', listener: (launchInfo: any) => void): this; - once(event: 'ready', listener: (launchInfo: any) => void): this; - addListener(event: 'ready', listener: (launchInfo: any) => void): this; - removeListener(event: 'ready', listener: (launchInfo: any) => void): this; - /** - * Emitted when remote.getBuiltin() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the module from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - once(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - addListener(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - removeListener(event: 'remote-get-builtin', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - /** - * Emitted when remote.getCurrentWebContents() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'remote-get-current-web-contents', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when remote.getCurrentWindow() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'remote-get-current-window', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted when remote.getGlobal() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the global from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - once(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - addListener(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - removeListener(event: 'remote-get-global', listener: (event: Event, - webContents: WebContents, - globalName: string) => void): this; - /** - * Emitted when .getWebContents() is called in the renderer process of - * webContents. Calling event.preventDefault() will prevent the object from being - * returned. Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - once(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - webContents: WebContents, - guestWebContents: WebContents) => void): this; - /** - * Emitted when remote.require() is called in the renderer process of webContents. - * Calling event.preventDefault() will prevent the module from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - once(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - addListener(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - removeListener(event: 'remote-require', listener: (event: Event, - webContents: WebContents, - moduleName: string) => void): this; - /** - * Emitted when the renderer process of webContents crashes or is killed. - */ - on(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - once(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - addListener(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - removeListener(event: 'renderer-process-crashed', listener: (event: Event, - webContents: WebContents, - killed: boolean) => void): this; - /** - * This event will be emitted inside the primary instance of your application when - * a second instance has been executed and calls app.requestSingleInstanceLock(). - * argv is an Array of the second instance's command line arguments, and - * workingDirectory is its current working directory. Usually applications respond - * to this by making their primary window focused and non-minimized. This event is - * guaranteed to be emitted after the ready event of app gets emitted. Note: Extra - * command line arguments might be added by Chromium, such as - * --original-process-start-time. - */ - on(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - once(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - addListener(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - removeListener(event: 'second-instance', listener: (event: Event, - /** - * An array of the second instance's command line arguments - */ - argv: string[], - /** - * The second instance's working directory - */ - workingDirectory: string) => void): this; - /** - * Emitted when a client certificate is requested. The url corresponds to the - * navigation entry requesting the client certificate and callback can be called - * with an entry filtered from the list. Using event.preventDefault() prevents the - * application from using the first certificate from the store. - */ - on(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - once(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - addListener(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - removeListener(event: 'select-client-certificate', listener: (event: Event, - webContents: WebContents, - url: string, - certificateList: Certificate[], - callback: (certificate?: Certificate) => void) => void): this; - /** - * Emitted when Electron has created a new session. - */ - on(event: 'session-created', listener: (session: Session) => void): this; - once(event: 'session-created', listener: (session: Session) => void): this; - addListener(event: 'session-created', listener: (session: Session) => void): this; - removeListener(event: 'session-created', listener: (session: Session) => void): this; - /** - * Emitted when Handoff is about to be resumed on another device. If you need to - * update the state to be transferred, you should call event.preventDefault() - * immediately, construct a new userInfo dictionary and call - * app.updateCurrentActiviy() in a timely manner. Otherwise, the operation will - * fail and continue-activity-error will be called. - */ - on(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - once(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - addListener(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - removeListener(event: 'update-activity-state', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string, - /** - * Contains app-specific state stored by the activity. - */ - userInfo: any) => void): this; - /** - * Emitted when a new webContents is created. - */ - on(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - once(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - addListener(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - removeListener(event: 'web-contents-created', listener: (event: Event, - webContents: WebContents) => void): this; - /** - * Emitted during Handoff before an activity from a different device wants to be - * resumed. You should call event.preventDefault() if you want to handle this - * event. - */ - on(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - once(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - addListener(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - removeListener(event: 'will-continue-activity', listener: (event: Event, - /** - * A string identifying the activity. Maps to . - */ - type: string) => void): this; - /** - * Emitted when the application has finished basic startup. On Windows and Linux, - * the will-finish-launching event is the same as the ready event; on macOS, this - * event represents the applicationWillFinishLaunching notification of - * NSApplication. You would usually set up listeners for the open-file and open-url - * events here, and start the crash reporter and auto updater. In most cases, you - * should do everything in the ready event handler. - */ - on(event: 'will-finish-launching', listener: Function): this; - once(event: 'will-finish-launching', listener: Function): this; - addListener(event: 'will-finish-launching', listener: Function): this; - removeListener(event: 'will-finish-launching', listener: Function): this; - /** - * Emitted when all windows have been closed and the application will quit. Calling - * event.preventDefault() will prevent the default behaviour, which is terminating - * the application. See the description of the window-all-closed event for the - * differences between the will-quit and window-all-closed events. Note: On - * Windows, this event will not be emitted if the app is closed due to a - * shutdown/restart of the system or a user logout. - */ - on(event: 'will-quit', listener: (event: Event) => void): this; - once(event: 'will-quit', listener: (event: Event) => void): this; - addListener(event: 'will-quit', listener: (event: Event) => void): this; - removeListener(event: 'will-quit', listener: (event: Event) => void): this; - /** - * Emitted when all windows have been closed. If you do not subscribe to this event - * and all windows are closed, the default behavior is to quit the app; however, if - * you subscribe, you control whether the app quits or not. If the user pressed Cmd - * + Q, or the developer called app.quit(), Electron will first try to close all - * the windows and then emit the will-quit event, and in this case the - * window-all-closed event would not be emitted. - */ - on(event: 'window-all-closed', listener: Function): this; - once(event: 'window-all-closed', listener: Function): this; - addListener(event: 'window-all-closed', listener: Function): this; - removeListener(event: 'window-all-closed', listener: Function): this; - /** - * Adds path to the recent documents list. This list is managed by the OS. On - * Windows, you can visit the list from the task bar, and on macOS, you can visit - * it from dock menu. - */ - addRecentDocument(path: string): void; - /** - * Clears the recent documents list. - */ - clearRecentDocuments(): void; - /** - * By default, Chromium disables 3D APIs (e.g. WebGL) until restart on a per domain - * basis if the GPU processes crashes too frequently. This function disables that - * behaviour. This method can only be called before app is ready. - */ - disableDomainBlockingFor3DAPIs(): void; - /** - * Disables hardware acceleration for current app. This method can only be called - * before app is ready. - */ - disableHardwareAcceleration(): void; - /** - * Enables full sandbox mode on the app. This method can only be called before app - * is ready. - */ - enableSandbox(): void; - /** - * Exits immediately with exitCode. exitCode defaults to 0. All windows will be - * closed immediately without asking the user, and the before-quit and will-quit - * events will not be emitted. - */ - exit(exitCode?: number): void; - /** - * On Linux, focuses on the first visible window. On macOS, makes the application - * the active app. On Windows, focuses on the application's first window. - */ - focus(): void; - getAppMetrics(): ProcessMetric[]; - getAppPath(): string; - getBadgeCount(): number; - getCurrentActivityType(): string; - /** - * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux - * and macOS, icons depend on the application associated with file mime type. - */ - getFileIcon(path: string, options?: FileIconOptions): Promise; - /** - * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On - * Linux and macOS, icons depend on the application associated with file mime type. - * Deprecated Soon - */ - getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; - /** - * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On - * Linux and macOS, icons depend on the application associated with file mime type. - * Deprecated Soon - */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; - getGPUFeatureStatus(): GPUFeatureStatus; - /** - * For infoType equal to complete: Promise is fulfilled with Object containing all - * the GPU Information as in chromium's GPUInfo object. This includes the version - * and driver information that's shown on chrome://gpu page. For infoType equal to - * basic: Promise is fulfilled with Object containing fewer attributes than when - * requested with complete. Here's an example of basic response: Using basic should - * be preferred if only basic information like vendorId or driverId is needed. - */ - getGPUInfo(infoType: string): Promise; - getJumpListSettings(): JumpListSettings; - /** - * To set the locale, you'll want to use a command line switch at app startup, - * which may be found here. Note: When distributing your packaged app, you have to - * also ship the locales folder. Note: On Windows, you have to call it after the - * ready events gets emitted. - */ - getLocale(): string; - /** - * Note: When unable to detect locale country code, it returns empty string. - */ - getLocaleCountryCode(): string; - /** - * If you provided path and args options to app.setLoginItemSettings, then you need - * to pass the same arguments here for openAtLogin to be set correctly. - */ - getLoginItemSettings(options?: LoginItemSettingsOptions): LoginItemSettings; - /** - * Usually the name field of package.json is a short lowercased name, according to - * the npm modules spec. You should usually also specify a productName field, which - * is your application's full capitalized name, and which will be preferred over - * name by Electron. - */ - getName(): string; - /** - * You can request the following paths by the name: - */ - getPath(name: string): string; - getVersion(): string; - /** - * This method returns whether or not this instance of your app is currently - * holding the single instance lock. You can request the lock with - * app.requestSingleInstanceLock() and release with app.releaseSingleInstanceLock() - */ - hasSingleInstanceLock(): boolean; - /** - * Hides all application windows without minimizing them. - */ - hide(): void; - /** - * Imports the certificate in pkcs12 format into the platform certificate store. - * callback is called with the result of import operation, a value of 0 indicates - * success while any other value indicates failure according to Chromium - * net_error_list. - */ - importCertificate(options: ImportCertificateOptions, callback: (result: number) => void): void; - /** - * Invalidates the current Handoff user activity. - */ - invalidateCurrentActivity(type: string): void; - /** - * Deprecated Soon - */ - isAccessibilitySupportEnabled(): boolean; - /** - * This method checks if the current executable is the default handler for a - * protocol (aka URI scheme). If so, it will return true. Otherwise, it will return - * false. Note: On macOS, you can use this method to check if the app has been - * registered as the default protocol handler for a protocol. You can also verify - * this by checking ~/Library/Preferences/com.apple.LaunchServices.plist on the - * macOS machine. Please refer to Apple's documentation for details. The API uses - * the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. - */ - isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - isEmojiPanelSupported(): boolean; - isInApplicationsFolder(): boolean; - isReady(): boolean; - isUnityRunning(): boolean; - /** - * No confirmation dialog will be presented by default. If you wish to allow the - * user to confirm the operation, you may do so using the dialog API. NOTE: This - * method throws errors if anything other than the user causes the move to fail. - * For instance if the user cancels the authorization dialog, this method returns - * false. If we fail to perform the copy, then this method will throw an error. The - * message in the error should be informative and tell you exactly what went wrong - */ - moveToApplicationsFolder(): boolean; - /** - * Try to close all windows. The before-quit event will be emitted first. If all - * windows are successfully closed, the will-quit event will be emitted and by - * default the application will terminate. This method guarantees that all - * beforeunload and unload event handlers are correctly executed. It is possible - * that a window cancels the quitting by returning false in the beforeunload event - * handler. - */ - quit(): void; - /** - * Relaunches the app when current instance exits. By default, the new instance - * will use the same working directory and command line arguments with current - * instance. When args is specified, the args will be passed as command line - * arguments instead. When execPath is specified, the execPath will be executed for - * relaunch instead of current app. Note that this method does not quit the app - * when executed, you have to call app.quit or app.exit after calling app.relaunch - * to make the app restart. When app.relaunch is called for multiple times, - * multiple instances will be started after current instance exited. An example of - * restarting current instance immediately and adding a new command line argument - * to the new instance: - */ - relaunch(options?: RelaunchOptions): void; - /** - * Releases all locks that were created by requestSingleInstanceLock. This will - * allow multiple instances of the application to once again run side by side. - */ - releaseSingleInstanceLock(): void; - /** - * This method checks if the current executable as the default handler for a - * protocol (aka URI scheme). If so, it will remove the app as the default handler. - */ - removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - /** - * The return value of this method indicates whether or not this instance of your - * application successfully obtained the lock. If it failed to obtain the lock, - * you can assume that another instance of your application is already running with - * the lock and exit immediately. I.e. This method returns true if your process is - * the primary instance of your application and your app should continue loading. - * It returns false if your process should immediately quit as it has sent its - * parameters to another instance that has already acquired the lock. On macOS, the - * system enforces single instance automatically when users try to open a second - * instance of your app in Finder, and the open-file and open-url events will be - * emitted for that. However when users start your app in command line, the - * system's single instance mechanism will be bypassed, and you have to use this - * method to ensure single instance. An example of activating the window of primary - * instance when a second instance starts: - */ - requestSingleInstanceLock(): boolean; - /** - * Set the about panel options. This will override the values defined in the app's - * .plist file on MacOS. See the Apple docs for more details. On Linux, values must - * be set in order to be shown; there are no defaults. - */ - setAboutPanelOptions(options: AboutPanelOptionsOptions): void; - /** - * Manually enables Chrome's accessibility support, allowing to expose - * accessibility switch to users in application settings. See Chromium's - * accessibility docs for more details. Disabled by default. This API must be - * called after the ready event is emitted. Note: Rendering accessibility tree can - * significantly affect the performance of your app. It should not be enabled by - * default. Deprecated Soon - */ - setAccessibilitySupportEnabled(enabled: boolean): void; - /** - * Sets or creates a directory your app's logs which can then be manipulated with - * app.getPath() or app.setPath(pathName, newPath). Calling app.setAppLogsPath() - * without a path parameter will result in this directory being set to - * /Library/Logs/YourAppName on macOS, and inside the userData directory on Linux - * and Windows. - */ - setAppLogsPath(path?: string): void; - /** - * Changes the Application User Model ID to id. - */ - setAppUserModelId(id: string): void; - /** - * This method sets the current executable as the default handler for a protocol - * (aka URI scheme). It allows you to integrate your app deeper into the operating - * system. Once registered, all links with your-protocol:// will be opened with the - * current executable. The whole link, including protocol, will be passed to your - * application as a parameter. On Windows, you can provide optional parameters - * path, the path to your executable, and args, an array of arguments to be passed - * to your executable when it launches. Note: On macOS, you can only register - * protocols that have been added to your app's info.plist, which can not be - * modified at runtime. You can however change the file with a simple text editor - * or script during build time. Please refer to Apple's documentation for details. - * Note: In a Windows Store environment (when packaged as an appx) this API will - * return true for all calls but the registry key it sets won't be accessible by - * other applications. In order to register your Windows Store application as a - * default protocol handler you must declare the protocol in your manifest. The API - * uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. - */ - setAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; - /** - * Sets the counter badge for current app. Setting the count to 0 will hide the - * badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity - * launcher. Note: Unity launcher requires the existence of a .desktop file to - * work, for more information please read Desktop Environment Integration. - */ - setBadgeCount(count: number): boolean; - /** - * Sets or removes a custom Jump List for the application, and returns one of the - * following strings: If categories is null the previously set custom Jump List (if - * any) will be replaced by the standard Jump List for the app (managed by - * Windows). Note: If a JumpListCategory object has neither the type nor the name - * property set then its type is assumed to be tasks. If the name property is set - * but the type property is omitted then the type is assumed to be custom. Note: - * Users can remove items from custom categories, and Windows will not allow a - * removed item to be added back into a custom category until after the next - * successful call to app.setJumpList(categories). Any attempt to re-add a removed - * item to a custom category earlier than that will result in the entire custom - * category being omitted from the Jump List. The list of removed items can be - * obtained using app.getJumpListSettings(). Here's a very simple example of - * creating a custom Jump List: - */ - setJumpList(categories: JumpListCategory[]): void; - /** - * Set the app's login item settings. To work with Electron's autoUpdater on - * Windows, which uses Squirrel, you'll want to set the launch path to Update.exe, - * and pass arguments that specify your application name. For example: - */ - setLoginItemSettings(settings: Settings): void; - /** - * Overrides the current application's name. - */ - setName(name: string): void; - /** - * Overrides the path to a special directory or file associated with name. If the - * path specifies a directory that does not exist, an Error is thrown. In that - * case, the directory should be created with fs.mkdirSync or similar. You can only - * override paths of a name defined in app.getPath. By default, web pages' cookies - * and caches will be stored under the userData directory. If you want to change - * this location, you have to override the userData path before the ready event of - * the app module is emitted. - */ - setPath(name: string, path: string): void; - /** - * Creates an NSUserActivity and sets it as the current activity. The activity is - * eligible for Handoff to another device afterward. - */ - setUserActivity(type: string, userInfo: any, webpageURL?: string): void; - /** - * Adds tasks to the Tasks category of the JumpList on Windows. tasks is an array - * of Task objects. Note: If you'd like to customize the Jump List even more use - * app.setJumpList(categories) instead. - */ - setUserTasks(tasks: Task[]): boolean; - /** - * Shows application windows after they were hidden. Does not automatically focus - * them. - */ - show(): void; - /** - * Show the app's about panel options. These options can be overridden with - * app.setAboutPanelOptions(options). - */ - showAboutPanel(): void; - /** - * Show the platform's native emoji picker. - */ - showEmojiPanel(): void; - /** - * Start accessing a security scoped resource. With this method Electron - * applications that are packaged for the Mac App Store may reach outside their - * sandbox to access files chosen by the user. See Apple's documentation for a - * description of how this system works. - */ - startAccessingSecurityScopedResource(bookmarkData: string): Function; - /** - * Updates the current activity if its type matches type, merging the entries from - * userInfo into its current userInfo dictionary. - */ - updateCurrentActivity(type: string, userInfo: any): void; - whenReady(): Promise; - /** - * A Boolean property that's true if Chrome's accessibility support is enabled, - * false otherwise. This property will be true if the use of assistive - * technologies, such as screen readers, has been detected. Setting this property - * to true manually enables Chrome's accessibility support, allowing developers to - * expose accessibility switch to users in application settings. See Chromium's - * accessibility docs for more details. Disabled by default. This API must be - * called after the ready event is emitted. Note: Rendering accessibility tree can - * significantly affect the performance of your app. It should not be enabled by - * default. - */ - accessibilitySupportEnabled?: boolean; - /** - * A Boolean which when true disables the overrides that Electron has in place to - * ensure renderer processes are restarted on every navigation. The current - * default value for this property is false. The intention is for these overrides - * to become disabled by default and then at some point in the future this property - * will be removed. This property impacts which native modules you can use in the - * renderer process. For more information on the direction Electron is going with - * renderer process restarts and usage of native modules in the renderer process - * please check out this Tracking Issue. - */ - allowRendererProcessReuse?: boolean; - /** - * A Menu property that return Menu if one has been set and null otherwise. Users - * can pass a Menu to set this property. - */ - applicationMenu?: Menu; - commandLine: CommandLine; - dock: Dock; - /** - * A Boolean property that returns true if the app is packaged, false otherwise. - * For many apps, this property can be used to distinguish development and - * production environments. - */ - isPackaged?: boolean; - /** - * A String which is the user agent string Electron will use as a global fallback. - * This is the user agent that will be used when no user agent is set at the - * webContents or session level. Useful for ensuring your entire app has the same - * user agent. Set to a custom value as early as possible in your apps - * initialization to ensure that your overridden value is used. - */ - userAgentFallback?: string; - } - - interface AutoUpdater extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/auto-updater - - /** - * This event is emitted after a user calls quitAndInstall(). When this API is - * called, the before-quit event is not emitted before all windows are closed. As a - * result you should listen to this event if you wish to perform actions before the - * windows are closed while a process is quitting, as well as listening to - * before-quit. - */ - on(event: 'before-quit-for-update', listener: Function): this; - once(event: 'before-quit-for-update', listener: Function): this; - addListener(event: 'before-quit-for-update', listener: Function): this; - removeListener(event: 'before-quit-for-update', listener: Function): this; - /** - * Emitted when checking if an update has started. - */ - on(event: 'checking-for-update', listener: Function): this; - once(event: 'checking-for-update', listener: Function): this; - addListener(event: 'checking-for-update', listener: Function): this; - removeListener(event: 'checking-for-update', listener: Function): this; - /** - * Emitted when there is an error while updating. - */ - on(event: 'error', listener: (error: Error) => void): this; - once(event: 'error', listener: (error: Error) => void): this; - addListener(event: 'error', listener: (error: Error) => void): this; - removeListener(event: 'error', listener: (error: Error) => void): this; - /** - * Emitted when there is an available update. The update is downloaded - * automatically. - */ - on(event: 'update-available', listener: Function): this; - once(event: 'update-available', listener: Function): this; - addListener(event: 'update-available', listener: Function): this; - removeListener(event: 'update-available', listener: Function): this; - /** - * Emitted when an update has been downloaded. On Windows only releaseName is - * available. Note: It is not strictly necessary to handle this event. A - * successfully downloaded update will still be applied the next time the - * application starts. - */ - on(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - once(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - addListener(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - removeListener(event: 'update-downloaded', listener: (event: Event, - releaseNotes: string, - releaseName: string, - releaseDate: Date, - updateURL: string) => void): this; - /** - * Emitted when there is no available update. - */ - on(event: 'update-not-available', listener: Function): this; - once(event: 'update-not-available', listener: Function): this; - addListener(event: 'update-not-available', listener: Function): this; - removeListener(event: 'update-not-available', listener: Function): this; - /** - * Asks the server whether there is an update. You must call setFeedURL before - * using this API. - */ - checkForUpdates(): void; - getFeedURL(): string; - /** - * Restarts the app and installs the update after it has been downloaded. It should - * only be called after update-downloaded has been emitted. Under the hood calling - * autoUpdater.quitAndInstall() will close all application windows first, and - * automatically call app.quit() after all windows have been closed. Note: It is - * not strictly necessary to call this function to apply an update, as a - * successfully downloaded update will always be applied the next time the - * application starts. - */ - quitAndInstall(): void; - /** - * Sets the url and initialize the auto updater. - */ - setFeedURL(options: FeedURLOptions): void; - } - - interface BluetoothDevice { - - // Docs: http://electronjs.org/docs/api/structures/bluetooth-device - - deviceId: string; - deviceName: string; - } - - class BrowserView extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-view - - constructor(options?: BrowserViewConstructorOptions); - static fromId(id: number): BrowserView; - static fromWebContents(webContents: WebContents): (BrowserView) | (null); - static getAllViews(): BrowserView[]; - /** - * Force closing the view, the unload and beforeunload events won't be emitted for - * the web page. After you're done with a view, call this function in order to free - * memory and other resources as soon as possible. - */ - destroy(): void; - isDestroyed(): boolean; - setAutoResize(options: AutoResizeOptions): void; - setBackgroundColor(color: string): void; - /** - * Resizes and moves the view to the supplied bounds relative to the window. - */ - setBounds(bounds: Rectangle): void; - id: number; - webContents: WebContents; - } - - class BrowserWindow extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-window - - /** - * Emitted when the window is set or unset to show always on top of other windows. - */ - on(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - once(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - addListener(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - removeListener(event: 'always-on-top-changed', listener: (event: Event, - isAlwaysOnTop: boolean) => void): this; - /** - * Emitted when an App Command is invoked. These are typically related to keyboard - * media keys or browser commands, as well as the "Back" button built into some - * mice on Windows. Commands are lowercased, underscores are replaced with hyphens, - * and the APPCOMMAND_ prefix is stripped off. e.g. APPCOMMAND_BROWSER_BACKWARD is - * emitted as browser-backward. The following app commands are explictly supported - * on Linux: - */ - on(event: 'app-command', listener: (event: Event, - command: string) => void): this; - once(event: 'app-command', listener: (event: Event, - command: string) => void): this; - addListener(event: 'app-command', listener: (event: Event, - command: string) => void): this; - removeListener(event: 'app-command', listener: (event: Event, - command: string) => void): this; - /** - * Emitted when the window loses focus. - */ - on(event: 'blur', listener: Function): this; - once(event: 'blur', listener: Function): this; - addListener(event: 'blur', listener: Function): this; - removeListener(event: 'blur', listener: Function): this; - /** - * Emitted when the window is going to be closed. It's emitted before the - * beforeunload and unload event of the DOM. Calling event.preventDefault() will - * cancel the close. Usually you would want to use the beforeunload handler to - * decide whether the window should be closed, which will also be called when the - * window is reloaded. In Electron, returning any value other than undefined would - * cancel the close. For example: Note: There is a subtle difference between the - * behaviors of window.onbeforeunload = handler and - * window.addEventListener('beforeunload', handler). It is recommended to always - * set the event.returnValue explicitly, instead of only returning a value, as the - * former works more consistently within Electron. - */ - on(event: 'close', listener: (event: Event) => void): this; - once(event: 'close', listener: (event: Event) => void): this; - addListener(event: 'close', listener: (event: Event) => void): this; - removeListener(event: 'close', listener: (event: Event) => void): this; - /** - * Emitted when the window is closed. After you have received this event you should - * remove the reference to the window and avoid using it any more. - */ - on(event: 'closed', listener: Function): this; - once(event: 'closed', listener: Function): this; - addListener(event: 'closed', listener: Function): this; - removeListener(event: 'closed', listener: Function): this; - /** - * Emitted when the window enters a full-screen state. - */ - on(event: 'enter-full-screen', listener: Function): this; - once(event: 'enter-full-screen', listener: Function): this; - addListener(event: 'enter-full-screen', listener: Function): this; - removeListener(event: 'enter-full-screen', listener: Function): this; - /** - * Emitted when the window enters a full-screen state triggered by HTML API. - */ - on(event: 'enter-html-full-screen', listener: Function): this; - once(event: 'enter-html-full-screen', listener: Function): this; - addListener(event: 'enter-html-full-screen', listener: Function): this; - removeListener(event: 'enter-html-full-screen', listener: Function): this; - /** - * Emitted when the window gains focus. - */ - on(event: 'focus', listener: Function): this; - once(event: 'focus', listener: Function): this; - addListener(event: 'focus', listener: Function): this; - removeListener(event: 'focus', listener: Function): this; - /** - * Emitted when the window is hidden. - */ - on(event: 'hide', listener: Function): this; - once(event: 'hide', listener: Function): this; - addListener(event: 'hide', listener: Function): this; - removeListener(event: 'hide', listener: Function): this; - /** - * Emitted when the window leaves a full-screen state. - */ - on(event: 'leave-full-screen', listener: Function): this; - once(event: 'leave-full-screen', listener: Function): this; - addListener(event: 'leave-full-screen', listener: Function): this; - removeListener(event: 'leave-full-screen', listener: Function): this; - /** - * Emitted when the window leaves a full-screen state triggered by HTML API. - */ - on(event: 'leave-html-full-screen', listener: Function): this; - once(event: 'leave-html-full-screen', listener: Function): this; - addListener(event: 'leave-html-full-screen', listener: Function): this; - removeListener(event: 'leave-html-full-screen', listener: Function): this; - /** - * Emitted when window is maximized. - */ - on(event: 'maximize', listener: Function): this; - once(event: 'maximize', listener: Function): this; - addListener(event: 'maximize', listener: Function): this; - removeListener(event: 'maximize', listener: Function): this; - /** - * Emitted when the window is minimized. - */ - on(event: 'minimize', listener: Function): this; - once(event: 'minimize', listener: Function): this; - addListener(event: 'minimize', listener: Function): this; - removeListener(event: 'minimize', listener: Function): this; - /** - * Emitted when the window is being moved to a new position. Note: On macOS this - * event is an alias of moved. - */ - on(event: 'move', listener: Function): this; - once(event: 'move', listener: Function): this; - addListener(event: 'move', listener: Function): this; - removeListener(event: 'move', listener: Function): this; - /** - * Emitted once when the window is moved to a new position. - */ - on(event: 'moved', listener: Function): this; - once(event: 'moved', listener: Function): this; - addListener(event: 'moved', listener: Function): this; - removeListener(event: 'moved', listener: Function): this; - /** - * Emitted when the native new tab button is clicked. - */ - on(event: 'new-window-for-tab', listener: Function): this; - once(event: 'new-window-for-tab', listener: Function): this; - addListener(event: 'new-window-for-tab', listener: Function): this; - removeListener(event: 'new-window-for-tab', listener: Function): this; - /** - * Emitted when the document changed its title, calling event.preventDefault() will - * prevent the native window's title from changing. explicitSet is false when title - * is synthesized from file url. - */ - on(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - once(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - addListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - removeListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - /** - * Emitted when the web page has been rendered (while not being shown) and window - * can be displayed without a visual flash. - */ - on(event: 'ready-to-show', listener: Function): this; - once(event: 'ready-to-show', listener: Function): this; - addListener(event: 'ready-to-show', listener: Function): this; - removeListener(event: 'ready-to-show', listener: Function): this; - /** - * Emitted after the window has been resized. - */ - on(event: 'resize', listener: Function): this; - once(event: 'resize', listener: Function): this; - addListener(event: 'resize', listener: Function): this; - removeListener(event: 'resize', listener: Function): this; - /** - * Emitted when the unresponsive web page becomes responsive again. - */ - on(event: 'responsive', listener: Function): this; - once(event: 'responsive', listener: Function): this; - addListener(event: 'responsive', listener: Function): this; - removeListener(event: 'responsive', listener: Function): this; - /** - * Emitted when the window is restored from a minimized state. - */ - on(event: 'restore', listener: Function): this; - once(event: 'restore', listener: Function): this; - addListener(event: 'restore', listener: Function): this; - removeListener(event: 'restore', listener: Function): this; - /** - * Emitted when scroll wheel event phase has begun. - */ - on(event: 'scroll-touch-begin', listener: Function): this; - once(event: 'scroll-touch-begin', listener: Function): this; - addListener(event: 'scroll-touch-begin', listener: Function): this; - removeListener(event: 'scroll-touch-begin', listener: Function): this; - /** - * Emitted when scroll wheel event phase filed upon reaching the edge of element. - */ - on(event: 'scroll-touch-edge', listener: Function): this; - once(event: 'scroll-touch-edge', listener: Function): this; - addListener(event: 'scroll-touch-edge', listener: Function): this; - removeListener(event: 'scroll-touch-edge', listener: Function): this; - /** - * Emitted when scroll wheel event phase has ended. - */ - on(event: 'scroll-touch-end', listener: Function): this; - once(event: 'scroll-touch-end', listener: Function): this; - addListener(event: 'scroll-touch-end', listener: Function): this; - removeListener(event: 'scroll-touch-end', listener: Function): this; - /** - * Emitted when window session is going to end due to force shutdown or machine - * restart or session log off. - */ - on(event: 'session-end', listener: Function): this; - once(event: 'session-end', listener: Function): this; - addListener(event: 'session-end', listener: Function): this; - removeListener(event: 'session-end', listener: Function): this; - /** - * Emitted when the window opens a sheet. - */ - on(event: 'sheet-begin', listener: Function): this; - once(event: 'sheet-begin', listener: Function): this; - addListener(event: 'sheet-begin', listener: Function): this; - removeListener(event: 'sheet-begin', listener: Function): this; - /** - * Emitted when the window has closed a sheet. - */ - on(event: 'sheet-end', listener: Function): this; - once(event: 'sheet-end', listener: Function): this; - addListener(event: 'sheet-end', listener: Function): this; - removeListener(event: 'sheet-end', listener: Function): this; - /** - * Emitted when the window is shown. - */ - on(event: 'show', listener: Function): this; - once(event: 'show', listener: Function): this; - addListener(event: 'show', listener: Function): this; - removeListener(event: 'show', listener: Function): this; - /** - * Emitted on 3-finger swipe. Possible directions are up, right, down, left. - */ - on(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - once(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - addListener(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - removeListener(event: 'swipe', listener: (event: Event, - direction: string) => void): this; - /** - * Emitted when the window exits from a maximized state. - */ - on(event: 'unmaximize', listener: Function): this; - once(event: 'unmaximize', listener: Function): this; - addListener(event: 'unmaximize', listener: Function): this; - removeListener(event: 'unmaximize', listener: Function): this; - /** - * Emitted when the web page becomes unresponsive. - */ - on(event: 'unresponsive', listener: Function): this; - once(event: 'unresponsive', listener: Function): this; - addListener(event: 'unresponsive', listener: Function): this; - removeListener(event: 'unresponsive', listener: Function): this; - /** - * Emitted before the window is moved. Calling event.preventDefault() will prevent - * the window from being moved. Note that this is only emitted when the window is - * being resized manually. Resizing the window with setBounds/setSize will not emit - * this event. - */ - on(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - once(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - addListener(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - removeListener(event: 'will-move', listener: (event: Event, - /** - * ` Location the window is being moved to. - */ - newBounds: Rectangle) => void): this; - /** - * Emitted before the window is resized. Calling event.preventDefault() will - * prevent the window from being resized. Note that this is only emitted when the - * window is being resized manually. Resizing the window with setBounds/setSize - * will not emit this event. - */ - on(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - once(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - addListener(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - removeListener(event: 'will-resize', listener: (event: Event, - /** - * ` Size the window is being resized to. - */ - newBounds: Rectangle) => void): this; - constructor(options?: BrowserWindowConstructorOptions); - /** - * Adds DevTools extension located at path, and returns extension's name. The - * extension will be remembered so you only need to call this API once, this API is - * not for programming use. If you try to add an extension that has already been - * loaded, this method will not return and instead log a warning to the console. - * The method will also not return if the extension's manifest is missing or - * incomplete. Note: This API cannot be called before the ready event of the app - * module is emitted. - */ - static addDevToolsExtension(path: string): void; - /** - * Adds Chrome extension located at path, and returns extension's name. The method - * will also not return if the extension's manifest is missing or incomplete. Note: - * This API cannot be called before the ready event of the app module is emitted. - */ - static addExtension(path: string): void; - static fromBrowserView(browserView: BrowserView): (BrowserWindow) | (null); - static fromId(id: number): BrowserWindow; - static fromWebContents(webContents: WebContents): BrowserWindow; - static getAllWindows(): BrowserWindow[]; - /** - * To check if a DevTools extension is installed you can run the following: Note: - * This API cannot be called before the ready event of the app module is emitted. - */ - static getDevToolsExtensions(): DevToolsExtensions; - /** - * Note: This API cannot be called before the ready event of the app module is - * emitted. - */ - static getExtensions(): Extensions; - static getFocusedWindow(): (BrowserWindow) | (null); - /** - * Remove a DevTools extension by name. Note: This API cannot be called before the - * ready event of the app module is emitted. - */ - static removeDevToolsExtension(name: string): void; - /** - * Remove a Chrome extension by name. Note: This API cannot be called before the - * ready event of the app module is emitted. - */ - static removeExtension(name: string): void; - /** - * Replacement API for setBrowserView supporting work with multi browser views. - */ - addBrowserView(browserView: BrowserView): void; - /** - * Adds a window as a tab on this window, after the tab for the window instance. - */ - addTabbedWindow(browserWindow: BrowserWindow): void; - /** - * Removes focus from the window. - */ - blur(): void; - blurWebView(): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Moves window to the center of the screen. - */ - center(): void; - /** - * Try to close the window. This has the same effect as a user manually clicking - * the close button of the window. The web page may cancel the close though. See - * the close event. - */ - close(): void; - /** - * Closes the currently open Quick Look panel. - */ - closeFilePreview(): void; - /** - * Force closing the window, the unload and beforeunload event won't be emitted for - * the web page, and close event will also not be emitted for this window, but it - * guarantees the closed event will be emitted. - */ - destroy(): void; - /** - * Starts or stops flashing the window to attract user's attention. - */ - flashFrame(flag: boolean): void; - /** - * Focuses on the window. - */ - focus(): void; - focusOnWebView(): void; - getBounds(): Rectangle; - getBrowserView(): (BrowserView) | (null); - /** - * Returns array of BrowserView what was an attached with addBrowserView or - * setBrowserView. Note: The BrowserView API is currently experimental and may - * change or be removed in future Electron releases. - */ - getBrowserViews(): void; - getChildWindows(): BrowserWindow[]; - getContentBounds(): Rectangle; - getContentSize(): number[]; - getMaximumSize(): number[]; - getMinimumSize(): number[]; - /** - * The native type of the handle is HWND on Windows, NSView* on macOS, and Window - * (unsigned long) on Linux. - */ - getNativeWindowHandle(): Buffer; - /** - * Note: whatever the current state of the window : maximized, minimized or in - * fullscreen, this function always returns the position and size of the window in - * normal state. In normal state, getBounds and getNormalBounds returns the same - * Rectangle. - */ - getNormalBounds(): Rectangle; - getOpacity(): number; - getParentWindow(): BrowserWindow; - getPosition(): number[]; - getRepresentedFilename(): string; - getSize(): number[]; - /** - * Note: The title of the web page can be different from the title of the native - * window. - */ - getTitle(): string; - hasShadow(): boolean; - /** - * Hides the window. - */ - hide(): void; - /** - * Hooks a windows message. The callback is called when the message is received in - * the WndProc. - */ - hookWindowMessage(message: number, callback: Function): void; - isAlwaysOnTop(): boolean; - /** - * On Linux always returns true. - */ - isClosable(): boolean; - isDestroyed(): boolean; - isDocumentEdited(): boolean; - isFocused(): boolean; - isFullScreen(): boolean; - isFullScreenable(): boolean; - isKiosk(): boolean; - /** - * On Linux always returns true. - */ - isMaximizable(): boolean; - isMaximized(): boolean; - isMenuBarAutoHide(): boolean; - isMenuBarVisible(): boolean; - /** - * On Linux always returns true. - */ - isMinimizable(): boolean; - isMinimized(): boolean; - isModal(): boolean; - /** - * On Linux always returns true. - */ - isMovable(): boolean; - isNormal(): boolean; - isResizable(): boolean; - isSimpleFullScreen(): boolean; - isVisible(): boolean; - /** - * Note: This API always returns false on Windows. - */ - isVisibleOnAllWorkspaces(): boolean; - isWindowMessageHooked(message: number): boolean; - /** - * Same as webContents.loadFile, filePath should be a path to an HTML file relative - * to the root of your application. See the webContents docs for more information. - */ - loadFile(filePath: string, options?: LoadFileOptions): Promise; - /** - * Same as webContents.loadURL(url[, options]). The url can be a remote address - * (e.g. http://) or a path to a local HTML file using the file:// protocol. To - * ensure that file URLs are properly formatted, it is recommended to use Node's - * url.format method: You can load a URL using a POST request with URL-encoded data - * by doing the following: - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Maximizes the window. This will also show (but not focus) the window if it isn't - * being displayed already. - */ - maximize(): void; - /** - * Merges all windows into one window with multiple tabs when native tabs are - * enabled and there is more than one open window. - */ - mergeAllWindows(): void; - /** - * Minimizes the window. On some platforms the minimized window will be shown in - * the Dock. - */ - minimize(): void; - /** - * Moves the current tab into a new window if native tabs are enabled and there is - * more than one tab in the current window. - */ - moveTabToNewWindow(): void; - /** - * Moves window to top(z-order) regardless of focus - */ - moveTop(): void; - /** - * Uses Quick Look to preview a file at a given path. - */ - previewFile(path: string, displayName?: string): void; - /** - * Same as webContents.reload. - */ - reload(): void; - removeBrowserView(browserView: BrowserView): void; - /** - * Remove the window's menu bar. - */ - removeMenu(): void; - /** - * Restores the window from minimized state to its previous state. - */ - restore(): void; - /** - * Selects the next tab when native tabs are enabled and there are other tabs in - * the window. - */ - selectNextTab(): void; - /** - * Selects the previous tab when native tabs are enabled and there are other tabs - * in the window. - */ - selectPreviousTab(): void; - /** - * Sets whether the window should show always on top of other windows. After - * setting this, the window is still a normal window, not a toolbox window which - * can not be focused on. - */ - setAlwaysOnTop(flag: boolean, level?: 'normal' | 'floating' | 'torn-off-menu' | 'modal-panel' | 'main-menu' | 'status' | 'pop-up-menu' | 'screen-saver', relativeLevel?: number): void; - /** - * Sets the properties for the window's taskbar button. Note: relaunchCommand and - * relaunchDisplayName must always be set together. If one of those properties is - * not set, then neither will be used. - */ - setAppDetails(options: AppDetailsOptions): void; - /** - * This will make a window maintain an aspect ratio. The extra size allows a - * developer to have space, specified in pixels, not included within the aspect - * ratio calculations. This API already takes into account the difference between a - * window's size and its content size. Consider a normal window with an HD video - * player and associated controls. Perhaps there are 15 pixels of controls on the - * left edge, 25 pixels of controls on the right edge and 50 pixels of controls - * below the player. In order to maintain a 16:9 aspect ratio (standard aspect - * ratio for HD @1920x1080) within the player itself we would call this function - * with arguments of 16/9 and [ 40, 50 ]. The second argument doesn't care where - * the extra width and height are within the content view--only that they exist. - * Sum any extra width and height areas you have within the overall content view. - * Calling this function with a value of 0 will remove any previously set aspect - * ratios. - */ - setAspectRatio(aspectRatio: number, extraSize: Size): void; - /** - * Controls whether to hide cursor when typing. - */ - setAutoHideCursor(autoHide: boolean): void; - /** - * Sets whether the window menu bar should hide itself automatically. Once set the - * menu bar will only show when users press the single Alt key. If the menu bar is - * already visible, calling setAutoHideMenuBar(true) won't hide it immediately. - */ - setAutoHideMenuBar(hide: boolean): void; - /** - * Sets the background color of the window. See Setting backgroundColor. - */ - setBackgroundColor(backgroundColor: string): void; - /** - * Resizes and moves the window to the supplied bounds. Any properties that are not - * supplied will default to their current values. - */ - setBounds(bounds: Rectangle, animate?: boolean): void; - setBrowserView(browserView: BrowserView): void; - /** - * Sets whether the window can be manually closed by user. On Linux does nothing. - */ - setClosable(closable: boolean): void; - /** - * Resizes and moves the window's client area (e.g. the web page) to the supplied - * bounds. - */ - setContentBounds(bounds: Rectangle, animate?: boolean): void; - /** - * Prevents the window contents from being captured by other apps. On macOS it sets - * the NSWindow's sharingType to NSWindowSharingNone. On Windows it calls - * SetWindowDisplayAffinity with WDA_MONITOR. - */ - setContentProtection(enable: boolean): void; - /** - * Resizes the window's client area (e.g. the web page) to width and height. - */ - setContentSize(width: number, height: number, animate?: boolean): void; - /** - * Specifies whether the window’s document has been edited, and the icon in title - * bar will become gray when set to true. - */ - setDocumentEdited(edited: boolean): void; - /** - * Disable or enable the window. - */ - setEnabled(enable: boolean): void; - /** - * Changes whether the window can be focused. - */ - setFocusable(focusable: boolean): void; - /** - * Sets whether the window should be in fullscreen mode. - */ - setFullScreen(flag: boolean): void; - /** - * Sets whether the maximize/zoom window button toggles fullscreen mode or - * maximizes the window. - */ - setFullScreenable(fullscreenable: boolean): void; - /** - * Sets whether the window should have a shadow. - */ - setHasShadow(hasShadow: boolean): void; - /** - * Changes window icon. - */ - setIcon(icon: NativeImage): void; - /** - * Makes the window ignore all mouse events. All mouse events happened in this - * window will be passed to the window below this window, but if this window has - * focus, it will still receive keyboard events. - */ - setIgnoreMouseEvents(ignore: boolean, options?: IgnoreMouseEventsOptions): void; - /** - * Enters or leaves the kiosk mode. - */ - setKiosk(flag: boolean): void; - /** - * Sets whether the window can be manually maximized by user. On Linux does - * nothing. - */ - setMaximizable(maximizable: boolean): void; - /** - * Sets the maximum size of window to width and height. - */ - setMaximumSize(width: number, height: number): void; - /** - * Sets the menu as the window's menu bar. - */ - setMenu(menu: (Menu) | (null)): void; - /** - * Sets whether the menu bar should be visible. If the menu bar is auto-hide, users - * can still bring up the menu bar by pressing the single Alt key. - */ - setMenuBarVisibility(visible: boolean): void; - /** - * Sets whether the window can be manually minimized by user. On Linux does - * nothing. - */ - setMinimizable(minimizable: boolean): void; - /** - * Sets the minimum size of window to width and height. - */ - setMinimumSize(width: number, height: number): void; - /** - * Sets whether the window can be moved by user. On Linux does nothing. - */ - setMovable(movable: boolean): void; - /** - * Sets the opacity of the window. On Linux does nothing. - */ - setOpacity(opacity: number): void; - /** - * Sets a 16 x 16 pixel overlay onto the current taskbar icon, usually used to - * convey some sort of application status or to passively notify the user. - */ - setOverlayIcon(overlay: (NativeImage) | (null), description: string): void; - /** - * Sets parent as current window's parent window, passing null will turn current - * window into a top-level window. - */ - setParentWindow(parent: BrowserWindow): void; - /** - * Moves window to x and y. - */ - setPosition(x: number, y: number, animate?: boolean): void; - /** - * Sets progress value in progress bar. Valid range is [0, 1.0]. Remove progress - * bar when progress < 0; Change to indeterminate mode when progress > 1. On Linux - * platform, only supports Unity desktop environment, you need to specify the - * *.desktop file name to desktopName field in package.json. By default, it will - * assume app.getName().desktop. On Windows, a mode can be passed. Accepted values - * are none, normal, indeterminate, error, and paused. If you call setProgressBar - * without a mode set (but with a value within the valid range), normal will be - * assumed. - */ - setProgressBar(progress: number, options?: ProgressBarOptions): void; - /** - * Sets the pathname of the file the window represents, and the icon of the file - * will show in window's title bar. - */ - setRepresentedFilename(filename: string): void; - /** - * Sets whether the window can be manually resized by user. - */ - setResizable(resizable: boolean): void; - /** - * Setting a window shape determines the area within the window where the system - * permits drawing and user interaction. Outside of the given region, no pixels - * will be drawn and no mouse events will be registered. Mouse events outside of - * the region will not be received by that window, but will fall through to - * whatever is behind the window. - */ - setShape(rects: Rectangle[]): void; - /** - * Changes the attachment point for sheets on macOS. By default, sheets are - * attached just below the window frame, but you may want to display them beneath a - * HTML-rendered toolbar. For example: - */ - setSheetOffset(offsetY: number, offsetX?: number): void; - /** - * Enters or leaves simple fullscreen mode. Simple fullscreen mode emulates the - * native fullscreen behavior found in versions of Mac OS X prior to Lion (10.7). - */ - setSimpleFullScreen(flag: boolean): void; - /** - * Resizes the window to width and height. If width or height are below any set - * minimum size constraints the window will snap to its minimum size. - */ - setSize(width: number, height: number, animate?: boolean): void; - /** - * Makes the window not show in the taskbar. - */ - setSkipTaskbar(skip: boolean): void; - /** - * Add a thumbnail toolbar with a specified set of buttons to the thumbnail image - * of a window in a taskbar button layout. Returns a Boolean object indicates - * whether the thumbnail has been added successfully. The number of buttons in - * thumbnail toolbar should be no greater than 7 due to the limited room. Once you - * setup the thumbnail toolbar, the toolbar cannot be removed due to the platform's - * limitation. But you can call the API with an empty array to clean the buttons. - * The buttons is an array of Button objects: The flags is an array that can - * include following Strings: - */ - setThumbarButtons(buttons: ThumbarButton[]): boolean; - /** - * Sets the region of the window to show as the thumbnail image displayed when - * hovering over the window in the taskbar. You can reset the thumbnail to be the - * entire window by specifying an empty region: { x: 0, y: 0, width: 0, height: 0 - * }. - */ - setThumbnailClip(region: Rectangle): void; - /** - * Sets the toolTip that is displayed when hovering over the window thumbnail in - * the taskbar. - */ - setThumbnailToolTip(toolTip: string): void; - /** - * Changes the title of native window to title. - */ - setTitle(title: string): void; - /** - * Sets the touchBar layout for the current window. Specifying null or undefined - * clears the touch bar. This method only has an effect if the machine has a touch - * bar and is running on macOS 10.12.1+. Note: The TouchBar API is currently - * experimental and may change or be removed in future Electron releases. - */ - setTouchBar(touchBar: TouchBar): void; - /** - * Adds a vibrancy effect to the browser window. Passing null or an empty string - * will remove the vibrancy effect on the window. - */ - setVibrancy(type: 'appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'medium-light' | 'ultra-dark'): void; - /** - * Sets whether the window should be visible on all workspaces. Note: This API does - * nothing on Windows. - */ - setVisibleOnAllWorkspaces(visible: boolean, options?: VisibleOnAllWorkspacesOptions): void; - /** - * Sets whether the window traffic light buttons should be visible. This cannot be - * called when titleBarStyle is set to customButtonsOnHover. - */ - setWindowButtonVisibility(visible: boolean): void; - /** - * Shows and gives focus to the window. - */ - show(): void; - /** - * Same as webContents.showDefinitionForSelection(). - */ - showDefinitionForSelection(): void; - /** - * Shows the window but doesn't focus on it. - */ - showInactive(): void; - /** - * Toggles the visibility of the tab bar if native tabs are enabled and there is - * only one tab in the current window. - */ - toggleTabBar(): void; - /** - * Unhooks all of the window messages. - */ - unhookAllWindowMessages(): void; - /** - * Unhook the window message. - */ - unhookWindowMessage(message: number): void; - /** - * Unmaximizes the window. - */ - unmaximize(): void; - id: number; - webContents: WebContents; - } - - class BrowserWindowProxy extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/browser-window-proxy - - /** - * Removes focus from the child window. - */ - blur(): void; - /** - * Forcefully closes the child window without calling its unload event. - */ - close(): void; - /** - * Evaluates the code in the child window. - */ - eval(code: string): void; - /** - * Focuses the child window (brings the window to front). - */ - focus(): void; - /** - * Sends a message to the child window with the specified origin or * for no origin - * preference. In addition to these methods, the child window implements - * window.opener object with no properties and a single method. - */ - postMessage(message: string, targetOrigin: string): void; - /** - * Invokes the print dialog on the child window. - */ - print(): void; - closed: boolean; - } - - interface Certificate { - - // Docs: http://electronjs.org/docs/api/structures/certificate - - /** - * PEM encoded data - */ - data: string; - /** - * Fingerprint of the certificate - */ - fingerprint: string; - /** - * Issuer principal - */ - issuer: CertificatePrincipal; - /** - * Issuer certificate (if not self-signed) - */ - issuerCert: Certificate; - /** - * Issuer's Common Name - */ - issuerName: string; - /** - * Hex value represented string - */ - serialNumber: string; - /** - * Subject principal - */ - subject: CertificatePrincipal; - /** - * Subject's Common Name - */ - subjectName: string; - /** - * End date of the certificate being valid in seconds - */ - validExpiry: number; - /** - * Start date of the certificate being valid in seconds - */ - validStart: number; - } - - interface CertificatePrincipal { - - // Docs: http://electronjs.org/docs/api/structures/certificate-principal - - /** - * Common Name. - */ - commonName: string; - /** - * Country or region. - */ - country: string; - /** - * Locality. - */ - locality: string; - /** - * Organization names. - */ - organizations: string[]; - /** - * Organization Unit names. - */ - organizationUnits: string[]; - /** - * State or province. - */ - state: string; - } - - class ClientRequest extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/client-request - - /** - * Emitted when the request is aborted. The abort event will not be fired if the - * request is already closed. - */ - on(event: 'abort', listener: Function): this; - once(event: 'abort', listener: Function): this; - addListener(event: 'abort', listener: Function): this; - removeListener(event: 'abort', listener: Function): this; - /** - * Emitted as the last event in the HTTP request-response transaction. The close - * event indicates that no more events will be emitted on either the request or - * response objects. - */ - on(event: 'close', listener: Function): this; - once(event: 'close', listener: Function): this; - addListener(event: 'close', listener: Function): this; - removeListener(event: 'close', listener: Function): this; - /** - * Emitted when the net module fails to issue a network request. Typically when the - * request object emits an error event, a close event will subsequently follow and - * no response object will be provided. - */ - on(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - once(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - addListener(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - removeListener(event: 'error', listener: ( - /** - * an error object providing some information about the failure. - */ - error: Error) => void): this; - /** - * Emitted just after the last chunk of the request's data has been written into - * the request object. - */ - on(event: 'finish', listener: Function): this; - once(event: 'finish', listener: Function): this; - addListener(event: 'finish', listener: Function): this; - removeListener(event: 'finish', listener: Function): this; - /** - * Emitted when an authenticating proxy is asking for user credentials. The - * callback function is expected to be called back with user credentials: Providing - * empty credentials will cancel the request and report an authentication error on - * the response object: - */ - on(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when there is redirection and the mode is manual. Calling - * request.followRedirect will continue with the redirection. - */ - on(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - once(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - addListener(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - removeListener(event: 'redirect', listener: (statusCode: number, - method: string, - redirectUrl: string, - responseHeaders: any) => void): this; - on(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - once(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - addListener(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - removeListener(event: 'response', listener: ( - /** - * An object representing the HTTP response message. - */ - response: IncomingMessage) => void): this; - constructor(options: 'method' | 'url' | 'session' | 'partition' | 'protocol' | 'host' | 'hostname' | 'port' | 'path' | 'redirect'); - /** - * Cancels an ongoing HTTP transaction. If the request has already emitted the - * close event, the abort operation will have no effect. Otherwise an ongoing event - * will emit abort and close events. Additionally, if there is an ongoing response - * object,it will emit the aborted event. - */ - abort(): void; - /** - * Sends the last chunk of the request data. Subsequent write or end operations - * will not be allowed. The finish event is emitted just after the end operation. - */ - end(chunk?: (string) | (Buffer), encoding?: string, callback?: Function): void; - /** - * Continues any deferred redirection request when the redirection mode is manual. - */ - followRedirect(): void; - getHeader(name: string): Header; - /** - * You can use this method in conjunction with POST requests to get the progress of - * a file upload or other data transfer. - */ - getUploadProgress(): UploadProgress; - /** - * Removes a previously set extra header name. This method can be called only - * before first write. Trying to call it after the first write will throw an error. - */ - removeHeader(name: string): void; - /** - * Adds an extra HTTP header. The header name will issued as it is without - * lowercasing. It can be called only before first write. Calling this method after - * the first write will throw an error. If the passed value is not a String, its - * toString() method will be called to obtain the final value. - */ - setHeader(name: string, value: any): void; - /** - * callback is essentially a dummy function introduced in the purpose of keeping - * similarity with the Node.js API. It is called asynchronously in the next tick - * after chunk content have been delivered to the Chromium networking layer. - * Contrary to the Node.js implementation, it is not guaranteed that chunk content - * have been flushed on the wire before callback is called. Adds a chunk of data to - * the request body. The first write operation may cause the request headers to be - * issued on the wire. After the first write operation, it is not allowed to add or - * remove a custom header. - */ - write(chunk: (string) | (Buffer), encoding?: string, callback?: Function): void; - chunkedEncoding: boolean; - } - - interface Clipboard extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/clipboard - - availableFormats(type?: 'selection' | 'clipboard'): string[]; - /** - * Clears the clipboard content. - */ - clear(type?: 'selection' | 'clipboard'): void; - has(format: string, type?: 'selection' | 'clipboard'): boolean; - read(format: string): string; - /** - * Returns an Object containing title and url keys representing the bookmark in the - * clipboard. The title and url values will be empty strings when the bookmark is - * unavailable. - */ - readBookmark(): ReadBookmark; - readBuffer(format: string): Buffer; - /** - * This method uses synchronous IPC when called from the renderer process. The - * cached value is reread from the find pasteboard whenever the application is - * activated. - */ - readFindText(): string; - readHTML(type?: 'selection' | 'clipboard'): string; - readImage(type?: 'selection' | 'clipboard'): NativeImage; - readRTF(type?: 'selection' | 'clipboard'): string; - readText(type?: 'selection' | 'clipboard'): string; - /** - * Writes data to the clipboard. - */ - write(data: Data, type?: 'selection' | 'clipboard'): void; - /** - * Writes the title and url into the clipboard as a bookmark. Note: Most apps on - * Windows don't support pasting bookmarks into them so you can use clipboard.write - * to write both a bookmark and fallback text to the clipboard. - */ - writeBookmark(title: string, url: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes the buffer into the clipboard as format. - */ - writeBuffer(format: string, buffer: Buffer, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the find pasteboard (the pasteboard that holds information - * about the current state of the active application’s find panel) as plain text. - * This method uses synchronous IPC when called from the renderer process. - */ - writeFindText(text: string): void; - /** - * Writes markup to the clipboard. - */ - writeHTML(markup: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes image to the clipboard. - */ - writeImage(image: NativeImage, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the clipboard in RTF. - */ - writeRTF(text: string, type?: 'selection' | 'clipboard'): void; - /** - * Writes the text into the clipboard as plain text. - */ - writeText(text: string, type?: 'selection' | 'clipboard'): void; - } - - interface ContentTracing extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/content-tracing - - /** - * Get a set of category groups. The category groups can change as new code paths - * are reached. Once all child processes have acknowledged the getCategories - * request the callback is invoked with an array of category groups. Deprecated - * Soon - */ - getCategories(callback: (categories: string[]) => void): void; - /** - * Get a set of category groups. The category groups can change as new code paths - * are reached. - */ - getCategories(): Promise; - /** - * Get the maximum usage across processes of trace buffer as a percentage of the - * full state. When the TraceBufferUsage value is determined the callback is - * called. Deprecated Soon - */ - getTraceBufferUsage(callback: (value: number) => void): void; - /** - * Get the maximum usage across processes of trace buffer as a percentage of the - * full state. - */ - getTraceBufferUsage(): Promise; - /** - * Start recording on all processes. Recording begins immediately locally and - * asynchronously on child processes as soon as they receive the EnableRecording - * request. The callback will be called once all child processes have acknowledged - * the startRecording request. Deprecated Soon - */ - startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig), callback: Function): void; - /** - * Start recording on all processes. Recording begins immediately locally and - * asynchronously on child processes as soon as they receive the EnableRecording - * request. - */ - startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig)): Promise; - /** - * Stop recording on all processes. Child processes typically cache trace data and - * only rarely flush and send trace data back to the main process. This helps to - * minimize the runtime overhead of tracing since sending trace data over IPC can - * be an expensive operation. So, to end tracing, we must asynchronously ask all - * child processes to flush any pending trace data. Once all child processes have - * acknowledged the stopRecording request, callback will be called with a file that - * contains the traced data. Trace data will be written into resultFilePath if it - * is not empty or into a temporary file. The actual file path will be passed to - * callback if it's not null. Deprecated Soon - */ - stopRecording(resultFilePath: string, callback: (resultFilePath: string) => void): void; - /** - * Stop recording on all processes. Child processes typically cache trace data and - * only rarely flush and send trace data back to the main process. This helps to - * minimize the runtime overhead of tracing since sending trace data over IPC can - * be an expensive operation. So, to end tracing, we must asynchronously ask all - * child processes to flush any pending trace data. Trace data will be written into - * resultFilePath if it is not empty or into a temporary file. - */ - stopRecording(resultFilePath: string): Promise; - } - - interface ContextBridge extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/context-bridge - - exposeInMainWorld(apiKey: string, api: Record): void; - } - - interface Cookie { - - // Docs: http://electronjs.org/docs/api/structures/cookie - - /** - * The domain of the cookie; this will be normalized with a preceding dot so that - * it's also valid for subdomains. - */ - domain?: string; - /** - * The expiration date of the cookie as the number of seconds since the UNIX epoch. - * Not provided for session cookies. - */ - expirationDate?: number; - /** - * Whether the cookie is a host-only cookie; this will only be true if no domain - * was passed. - */ - hostOnly?: boolean; - /** - * Whether the cookie is marked as HTTP only. - */ - httpOnly?: boolean; - /** - * The name of the cookie. - */ - name: string; - /** - * The path of the cookie. - */ - path?: string; - /** - * Whether the cookie is marked as secure. - */ - secure?: boolean; - /** - * Whether the cookie is a session cookie or a persistent cookie with an expiration - * date. - */ - session?: boolean; - /** - * The value of the cookie. - */ - value: string; - } - - class Cookies extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/cookies - - /** - * Emitted when a cookie is changed because it was added, edited, removed, or - * expired. - */ - on(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - once(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - addListener(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - removeListener(event: 'changed', listener: (event: Event, - /** - * The cookie that was changed. - */ - cookie: Cookie, - /** - * The cause of the change with one of the following values: - */ - cause: ('explicit' | 'overwrite' | 'expired' | 'evicted' | 'expired-overwrite'), - /** - * `true` if the cookie was removed, `false` otherwise. - */ - removed: boolean) => void): this; - /** - * Writes any unwritten cookies data to disk. - */ - flushStore(): Promise; - /** - * Writes any unwritten cookies data to disk. Deprecated Soon - */ - flushStore(callback: Function): void; - /** - * Sends a request to get all cookies matching filter, and resolves a promise with - * the response. - */ - get(filter: Filter): Promise; - /** - * Sends a request to get all cookies matching filter, callback will be called with - * callback(error, cookies) on complete. Deprecated Soon - */ - get(filter: Filter, callback: (error: Error, cookies: Cookie[]) => void): void; - /** - * Removes the cookies matching url and name - */ - remove(url: string, name: string): Promise; - /** - * Removes the cookies matching url and name, callback will called with callback() - * on complete. Deprecated Soon - */ - remove(url: string, name: string, callback: Function): void; - /** - * Sets a cookie with details. - */ - set(details: Details): Promise; - /** - * Sets a cookie with details, callback will be called with callback(error) on - * complete. Deprecated Soon - */ - set(details: Details, callback: (error: Error) => void): void; - } - - interface CPUUsage { - - // Docs: http://electronjs.org/docs/api/structures/cpu-usage - - /** - * The number of average idle cpu wakeups per second since the last call to - * getCPUUsage. First call returns 0. Will always return 0 on Windows. - */ - idleWakeupsPerSecond: number; - /** - * Percentage of CPU used since the last call to getCPUUsage. First call returns 0. - */ - percentCPUUsage: number; - } - - interface CrashReport { - - // Docs: http://electronjs.org/docs/api/structures/crash-report - - date: Date; - id: string; - } - - interface CrashReporter { - - // Docs: http://electronjs.org/docs/api/crash-reporter - - /** - * Set an extra parameter to be sent with the crash report. The values specified - * here will be sent in addition to any values set via the extra option when start - * was called. This API is only available on macOS and windows, if you need to - * add/update extra parameters on Linux after your first call to start you can call - * start again with the updated extra options. - */ - addExtraParameter(key: string, value: string): void; - /** - * Returns the date and ID of the last crash report. Only crash reports that have - * been uploaded will be returned; even if a crash report is present on disk it - * will not be returned until it is uploaded. In the case that there are no - * uploaded reports, null is returned. - */ - getLastCrashReport(): CrashReport; - /** - * See all of the current parameters being passed to the crash reporter. - */ - getParameters(): void; - /** - * Returns all uploaded crash reports. Each report contains the date and uploaded - * ID. - */ - getUploadedReports(): CrashReport[]; - /** - * Note: This API can only be called from the main process. - */ - getUploadToServer(): boolean; - /** - * Remove a extra parameter from the current set of parameters so that it will not - * be sent with the crash report. - */ - removeExtraParameter(key: string): void; - /** - * This would normally be controlled by user preferences. This has no effect if - * called before start is called. Note: This API can only be called from the main - * process. - */ - setUploadToServer(uploadToServer: boolean): void; - /** - * You are required to call this method before using any other crashReporter APIs - * and in each process (main/renderer) from which you want to collect crash - * reports. You can pass different options to crashReporter.start when calling from - * different processes. Note Child processes created via the child_process module - * will not have access to the Electron modules. Therefore, to collect crash - * reports from them, use process.crashReporter.start instead. Pass the same - * options as above along with an additional one called crashesDirectory that - * should point to a directory to store the crash reports temporarily. You can test - * this out by calling process.crash() to crash the child process. Note: If you - * need send additional/updated extra parameters after your first call start you - * can call addExtraParameter on macOS or call start again with the new/updated - * extra parameters on Linux and Windows. Note: On macOS and windows, Electron uses - * a new crashpad client for crash collection and reporting. If you want to enable - * crash reporting, initializing crashpad from the main process using - * crashReporter.start is required regardless of which process you want to collect - * crashes from. Once initialized this way, the crashpad handler collects crashes - * from all processes. You still have to call crashReporter.start from the renderer - * or child process, otherwise crashes from them will get reported without - * companyName, productName or any of the extra information. - */ - start(options: CrashReporterStartOptions): void; - } - - interface CustomScheme { - - // Docs: http://electronjs.org/docs/api/structures/custom-scheme - - privileges?: Privileges; - /** - * Custom schemes to be registered with options. - */ - scheme: string; - } - - class Debugger extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/debugger - - /** - * Emitted when debugging session is terminated. This happens either when - * webContents is closed or devtools is invoked for the attached webContents. - */ - on(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - once(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - addListener(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - removeListener(event: 'detach', listener: (event: Event, - /** - * Reason for detaching debugger. - */ - reason: string) => void): this; - /** - * Emitted whenever debugging target issues instrumentation event. - */ - on(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - once(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - addListener(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - removeListener(event: 'message', listener: (event: Event, - /** - * Method name. - */ - method: string, - /** - * Event parameters defined by the 'parameters' attribute in the remote debugging - * protocol. - */ - params: any) => void): this; - /** - * Attaches the debugger to the webContents. - */ - attach(protocolVersion?: string): void; - /** - * Detaches the debugger from the webContents. - */ - detach(): void; - isAttached(): boolean; - /** - * Send given command to the debugging target. Deprecated Soon - */ - sendCommand(method: string, commandParams?: any, callback?: (error: any, result: any) => void): void; - /** - * Send given command to the debugging target. - */ - sendCommand(method: string, commandParams?: any): Promise; - } - - interface DesktopCapturer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/desktop-capturer - - /** - * Starts gathering information about all available desktop media sources, and - * calls callback(error, sources) when finished. sources is an array of - * DesktopCapturerSource objects, each DesktopCapturerSource represents a screen or - * an individual window that can be captured. Deprecated Soon - */ - getSources(options: SourcesOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => void): void; - getSources(options: SourcesOptions): Promise; - } - - interface DesktopCapturerSource { - - // Docs: http://electronjs.org/docs/api/structures/desktop-capturer-source - - /** - * An icon image of the application that owns the window or null if the source has - * a type screen. The size of the icon is not known in advance and depends on what - * the the application provides. - */ - appIcon: NativeImage; - /** - * A unique identifier that will correspond to the id of the matching returned by - * the . On some platforms, this is equivalent to the XX portion of the id field - * above and on others it will differ. It will be an empty string if not available. - */ - display_id: string; - /** - * The identifier of a window or screen that can be used as a chromeMediaSourceId - * constraint when calling [navigator.webkitGetUserMedia]. The format of the - * identifier will be window:XX or screen:XX, where XX is a random generated - * number. - */ - id: string; - /** - * A screen source will be named either Entire Screen or Screen , while the name of - * a window source will match the window title. - */ - name: string; - /** - * A thumbnail image. There is no guarantee that the size of the thumbnail is the - * same as the thumbnailSize specified in the options passed to - * desktopCapturer.getSources. The actual size depends on the scale of the screen - * or window. - */ - thumbnail: NativeImage; - } - - interface Dialog extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/dialog - - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: - */ - showCertificateTrustDialog(options: CertificateTrustDialogOptions): Promise; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(options: CertificateTrustDialogOptions, callback: Function): void; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions): Promise; - /** - * On macOS, this displays a modal dialog that shows a message and certificate - * information, and gives the user the option of trusting/importing the - * certificate. If you provide a browserWindow argument the dialog will be attached - * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: Deprecated Soon - */ - showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; - /** - * Displays a modal dialog that shows an error message. This API can be called - * safely before the ready event the app module emits, it is usually used to report - * errors in early stage of startup. If called before the app readyevent on Linux, - * the message will be emitted to stderr, and no GUI dialog will appear. - */ - showErrorBox(title: string, content: string): void; - /** - * Shows a message box, it will block the process until the message box is closed. - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. - */ - showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions): Promise; - /** - * Shows a message box, it will block the process until the message box is closed. - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. - */ - showMessageBox(options: MessageBoxOptions): Promise; - /** - * Shows a message box, it will block the process until the message box is closed. - * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. - */ - showMessageBoxSync(browserWindow: BrowserWindow, options: MessageBoxSyncOptions): number; - /** - * Shows a message box, it will block the process until the message box is closed. - * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. - */ - showMessageBoxSync(options: MessageBoxSyncOptions): number; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: Function): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialog(options: OpenDialogOptions, callback?: Function): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialogSync(browserWindow: BrowserWindow, options: OpenDialogSyncOptions): (string[]) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed or selected when you want to limit the user to a specific type. For - * example: The extensions array should contain extensions without wildcards or - * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an - * open dialog can not be both a file selector and a directory selector, so if you - * set properties to ['openFile', 'openDirectory'] on these platforms, a directory - * selector will be shown. - */ - showOpenDialogSync(options: OpenDialogSyncOptions): (string[]) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using - * the asynchronous version is recommended to avoid issues when expanding and - * collapsing the dialog. - */ - showSaveDialog(options: SaveDialogOptions): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using - * the asynchronous version is recommended to avoid issues when expanding and - * collapsing the dialog. - */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions): Promise; - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. - */ - showSaveDialogSync(options: SaveDialogSyncOptions): (string) | (undefined); - /** - * The browserWindow argument allows the dialog to attach itself to a parent - * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. - */ - showSaveDialogSync(browserWindow: BrowserWindow, options: SaveDialogSyncOptions): (string) | (undefined); - } - - interface Display { - - // Docs: http://electronjs.org/docs/api/structures/display - - /** - * Can be available, unavailable, unknown. - */ - accelerometerSupport: ('available' | 'unavailable' | 'unknown'); - bounds: Rectangle; - /** - * The number of bits per pixel. - */ - colorDepth: number; - /** - * represent a color space (three-dimensional object which contains all realizable - * color combinations) for the purpose of color conversions - */ - colorSpace: string; - /** - * The number of bits per color component. - */ - depthPerComponent: number; - /** - * Unique identifier associated with the display. - */ - id: number; - /** - * true for an internal display and false for an external display - */ - internal: boolean; - /** - * Whether or not the display is a monochrome display. - */ - monochrome: boolean; - /** - * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. - */ - rotation: number; - /** - * Output device's pixel scale factor. - */ - scaleFactor: number; - size: Size; - /** - * Can be available, unavailable, unknown. - */ - touchSupport: ('available' | 'unavailable' | 'unknown'); - workArea: Rectangle; - workAreaSize: Size; - } - - class DownloadItem extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/download-item - - /** - * Emitted when the download is in a terminal state. This includes a completed - * download, a cancelled download (via downloadItem.cancel()), and interrupted - * download that can't be resumed. The state can be one of following: - */ - on(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - once(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - addListener(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - removeListener(event: 'done', listener: (event: Event, - /** - * Can be `completed`, `cancelled` or `interrupted`. - */ - state: ('completed' | 'cancelled' | 'interrupted')) => void): this; - /** - * Emitted when the download has been updated and is not done. The state can be one - * of following: - */ - on(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - once(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - addListener(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - removeListener(event: 'updated', listener: (event: Event, - /** - * Can be `progressing` or `interrupted`. - */ - state: ('progressing' | 'interrupted')) => void): this; - /** - * Cancels the download operation. - */ - cancel(): void; - canResume(): boolean; - getContentDisposition(): string; - getETag(): string; - /** - * Note: The file name is not always the same as the actual one saved in local - * disk. If user changes the file name in a prompted download saving dialog, the - * actual name of saved file will be different. - */ - getFilename(): string; - getLastModifiedTime(): string; - getMimeType(): string; - getReceivedBytes(): number; - getSaveDialogOptions(): SaveDialogOptions; - getSavePath(): string; - getStartTime(): number; - /** - * Note: The following methods are useful specifically to resume a cancelled item - * when session is restarted. - */ - getState(): ('progressing' | 'completed' | 'cancelled' | 'interrupted'); - /** - * If the size is unknown, it returns 0. - */ - getTotalBytes(): number; - getURL(): string; - getURLChain(): string[]; - hasUserGesture(): boolean; - isPaused(): boolean; - /** - * Pauses the download. - */ - pause(): void; - /** - * Resumes the download that has been paused. Note: To enable resumable downloads - * the server you are downloading from must support range requests and provide both - * Last-Modified and ETag header values. Otherwise resume() will dismiss previously - * received bytes and restart the download from the beginning. - */ - resume(): void; - /** - * This API allows the user to set custom options for the save dialog that opens - * for the download item by default. The API is only available in session's - * will-download callback function. - */ - setSaveDialogOptions(options: SaveDialogOptions): void; - /** - * The API is only available in session's will-download callback function. If user - * doesn't set the save path via the API, Electron will use the original routine to - * determine the save path(Usually prompts a save dialog). - */ - setSavePath(path: string): void; - } - - interface Event extends GlobalEvent { - - // Docs: http://electronjs.org/docs/api/structures/event - - preventDefault: (() => void); - } - - interface FileFilter { - - // Docs: http://electronjs.org/docs/api/structures/file-filter - - extensions: string[]; - name: string; - } - - interface GlobalShortcut extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/global-shortcut - - /** - * When the accelerator is already taken by other applications, this call will - * still return false. This behavior is intended by operating systems, since they - * don't want applications to fight for global shortcuts. - */ - isRegistered(accelerator: Accelerator): boolean; - /** - * Registers a global shortcut of accelerator. The callback is called when the - * registered shortcut is pressed by the user. When the accelerator is already - * taken by other applications, this call will silently fail. This behavior is - * intended by operating systems, since they don't want applications to fight for - * global shortcuts. The following accelerators will not be registered successfully - * on macOS 10.14 Mojave unless the app has been authorized as a trusted - * accessibility client: - */ - register(accelerator: Accelerator, callback: Function): boolean; - /** - * Registers a global shortcut of all accelerator items in accelerators. The - * callback is called when any of the registered shortcuts are pressed by the user. - * When a given accelerator is already taken by other applications, this call will - * silently fail. This behavior is intended by operating systems, since they don't - * want applications to fight for global shortcuts. The following accelerators will - * not be registered successfully on macOS 10.14 Mojave unless the app has been - * authorized as a trusted accessibility client: - */ - registerAll(accelerators: string[], callback: Function): void; - /** - * Unregisters the global shortcut of accelerator. - */ - unregister(accelerator: Accelerator): void; - /** - * Unregisters all of the global shortcuts. - */ - unregisterAll(): void; - } - - interface GPUFeatureStatus { - - // Docs: http://electronjs.org/docs/api/structures/gpu-feature-status - - /** - * Canvas. - */ - '2d_canvas': string; - /** - * Flash. - */ - flash_3d: string; - /** - * Flash Stage3D. - */ - flash_stage3d: string; - /** - * Flash Stage3D Baseline profile. - */ - flash_stage3d_baseline: string; - /** - * Compositing. - */ - gpu_compositing: string; - /** - * Multiple Raster Threads. - */ - multiple_raster_threads: string; - /** - * Native GpuMemoryBuffers. - */ - native_gpu_memory_buffers: string; - /** - * Rasterization. - */ - rasterization: string; - /** - * Video Decode. - */ - video_decode: string; - /** - * Video Encode. - */ - video_encode: string; - /** - * VPx Video Decode. - */ - vpx_decode: string; - /** - * WebGL. - */ - webgl: string; - /** - * WebGL2. - */ - webgl2: string; - } - - interface InAppPurchase extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/in-app-purchase - - /** - * Emitted when one or more transactions have been updated. - */ - on(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - once(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - addListener(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - removeListener(event: 'transactions-updated', listener: (event: Event, - /** - * Array of objects. - */ - transactions: Transaction[]) => void): this; - canMakePayments(): boolean; - /** - * Completes all pending transactions. - */ - finishAllTransactions(): void; - /** - * Completes the pending transactions corresponding to the date. - */ - finishTransactionByDate(date: string): void; - /** - * Retrieves the product descriptions. Deprecated Soon - */ - getProducts(productIDs: string[], callback: (products: Product[]) => void): void; - /** - * Retrieves the product descriptions. - */ - getProducts(productIDs: string[]): Promise; - getReceiptURL(): string; - /** - * You should listen for the transactions-updated event as soon as possible and - * certainly before you call purchaseProduct. Deprecated Soon - */ - purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; - /** - * You should listen for the transactions-updated event as soon as possible and - * certainly before you call purchaseProduct. - */ - purchaseProduct(productID: string, quantity?: number): Promise; - } - - class IncomingMessage extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/incoming-message - - /** - * Emitted when a request has been canceled during an ongoing HTTP transaction. - */ - on(event: 'aborted', listener: Function): this; - once(event: 'aborted', listener: Function): this; - addListener(event: 'aborted', listener: Function): this; - removeListener(event: 'aborted', listener: Function): this; - /** - * The data event is the usual method of transferring response data into - * applicative code. - */ - on(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - once(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - addListener(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - removeListener(event: 'data', listener: ( - /** - * A chunk of response body's data. - */ - chunk: Buffer) => void): this; - /** - * Indicates that response body has ended. - */ - on(event: 'end', listener: Function): this; - once(event: 'end', listener: Function): this; - addListener(event: 'end', listener: Function): this; - removeListener(event: 'end', listener: Function): this; - /** - * error Error - Typically holds an error string identifying failure root cause. - * Emitted when an error was encountered while streaming response data events. For - * instance, if the server closes the underlying while the response is still - * streaming, an error event will be emitted on the response object and a close - * event will subsequently follow on the request object. - */ - on(event: 'error', listener: Function): this; - once(event: 'error', listener: Function): this; - addListener(event: 'error', listener: Function): this; - removeListener(event: 'error', listener: Function): this; - headers: any; - httpVersion: string; - httpVersionMajor: number; - httpVersionMinor: number; - statusCode: number; - statusMessage: string; - } - - interface IOCounters { - - // Docs: http://electronjs.org/docs/api/structures/io-counters - - /** - * Then number of I/O other operations. - */ - otherOperationCount: number; - /** - * Then number of I/O other transfers. - */ - otherTransferCount: number; - /** - * The number of I/O read operations. - */ - readOperationCount: number; - /** - * The number of I/O read transfers. - */ - readTransferCount: number; - /** - * The number of I/O write operations. - */ - writeOperationCount: number; - /** - * The number of I/O write transfers. - */ - writeTransferCount: number; - } - - interface IpcMain extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/ipc-main - - /** - * Listens to channel, when a new message arrives listener would be called with - * listener(event, args...). - */ - on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; - /** - * Adds a one time listener function for the event. This listener is invoked only - * the next time a message is sent to channel, after which it is removed. - */ - once(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; - /** - * Removes listeners of the specified channel. - */ - removeAllListeners(channel: string): this; - /** - * Removes the specified listener from the listener array for the specified - * channel. - */ - removeListener(channel: string, listener: Function): this; - } - - interface IpcMainEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/ipc-main-event - - /** - * The ID of the renderer frame that sent this message - */ - frameId: number; - /** - * A function that will send an IPC message to the renderer frame that sent the - * original message that you are currently handling. You should use this method to - * "reply" to the sent message in order to guaruntee the reply will go to the - * correct process and frame. - */ - reply: Function; - /** - * Set this to the value to be returned in a syncronous message - */ - returnValue: any; - /** - * Returns the webContents that sent the message - */ - sender: WebContents; - } - - interface IpcRenderer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/ipc-renderer - - /** - * Listens to channel, when a new message arrives listener would be called with - * listener(event, args...). - */ - on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; - /** - * Adds a one time listener function for the event. This listener is invoked only - * the next time a message is sent to channel, after which it is removed. - */ - once(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; - /** - * Removes all listeners, or those of the specified channel. - */ - removeAllListeners(channel: string): this; - /** - * Removes the specified listener from the listener array for the specified - * channel. - */ - removeListener(channel: string, listener: Function): this; - /** - * Send a message to the main process asynchronously via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The main process handles it by - * listening for channel with ipcMain module. - */ - send(channel: string, ...args: any[]): void; - /** - * Send a message to the main process synchronously via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The main process handles it by - * listening for channel with ipcMain module, and replies by setting - * event.returnValue. Note: Sending a synchronous message will block the whole - * renderer process, unless you know what you are doing you should never use it. - */ - // sendSync(channel: string, ...args: any[]): any; ### VSCODE CHANGE (we do not want to use sendSync) - /** - * Sends a message to a window with webContentsId via channel. - */ - sendTo(webContentsId: number, channel: string, ...args: any[]): void; - /** - * Like ipcRenderer.send but the event will be sent to the element in the - * host page instead of the main process. - */ - sendToHost(channel: string, ...args: any[]): void; - } - - interface IpcRendererEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/ipc-renderer-event - - /** - * The IpcRenderer instance that emitted the event originally - */ - sender: IpcRenderer; - /** - * The webContents.id that sent the message, you can call - * event.sender.sendTo(event.senderId, ...) to reply to the message, see for more - * information. This only applies to messages sent from a different renderer. - * Messages sent directly from the main process set event.senderId to 0. - */ - senderId: number; - } - - interface JumpListCategory { - - // Docs: http://electronjs.org/docs/api/structures/jump-list-category - - /** - * Array of objects if type is tasks or custom, otherwise it should be omitted. - */ - items?: JumpListItem[]; - /** - * Must be set if type is custom, otherwise it should be omitted. - */ - name?: string; - /** - * One of the following: - */ - type?: ('tasks' | 'frequent' | 'recent' | 'custom'); - } - - interface JumpListItem { - - // Docs: http://electronjs.org/docs/api/structures/jump-list-item - - /** - * The command line arguments when program is executed. Should only be set if type - * is task. - */ - args?: string; - /** - * Description of the task (displayed in a tooltip). Should only be set if type is - * task. - */ - description?: string; - /** - * The index of the icon in the resource file. If a resource file contains multiple - * icons this value can be used to specify the zero-based index of the icon that - * should be displayed for this task. If a resource file contains only one icon, - * this property should be set to zero. - */ - iconIndex?: number; - /** - * The absolute path to an icon to be displayed in a Jump List, which can be an - * arbitrary resource file that contains an icon (e.g. .ico, .exe, .dll). You can - * usually specify process.execPath to show the program icon. - */ - iconPath?: string; - /** - * Path of the file to open, should only be set if type is file. - */ - path?: string; - /** - * Path of the program to execute, usually you should specify process.execPath - * which opens the current program. Should only be set if type is task. - */ - program?: string; - /** - * The text to be displayed for the item in the Jump List. Should only be set if - * type is task. - */ - title?: string; - /** - * One of the following: - */ - type?: ('task' | 'separator' | 'file'); - /** - * The working directory. Default is empty. - */ - workingDirectory?: string; - } - - interface KeyboardEvent extends Event { - - // Docs: http://electronjs.org/docs/api/structures/keyboard-event - - /** - * whether an Alt key was used in an accelerator to trigger the Event - */ - altKey?: boolean; - /** - * whether the Control key was used in an accelerator to trigger the Event - */ - ctrlKey?: boolean; - /** - * whether a meta key was used in an accelerator to trigger the Event - */ - metaKey?: boolean; - /** - * whether a Shift key was used in an accelerator to trigger the Event - */ - shiftKey?: boolean; - /** - * whether an accelerator was used to trigger the event as opposed to another user - * gesture like mouse click - */ - triggeredByAccelerator?: boolean; - } - - interface MemoryUsageDetails { - - // Docs: http://electronjs.org/docs/api/structures/memory-usage-details - - count: number; - liveSize: number; - size: number; - } - - class Menu { - - // Docs: http://electronjs.org/docs/api/menu - - /** - * Emitted when a popup is closed either manually or with menu.closePopup(). - */ - on(event: 'menu-will-close', listener: (event: Event) => void): this; - once(event: 'menu-will-close', listener: (event: Event) => void): this; - addListener(event: 'menu-will-close', listener: (event: Event) => void): this; - removeListener(event: 'menu-will-close', listener: (event: Event) => void): this; - /** - * Emitted when menu.popup() is called. - */ - on(event: 'menu-will-show', listener: (event: Event) => void): this; - once(event: 'menu-will-show', listener: (event: Event) => void): this; - addListener(event: 'menu-will-show', listener: (event: Event) => void): this; - removeListener(event: 'menu-will-show', listener: (event: Event) => void): this; - constructor(); - /** - * Generally, the template is an array of options for constructing a MenuItem. The - * usage can be referenced above. You can also attach other fields to the element - * of the template and they will become properties of the constructed menu items. - */ - static buildFromTemplate(template: Array<(MenuItemConstructorOptions) | (MenuItem)>): Menu; - /** - * Note: The returned Menu instance doesn't support dynamic addition or removal of - * menu items. Instance properties can still be dynamically modified. - */ - static getApplicationMenu(): (Menu) | (null); - /** - * Sends the action to the first responder of application. This is used for - * emulating default macOS menu behaviors. Usually you would use the role property - * of a MenuItem. See the macOS Cocoa Event Handling Guide for more information on - * macOS' native actions. - */ - static sendActionToFirstResponder(action: string): void; - /** - * Sets menu as the application menu on macOS. On Windows and Linux, the menu will - * be set as each window's top menu. Also on Windows and Linux, you can use a & in - * the top-level item name to indicate which letter should get a generated - * accelerator. For example, using &File for the file menu would result in a - * generated Alt-F accelerator that opens the associated menu. The indicated - * character in the button label gets an underline. The & character is not - * displayed on the button label. Passing null will suppress the default menu. On - * Windows and Linux, this has the additional effect of removing the menu bar from - * the window. Note: The default menu will be created automatically if the app does - * not set one. It contains standard items such as File, Edit, View, Window and - * Help. - */ - static setApplicationMenu(menu: (Menu) | (null)): void; - /** - * Appends the menuItem to the menu. - */ - append(menuItem: MenuItem): void; - /** - * Closes the context menu in the browserWindow. - */ - closePopup(browserWindow?: BrowserWindow): void; - getMenuItemById(id: string): MenuItem; - /** - * Inserts the menuItem to the pos position of the menu. - */ - insert(pos: number, menuItem: MenuItem): void; - /** - * Pops up this menu as a context menu in the BrowserWindow. - */ - popup(options?: PopupOptions): void; - items: MenuItem[]; - } - - class MenuItem { - - // Docs: http://electronjs.org/docs/api/menu-item - - constructor(options: MenuItemConstructorOptions); - accelerator: string; - checked: boolean; - click: Function; - commandId: number; - enabled: boolean; - icon: NativeImage; - id: string; - label: string; - menu: Menu; - registerAccelerator: boolean; - role: string; - sublabel: string; - submenu: Menu; - type: string; - visible: boolean; - } - - interface MimeTypedBuffer { - - // Docs: http://electronjs.org/docs/api/structures/mime-typed-buffer - - /** - * The actual Buffer content. - */ - data: Buffer; - /** - * The mimeType of the Buffer that you are sending. - */ - mimeType: string; - } - - class NativeImage { - - // Docs: http://electronjs.org/docs/api/native-image - - /** - * Creates an empty NativeImage instance. - */ - static createEmpty(): NativeImage; - /** - * Creates a new NativeImage instance from buffer that contains the raw bitmap - * pixel data returned by toBitmap(). The specific format is platform-dependent. - */ - static createFromBitmap(buffer: Buffer, options: CreateFromBitmapOptions): NativeImage; - /** - * Creates a new NativeImage instance from buffer. Tries to decode as PNG or JPEG - * first. - */ - static createFromBuffer(buffer: Buffer, options?: CreateFromBufferOptions): NativeImage; - /** - * Creates a new NativeImage instance from dataURL. - */ - static createFromDataURL(dataURL: string): NativeImage; - /** - * Creates a new NativeImage instance from the NSImage that maps to the given image - * name. See NSImageName for a list of possible values. The hslShift is applied to - * the image with the following rules This means that [-1, 0, 1] will make the - * image completely white and [-1, 1, 0] will make the image completely black. In - * some cases, the NSImageName doesn't match its string representation; one example - * of this is NSFolderImageName, whose string representation would actually be - * NSFolder. Therefore, you'll need to determine the correct string representation - * for your image before passing it in. This can be done with the following: echo - * -e '#import \nint main() { NSLog(@"%@", SYSTEM_IMAGE_NAME); }' | - * clang -otest -x objective-c -framework Cocoa - && ./test where SYSTEM_IMAGE_NAME - * should be replaced with any value from this list. - */ - static createFromNamedImage(imageName: string, hslShift: number[]): NativeImage; - /** - * Creates a new NativeImage instance from a file located at path. This method - * returns an empty image if the path does not exist, cannot be read, or is not a - * valid image. - */ - static createFromPath(path: string): NativeImage; - /** - * Add an image representation for a specific scale factor. This can be used to - * explicitly add different scale factor representations to an image. This can be - * called on empty images. - */ - addRepresentation(options: AddRepresentationOptions): void; - crop(rect: Rectangle): NativeImage; - getAspectRatio(): number; - /** - * The difference between getBitmap() and toBitmap() is, getBitmap() does not copy - * the bitmap data, so you have to use the returned Buffer immediately in current - * event loop tick, otherwise the data might be changed or destroyed. - */ - getBitmap(options?: BitmapOptions): Buffer; - /** - * Notice that the returned pointer is a weak pointer to the underlying native - * image instead of a copy, so you must ensure that the associated nativeImage - * instance is kept around. - */ - getNativeHandle(): Buffer; - getSize(): Size; - isEmpty(): boolean; - isTemplateImage(): boolean; - /** - * If only the height or the width are specified then the current aspect ratio will - * be preserved in the resized image. - */ - resize(options: ResizeOptions): NativeImage; - /** - * Marks the image as a template image. - */ - setTemplateImage(option: boolean): void; - toBitmap(options?: ToBitmapOptions): Buffer; - toDataURL(options?: ToDataURLOptions): string; - toJPEG(quality: number): Buffer; - toPNG(options?: ToPNGOptions): Buffer; - } - - interface Net extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/net - - /** - * Creates a ClientRequest instance using the provided options which are directly - * forwarded to the ClientRequest constructor. The net.request method would be used - * to issue both secure and insecure HTTP requests according to the specified - * protocol scheme in the options object. - */ - request(options: (any) | (string)): ClientRequest; - } - - interface NetLog extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/net-log - - /** - * Starts recording network events to path. - */ - startLogging(path: string): void; - /** - * Stops recording network events. If not called, net logging will automatically - * end when app quits. Deprecated Soon - */ - stopLogging(callback?: (path: string) => void): void; - /** - * Stops recording network events. If not called, net logging will automatically - * end when app quits. - */ - stopLogging(): Promise; - /** - * A Boolean property that indicates whether network logs are recorded. - */ - currentlyLogging?: boolean; - /** - * A String property that returns the path to the current log file. - */ - currentlyLoggingPath?: string; - } - - class Notification extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/notification - - on(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - once(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - addListener(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - removeListener(event: 'action', listener: (event: Event, - /** - * The index of the action that was activated. - */ - index: number) => void): this; - /** - * Emitted when the notification is clicked by the user. - */ - on(event: 'click', listener: (event: Event) => void): this; - once(event: 'click', listener: (event: Event) => void): this; - addListener(event: 'click', listener: (event: Event) => void): this; - removeListener(event: 'click', listener: (event: Event) => void): this; - /** - * Emitted when the notification is closed by manual intervention from the user. - * This event is not guaranteed to be emitted in all cases where the notification - * is closed. - */ - on(event: 'close', listener: (event: Event) => void): this; - once(event: 'close', listener: (event: Event) => void): this; - addListener(event: 'close', listener: (event: Event) => void): this; - removeListener(event: 'close', listener: (event: Event) => void): this; - /** - * Emitted when the user clicks the "Reply" button on a notification with hasReply: - * true. - */ - on(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - once(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - addListener(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - removeListener(event: 'reply', listener: (event: Event, - /** - * The string the user entered into the inline reply field. - */ - reply: string) => void): this; - /** - * Emitted when the notification is shown to the user, note this could be fired - * multiple times as a notification can be shown multiple times through the show() - * method. - */ - on(event: 'show', listener: (event: Event) => void): this; - once(event: 'show', listener: (event: Event) => void): this; - addListener(event: 'show', listener: (event: Event) => void): this; - removeListener(event: 'show', listener: (event: Event) => void): this; - constructor(options: NotificationConstructorOptions); - static isSupported(): boolean; - /** - * Dismisses the notification. - */ - close(): void; - /** - * Immediately shows the notification to the user, please note this means unlike - * the HTML5 Notification implementation, instantiating a new Notification does not - * immediately show it to the user, you need to call this method before the OS will - * display it. If the notification has been shown before, this method will dismiss - * the previously shown notification and create a new one with identical - * properties. - */ - show(): void; - } - - interface NotificationAction { - - // Docs: http://electronjs.org/docs/api/structures/notification-action - - /** - * The label for the given action. - */ - text?: string; - /** - * The type of action, can be button. - */ - type: ('button'); - } - - interface Point { - - // Docs: http://electronjs.org/docs/api/structures/point - - x: number; - y: number; - } - - interface PowerMonitor extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/power-monitor - - /** - * Emitted when the system is about to lock the screen. - */ - on(event: 'lock-screen', listener: Function): this; - once(event: 'lock-screen', listener: Function): this; - addListener(event: 'lock-screen', listener: Function): this; - removeListener(event: 'lock-screen', listener: Function): this; - /** - * Emitted when the system changes to AC power. - */ - on(event: 'on-ac', listener: Function): this; - once(event: 'on-ac', listener: Function): this; - addListener(event: 'on-ac', listener: Function): this; - removeListener(event: 'on-ac', listener: Function): this; - /** - * Emitted when system changes to battery power. - */ - on(event: 'on-battery', listener: Function): this; - once(event: 'on-battery', listener: Function): this; - addListener(event: 'on-battery', listener: Function): this; - removeListener(event: 'on-battery', listener: Function): this; - /** - * Emitted when system is resuming. - */ - on(event: 'resume', listener: Function): this; - once(event: 'resume', listener: Function): this; - addListener(event: 'resume', listener: Function): this; - removeListener(event: 'resume', listener: Function): this; - /** - * Emitted when the system is about to reboot or shut down. If the event handler - * invokes e.preventDefault(), Electron will attempt to delay system shutdown in - * order for the app to exit cleanly. If e.preventDefault() is called, the app - * should exit as soon as possible by calling something like app.quit(). - */ - on(event: 'shutdown', listener: Function): this; - once(event: 'shutdown', listener: Function): this; - addListener(event: 'shutdown', listener: Function): this; - removeListener(event: 'shutdown', listener: Function): this; - /** - * Emitted when the system is suspending. - */ - on(event: 'suspend', listener: Function): this; - once(event: 'suspend', listener: Function): this; - addListener(event: 'suspend', listener: Function): this; - removeListener(event: 'suspend', listener: Function): this; - /** - * Emitted as soon as the systems screen is unlocked. - */ - on(event: 'unlock-screen', listener: Function): this; - once(event: 'unlock-screen', listener: Function): this; - addListener(event: 'unlock-screen', listener: Function): this; - removeListener(event: 'unlock-screen', listener: Function): this; - /** - * Calculate the system idle state. idleThreshold is the amount of time (in - * seconds) before considered idle. locked is available on supported systems only. - */ - getSystemIdleState(idleThreshold: number): ('active' | 'idle' | 'locked' | 'unknown'); - /** - * Calculate system idle time in seconds. - */ - getSystemIdleTime(): number; - /** - * Calculate the system idle state. idleThreshold is the amount of time (in - * seconds) before considered idle. callback will be called synchronously on some - * systems and with an idleState argument that describes the system's state. locked - * is available on supported systems only. - */ - querySystemIdleState(idleThreshold: number, callback: (idleState: 'active' | 'idle' | 'locked' | 'unknown') => void): void; - /** - * Calculate system idle time in seconds. - */ - querySystemIdleTime(callback: (idleTime: number) => void): void; - } - - interface PowerSaveBlocker extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/power-save-blocker - - isStarted(id: number): boolean; - /** - * Starts preventing the system from entering lower-power mode. Returns an integer - * identifying the power save blocker. Note: prevent-display-sleep has higher - * precedence over prevent-app-suspension. Only the highest precedence type takes - * effect. In other words, prevent-display-sleep always takes precedence over - * prevent-app-suspension. For example, an API calling A requests for - * prevent-app-suspension, and another calling B requests for - * prevent-display-sleep. prevent-display-sleep will be used until B stops its - * request. After that, prevent-app-suspension is used. - */ - start(type: 'prevent-app-suspension' | 'prevent-display-sleep'): number; - /** - * Stops the specified power save blocker. - */ - stop(id: number): void; - } - - interface PrinterInfo { - - // Docs: http://electronjs.org/docs/api/structures/printer-info - - description: string; - isDefault: boolean; - name: string; - status: number; - } - - interface ProcessMemoryInfo { - - // Docs: http://electronjs.org/docs/api/structures/process-memory-info - - /** - * The amount of memory not shared by other processes, such as JS heap or HTML - * content in Kilobytes. - */ - private: number; - /** - * and The amount of memory currently pinned to actual physical RAM in Kilobytes. - */ - residentSet: number; - /** - * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself in Kilobytes. - */ - shared: number; - } - - interface ProcessMetric { - - // Docs: http://electronjs.org/docs/api/structures/process-metric - - /** - * CPU usage of the process. - */ - cpu: CPUUsage; - /** - * Process id of the process. - */ - pid: number; - /** - * Process type. One of the following values: - */ - type: ('Browser' | 'Tab' | 'Utility' | 'Zygote' | 'GPU' | 'Unknown'); - } - - interface Product { - - // Docs: http://electronjs.org/docs/api/structures/product - - /** - * The total size of the content, in bytes. - */ - contentLengths: number[]; - /** - * A string that identifies the version of the content. - */ - contentVersion: string; - /** - * The locale formatted price of the product. - */ - formattedPrice: string; - /** - * A Boolean value that indicates whether the App Store has downloadable content - * for this product. true if at least one file has been associated with the - * product. - */ - isDownloadable: boolean; - /** - * A description of the product. - */ - localizedDescription: string; - /** - * The name of the product. - */ - localizedTitle: string; - /** - * The cost of the product in the local currency. - */ - price: number; - /** - * The string that identifies the product to the Apple App Store. - */ - productIdentifier: string; - } - - interface Protocol extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/protocol - - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a Buffer as a response. - */ - interceptBufferProtocol(scheme: string, handler: (request: InterceptBufferProtocolRequest, callback: (buffer?: Buffer) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a file as a response. - */ - interceptFileProtocol(scheme: string, handler: (request: InterceptFileProtocolRequest, callback: (filePath: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a new HTTP request as a response. - */ - interceptHttpProtocol(scheme: string, handler: (request: InterceptHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - /** - * Same as protocol.registerStreamProtocol, except that it replaces an existing - * protocol handler. - */ - interceptStreamProtocol(scheme: string, handler: (request: InterceptStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; - /** - * Intercepts scheme protocol and uses handler as the protocol's new handler which - * sends a String as a response. - */ - interceptStringProtocol(scheme: string, handler: (request: InterceptStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * The callback will be called with a boolean that indicates whether there is - * already a handler for scheme. Deprecated Soon - */ - isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; - isProtocolHandled(scheme: string): Promise; - /** - * Registers a protocol of scheme that will send a Buffer as a response. The usage - * is the same with registerFileProtocol, except that the callback should be called - * with either a Buffer object or an object that has the data, mimeType, and - * charset properties. Example: - */ - registerBufferProtocol(scheme: string, handler: (request: RegisterBufferProtocolRequest, callback: (buffer?: (Buffer) | (MimeTypedBuffer)) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send the file as a response. The - * handler will be called with handler(request, callback) when a request is going - * to be created with scheme. completion will be called with completion(null) when - * scheme is successfully registered or completion(error) when failed. To handle - * the request, the callback should be called with either the file's path or an - * object that has a path property, e.g. callback(filePath) or callback({ path: - * filePath }). The object may also have a headers property which gives a map of - * headers to values for the response headers, e.g. callback({ path: filePath, - * headers: {"Content-Security-Policy": "default-src 'none'"]}). When callback is - * called with nothing, a number, or an object that has an error property, the - * request will fail with the error number you specified. For the available error - * numbers you can use, please see the net error list. By default the scheme is - * treated like http:, which is parsed differently than protocols that follow the - * "generic URI syntax" like file:. - */ - registerFileProtocol(scheme: string, handler: (request: RegisterFileProtocolRequest, callback: (filePath?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send an HTTP request as a response. The - * usage is the same with registerFileProtocol, except that the callback should be - * called with a redirectRequest object that has the url, method, referrer, - * uploadData and session properties. By default the HTTP request will reuse the - * current session. If you want the request to have a different session you should - * set session to null. For POST requests the uploadData object must be provided. - */ - registerHttpProtocol(scheme: string, handler: (request: RegisterHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - /** - * Note: This method can only be used before the ready event of the app module gets - * emitted and can be called only once. Registers the scheme as standard, secure, - * bypasses content security policy for resources, allows registering ServiceWorker - * and supports fetch API. Specify a privilege with the value of true to enable the - * capability. An example of registering a privileged scheme, with bypassing - * Content Security Policy: A standard scheme adheres to what RFC 3986 calls - * generic URI syntax. For example http and https are standard schemes, while file - * is not. Registering a scheme as standard, will allow relative and absolute - * resources to be resolved correctly when served. Otherwise the scheme will behave - * like the file protocol, but without the ability to resolve relative URLs. For - * example when you load following page with custom protocol without registering it - * as standard scheme, the image will not be loaded because non-standard schemes - * can not recognize relative URLs: Registering a scheme as standard will allow - * access to files through the FileSystem API. Otherwise the renderer will throw a - * security error for the scheme. By default web storage apis (localStorage, - * sessionStorage, webSQL, indexedDB, cookies) are disabled for non standard - * schemes. So in general if you want to register a custom protocol to replace the - * http protocol, you have to register it as a standard scheme. - * protocol.registerSchemesAsPrivileged can be used to replicate the functionality - * of the previous protocol.registerStandardSchemes, webFrame.registerURLSchemeAs* - * and protocol.registerServiceWorkerSchemes functions that existed prior to - * Electron 5.0.0, for example: before (<= v4.x) after (>= v5.x) - */ - registerSchemesAsPrivileged(customSchemes: CustomScheme[]): void; - /** - * Registers a protocol of scheme that will send a Readable as a response. The - * usage is similar to the other register{Any}Protocol, except that the callback - * should be called with either a Readable object or an object that has the data, - * statusCode, and headers properties. Example: It is possible to pass any object - * that implements the readable stream API (emits data/end/error events). For - * example, here's how a file could be returned: - */ - registerStreamProtocol(scheme: string, handler: (request: RegisterStreamProtocolRequest, callback: (stream?: (NodeJS.ReadableStream) | (StreamProtocolResponse)) => void) => void, completion?: (error: Error) => void): void; - /** - * Registers a protocol of scheme that will send a String as a response. The usage - * is the same with registerFileProtocol, except that the callback should be called - * with either a String or an object that has the data, mimeType, and charset - * properties. - */ - registerStringProtocol(scheme: string, handler: (request: RegisterStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; - /** - * Remove the interceptor installed for scheme and restore its original handler. - */ - uninterceptProtocol(scheme: string, completion?: (error: Error) => void): void; - /** - * Unregisters the custom protocol of scheme. - */ - unregisterProtocol(scheme: string, completion?: (error: Error) => void): void; - } - - interface Rectangle { - - // Docs: http://electronjs.org/docs/api/structures/rectangle - - /** - * The height of the rectangle (must be an integer). - */ - height: number; - /** - * The width of the rectangle (must be an integer). - */ - width: number; - /** - * The x coordinate of the origin of the rectangle (must be an integer). - */ - x: number; - /** - * The y coordinate of the origin of the rectangle (must be an integer). - */ - y: number; - } - - interface Referrer { - - // Docs: http://electronjs.org/docs/api/structures/referrer - - /** - * Can be default, unsafe-url, no-referrer-when-downgrade, no-referrer, origin, - * strict-origin-when-cross-origin, same-origin or strict-origin. See the for more - * details on the meaning of these values. - */ - policy: ('default' | 'unsafe-url' | 'no-referrer-when-downgrade' | 'no-referrer' | 'origin' | 'strict-origin-when-cross-origin' | 'same-origin' | 'strict-origin'); - /** - * HTTP Referrer URL. - */ - url: string; - } - - interface Remote extends MainInterface { - - // Docs: http://electronjs.org/docs/api/remote - - getCurrentWebContents(): WebContents; - /** - * Note: Do not use removeAllListeners on BrowserWindow. Use of this can remove all - * blur listeners, disable click events on touch bar buttons, and other unintended - * consequences. - */ - getCurrentWindow(): BrowserWindow; - getGlobal(name: string): any; - /** - * e.g. - */ - require(module: string): any; - /** - * The process object in the main process. This is the same as - * remote.getGlobal('process') but is cached. - */ - process?: any; - } - - interface RemoveClientCertificate { - - // Docs: http://electronjs.org/docs/api/structures/remove-client-certificate - - /** - * Origin of the server whose associated client certificate must be removed from - * the cache. - */ - origin: string; - /** - * clientCertificate. - */ - type: string; - } - - interface RemovePassword { - - // Docs: http://electronjs.org/docs/api/structures/remove-password - - /** - * When provided, the authentication info related to the origin will only be - * removed otherwise the entire cache will be cleared. - */ - origin?: string; - /** - * Credentials of the authentication. Must be provided if removing by origin. - */ - password?: string; - /** - * Realm of the authentication. Must be provided if removing by origin. - */ - realm?: string; - /** - * Scheme of the authentication. Can be basic, digest, ntlm, negotiate. Must be - * provided if removing by origin. - */ - scheme?: ('basic' | 'digest' | 'ntlm' | 'negotiate'); - /** - * password. - */ - type: string; - /** - * Credentials of the authentication. Must be provided if removing by origin. - */ - username?: string; - } - - interface Screen extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/screen - - /** - * Emitted when newDisplay has been added. - */ - on(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - once(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - addListener(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - removeListener(event: 'display-added', listener: (event: Event, - newDisplay: Display) => void): this; - /** - * Emitted when one or more metrics change in a display. The changedMetrics is an - * array of strings that describe the changes. Possible changes are bounds, - * workArea, scaleFactor and rotation. - */ - on(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - once(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - addListener(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - removeListener(event: 'display-metrics-changed', listener: (event: Event, - display: Display, - changedMetrics: string[]) => void): this; - /** - * Emitted when oldDisplay has been removed. - */ - on(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - once(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - addListener(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - removeListener(event: 'display-removed', listener: (event: Event, - oldDisplay: Display) => void): this; - /** - * Converts a screen DIP point to a screen physical point. The DPI scale is - * performed relative to the display containing the DIP point. - */ - dipToScreenPoint(point: Point): Point; - /** - * Converts a screen DIP rect to a screen physical rect. The DPI scale is performed - * relative to the display nearest to window. If window is null, scaling will be - * performed to the display nearest to rect. - */ - dipToScreenRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; - getAllDisplays(): Display[]; - /** - * The current absolute position of the mouse pointer. - */ - getCursorScreenPoint(): Point; - getDisplayMatching(rect: Rectangle): Display; - getDisplayNearestPoint(point: Point): Display; - getPrimaryDisplay(): Display; - /** - * Converts a screen physical point to a screen DIP point. The DPI scale is - * performed relative to the display containing the physical point. - */ - screenToDipPoint(point: Point): Point; - /** - * Converts a screen physical rect to a screen DIP rect. The DPI scale is performed - * relative to the display nearest to window. If window is null, scaling will be - * performed to the display nearest to rect. - */ - screenToDipRect(window: (BrowserWindow) | (null), rect: Rectangle): Rectangle; - } - - interface ScrubberItem { - - // Docs: http://electronjs.org/docs/api/structures/scrubber-item - - /** - * The image to appear in this item. - */ - icon?: NativeImage; - /** - * The text to appear in this item. - */ - label?: string; - } - - interface SegmentedControlSegment { - - // Docs: http://electronjs.org/docs/api/structures/segmented-control-segment - - /** - * Whether this segment is selectable. Default: true. - */ - enabled?: boolean; - /** - * The image to appear in this segment. - */ - icon?: NativeImage; - /** - * The text to appear in this segment. - */ - label?: string; - } - - class Session extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/session - - /** - * If partition starts with persist:, the page will use a persistent session - * available to all pages in the app with the same partition. if there is no - * persist: prefix, the page will use an in-memory session. If the partition is - * empty then default session of the app will be returned. To create a Session with - * options, you have to ensure the Session with the partition has never been used - * before. There is no way to change the options of an existing Session object. - */ - static fromPartition(partition: string, options?: FromPartitionOptions): Session; - /** - * A Session object, the default session object of the app. - */ - static defaultSession?: Session; - /** - * Emitted when Electron is about to download item in webContents. Calling - * event.preventDefault() will cancel the download and item will not be available - * from next tick of the process. - */ - on(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - once(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - addListener(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - removeListener(event: 'will-download', listener: (event: Event, - item: DownloadItem, - webContents: WebContents) => void): this; - /** - * Dynamically sets whether to always send credentials for HTTP NTLM or Negotiate - * authentication. - */ - allowNTLMCredentialsForDomains(domains: string): void; - clearAuthCache(): Promise; - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate)): Promise; - /** - * Clears the session’s HTTP authentication cache. Deprecated Soon - */ - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback: Function): void; - /** - * Clears the session’s HTTP cache. - */ - clearCache(): Promise; - /** - * Clears the session’s HTTP cache. Deprecated Soon - */ - clearCache(callback: (error: number) => void): void; - /** - * Clears the host resolver cache. - */ - clearHostResolverCache(): Promise; - /** - * Clears the host resolver cache. Deprecated Soon - */ - clearHostResolverCache(callback?: Function): void; - /** - * Clears the storage data for the current session. Deprecated Soon - */ - clearStorageData(options?: ClearStorageDataOptions, callback?: Function): void; - clearStorageData(options?: ClearStorageDataOptions): Promise; - /** - * Allows resuming cancelled or interrupted downloads from previous Session. The - * API will generate a DownloadItem that can be accessed with the will-download - * event. The DownloadItem will not have any WebContents associated with it and the - * initial state will be interrupted. The download will start only when the resume - * API is called on the DownloadItem. - */ - createInterruptedDownload(options: CreateInterruptedDownloadOptions): void; - /** - * Disables any network emulation already active for the session. Resets to the - * original network configuration. - */ - disableNetworkEmulation(): void; - /** - * Emulates network with the given configuration for the session. - */ - enableNetworkEmulation(options: EnableNetworkEmulationOptions): void; - /** - * Writes any unwritten DOMStorage data to disk. - */ - flushStorageData(): void; - /** - * Deprecated Soon - */ - getBlobData(identifier: string, callback: (result: Buffer) => void): void; - getBlobData(identifier: string): Promise; - getCacheSize(): Promise; - /** - * Callback is invoked with the session's current cache size. Deprecated Soon - */ - getCacheSize(callback: (size: number, error: number) => void): void; - getPreloads(): string[]; - getUserAgent(): string; - resolveProxy(url: string): Promise; - /** - * Resolves the proxy information for url. The callback will be called with - * callback(proxy) when the request is performed. Deprecated Soon - */ - resolveProxy(url: string, callback: (proxy: string) => void): void; - /** - * Sets the certificate verify proc for session, the proc will be called with - * proc(request, callback) whenever a server certificate verification is requested. - * Calling callback(0) accepts the certificate, calling callback(-2) rejects it. - * Calling setCertificateVerifyProc(null) will revert back to default certificate - * verify proc. - */ - setCertificateVerifyProc(proc: (request: CertificateVerifyProcRequest, callback: (verificationResult: number) => void) => void): void; - /** - * Sets download saving directory. By default, the download directory will be the - * Downloads under the respective app folder. - */ - setDownloadPath(path: string): void; - /** - * Sets the handler which can be used to respond to permission checks for the - * session. Returning true will allow the permission and false will reject it. To - * clear the handler, call setPermissionCheckHandler(null). - */ - setPermissionCheckHandler(handler: ((webContents: WebContents, permission: string, requestingOrigin: string, details: PermissionCheckHandlerDetails) => boolean) | (null)): void; - /** - * Sets the handler which can be used to respond to permission requests for the - * session. Calling callback(true) will allow the permission and callback(false) - * will reject it. To clear the handler, call setPermissionRequestHandler(null). - */ - setPermissionRequestHandler(handler: ((webContents: WebContents, permission: string, callback: (permissionGranted: boolean) => void, details: PermissionRequestHandlerDetails) => void) | (null)): void; - /** - * Adds scripts that will be executed on ALL web contents that are associated with - * this session just before normal preload scripts run. - */ - setPreloads(preloads: string[]): void; - /** - * Sets the proxy settings. When pacScript and proxyRules are provided together, - * the proxyRules option is ignored and pacScript configuration is applied. The - * proxyRules has to follow the rules below: For example: The proxyBypassRules is a - * comma separated list of rules described below: - */ - setProxy(config: Config): Promise; - /** - * Sets the proxy settings. When pacScript and proxyRules are provided together, - * the proxyRules option is ignored and pacScript configuration is applied. The - * proxyRules has to follow the rules below: For example: The proxyBypassRules is a - * comma separated list of rules described below: Deprecated Soon - */ - setProxy(config: Config, callback: Function): void; - /** - * Overrides the userAgent and acceptLanguages for this session. The - * acceptLanguages must a comma separated ordered list of language codes, for - * example "en-US,fr,de,ko,zh-CN,ja". This doesn't affect existing WebContents, and - * each WebContents can use webContents.setUserAgent to override the session-wide - * user agent. - */ - setUserAgent(userAgent: string, acceptLanguages?: string): void; - cookies: Cookies; - netLog: NetLog; - protocol: Protocol; - webRequest: WebRequest; - } - - interface Shell { - - // Docs: http://electronjs.org/docs/api/shell - - /** - * Play the beep sound. - */ - beep(): void; - /** - * Move the given file to trash and returns a boolean status for the operation. - */ - moveItemToTrash(fullPath: string): boolean; - /** - * Open the given external protocol URL in the desktop's default manner. (For - * example, mailto: URLs in the user's default mail agent). - */ - openExternal(url: string, options?: OpenExternalOptions): Promise; - /** - * Open the given external protocol URL in the desktop's default manner. (For - * example, mailto: URLs in the user's default mail agent). Deprecated - */ - openExternalSync(url: string, options?: OpenExternalSyncOptions): boolean; - /** - * Open the given file in the desktop's default manner. - */ - openItem(fullPath: string): boolean; - /** - * Resolves the shortcut link at shortcutPath. An exception will be thrown when any - * error happens. - */ - readShortcutLink(shortcutPath: string): ShortcutDetails; - /** - * Show the given file in a file manager. If possible, select the file. - */ - showItemInFolder(fullPath: string): void; - /** - * Creates or updates a shortcut link at shortcutPath. - */ - writeShortcutLink(shortcutPath: string, operation: 'create' | 'update' | 'replace', options: ShortcutDetails): boolean; - /** - * Creates or updates a shortcut link at shortcutPath. - */ - writeShortcutLink(shortcutPath: string, options: ShortcutDetails): boolean; - } - - interface ShortcutDetails { - - // Docs: http://electronjs.org/docs/api/structures/shortcut-details - - /** - * The Application User Model ID. Default is empty. - */ - appUserModelId?: string; - /** - * The arguments to be applied to target when launching from this shortcut. Default - * is empty. - */ - args?: string; - /** - * The working directory. Default is empty. - */ - cwd?: string; - /** - * The description of the shortcut. Default is empty. - */ - description?: string; - /** - * The path to the icon, can be a DLL or EXE. icon and iconIndex have to be set - * together. Default is empty, which uses the target's icon. - */ - icon?: string; - /** - * The resource ID of icon when icon is a DLL or EXE. Default is 0. - */ - iconIndex?: number; - /** - * The target to launch from this shortcut. - */ - target: string; - } - - interface Size { - - // Docs: http://electronjs.org/docs/api/structures/size - - height: number; - width: number; - } - - interface StreamProtocolResponse { - - // Docs: http://electronjs.org/docs/api/structures/stream-protocol-response - - /** - * A Node.js readable stream representing the response body. - */ - data: NodeJS.ReadableStream; - /** - * An object containing the response headers. - */ - headers: Headers; - /** - * The HTTP response code. - */ - statusCode: number; - } - - interface SystemPreferences extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/system-preferences - - on(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - once(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - addListener(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - removeListener(event: 'accent-color-changed', listener: (event: Event, - /** - * The new RGBA color the user assigned to be their system accent color. - */ - newColor: string) => void): this; - on(event: 'color-changed', listener: (event: Event) => void): this; - once(event: 'color-changed', listener: (event: Event) => void): this; - addListener(event: 'color-changed', listener: (event: Event) => void): this; - removeListener(event: 'color-changed', listener: (event: Event) => void): this; - on(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - once(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - addListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - removeListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, - /** - * `true` if a high contrast theme is being used, `false` otherwise. - */ - highContrastColorScheme: boolean) => void): this; - on(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - once(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - addListener(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - removeListener(event: 'inverted-color-scheme-changed', listener: (event: Event, - /** - * `true` if an inverted color scheme (a high contrast color scheme with light text - * and dark backgrounds) is being used, `false` otherwise. - */ - invertedColorScheme: boolean) => void): this; - /** - * Important: In order to properly leverage this API, you must set the - * NSMicrophoneUsageDescription and NSCameraUsageDescription strings in your app's - * Info.plist file. The values for these keys will be used to populate the - * permission dialogs so that the user will be properly informed as to the purpose - * of the permission request. See Electron Application Distribution for more - * information about how to set these in the context of Electron. This user consent - * was not required until macOS 10.14 Mojave, so this method will always return - * true if your system is running 10.13 High Sierra or lower. - */ - askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; - /** - * NOTE: This API will return false on macOS systems older than Sierra 10.12.2. - */ - canPromptTouchID(): boolean; - /** - * This API is only available on macOS 10.14 Mojave or newer. - */ - getAccentColor(): string; - /** - * Returns an object with system animation settings. - */ - getAnimationSettings(): AnimationSettings; - /** - * Gets the macOS appearance setting that you have declared you want for your - * application, maps to NSApplication.appearance. You can use the - * setAppLevelAppearance API to set this value. - */ - getAppLevelAppearance(): ('dark' | 'light' | 'unknown'); - getColor(color: '3d-dark-shadow' | '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text' | 'alternate-selected-control-text' | 'alternate-selected-control-text' | 'control-background' | 'control' | 'control-text' | 'disabled-control-text' | 'find-highlight' | 'grid' | 'header-text' | 'highlight' | 'keyboard-focus-indicator' | 'label' | 'link' | 'placeholder-text' | 'quaternary-label' | 'scrubber-textured-background' | 'secondary-label' | 'selected-content-background' | 'selected-control' | 'selected-control-text' | 'selected-menu-item' | 'selected-text-background' | 'selected-text' | 'separator' | 'shadow' | 'tertiary-label' | 'text-background' | 'text' | 'under-page-background' | 'unemphasized-selected-content-background' | 'unemphasized-selected-text-background' | 'unemphasized-selected-text' | 'window-background' | 'window-frame-text'): string; - /** - * Gets the macOS appearance setting that is currently applied to your application, - * maps to NSApplication.effectiveAppearance Please note that until Electron is - * built targeting the 10.14 SDK, your application's effectiveAppearance will - * default to 'light' and won't inherit the OS preference. In the interim in order - * for your application to inherit the OS preference you must set the - * NSRequiresAquaSystemAppearance key in your apps Info.plist to false. If you are - * using electron-packager or electron-forge just set the enableDarwinDarkMode - * packager option to true. See the Electron Packager API for more details. - */ - getEffectiveAppearance(): ('dark' | 'light' | 'unknown'); - /** - * This user consent was not required until macOS 10.14 Mojave, so this method will - * always return granted if your system is running 10.13 High Sierra or lower. - */ - getMediaAccessStatus(mediaType: string): ('not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'); - /** - * Returns one of several standard system colors that automatically adapt to - * vibrancy and changes in accessibility settings like 'Increase contrast' and - * 'Reduce transparency'. See Apple Documentation for more details. - */ - getSystemColor(color: 'blue' | 'brown' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'yellow'): void; - /** - * Some popular key and types are: - */ - getUserDefault(key: string, type: 'string' | 'boolean' | 'integer' | 'float' | 'double' | 'url' | 'array' | 'dictionary'): any; - /** - * An example of using it to determine if you should create a transparent window or - * not (transparent windows won't work correctly when DWM composition is disabled): - */ - isAeroGlassEnabled(): boolean; - isDarkMode(): boolean; - isHighContrastColorScheme(): boolean; - isInvertedColorScheme(): boolean; - isSwipeTrackingFromScrollEventsEnabled(): boolean; - isTrustedAccessibilityClient(prompt: boolean): boolean; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postLocalNotification(event: string, userInfo: any): void; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postNotification(event: string, userInfo: any, deliverImmediately?: boolean): void; - /** - * Posts event as native notifications of macOS. The userInfo is an Object that - * contains the user information dictionary sent along with the notification. - */ - postWorkspaceNotification(event: string, userInfo: any): void; - /** - * This API itself will not protect your user data; rather, it is a mechanism to - * allow you to do so. Native apps will need to set Access Control Constants like - * kSecAccessControlUserPresence on the their keychain entry so that reading it - * would auto-prompt for Touch ID biometric consent. This could be done with - * node-keytar, such that one would store an encryption key with node-keytar and - * only fetch it if promptTouchID() resolves. NOTE: This API will return a rejected - * Promise on macOS systems older than Sierra 10.12.2. - */ - promptTouchID(reason: string): Promise; - /** - * Add the specified defaults to your application's NSUserDefaults. - */ - registerDefaults(defaults: any): void; - /** - * Removes the key in NSUserDefaults. This can be used to restore the default or - * global value of a key previously set with setUserDefault. - */ - removeUserDefault(key: string): void; - /** - * Sets the appearance setting for your application, this should override the - * system default and override the value of getEffectiveAppearance. - */ - setAppLevelAppearance(appearance: 'dark' | 'light'): void; - /** - * Set the value of key in NSUserDefaults. Note that type should match actual type - * of value. An exception is thrown if they don't. Some popular key and types are: - */ - setUserDefault(key: string, type: string, value: string): void; - /** - * Same as subscribeNotification, but uses NSNotificationCenter for local defaults. - * This is necessary for events such as NSUserDefaultsDidChangeNotification. - */ - subscribeLocalNotification(event: string, callback: (event: string, userInfo: any) => void): number; - /** - * Subscribes to native notifications of macOS, callback will be called with - * callback(event, userInfo) when the corresponding event happens. The userInfo is - * an Object that contains the user information dictionary sent along with the - * notification. The id of the subscriber is returned, which can be used to - * unsubscribe the event. Under the hood this API subscribes to - * NSDistributedNotificationCenter, example values of event are: - */ - subscribeNotification(event: string, callback: (event: string, userInfo: any) => void): number; - /** - * Same as subscribeNotification, but uses - * NSWorkspace.sharedWorkspace.notificationCenter. This is necessary for events - * such as NSWorkspaceDidActivateApplicationNotification. - */ - subscribeWorkspaceNotification(event: string, callback: (event: string, userInfo: any) => void): void; - /** - * Same as unsubscribeNotification, but removes the subscriber from - * NSNotificationCenter. - */ - unsubscribeLocalNotification(id: number): void; - /** - * Removes the subscriber with id. - */ - unsubscribeNotification(id: number): void; - /** - * Same as unsubscribeNotification, but removes the subscriber from - * NSWorkspace.sharedWorkspace.notificationCenter. - */ - unsubscribeWorkspaceNotification(id: number): void; - } - - interface Task { - - // Docs: http://electronjs.org/docs/api/structures/task - - /** - * The command line arguments when program is executed. - */ - arguments: string; - /** - * Description of this task. - */ - description: string; - /** - * The icon index in the icon file. If an icon file consists of two or more icons, - * set this value to identify the icon. If an icon file consists of one icon, this - * value is 0. - */ - iconIndex: number; - /** - * The absolute path to an icon to be displayed in a JumpList, which can be an - * arbitrary resource file that contains an icon. You can usually specify - * process.execPath to show the icon of the program. - */ - iconPath: string; - /** - * Path of the program to execute, usually you should specify process.execPath - * which opens the current program. - */ - program: string; - /** - * The string to be displayed in a JumpList. - */ - title: string; - /** - * The working directory. Default is empty. - */ - workingDirectory?: string; - } - - interface ThumbarButton { - - // Docs: http://electronjs.org/docs/api/structures/thumbar-button - - click: Function; - /** - * Control specific states and behaviors of the button. By default, it is - * ['enabled']. - */ - flags?: string[]; - /** - * The icon showing in thumbnail toolbar. - */ - icon: NativeImage; - /** - * The text of the button's tooltip. - */ - tooltip?: string; - } - - class TouchBarButton extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-button - - constructor(options: TouchBarButtonConstructorOptions); - backgroundColor: string; - icon: NativeImage; - label: string; - } - - class TouchBarColorPicker extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-color-picker - - constructor(options: TouchBarColorPickerConstructorOptions); - availableColors: string[]; - selectedColor: string; - } - - class TouchBarGroup extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-group - - constructor(options: TouchBarGroupConstructorOptions); - } - - class TouchBarLabel extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-label - - constructor(options: TouchBarLabelConstructorOptions); - label: string; - textColor: string; - } - - class TouchBarPopover extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-popover - - constructor(options: TouchBarPopoverConstructorOptions); - icon: NativeImage; - label: string; - } - - class TouchBarScrubber extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-scrubber - - constructor(options: TouchBarScrubberConstructorOptions); - continuous: boolean; - items: ScrubberItem[]; - mode: string; - overlayStyle: string; - selectedStyle: string; - showArrowButtons: boolean; - } - - class TouchBarSegmentedControl extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-segmented-control - - constructor(options: TouchBarSegmentedControlConstructorOptions); - segments: SegmentedControlSegment[]; - segmentStyle: string; - selectedIndex: number; - } - - class TouchBarSlider extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-slider - - constructor(options: TouchBarSliderConstructorOptions); - label: string; - maxValue: number; - minValue: number; - value: number; - } - - class TouchBarSpacer extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar-spacer - - constructor(options: TouchBarSpacerConstructorOptions); - } - - class TouchBar extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/touch-bar - - constructor(options: TouchBarConstructorOptions); - escapeItem: (TouchBarButton | TouchBarColorPicker | TouchBarGroup | TouchBarLabel | TouchBarPopover | TouchBarScrubber | TouchBarSegmentedControl | TouchBarSlider | TouchBarSpacer | null); - static TouchBarButton: typeof TouchBarButton; - static TouchBarColorPicker: typeof TouchBarColorPicker; - static TouchBarGroup: typeof TouchBarGroup; - static TouchBarLabel: typeof TouchBarLabel; - static TouchBarPopover: typeof TouchBarPopover; - static TouchBarScrubber: typeof TouchBarScrubber; - static TouchBarSegmentedControl: typeof TouchBarSegmentedControl; - static TouchBarSlider: typeof TouchBarSlider; - static TouchBarSpacer: typeof TouchBarSpacer; - } - - interface TraceCategoriesAndOptions { - - // Docs: http://electronjs.org/docs/api/structures/trace-categories-and-options - - /** - * – is a filter to control what category groups should be traced. A filter can - * have an optional prefix to exclude category groups that contain a matching - * category. Having both included and excluded category patterns in the same list - * is not supported. Examples: test_MyTest*, test_MyTest*,test_OtherStuff, - * -excluded_category1,-excluded_category2. - */ - categoryFilter: string; - /** - * Controls what kind of tracing is enabled, it is a comma-delimited sequence of - * the following strings: record-until-full, record-continuously, trace-to-console, - * enable-sampling, enable-systrace, e.g. 'record-until-full,enable-sampling'. The - * first 3 options are trace recording modes and hence mutually exclusive. If more - * than one trace recording modes appear in the traceOptions string, the last one - * takes precedence. If none of the trace recording modes are specified, recording - * mode is record-until-full. The trace option will first be reset to the default - * option (record_mode set to record-until-full, enable_sampling and - * enable_systrace set to false) before options parsed from traceOptions are - * applied on it. - */ - traceOptions: string; - } - - interface TraceConfig { - - // Docs: http://electronjs.org/docs/api/structures/trace-config - - excluded_categories?: string[]; - included_categories?: string[]; - memory_dump_config?: MemoryDumpConfig; - } - - interface Transaction { - - // Docs: http://electronjs.org/docs/api/structures/transaction - - /** - * The error code if an error occurred while processing the transaction. - */ - errorCode: number; - /** - * The error message if an error occurred while processing the transaction. - */ - errorMessage: string; - /** - * The identifier of the restored transaction by the App Store. - */ - originalTransactionIdentifier: string; - payment: Payment; - /** - * The date the transaction was added to the App Store’s payment queue. - */ - transactionDate: string; - /** - * A string that uniquely identifies a successful payment transaction. - */ - transactionIdentifier: string; - /** - * The transaction state, can be purchasing, purchased, failed, restored or - * deferred. - */ - transactionState: ('purchasing' | 'purchased' | 'failed' | 'restored' | 'deferred'); - } - - class Tray extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/tray - - /** - * Emitted when the tray balloon is clicked. - */ - on(event: 'balloon-click', listener: Function): this; - once(event: 'balloon-click', listener: Function): this; - addListener(event: 'balloon-click', listener: Function): this; - removeListener(event: 'balloon-click', listener: Function): this; - /** - * Emitted when the tray balloon is closed because of timeout or user manually - * closes it. - */ - on(event: 'balloon-closed', listener: Function): this; - once(event: 'balloon-closed', listener: Function): this; - addListener(event: 'balloon-closed', listener: Function): this; - removeListener(event: 'balloon-closed', listener: Function): this; - /** - * Emitted when the tray balloon shows. - */ - on(event: 'balloon-show', listener: Function): this; - once(event: 'balloon-show', listener: Function): this; - addListener(event: 'balloon-show', listener: Function): this; - removeListener(event: 'balloon-show', listener: Function): this; - /** - * Emitted when the tray icon is clicked. - */ - on(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the tray icon is double clicked. - */ - on(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - once(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - addListener(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - removeListener(event: 'double-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - /** - * Emitted when a drag operation ends on the tray or ends at another location. - */ - on(event: 'drag-end', listener: Function): this; - once(event: 'drag-end', listener: Function): this; - addListener(event: 'drag-end', listener: Function): this; - removeListener(event: 'drag-end', listener: Function): this; - /** - * Emitted when a drag operation enters the tray icon. - */ - on(event: 'drag-enter', listener: Function): this; - once(event: 'drag-enter', listener: Function): this; - addListener(event: 'drag-enter', listener: Function): this; - removeListener(event: 'drag-enter', listener: Function): this; - /** - * Emitted when a drag operation exits the tray icon. - */ - on(event: 'drag-leave', listener: Function): this; - once(event: 'drag-leave', listener: Function): this; - addListener(event: 'drag-leave', listener: Function): this; - removeListener(event: 'drag-leave', listener: Function): this; - /** - * Emitted when any dragged items are dropped on the tray icon. - */ - on(event: 'drop', listener: Function): this; - once(event: 'drop', listener: Function): this; - addListener(event: 'drop', listener: Function): this; - removeListener(event: 'drop', listener: Function): this; - /** - * Emitted when dragged files are dropped in the tray icon. - */ - on(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - once(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - addListener(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - removeListener(event: 'drop-files', listener: (event: Event, - /** - * The paths of the dropped files. - */ - files: string[]) => void): this; - /** - * Emitted when dragged text is dropped in the tray icon. - */ - on(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - once(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - addListener(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - removeListener(event: 'drop-text', listener: (event: Event, - /** - * the dropped text string. - */ - text: string) => void): this; - /** - * Emitted when the mouse enters the tray icon. - */ - on(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-enter', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the mouse exits the tray icon. - */ - on(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-leave', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the mouse moves in the tray icon. - */ - on(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - once(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - addListener(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - removeListener(event: 'mouse-move', listener: (event: KeyboardEvent, - /** - * The position of the event. - */ - position: Point) => void): this; - /** - * Emitted when the tray icon is right clicked. - */ - on(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - once(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - addListener(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - removeListener(event: 'right-click', listener: (event: KeyboardEvent, - /** - * The bounds of tray icon. - */ - bounds: Rectangle) => void): this; - constructor(image: (NativeImage) | (string)); - /** - * Destroys the tray icon immediately. - */ - destroy(): void; - /** - * Displays a tray balloon. - */ - displayBalloon(options: DisplayBalloonOptions): void; - /** - * The bounds of this tray icon as Object. - */ - getBounds(): Rectangle; - getIgnoreDoubleClickEvents(): boolean; - getTitle(title: string): string; - isDestroyed(): boolean; - /** - * Pops up the context menu of the tray icon. When menu is passed, the menu will be - * shown instead of the tray icon's context menu. The position is only available on - * Windows, and it is (0, 0) by default. - */ - popUpContextMenu(menu?: Menu, position?: Point): void; - /** - * Sets the context menu for this icon. - */ - setContextMenu(menu: (Menu) | (null)): void; - /** - * Sets when the tray's icon background becomes highlighted (in blue). Deprecated - * Note: You can use highlightMode with a BrowserWindow by toggling between 'never' - * and 'always' modes when the window visibility changes. - */ - setHighlightMode(mode: 'selection' | 'always' | 'never'): void; - /** - * Sets the option to ignore double click events. Ignoring these events allows you - * to detect every individual click of the tray icon. This value is set to false by - * default. - */ - setIgnoreDoubleClickEvents(ignore: boolean): void; - /** - * Sets the image associated with this tray icon. - */ - setImage(image: (NativeImage) | (string)): void; - /** - * Sets the image associated with this tray icon when pressed on macOS. - */ - setPressedImage(image: (NativeImage) | (string)): void; - /** - * Sets the title displayed next to the tray icon in the status bar (Support ANSI - * colors). - */ - setTitle(title: string): void; - /** - * Sets the hover text for this tray icon. - */ - setToolTip(toolTip: string): void; - } - - interface UploadBlob { - - // Docs: http://electronjs.org/docs/api/structures/upload-blob - - /** - * UUID of blob data to upload. - */ - blobUUID: string; - /** - * blob. - */ - type: string; - } - - interface UploadData { - - // Docs: http://electronjs.org/docs/api/structures/upload-data - - /** - * UUID of blob data. Use method to retrieve the data. - */ - blobUUID: string; - /** - * Content being sent. - */ - bytes: Buffer; - /** - * Path of file being uploaded. - */ - file: string; - } - - interface UploadFile { - - // Docs: http://electronjs.org/docs/api/structures/upload-file - - /** - * Path of file to be uploaded. - */ - filePath: string; - /** - * Number of bytes to read from offset. Defaults to 0. - */ - length: number; - /** - * Last Modification time in number of seconds since the UNIX epoch. - */ - modificationTime: number; - /** - * Defaults to 0. - */ - offset: number; - /** - * file. - */ - type: string; - } - - interface UploadRawData { - - // Docs: http://electronjs.org/docs/api/structures/upload-raw-data - - /** - * Data to be uploaded. - */ - bytes: Buffer; - /** - * rawData. - */ - type: string; - } - - class WebContents extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-contents - - static fromId(id: number): WebContents; - static getAllWebContents(): WebContents[]; - static getFocusedWebContents(): WebContents; - /** - * Emitted before dispatching the keydown and keyup events in the page. Calling - * event.preventDefault will prevent the page keydown/keyup events and the menu - * shortcuts. To only prevent the menu shortcuts, use setIgnoreMenuShortcuts: - */ - on(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - once(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - addListener(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - removeListener(event: 'before-input-event', listener: (event: Event, - /** - * Input properties. - */ - input: Input) => void): this; - /** - * Emitted when failed to verify the certificate for url. The usage is the same - * with the certificate-error event of app. - */ - on(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - once(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - addListener(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - removeListener(event: 'certificate-error', listener: (event: Event, - url: string, - /** - * The error code. - */ - error: string, - certificate: Certificate, - callback: (isTrusted: boolean) => void) => void): this; - /** - * Emitted when the associated window logs a console message. Will not be emitted - * for windows with offscreen rendering enabled. - */ - on(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - once(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - addListener(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - removeListener(event: 'console-message', listener: (event: Event, - level: number, - message: string, - line: number, - sourceId: string) => void): this; - /** - * Emitted when there is a new context menu that needs to be handled. - */ - on(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - once(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - addListener(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - removeListener(event: 'context-menu', listener: (event: Event, - params: ContextMenuParams) => void): this; - /** - * Emitted when the renderer process crashes or is killed. - */ - on(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - once(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - addListener(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - removeListener(event: 'crashed', listener: (event: Event, - killed: boolean) => void): this; - /** - * Emitted when the cursor's type changes. The type parameter can be default, - * crosshair, pointer, text, wait, help, e-resize, n-resize, ne-resize, nw-resize, - * s-resize, se-resize, sw-resize, w-resize, ns-resize, ew-resize, nesw-resize, - * nwse-resize, col-resize, row-resize, m-panning, e-panning, n-panning, - * ne-panning, nw-panning, s-panning, se-panning, sw-panning, w-panning, move, - * vertical-text, cell, context-menu, alias, progress, nodrop, copy, none, - * not-allowed, zoom-in, zoom-out, grab, grabbing or custom. If the type parameter - * is custom, the image parameter will hold the custom cursor image in a - * NativeImage, and scale, size and hotspot will hold additional information about - * the custom cursor. - */ - on(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - once(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - addListener(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - removeListener(event: 'cursor-changed', listener: (event: Event, - type: string, - image?: NativeImage, - /** - * scaling factor for the custom cursor. - */ - scale?: number, - /** - * the size of the `image`. - */ - size?: Size, - /** - * coordinates of the custom cursor's hotspot. - */ - hotspot?: Point) => void): this; - /** - * Emitted when desktopCapturer.getSources() is called in the renderer process. - * Calling event.preventDefault() will make it return empty sources. - */ - on(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - once(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - addListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; - /** - * Emitted when webContents is destroyed. - */ - on(event: 'destroyed', listener: Function): this; - once(event: 'destroyed', listener: Function): this; - addListener(event: 'destroyed', listener: Function): this; - removeListener(event: 'destroyed', listener: Function): this; - /** - * Emitted when DevTools is closed. - */ - on(event: 'devtools-closed', listener: Function): this; - once(event: 'devtools-closed', listener: Function): this; - addListener(event: 'devtools-closed', listener: Function): this; - removeListener(event: 'devtools-closed', listener: Function): this; - /** - * Emitted when DevTools is focused / opened. - */ - on(event: 'devtools-focused', listener: Function): this; - once(event: 'devtools-focused', listener: Function): this; - addListener(event: 'devtools-focused', listener: Function): this; - removeListener(event: 'devtools-focused', listener: Function): this; - /** - * Emitted when DevTools is opened. - */ - on(event: 'devtools-opened', listener: Function): this; - once(event: 'devtools-opened', listener: Function): this; - addListener(event: 'devtools-opened', listener: Function): this; - removeListener(event: 'devtools-opened', listener: Function): this; - /** - * Emitted when the devtools window instructs the webContents to reload - */ - on(event: 'devtools-reload-page', listener: Function): this; - once(event: 'devtools-reload-page', listener: Function): this; - addListener(event: 'devtools-reload-page', listener: Function): this; - removeListener(event: 'devtools-reload-page', listener: Function): this; - /** - * Emitted when a has been attached to this web contents. - */ - on(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - once(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - addListener(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - removeListener(event: 'did-attach-webview', listener: (event: Event, - /** - * The guest web contents that is used by the ` - */ - webContents: WebContents) => void): this; - /** - * Emitted when a page's theme color changes. This is usually due to encountering a - * meta tag: - */ - on(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - once(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - addListener(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - removeListener(event: 'did-change-theme-color', listener: (event: Event, - /** - * Theme color is in format of '#rrggbb'. It is `null` when no theme color is set. - */ - color: (string) | (null)) => void): this; - /** - * This event is like did-finish-load but emitted when the load failed or was - * cancelled, e.g. window.stop() is invoked. The full list of error codes and their - * meaning is available here. - */ - on(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-fail-load', listener: (event: Event, - errorCode: number, - errorDescription: string, - validatedURL: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when the navigation is done, i.e. the spinner of the tab has stopped - * spinning, and the onload event was dispatched. - */ - on(event: 'did-finish-load', listener: Function): this; - once(event: 'did-finish-load', listener: Function): this; - addListener(event: 'did-finish-load', listener: Function): this; - removeListener(event: 'did-finish-load', listener: Function): this; - /** - * Emitted when a frame has done navigation. - */ - on(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-frame-finish-load', listener: (event: Event, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when any frame navigation is done. This event is not emitted for in-page - * navigations, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. - */ - on(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-frame-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations, - */ - httpStatusText: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted when a main frame navigation is done. This event is not emitted for - * in-page navigations, such as clicking anchor links or updating the - * window.location.hash. Use did-navigate-in-page event for this purpose. - */ - on(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - once(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - addListener(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - removeListener(event: 'did-navigate', listener: (event: Event, - url: string, - /** - * -1 for non HTTP navigations - */ - httpResponseCode: number, - /** - * empty for non HTTP navigations - */ - httpStatusText: string) => void): this; - /** - * Emitted when an in-page navigation happened in any frame. When in-page - * navigation happens, the page URL changes but does not cause navigation outside - * of the page. Examples of this occurring are when anchor links are clicked or - * when the DOM hashchange event is triggered. - */ - on(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-navigate-in-page', listener: (event: Event, - url: string, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Emitted after a server side redirect occurs during navigation. For example a - * 302 redirect. This event can not be prevented, if you want to prevent redirects - * you should checkout out the will-redirect event above. - */ - on(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-redirect-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab started spinning. - */ - on(event: 'did-start-loading', listener: Function): this; - once(event: 'did-start-loading', listener: Function): this; - addListener(event: 'did-start-loading', listener: Function): this; - removeListener(event: 'did-start-loading', listener: Function): this; - /** - * Emitted when any frame (including main) starts navigating. isInplace will be - * true for in-page navigations. - */ - on(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'did-start-navigation', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab stopped spinning. - */ - on(event: 'did-stop-loading', listener: Function): this; - once(event: 'did-stop-loading', listener: Function): this; - addListener(event: 'did-stop-loading', listener: Function): this; - removeListener(event: 'did-stop-loading', listener: Function): this; - /** - * Emitted when the document in the given frame is loaded. - */ - on(event: 'dom-ready', listener: (event: Event) => void): this; - once(event: 'dom-ready', listener: (event: Event) => void): this; - addListener(event: 'dom-ready', listener: (event: Event) => void): this; - removeListener(event: 'dom-ready', listener: (event: Event) => void): this; - /** - * Emitted when the window enters a full-screen state triggered by HTML API. - */ - on(event: 'enter-html-full-screen', listener: Function): this; - once(event: 'enter-html-full-screen', listener: Function): this; - addListener(event: 'enter-html-full-screen', listener: Function): this; - removeListener(event: 'enter-html-full-screen', listener: Function): this; - /** - * Emitted when a result is available for [webContents.findInPage] request. - */ - on(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - once(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - addListener(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - removeListener(event: 'found-in-page', listener: (event: Event, - result: Result) => void): this; - /** - * Emitted when the renderer process sends an asynchronous message via - * ipcRenderer.send(). - */ - on(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - once(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - addListener(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - removeListener(event: 'ipc-message', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - /** - * Emitted when the renderer process sends a synchronous message via - * ipcRenderer.sendSync(). - */ - on(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - once(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - addListener(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - removeListener(event: 'ipc-message-sync', listener: (event: Event, - channel: string, - ...args: any[]) => void): this; - /** - * Emitted when the window leaves a full-screen state triggered by HTML API. - */ - on(event: 'leave-html-full-screen', listener: Function): this; - once(event: 'leave-html-full-screen', listener: Function): this; - addListener(event: 'leave-html-full-screen', listener: Function): this; - removeListener(event: 'leave-html-full-screen', listener: Function): this; - /** - * Emitted when webContents wants to do basic auth. The usage is the same with the - * login event of app. - */ - on(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - once(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - addListener(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - removeListener(event: 'login', listener: (event: Event, - request: Request, - authInfo: AuthInfo, - callback: (username: string, password: string) => void) => void): this; - /** - * Emitted when media is paused or done playing. - */ - on(event: 'media-paused', listener: Function): this; - once(event: 'media-paused', listener: Function): this; - addListener(event: 'media-paused', listener: Function): this; - removeListener(event: 'media-paused', listener: Function): this; - /** - * Emitted when media starts playing. - */ - on(event: 'media-started-playing', listener: Function): this; - once(event: 'media-started-playing', listener: Function): this; - addListener(event: 'media-started-playing', listener: Function): this; - removeListener(event: 'media-started-playing', listener: Function): this; - /** - * Emitted when the page requests to open a new window for a url. It could be - * requested by window.open or an external link like . By - * default a new BrowserWindow will be created for the url. Calling - * event.preventDefault() will prevent Electron from automatically creating a new - * BrowserWindow. If you call event.preventDefault() and manually create a new - * BrowserWindow then you must set event.newGuest to reference the new - * BrowserWindow instance, failing to do so may result in unexpected behavior. For - * example: - */ - on(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - once(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - addListener(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - removeListener(event: 'new-window', listener: (event: Event, - url: string, - frameName: string, - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'), - /** - * The options which will be used for creating the new . - */ - options: any, - /** - * The non-standard features (features not handled by Chromium or Electron) given - * to `window.open()`. - */ - additionalFeatures: string[], - /** - * The referrer that will be passed to the new window. May or may not result in the - * `Referer` header being sent, depending on the referrer policy. - */ - referrer: Referrer) => void): this; - /** - * Emitted when page receives favicon urls. - */ - on(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - once(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - addListener(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - removeListener(event: 'page-favicon-updated', listener: (event: Event, - /** - * Array of URLs. - */ - favicons: string[]) => void): this; - /** - * Fired when page title is set during navigation. explicitSet is false when title - * is synthesized from file url. - */ - on(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - once(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - addListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - removeListener(event: 'page-title-updated', listener: (event: Event, - title: string, - explicitSet: boolean) => void): this; - /** - * Emitted when a new frame is generated. Only the dirty area is passed in the - * buffer. - */ - on(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - once(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - addListener(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - removeListener(event: 'paint', listener: (event: Event, - dirtyRect: Rectangle, - /** - * The image data of the whole frame. - */ - image: NativeImage) => void): this; - /** - * Emitted when a plugin process has crashed. - */ - on(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - once(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - addListener(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - removeListener(event: 'plugin-crashed', listener: (event: Event, - name: string, - version: string) => void): this; - /** - * Emitted when the preload script preloadPath throws an unhandled exception error. - */ - on(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - once(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - addListener(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - removeListener(event: 'preload-error', listener: (event: Event, - preloadPath: string, - error: Error) => void): this; - /** - * Emitted when remote.getBuiltin() is called in the renderer process. Calling - * event.preventDefault() will prevent the module from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - once(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - addListener(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - removeListener(event: 'remote-get-builtin', listener: (event: Event, - moduleName: string) => void): this; - /** - * Emitted when remote.getCurrentWebContents() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - once(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - addListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - removeListener(event: 'remote-get-current-web-contents', listener: (event: Event) => void): this; - /** - * Emitted when remote.getCurrentWindow() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-current-window', listener: (event: Event) => void): this; - once(event: 'remote-get-current-window', listener: (event: Event) => void): this; - addListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; - removeListener(event: 'remote-get-current-window', listener: (event: Event) => void): this; - /** - * Emitted when remote.getGlobal() is called in the renderer process. Calling - * event.preventDefault() will prevent the global from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - once(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - addListener(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - removeListener(event: 'remote-get-global', listener: (event: Event, - globalName: string) => void): this; - /** - * Emitted when .getWebContents() is called in the renderer process. - * Calling event.preventDefault() will prevent the object from being returned. - * Custom value can be returned by setting event.returnValue. - */ - on(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - once(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - addListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - removeListener(event: 'remote-get-guest-web-contents', listener: (event: Event, - guestWebContents: WebContents) => void): this; - /** - * Emitted when remote.require() is called in the renderer process. Calling - * event.preventDefault() will prevent the module from being returned. Custom value - * can be returned by setting event.returnValue. - */ - on(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - once(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - addListener(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - removeListener(event: 'remote-require', listener: (event: Event, - moduleName: string) => void): this; - /** - * Emitted when the unresponsive web page becomes responsive again. - */ - on(event: 'responsive', listener: Function): this; - once(event: 'responsive', listener: Function): this; - addListener(event: 'responsive', listener: Function): this; - removeListener(event: 'responsive', listener: Function): this; - /** - * Emitted when bluetooth device needs to be selected on call to - * navigator.bluetooth.requestDevice. To use navigator.bluetooth api webBluetooth - * should be enabled. If event.preventDefault is not called, first available device - * will be selected. callback should be called with deviceId to be selected, - * passing empty string to callback will cancel the request. - */ - on(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - once(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - addListener(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - removeListener(event: 'select-bluetooth-device', listener: (event: Event, - devices: BluetoothDevice[], - callback: (deviceId: string) => void) => void): this; - /** - * Emitted when a client certificate is requested. The usage is the same with the - * select-client-certificate event of app. - */ - on(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - once(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - addListener(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - removeListener(event: 'select-client-certificate', listener: (event: Event, - url: string, - certificateList: Certificate[], - callback: (certificate: Certificate) => void) => void): this; - /** - * Emitted when the web page becomes unresponsive. - */ - on(event: 'unresponsive', listener: Function): this; - once(event: 'unresponsive', listener: Function): this; - addListener(event: 'unresponsive', listener: Function): this; - removeListener(event: 'unresponsive', listener: Function): this; - /** - * Emitted when mouse moves over a link or the keyboard moves the focus to a link. - */ - on(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - once(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - addListener(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'update-target-url', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when a 's web contents is being attached to this web contents. - * Calling event.preventDefault() will destroy the guest page. This event can be - * used to configure webPreferences for the webContents of a before it's - * loaded, and provides the ability to set settings that can't be set via - * attributes. Note: The specified preload script option will be appear as - * preloadURL (not preload) in the webPreferences object emitted with this event. - */ - on(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - once(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - addListener(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - removeListener(event: 'will-attach-webview', listener: (event: Event, - /** - * The web preferences that will be used by the guest page. This object can be - * modified to adjust the preferences for the guest page. - */ - webPreferences: any, - /** - * The other ` - */ - params: any) => void): this; - /** - * Emitted when a user or the page wants to start navigation. It can happen when - * the window.location object is changed or a user clicks a link in the page. This - * event will not emit when the navigation is started programmatically with APIs - * like webContents.loadURL and webContents.back. It is also not emitted for - * in-page navigations, such as clicking anchor links or updating the - * window.location.hash. Use did-navigate-in-page event for this purpose. Calling - * event.preventDefault() will prevent the navigation. - */ - on(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - once(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - addListener(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - removeListener(event: 'will-navigate', listener: (event: Event, - url: string) => void): this; - /** - * Emitted when a beforeunload event handler is attempting to cancel a page unload. - * Calling event.preventDefault() will ignore the beforeunload event handler and - * allow the page to be unloaded. - */ - on(event: 'will-prevent-unload', listener: (event: Event) => void): this; - once(event: 'will-prevent-unload', listener: (event: Event) => void): this; - addListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; - removeListener(event: 'will-prevent-unload', listener: (event: Event) => void): this; - /** - * Emitted as a server side redirect occurs during navigation. For example a 302 - * redirect. This event will be emitted after did-start-navigation and always - * before the did-redirect-navigation event for the same navigation. Calling - * event.preventDefault() will prevent the navigation (not just the redirect). - */ - on(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - once(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - addListener(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - removeListener(event: 'will-redirect', listener: (event: Event, - url: string, - isInPlace: boolean, - isMainFrame: boolean, - frameProcessId: number, - frameRoutingId: number) => void): this; - /** - * Adds the specified path to DevTools workspace. Must be used after DevTools - * creation: - */ - addWorkSpace(path: string): void; - /** - * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(image, dirtyRect) when there is a presentation event. - * The image is an instance of NativeImage that stores the captured frame. The - * dirtyRect is an object with x, y, width, height properties that describes which - * part of the page was repainted. If onlyDirty is set to true, image will only - * contain the repainted area. onlyDirty defaults to false. - */ - beginFrameSubscription(callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; - /** - * Begin subscribing for presentation events and captured frames, the callback will - * be called with callback(image, dirtyRect) when there is a presentation event. - * The image is an instance of NativeImage that stores the captured frame. The - * dirtyRect is an object with x, y, width, height properties that describes which - * part of the page was repainted. If onlyDirty is set to true, image will only - * contain the repainted area. onlyDirty defaults to false. - */ - beginFrameSubscription(onlyDirty: boolean, callback: (image: NativeImage, dirtyRect: Rectangle) => void): void; - canGoBack(): boolean; - canGoForward(): boolean; - canGoToOffset(offset: number): boolean; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Clears the navigation history. - */ - clearHistory(): void; - /** - * Closes the devtools. - */ - closeDevTools(): void; - /** - * Executes the editing command copy in web page. - */ - copy(): void; - /** - * Copy the image at the given position to the clipboard. - */ - copyImageAt(x: number, y: number): void; - /** - * Executes the editing command cut in web page. - */ - cut(): void; - /** - * Executes the editing command delete in web page. - */ - delete(): void; - /** - * Disable device emulation enabled by webContents.enableDeviceEmulation. - */ - disableDeviceEmulation(): void; - /** - * Initiates a download of the resource at url without navigating. The - * will-download event of session will be triggered. - */ - downloadURL(url: string): void; - /** - * Enable device emulation with the given parameters. - */ - enableDeviceEmulation(parameters: Parameters): void; - /** - * End subscribing for frame presentation events. - */ - endFrameSubscription(): void; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Starts a request to find all matches for the text in the web page. The result of - * the request can be obtained by subscribing to found-in-page event. - */ - findInPage(text: string, options?: FindInPageOptions): number; - /** - * Focuses the web page. - */ - focus(): void; - getFrameRate(): number; - getOSProcessId(): number; - /** - * Get the system printer list. - */ - getPrinters(): PrinterInfo[]; - getProcessId(): number; - getTitle(): string; - getType(): ('backgroundPage' | 'window' | 'browserView' | 'remote' | 'webview' | 'offscreen'); - getURL(): string; - getUserAgent(): string; - getWebRTCIPHandlingPolicy(): string; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Makes the browser go back a web page. - */ - goBack(): void; - /** - * Makes the browser go forward a web page. - */ - goForward(): void; - /** - * Navigates browser to the specified absolute web page index. - */ - goToIndex(index: number): void; - /** - * Navigates to the specified offset from the "current entry". - */ - goToOffset(offset: number): void; - /** - * Injects CSS into the current web page. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Starts inspecting element at position (x, y). - */ - inspectElement(x: number, y: number): void; - /** - * Opens the developer tools for the service worker context. - */ - inspectServiceWorker(): void; - /** - * Opens the developer tools for the shared worker context. - */ - inspectSharedWorker(): void; - /** - * Schedules a full repaint of the window this web contents is in. If offscreen - * rendering is enabled invalidates the frame and generates a new one through the - * 'paint' event. - */ - invalidate(): void; - isAudioMuted(): boolean; - isCrashed(): boolean; - isCurrentlyAudible(): boolean; - isDestroyed(): boolean; - isDevToolsFocused(): boolean; - isDevToolsOpened(): boolean; - isFocused(): boolean; - isLoading(): boolean; - isLoadingMainFrame(): boolean; - isOffscreen(): boolean; - isPainting(): boolean; - isWaitingForResponse(): boolean; - /** - * Loads the given file in the window, filePath should be a path to an HTML file - * relative to the root of your application. For instance an app structure like - * this: Would require code like this - */ - loadFile(filePath: string, options?: LoadFileOptions): Promise; - /** - * Loads the url in the window. The url must contain the protocol prefix, e.g. the - * http:// or file://. If the load should bypass http cache then use the pragma - * header to achieve it. - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Opens the devtools. When contents is a tag, the mode would be detach - * by default, explicitly passing an empty mode can force using last used dock - * state. - */ - openDevTools(options?: OpenDevToolsOptions): void; - /** - * Executes the editing command paste in web page. - */ - paste(): void; - /** - * Executes the editing command pasteAndMatchStyle in web page. - */ - pasteAndMatchStyle(): void; - /** - * Prints window's web page. When silent is set to true, Electron will pick the - * system's default printer if deviceName is empty and the default settings for - * printing. Calling window.print() in web page is equivalent to calling - * webContents.print({ silent: false, printBackground: false, deviceName: '' }). - * Use page-break-before: always; CSS style to force to print to a new page. - */ - print(options?: PrintOptions, callback?: (success: boolean) => void): void; - /** - * Prints window's web page as PDF with Chromium's preview printing custom - * settings. The landscape will be ignored if @page CSS at-rule is used in the web - * page. By default, an empty options will be regarded as: Use page-break-before: - * always; CSS style to force to print to a new page. An example of - * webContents.printToPDF: - */ - printToPDF(options: PrintToPDFOptions): Promise; - /** - * Prints window's web page as PDF with Chromium's preview printing custom - * settings. The callback will be called with callback(error, data) on completion. - * The data is a Buffer that contains the generated PDF data. Deprecated Soon - */ - printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; - /** - * Executes the editing command redo in web page. - */ - redo(): void; - /** - * Reloads the current web page. - */ - reload(): void; - /** - * Reloads current page and ignores cache. - */ - reloadIgnoringCache(): void; - /** - * Removes the specified path from DevTools workspace. - */ - removeWorkSpace(path: string): void; - /** - * Executes the editing command replace in web page. - */ - replace(text: string): void; - /** - * Executes the editing command replaceMisspelling in web page. - */ - replaceMisspelling(text: string): void; - savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'): Promise; - /** - * Executes the editing command selectAll in web page. - */ - selectAll(): void; - /** - * Send an asynchronous message to renderer process via channel, you can also send - * arbitrary arguments. Arguments will be serialized in JSON internally and hence - * no functions or prototype chain will be included. The renderer process can - * handle the message by listening to channel with the ipcRenderer module. An - * example of sending messages from the main process to the renderer process: - */ - send(channel: string, ...args: any[]): void; - /** - * Sends an input event to the page. Note: The BrowserWindow containing the - * contents needs to be focused for sendInputEvent() to work. For keyboard events, - * the event object also have following properties: For mouse events, the event - * object also have following properties: For the mouseWheel event, the event - * object also have following properties: - */ - sendInputEvent(event: Event): void; - /** - * Send an asynchronous message to a specific frame in a renderer process via - * channel. Arguments will be serialized as JSON internally and as such no - * functions or prototype chains will be included. The renderer process can handle - * the message by listening to channel with the ipcRenderer module. If you want to - * get the frameId of a given renderer context you should use the - * webFrame.routingId value. E.g. You can also read frameId from all incoming IPC - * messages in the main process. - */ - sendToFrame(frameId: number, channel: string, ...args: any[]): void; - /** - * Mute the audio on the current web page. - */ - setAudioMuted(muted: boolean): void; - /** - * Controls whether or not this WebContents will throttle animations and timers - * when the page becomes backgrounded. This also affects the Page Visibility API. - */ - setBackgroundThrottling(allowed: boolean): void; - /** - * Uses the devToolsWebContents as the target WebContents to show devtools. The - * devToolsWebContents must not have done any navigation, and it should not be used - * for other purposes after the call. By default Electron manages the devtools by - * creating an internal WebContents with native view, which developers have very - * limited control of. With the setDevToolsWebContents method, developers can use - * any WebContents to show the devtools in it, including BrowserWindow, BrowserView - * and tag. Note that closing the devtools does not destroy the - * devToolsWebContents, it is caller's responsibility to destroy - * devToolsWebContents. An example of showing devtools in a tag: An - * example of showing devtools in a BrowserWindow: - */ - setDevToolsWebContents(devToolsWebContents: WebContents): void; - /** - * If offscreen rendering is enabled sets the frame rate to the specified number. - * Only values between 1 and 60 are accepted. - */ - setFrameRate(fps: number): void; - /** - * Ignore application menu shortcuts while this web contents is focused. - */ - setIgnoreMenuShortcuts(ignore: boolean): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Overrides the user agent for this web page. - */ - setUserAgent(userAgent: string): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Setting the WebRTC IP handling policy allows you to control which IPs are - * exposed via WebRTC. See BrowserLeaks for more details. - */ - setWebRTCIPHandlingPolicy(policy: 'default' | 'default_public_interface_only' | 'default_public_and_private_interfaces' | 'disable_non_proxied_udp'): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * Changes the zoom level to the specified level. The original size is 0 and each - * increment above or below represents zooming 20% larger or smaller to default - * limits of 300% and 50% of original size, respectively. The formula for this is - * scale := 1.2 ^ level. - */ - setZoomLevel(level: number): void; - /** - * Shows pop-up dictionary that searches the selected word on the page. - */ - showDefinitionForSelection(): void; - /** - * Sets the item as dragging item for current drag-drop operation, file is the - * absolute path of the file to be dragged, and icon is the image showing under the - * cursor when dragging. - */ - startDrag(item: Item): void; - /** - * If offscreen rendering is enabled and not painting, start painting. - */ - startPainting(): void; - /** - * Stops any pending navigation. - */ - stop(): void; - /** - * Stops any findInPage request for the webContents with the provided action. - */ - stopFindInPage(action: 'clearSelection' | 'keepSelection' | 'activateSelection'): void; - /** - * If offscreen rendering is enabled and painting, stop painting. - */ - stopPainting(): void; - /** - * Takes a V8 heap snapshot and saves it to filePath. - */ - takeHeapSnapshot(filePath: string): Promise; - /** - * Toggles the developer tools. - */ - toggleDevTools(): void; - /** - * Executes the editing command undo in web page. - */ - undo(): void; - /** - * Executes the editing command unselect in web page. - */ - unselect(): void; - debugger: Debugger; - devToolsWebContents: WebContents; - hostWebContents: WebContents; - id: number; - session: Session; - } - - interface WebFrame extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-frame - - /** - * Attempts to free memory that is no longer being used (like images from a - * previous navigation). Note that blindly calling this method probably makes - * Electron slower since it will have to refill these emptied caches, you should - * only call it if an event in your app has occurred that makes you think your page - * is actually using less memory (i.e. you have navigated from a super heavy page - * to a mostly empty one, and intend to stay there). - */ - clearCache(): void; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Evaluates code in page. In the browser window some HTML APIs like - * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Works like executeJavaScript but evaluates scripts in an isolated context. - * Deprecated Soon - */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Works like executeJavaScript but evaluates scripts in an isolated context. - */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean): Promise; - findFrameByName(name: string): WebFrame; - findFrameByRoutingId(routingId: number): WebFrame; - getFrameForSelector(selector: string): WebFrame; - /** - * Returns an object describing usage information of Blink's internal memory - * caches. This will generate: - */ - getResourceUsage(): ResourceUsage; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Inserts css as a style sheet in the document. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Set the content security policy of the isolated world. - */ - setIsolatedWorldContentSecurityPolicy(worldId: number, csp: string): void; - /** - * Set the name of the isolated world. Useful in devtools. - */ - setIsolatedWorldHumanReadableName(worldId: number, name: string): void; - /** - * Set the security origin, content security policy and name of the isolated world. - * Note: If the csp is specified, then the securityOrigin also has to be specified. - */ - setIsolatedWorldInfo(worldId: number, info: Info): void; - /** - * Set the security origin of the isolated world. - */ - setIsolatedWorldSecurityOrigin(worldId: number, securityOrigin: string): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Sets a provider for spell checking in input fields and text areas. The provider - * must be an object that has a spellCheck method that accepts an array of - * individual words for spellchecking. The spellCheck function runs asynchronously - * and calls the callback function with an array of misspelt words when complete. - * An example of using node-spellchecker as provider: - */ - setSpellCheckProvider(language: string, provider: Provider): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * Changes the zoom level to the specified level. The original size is 0 and each - * increment above or below represents zooming 20% larger or smaller to default - * limits of 300% and 50% of original size, respectively. - */ - setZoomLevel(level: number): void; - /** - * A WebFrame representing the first child frame of webFrame, the property would be - * null if webFrame has no children or if first child is not in the current - * renderer process. - */ - firstChild?: WebFrame; - /** - * A WebFrame representing next sibling frame, the property would be null if - * webFrame is the last frame in its parent or if the next sibling is not in the - * current renderer process. - */ - nextSibling?: WebFrame; - /** - * A WebFrame representing the frame which opened webFrame, the property would be - * null if there's no opener or opener is not in the current renderer process. - */ - opener?: WebFrame; - /** - * A WebFrame representing parent frame of webFrame, the property would be null if - * webFrame is top or parent is not in the current renderer process. - */ - parent?: WebFrame; - /** - * An Integer representing the unique frame id in the current renderer process. - * Distinct WebFrame instances that refer to the same underlying frame will have - * the same routingId. - */ - routingId?: number; - /** - * A WebFrame representing top frame in frame hierarchy to which webFrame belongs, - * the property would be null if top frame is not in the current renderer process. - */ - top?: WebFrame; - } - - class WebRequest extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/web-request - - /** - * The listener will be called with listener(details) when a server initiated - * redirect is about to occur. - */ - onBeforeRedirect(listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a server initiated - * redirect is about to occur. - */ - onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when a request is - * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. Some examples of valid urls: - */ - onBeforeRequest(listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when a request is - * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. Some examples of valid urls: - */ - onBeforeRequest(filter: OnBeforeRequestFilter, listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) before sending an - * HTTP request, once the request headers are available. This may occur after a TCP - * connection is made to the server, but before any http data is sent. The callback - * has to be called with an response object. - */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) before sending an - * HTTP request, once the request headers are available. This may occur after a TCP - * connection is made to the server, but before any http data is sent. The callback - * has to be called with an response object. - */ - onBeforeSendHeaders(listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a request is completed. - */ - onCompleted(filter: OnCompletedFilter, listener: ((details: OnCompletedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when a request is completed. - */ - onCompleted(listener: ((details: OnCompletedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when an error occurs. - */ - onErrorOccurred(listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when an error occurs. - */ - onErrorOccurred(filter: OnErrorOccurredFilter, listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when HTTP response - * headers of a request have been received. The callback has to be called with an - * response object. - */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details, callback) when HTTP response - * headers of a request have been received. The callback has to be called with an - * response object. - */ - onHeadersReceived(listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; - /** - * The listener will be called with listener(details) when first byte of the - * response body is received. For HTTP requests, this means that the status line - * and response headers are available. - */ - onResponseStarted(listener: ((details: OnResponseStartedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) when first byte of the - * response body is received. For HTTP requests, this means that the status line - * and response headers are available. - */ - onResponseStarted(filter: OnResponseStartedFilter, listener: ((details: OnResponseStartedDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) just before a request is - * going to be sent to the server, modifications of previous onBeforeSendHeaders - * response are visible by the time this listener is fired. - */ - onSendHeaders(filter: OnSendHeadersFilter, listener: ((details: OnSendHeadersDetails) => void) | (null)): void; - /** - * The listener will be called with listener(details) just before a request is - * going to be sent to the server, modifications of previous onBeforeSendHeaders - * response are visible by the time this listener is fired. - */ - onSendHeaders(listener: ((details: OnSendHeadersDetails) => void) | (null)): void; - } - - interface WebSource { - - // Docs: http://electronjs.org/docs/api/structures/web-source - - code: string; - /** - * Default is 1. - */ - startLine?: number; - url?: string; - } - - interface WebviewTag extends HTMLElement { - - // Docs: http://electronjs.org/docs/api/webview-tag - - /** - * Fired when a load has committed. This includes navigation within the current - * document as well as subframe document-level loads, but does not include - * asynchronous resource loads. - */ - addEventListener(event: 'load-commit', listener: (event: LoadCommitEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'load-commit', listener: (event: LoadCommitEvent) => void): this; - /** - * Fired when the navigation is done, i.e. the spinner of the tab will stop - * spinning, and the onload event is dispatched. - */ - addEventListener(event: 'did-finish-load', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-finish-load', listener: (event: Event) => void): this; - /** - * This event is like did-finish-load, but fired when the load failed or was - * cancelled, e.g. window.stop() is invoked. - */ - addEventListener(event: 'did-fail-load', listener: (event: DidFailLoadEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-fail-load', listener: (event: DidFailLoadEvent) => void): this; - /** - * Fired when a frame has done navigation. - */ - addEventListener(event: 'did-frame-finish-load', listener: (event: DidFrameFinishLoadEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-frame-finish-load', listener: (event: DidFrameFinishLoadEvent) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab starts spinning. - */ - addEventListener(event: 'did-start-loading', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-start-loading', listener: (event: Event) => void): this; - /** - * Corresponds to the points in time when the spinner of the tab stops spinning. - */ - addEventListener(event: 'did-stop-loading', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-stop-loading', listener: (event: Event) => void): this; - /** - * Fired when document in the given frame is loaded. - */ - addEventListener(event: 'dom-ready', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'dom-ready', listener: (event: Event) => void): this; - /** - * Fired when page title is set during navigation. explicitSet is false when title - * is synthesized from file url. - */ - addEventListener(event: 'page-title-updated', listener: (event: PageTitleUpdatedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'page-title-updated', listener: (event: PageTitleUpdatedEvent) => void): this; - /** - * Fired when page receives favicon urls. - */ - addEventListener(event: 'page-favicon-updated', listener: (event: PageFaviconUpdatedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'page-favicon-updated', listener: (event: PageFaviconUpdatedEvent) => void): this; - /** - * Fired when page enters fullscreen triggered by HTML API. - */ - addEventListener(event: 'enter-html-full-screen', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'enter-html-full-screen', listener: (event: Event) => void): this; - /** - * Fired when page leaves fullscreen triggered by HTML API. - */ - addEventListener(event: 'leave-html-full-screen', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'leave-html-full-screen', listener: (event: Event) => void): this; - /** - * Fired when the guest window logs a console message. The following example code - * forwards all log messages to the embedder's console without regard for log level - * or other properties. - */ - addEventListener(event: 'console-message', listener: (event: ConsoleMessageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'console-message', listener: (event: ConsoleMessageEvent) => void): this; - /** - * Fired when a result is available for webview.findInPage request. - */ - addEventListener(event: 'found-in-page', listener: (event: FoundInPageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'found-in-page', listener: (event: FoundInPageEvent) => void): this; - /** - * Fired when the guest page attempts to open a new browser window. The following - * example code opens the new url in system's default browser. - */ - addEventListener(event: 'new-window', listener: (event: NewWindowEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'new-window', listener: (event: NewWindowEvent) => void): this; - /** - * Emitted when a user or the page wants to start navigation. It can happen when - * the window.location object is changed or a user clicks a link in the page. This - * event will not emit when the navigation is started programmatically with APIs - * like .loadURL and .back. It is also not emitted during in-page - * navigation, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. Calling event.preventDefault() - * does NOT have any effect. - */ - addEventListener(event: 'will-navigate', listener: (event: WillNavigateEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'will-navigate', listener: (event: WillNavigateEvent) => void): this; - /** - * Emitted when a navigation is done. This event is not emitted for in-page - * navigations, such as clicking anchor links or updating the window.location.hash. - * Use did-navigate-in-page event for this purpose. - */ - addEventListener(event: 'did-navigate', listener: (event: DidNavigateEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-navigate', listener: (event: DidNavigateEvent) => void): this; - /** - * Emitted when an in-page navigation happened. When in-page navigation happens, - * the page URL changes but does not cause navigation outside of the page. Examples - * of this occurring are when anchor links are clicked or when the DOM hashchange - * event is triggered. - */ - addEventListener(event: 'did-navigate-in-page', listener: (event: DidNavigateInPageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-navigate-in-page', listener: (event: DidNavigateInPageEvent) => void): this; - /** - * Fired when the guest page attempts to close itself. The following example code - * navigates the webview to about:blank when the guest attempts to close itself. - */ - addEventListener(event: 'close', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'close', listener: (event: Event) => void): this; - /** - * Fired when the guest page has sent an asynchronous message to embedder page. - * With sendToHost method and ipc-message event you can communicate between guest - * page and embedder page: - */ - addEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'ipc-message', listener: (event: IpcMessageEvent) => void): this; - /** - * Fired when the renderer process is crashed. - */ - addEventListener(event: 'crashed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'crashed', listener: (event: Event) => void): this; - /** - * Fired when a plugin process is crashed. - */ - addEventListener(event: 'plugin-crashed', listener: (event: PluginCrashedEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'plugin-crashed', listener: (event: PluginCrashedEvent) => void): this; - /** - * Fired when the WebContents is destroyed. - */ - addEventListener(event: 'destroyed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'destroyed', listener: (event: Event) => void): this; - /** - * Emitted when media starts playing. - */ - addEventListener(event: 'media-started-playing', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'media-started-playing', listener: (event: Event) => void): this; - /** - * Emitted when media is paused or done playing. - */ - addEventListener(event: 'media-paused', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'media-paused', listener: (event: Event) => void): this; - /** - * Emitted when a page's theme color changes. This is usually due to encountering a - * meta tag: - */ - addEventListener(event: 'did-change-theme-color', listener: (event: DidChangeThemeColorEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'did-change-theme-color', listener: (event: DidChangeThemeColorEvent) => void): this; - /** - * Emitted when mouse moves over a link or the keyboard moves the focus to a link. - */ - addEventListener(event: 'update-target-url', listener: (event: UpdateTargetUrlEvent) => void, useCapture?: boolean): this; - removeEventListener(event: 'update-target-url', listener: (event: UpdateTargetUrlEvent) => void): this; - /** - * Emitted when DevTools is opened. - */ - addEventListener(event: 'devtools-opened', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-opened', listener: (event: Event) => void): this; - /** - * Emitted when DevTools is closed. - */ - addEventListener(event: 'devtools-closed', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-closed', listener: (event: Event) => void): this; - /** - * Emitted when DevTools is focused / opened. - */ - addEventListener(event: 'devtools-focused', listener: (event: Event) => void, useCapture?: boolean): this; - removeEventListener(event: 'devtools-focused', listener: (event: Event) => void): this; - addEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; - addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; - removeEventListener(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, useCapture?: boolean): void; - removeEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void; - canGoBack(): boolean; - canGoForward(): boolean; - canGoToOffset(offset: number): boolean; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Upon completion callback will be - * called with callback(image). The image is an instance of NativeImage that stores - * data of the snapshot. Omitting rect will capture the whole visible page. - * Deprecated Soon - */ - capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; - /** - * Captures a snapshot of the page within rect. Omitting rect will capture the - * whole visible page. - */ - capturePage(rect?: Rectangle): Promise; - /** - * Clears the navigation history. - */ - clearHistory(): void; - /** - * Closes the DevTools window of guest page. - */ - closeDevTools(): void; - /** - * Executes editing command copy in page. - */ - copy(): void; - /** - * Executes editing command cut in page. - */ - cut(): void; - /** - * Executes editing command delete in page. - */ - delete(): void; - /** - * Initiates a download of the resource at url without navigating. - */ - downloadURL(url: string): void; - /** - * Evaluates code in page. If userGesture is set, it will create the user gesture - * context in the page. HTML APIs like requestFullScreen, which require user - * action, can take advantage of this option for automation. Deprecated Soon - */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; - /** - * Evaluates code in page. If userGesture is set, it will create the user gesture - * context in the page. HTML APIs like requestFullScreen, which require user - * action, can take advantage of this option for automation. - */ - executeJavaScript(code: string, userGesture?: boolean): Promise; - /** - * Starts a request to find all matches for the text in the web page. The result of - * the request can be obtained by subscribing to found-in-page event. - */ - findInPage(text: string, options?: FindInPageOptions): number; - getTitle(): string; - getURL(): string; - getUserAgent(): string; - /** - * It depends on the remote module, it is therefore not available when this module - * is disabled. - */ - getWebContents(): WebContents; - getWebContentsId(): number; - getZoomFactor(): number; - getZoomLevel(): number; - /** - * Makes the guest page go back. - */ - goBack(): void; - /** - * Makes the guest page go forward. - */ - goForward(): void; - /** - * Navigates to the specified absolute index. - */ - goToIndex(index: number): void; - /** - * Navigates to the specified offset from the "current entry". - */ - goToOffset(offset: number): void; - /** - * Injects CSS into the guest page. - */ - insertCSS(css: string): void; - /** - * Inserts text to the focused element. - */ - insertText(text: string): void; - /** - * Starts inspecting element at position (x, y) of guest page. - */ - inspectElement(x: number, y: number): void; - /** - * Opens the DevTools for the service worker context present in the guest page. - */ - inspectServiceWorker(): void; - /** - * Opens the DevTools for the shared worker context present in the guest page. - */ - inspectSharedWorker(): void; - isAudioMuted(): boolean; - isCrashed(): boolean; - isCurrentlyAudible(): boolean; - isDevToolsFocused(): boolean; - isDevToolsOpened(): boolean; - isLoading(): boolean; - isLoadingMainFrame(): boolean; - isWaitingForResponse(): boolean; - /** - * Loads the url in the webview, the url must contain the protocol prefix, e.g. the - * http:// or file://. - */ - loadURL(url: string, options?: LoadURLOptions): Promise; - /** - * Opens a DevTools window for guest page. - */ - openDevTools(): void; - /** - * Executes editing command paste in page. - */ - paste(): void; - /** - * Executes editing command pasteAndMatchStyle in page. - */ - pasteAndMatchStyle(): void; - /** - * Prints webview's web page. Same as webContents.print([options]). - */ - print(options?: PrintOptions): void; - /** - * Prints webview's web page as PDF, Same as webContents.printToPDF(options, - * callback). Deprecated Soon - */ - printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; - /** - * Prints webview's web page as PDF, Same as webContents.printToPDF(options). - */ - printToPDF(options: PrintToPDFOptions): Promise; - /** - * Executes editing command redo in page. - */ - redo(): void; - /** - * Reloads the guest page. - */ - reload(): void; - /** - * Reloads the guest page and ignores cache. - */ - reloadIgnoringCache(): void; - /** - * Executes editing command replace in page. - */ - replace(text: string): void; - /** - * Executes editing command replaceMisspelling in page. - */ - replaceMisspelling(text: string): void; - /** - * Executes editing command selectAll in page. - */ - selectAll(): void; - /** - * Send an asynchronous message to renderer process via channel, you can also send - * arbitrary arguments. The renderer process can handle the message by listening to - * the channel event with the ipcRenderer module. See webContents.send for - * examples. - */ - send(channel: string, ...args: any[]): void; - /** - * Sends an input event to the page. See webContents.sendInputEvent for detailed - * description of event object. - */ - sendInputEvent(event: any): void; - /** - * Set guest page muted. - */ - setAudioMuted(muted: boolean): void; - /** - * Sets the maximum and minimum layout-based (i.e. non-visual) zoom level. - */ - setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Overrides the user agent for the guest page. - */ - setUserAgent(userAgent: string): void; - /** - * Sets the maximum and minimum pinch-to-zoom level. - */ - setVisualZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; - /** - * Changes the zoom factor to the specified factor. Zoom factor is zoom percent - * divided by 100, so 300% = 3.0. - */ - setZoomFactor(factor: number): void; - /** - * Changes the zoom level to the specified level. The original size is 0 and each - * increment above or below represents zooming 20% larger or smaller to default - * limits of 300% and 50% of original size, respectively. The formula for this is - * scale := 1.2 ^ level. - */ - setZoomLevel(level: number): void; - /** - * Shows pop-up dictionary that searches the selected word on the page. - */ - showDefinitionForSelection(): void; - /** - * Stops any pending navigation. - */ - stop(): void; - /** - * Stops any findInPage request for the webview with the provided action. - */ - stopFindInPage(action: 'clearSelection' | 'keepSelection' | 'activateSelection'): void; - /** - * Executes editing command undo in page. - */ - undo(): void; - /** - * Executes editing command unselect in page. - */ - unselect(): void; - /** - * When this attribute is present the guest page will be allowed to open new - * windows. Popups are disabled by default. - */ - // allowpopups?: string; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * A list of strings which specifies the blink features to be disabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - disableblinkfeatures?: string; - /** - * When this attribute is present the guest page will have web security disabled. - * Web security is enabled by default. - */ - // disablewebsecurity?: string; ### VSCODE CHANGE(https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * A list of strings which specifies the blink features to be enabled separated by - * ,. The full list of supported feature strings can be found in the - * RuntimeEnabledFeatures.json5 file. - */ - enableblinkfeatures?: string; - /** - * When this attribute is false the guest page in webview will not have access to - * the remote module. The remote module is available by default. - */ - enableremotemodule?: string; - /** - * Sets the referrer URL for the guest page. - */ - httpreferrer?: string; - /** - * When this attribute is present the guest page in webview will have node - * integration and can use node APIs like require and process to access low level - * system resources. Node integration is disabled by default in the guest page. - */ - nodeintegration?: string; - /** - * Experimental option for enabling NodeJS support in sub-frames such as iframes - * inside the webview. All your preloads will load for every iframe, you can use - * process.isMainFrame to determine if you are in the main frame or not. This - * option is disabled by default in the guest page. - */ - nodeintegrationinsubframes?: string; - /** - * Sets the session used by the page. If partition starts with persist:, the page - * will use a persistent session available to all pages in the app with the same - * partition. if there is no persist: prefix, the page will use an in-memory - * session. By assigning the same partition, multiple pages can share the same - * session. If the partition is unset then default session of the app will be used. - * This value can only be modified before the first navigation, since the session - * of an active renderer process cannot change. Subsequent attempts to modify the - * value will fail with a DOM exception. - */ - partition?: string; - /** - * When this attribute is present the guest page in webview will be able to use - * browser plugins. Plugins are disabled by default. - */ - plugins?: string; - /** - * Specifies a script that will be loaded before other scripts run in the guest - * page. The protocol of script's URL must be either file: or asar:, because it - * will be loaded by require in guest page under the hood. When the guest page - * doesn't have node integration this script will still have access to all Node - * APIs, but global objects injected by Node will be deleted after this script has - * finished executing. Note: This option will be appear as preloadURL (not preload) - * in the webPreferences specified to the will-attach-webview event. - */ - preload?: string; - /** - * Returns the visible URL. Writing to this attribute initiates top-level - * navigation. Assigning src its own value will reload the current page. The src - * attribute can also accept data URLs, such as data:text/plain,Hello, world!. - */ - src?: string; - /** - * Sets the user agent for the guest page before the page is navigated to. Once the - * page is loaded, use the setUserAgent method to change the user agent. - */ - useragent?: string; - /** - * A list of strings which specifies the web preferences to be set on the webview, - * separated by ,. The full list of supported preference strings can be found in - * BrowserWindow. The string follows the same format as the features string in - * window.open. A name by itself is given a true boolean value. A preference can be - * set to another value by including an =, followed by the value. Special values - * yes and 1 are interpreted as true, while no and 0 are interpreted as false. - */ - webpreferences?: string; - } - - interface AboutPanelOptionsOptions { - /** - * The app's name. - */ - applicationName?: string; - /** - * The app's version. - */ - applicationVersion?: string; - /** - * Copyright information. - */ - copyright?: string; - /** - * The app's build version number. - */ - version?: string; - /** - * Credit information. - */ - credits?: string; - /** - * The app's website. - */ - website?: string; - /** - * Path to the app's icon. Will be shown as 64x64 pixels while retaining aspect - * ratio. - */ - iconPath?: string; - } - - interface AddRepresentationOptions { - /** - * The scale factor to add the image representation for. - */ - scaleFactor: number; - /** - * Defaults to 0. Required if a bitmap buffer is specified as buffer. - */ - width?: number; - /** - * Defaults to 0. Required if a bitmap buffer is specified as buffer. - */ - height?: number; - /** - * The buffer containing the raw image data. - */ - buffer?: Buffer; - /** - * The data URL containing either a base 64 encoded PNG or JPEG image. - */ - dataURL?: string; - } - - interface AnimationSettings { - /** - * Returns true if rich animations should be rendered. Looks at session type (e.g. - * remote desktop) and accessibility settings to give guidance for heavy - * animations. - */ - shouldRenderRichAnimation: boolean; - /** - * Determines on a per-platform basis whether scroll animations (e.g. produced by - * home/end key) should be enabled. - */ - scrollAnimationsEnabledBySystem: boolean; - /** - * Determines whether the user desires reduced motion based on platform APIs. - */ - prefersReducedMotion: boolean; - } - - interface AppDetailsOptions { - /** - * Window's . It has to be set, otherwise the other options will have no effect. - */ - appId?: string; - /** - * Window's . - */ - appIconPath?: string; - /** - * Index of the icon in appIconPath. Ignored when appIconPath is not set. Default - * is 0. - */ - appIconIndex?: number; - /** - * Window's . - */ - relaunchCommand?: string; - /** - * Window's . - */ - relaunchDisplayName?: string; - } - - interface AuthInfo { - isProxy: boolean; - scheme: string; - host: string; - port: number; - realm: string; - } - - interface AutoResizeOptions { - /** - * If true, the view's width will grow and shrink together with the window. false - * by default. - */ - width: boolean; - /** - * If true, the view's height will grow and shrink together with the window. false - * by default. - */ - height: boolean; - /** - * If true, the view's x position and width will grow and shrink proportionly with - * the window. false by default. - */ - horizontal: boolean; - /** - * If true, the view's y position and height will grow and shrink proportinaly with - * the window. false by default. - */ - vertical: boolean; - } - - interface BitmapOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface BrowserViewConstructorOptions { - /** - * See . - */ - webPreferences?: WebPreferences; - } - - interface BrowserWindowConstructorOptions { - /** - * Window's width in pixels. Default is 800. - */ - width?: number; - /** - * Window's height in pixels. Default is 600. - */ - height?: number; - /** - * ( if y is used) Window's left offset from screen. Default is to center the - * window. - */ - x?: number; - /** - * ( if x is used) Window's top offset from screen. Default is to center the - * window. - */ - y?: number; - /** - * The width and height would be used as web page's size, which means the actual - * window's size will include window frame's size and be slightly larger. Default - * is false. - */ - useContentSize?: boolean; - /** - * Show window in the center of the screen. - */ - center?: boolean; - /** - * Window's minimum width. Default is 0. - */ - minWidth?: number; - /** - * Window's minimum height. Default is 0. - */ - minHeight?: number; - /** - * Window's maximum width. Default is no limit. - */ - maxWidth?: number; - /** - * Window's maximum height. Default is no limit. - */ - maxHeight?: number; - /** - * Whether window is resizable. Default is true. - */ - resizable?: boolean; - /** - * Whether window is movable. This is not implemented on Linux. Default is true. - */ - movable?: boolean; - /** - * Whether window is minimizable. This is not implemented on Linux. Default is - * true. - */ - minimizable?: boolean; - /** - * Whether window is maximizable. This is not implemented on Linux. Default is - * true. - */ - maximizable?: boolean; - /** - * Whether window is closable. This is not implemented on Linux. Default is true. - */ - closable?: boolean; - /** - * Whether the window can be focused. Default is true. On Windows setting - * focusable: false also implies setting skipTaskbar: true. On Linux setting - * focusable: false makes the window stop interacting with wm, so the window will - * always stay on top in all workspaces. - */ - focusable?: boolean; - /** - * Whether the window should always stay on top of other windows. Default is false. - */ - alwaysOnTop?: boolean; - /** - * Whether the window should show in fullscreen. When explicitly set to false the - * fullscreen button will be hidden or disabled on macOS. Default is false. - */ - fullscreen?: boolean; - /** - * Whether the window can be put into fullscreen mode. On macOS, also whether the - * maximize/zoom button should toggle full screen mode or maximize window. Default - * is true. - */ - fullscreenable?: boolean; - /** - * Use pre-Lion fullscreen on macOS. Default is false. - */ - simpleFullscreen?: boolean; - /** - * Whether to show the window in taskbar. Default is false. - */ - skipTaskbar?: boolean; - /** - * The kiosk mode. Default is false. - */ - kiosk?: boolean; - /** - * Default window title. Default is "Electron". If the HTML tag is defined - * in the HTML file loaded by loadURL(), this property will be - * ignored. - */ - title?: string; - /** - * The window icon. On Windows it is recommended to use ICO icons to get best - * visual effects, you can also leave it undefined so the executable's icon will be - * used. - */ - icon?: (NativeImage) | (string); - /** - * Whether window should be shown when created. Default is true. - */ - show?: boolean; - /** - * Specify false to create a . Default is true. - */ - frame?: boolean; - /** - * Specify parent window. Default is null. - */ - parent?: BrowserWindow; - /** - * Whether this is a modal window. This only works when the window is a child - * window. Default is false. - */ - modal?: boolean; - /** - * Whether the web view accepts a single mouse-down event that simultaneously - * activates the window. Default is false. - */ - acceptFirstMouse?: boolean; - /** - * Whether to hide cursor when typing. Default is false. - */ - disableAutoHideCursor?: boolean; - /** - * Auto hide the menu bar unless the Alt key is pressed. Default is false. - */ - autoHideMenuBar?: boolean; - /** - * Enable the window to be resized larger than screen. Default is false. - */ - enableLargerThanScreen?: boolean; - /** - * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha in #AARRGGBB format is supported if transparent is set to - * true). Default is #FFF (white). - */ - backgroundColor?: string; - /** - * Whether window should have a shadow. This is only implemented on macOS. Default - * is true. - */ - hasShadow?: boolean; - /** - * Set the initial opacity of the window, between 0.0 (fully transparent) and 1.0 - * (fully opaque). This is only implemented on Windows and macOS. - */ - opacity?: number; - /** - * Forces using dark theme for the window, only works on some GTK+3 desktop - * environments. Default is false. - */ - darkTheme?: boolean; - /** - * Makes the window . Default is false. - */ - transparent?: boolean; - /** - * The type of window, default is normal window. See more about this below. - */ - type?: string; - /** - * The style of window title bar. Default is default. Possible values are: - */ - titleBarStyle?: ('default' | 'hidden' | 'hiddenInset' | 'customButtonsOnHover'); - /** - * Shows the title in the title bar in full screen mode on macOS for all - * titleBarStyle options. Default is false. - */ - fullscreenWindowTitle?: boolean; - /** - * Use WS_THICKFRAME style for frameless windows on Windows, which adds standard - * window frame. Setting it to false will remove window shadow and window - * animations. Default is true. - */ - thickFrame?: boolean; - /** - * Add a type of vibrancy effect to the window, only on macOS. Can be - * appearance-based, light, dark, titlebar, selection, menu, popover, sidebar, - * medium-light or ultra-dark. Please note that using frame: false in combination - * with a vibrancy value requires that you use a non-default titleBarStyle as well. - */ - vibrancy?: ('appearance-based' | 'light' | 'dark' | 'titlebar' | 'selection' | 'menu' | 'popover' | 'sidebar' | 'medium-light' | 'ultra-dark'); - /** - * Controls the behavior on macOS when option-clicking the green stoplight button - * on the toolbar or by clicking the Window > Zoom menu item. If true, the window - * will grow to the preferred width of the web page when zoomed, false will cause - * it to zoom to the width of the screen. This will also affect the behavior when - * calling maximize() directly. Default is false. - */ - zoomToPageWidth?: boolean; - /** - * Tab group name, allows opening the window as a native tab on macOS 10.12+. - * Windows with the same tabbing identifier will be grouped together. This also - * adds a native new tab button to your window's tab bar and allows your app and - * window to receive the new-window-for-tab event. - */ - tabbingIdentifier?: string; - /** - * Settings of web page's features. - */ - webPreferences?: WebPreferences; - } - - interface CertificateTrustDialogOptions { - /** - * The certificate to trust/import. - */ - certificate: Certificate; - /** - * The message to display to the user. - */ - message: string; - } - - interface CertificateVerifyProcRequest { - hostname: string; - certificate: Certificate; - /** - * Verification result from chromium. - */ - verificationResult: string; - /** - * Error code. - */ - errorCode: number; - } - - interface ClearStorageDataOptions { - /** - * Should follow window.location.origin’s representation scheme://host:port. - */ - origin?: string; - /** - * The types of storages to clear, can contain: appcache, cookies, filesystem, - * indexdb, localstorage, shadercache, websql, serviceworkers, cachestorage. - */ - storages?: string[]; - /** - * The types of quotas to clear, can contain: temporary, persistent, syncable. - */ - quotas?: string[]; - } - - interface CommandLine { - /** - * Append a switch (with optional value) to Chromium's command line. Note: This - * will not affect process.argv. The intended usage of this function is to control - * Chromium's behavior. - */ - appendSwitch: (the_switch: string, value?: string) => void; - /** - * Append an argument to Chromium's command line. The argument will be quoted - * correctly. Switches will precede arguments regardless of appending order. If - * you're appending an argument like --switch=value, consider using - * appendSwitch('switch', 'value') instead. Note: This will not affect - * process.argv. The intended usage of this function is to control Chromium's - * behavior. - */ - appendArgument: (value: string) => void; - hasSwitch: (the_switch: string) => boolean; - /** - * Note: When the switch is not present or has no value, it returns empty string. - */ - getSwitchValue: (the_switch: string) => string; - } - - interface Config { - /** - * The URL associated with the PAC file. - */ - pacScript: string; - /** - * Rules indicating which proxies to use. - */ - proxyRules: string; - /** - * Rules indicating which URLs should bypass the proxy settings. - */ - proxyBypassRules: string; - } - - interface ConsoleMessageEvent extends Event { - level: number; - message: string; - line: number; - sourceId: string; - } - - interface ContextMenuParams { - /** - * x coordinate. - */ - x: number; - /** - * y coordinate. - */ - y: number; - /** - * URL of the link that encloses the node the context menu was invoked on. - */ - linkURL: string; - /** - * Text associated with the link. May be an empty string if the contents of the - * link are an image. - */ - linkText: string; - /** - * URL of the top level page that the context menu was invoked on. - */ - pageURL: string; - /** - * URL of the subframe that the context menu was invoked on. - */ - frameURL: string; - /** - * Source URL for the element that the context menu was invoked on. Elements with - * source URLs are images, audio and video. - */ - srcURL: string; - /** - * Type of the node the context menu was invoked on. Can be none, image, audio, - * video, canvas, file or plugin. - */ - mediaType: ('none' | 'image' | 'audio' | 'video' | 'canvas' | 'file' | 'plugin'); - /** - * Whether the context menu was invoked on an image which has non-empty contents. - */ - hasImageContents: boolean; - /** - * Whether the context is editable. - */ - isEditable: boolean; - /** - * Text of the selection that the context menu was invoked on. - */ - selectionText: string; - /** - * Title or alt text of the selection that the context was invoked on. - */ - titleText: string; - /** - * The misspelled word under the cursor, if any. - */ - misspelledWord: string; - /** - * The character encoding of the frame on which the menu was invoked. - */ - frameCharset: string; - /** - * If the context menu was invoked on an input field, the type of that field. - * Possible values are none, plainText, password, other. - */ - inputFieldType: string; - /** - * Input source that invoked the context menu. Can be none, mouse, keyboard, touch - * or touchMenu. - */ - menuSourceType: ('none' | 'mouse' | 'keyboard' | 'touch' | 'touchMenu'); - /** - * The flags for the media element the context menu was invoked on. - */ - mediaFlags: MediaFlags; - /** - * These flags indicate whether the renderer believes it is able to perform the - * corresponding action. - */ - editFlags: EditFlags; - } - - interface CrashReporterStartOptions { - companyName: string; - /** - * URL that crash reports will be sent to as POST. - */ - submitURL: string; - /** - * Defaults to app.getName(). - */ - productName?: string; - /** - * Whether crash reports should be sent to the server Default is true. - */ - uploadToServer?: boolean; - /** - * Default is false. - */ - ignoreSystemCrashHandler?: boolean; - /** - * An object you can define that will be sent along with the report. Only string - * properties are sent correctly. Nested objects are not supported and the property - * names and values must be less than 64 characters long. - */ - extra?: Extra; - /** - * Directory to store the crashreports temporarily (only used when the crash - * reporter is started via process.crashReporter.start). - */ - crashesDirectory?: string; - } - - interface CreateFromBitmapOptions { - width: number; - height: number; - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface CreateFromBufferOptions { - /** - * Required for bitmap buffers. - */ - width?: number; - /** - * Required for bitmap buffers. - */ - height?: number; - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface CreateInterruptedDownloadOptions { - /** - * Absolute path of the download. - */ - path: string; - /** - * Complete URL chain for the download. - */ - urlChain: string[]; - mimeType?: string; - /** - * Start range for the download. - */ - offset: number; - /** - * Total length of the download. - */ - length: number; - /** - * Last-Modified header value. - */ - lastModified: string; - /** - * ETag header value. - */ - eTag: string; - /** - * Time when download was started in number of seconds since UNIX epoch. - */ - startTime?: number; - } - - interface Data { - text?: string; - html?: string; - image?: NativeImage; - rtf?: string; - /** - * The title of the URL at text. - */ - bookmark?: string; - } - - interface Details { - /** - * The url to associate the cookie with. - */ - url: string; - /** - * The name of the cookie. Empty by default if omitted. - */ - name?: string; - /** - * The value of the cookie. Empty by default if omitted. - */ - value?: string; - /** - * The domain of the cookie. Empty by default if omitted. - */ - domain?: string; - /** - * The path of the cookie. Empty by default if omitted. - */ - path?: string; - /** - * Whether the cookie should be marked as Secure. Defaults to false. - */ - secure?: boolean; - /** - * Whether the cookie should be marked as HTTP only. Defaults to false. - */ - httpOnly?: boolean; - /** - * The expiration date of the cookie as the number of seconds since the UNIX epoch. - * If omitted then the cookie becomes a session cookie and will not be retained - * between sessions. - */ - expirationDate?: number; - } - - interface DevToolsExtensions { - } - - interface DidChangeThemeColorEvent extends Event { - themeColor: string; - } - - interface DidFailLoadEvent extends Event { - errorCode: number; - errorDescription: string; - validatedURL: string; - isMainFrame: boolean; - } - - interface DidFrameFinishLoadEvent extends Event { - isMainFrame: boolean; - } - - interface DidNavigateEvent extends Event { - url: string; - } - - interface DidNavigateInPageEvent extends Event { - isMainFrame: boolean; - url: string; - } - - interface DisplayBalloonOptions { - /** - * - - */ - icon?: (NativeImage) | (string); - title: string; - content: string; - } - - interface Dock { - /** - * When critical is passed, the dock icon will bounce until either the application - * becomes active or the request is canceled. When informational is passed, the - * dock icon will bounce for one second. However, the request remains active until - * either the application becomes active or the request is canceled. Nota Bene: - * This method can only be used while the app is not focused; when the app is - * focused it will return -1. - */ - bounce: (type?: 'critical' | 'informational') => number; - /** - * Cancel the bounce of id. - */ - cancelBounce: (id: number) => void; - /** - * Bounces the Downloads stack if the filePath is inside the Downloads folder. - */ - downloadFinished: (filePath: string) => void; - /** - * Sets the string to be displayed in the dock’s badging area. - */ - setBadge: (text: string) => void; - getBadge: () => string; - /** - * Hides the dock icon. - */ - hide: () => void; - show: () => Promise; - isVisible: () => boolean; - /** - * Sets the application's dock menu. - */ - setMenu: (menu: Menu) => void; - getMenu: () => (Menu) | (null); - /** - * Sets the image associated with this dock icon. - */ - setIcon: (image: (NativeImage) | (string)) => void; - } - - interface EnableNetworkEmulationOptions { - /** - * Whether to emulate network outage. Defaults to false. - */ - offline?: boolean; - /** - * RTT in ms. Defaults to 0 which will disable latency throttling. - */ - latency?: number; - /** - * Download rate in Bps. Defaults to 0 which will disable download throttling. - */ - downloadThroughput?: number; - /** - * Upload rate in Bps. Defaults to 0 which will disable upload throttling. - */ - uploadThroughput?: number; - } - - interface Extensions { - } - - interface FeedURLOptions { - url: string; - /** - * HTTP request headers. - */ - headers?: Headers; - /** - * Either json or default, see the README for more information. - */ - serverType?: string; - } - - interface FileIconOptions { - size: ('small' | 'normal' | 'large'); - } - - interface Filter { - /** - * Retrieves cookies which are associated with url. Empty implies retrieving - * cookies of all urls. - */ - url?: string; - /** - * Filters cookies by name. - */ - name?: string; - /** - * Retrieves cookies whose domains match or are subdomains of domains. - */ - domain?: string; - /** - * Retrieves cookies whose path matches path. - */ - path?: string; - /** - * Filters cookies by their Secure property. - */ - secure?: boolean; - /** - * Filters out session or persistent cookies. - */ - session?: boolean; - } - - interface FindInPageOptions { - /** - * Whether to search forward or backward, defaults to true. - */ - forward?: boolean; - /** - * Whether the operation is first request or a follow up, defaults to false. - */ - findNext?: boolean; - /** - * Whether search should be case-sensitive, defaults to false. - */ - matchCase?: boolean; - /** - * Whether to look only at the start of words. defaults to false. - */ - wordStart?: boolean; - /** - * When combined with wordStart, accepts a match in the middle of a word if the - * match begins with an uppercase letter followed by a lowercase or non-letter. - * Accepts several other intra-word matches, defaults to false. - */ - medialCapitalAsWordStart?: boolean; - } - - interface FoundInPageEvent extends Event { - result: FoundInPageResult; - } - - interface FromPartitionOptions { - /** - * Whether to enable cache. - */ - cache: boolean; - } - - interface Header { - /** - * Specify an extra header name. - */ - name: string; - } - - interface Headers { - } - - interface HeapStatistics { - totalHeapSize: number; - totalHeapSizeExecutable: number; - totalPhysicalSize: number; - totalAvailableSize: number; - usedHeapSize: number; - heapSizeLimit: number; - mallocedMemory: number; - peakMallocedMemory: number; - doesZapGarbage: boolean; - } - - interface IgnoreMouseEventsOptions { - /** - * If true, forwards mouse move messages to Chromium, enabling mouse related events - * such as mouseleave. Only used when ignore is true. If ignore is false, - * forwarding is always disabled regardless of this value. - */ - forward?: boolean; - } - - interface ImportCertificateOptions { - /** - * Path for the pkcs12 file. - */ - certificate: string; - /** - * Passphrase for the certificate. - */ - password: string; - } - - interface Info { - /** - * Security origin for the isolated world. - */ - securityOrigin?: string; - /** - * Content Security Policy for the isolated world. - */ - csp?: string; - /** - * Name for isolated world. Useful in devtools. - */ - name?: string; - } - - interface Input { - /** - * Either keyUp or keyDown. - */ - type: string; - /** - * Equivalent to . - */ - key: string; - /** - * Equivalent to . - */ - code: string; - /** - * Equivalent to . - */ - isAutoRepeat: boolean; - /** - * Equivalent to . - */ - shift: boolean; - /** - * Equivalent to . - */ - control: boolean; - /** - * Equivalent to . - */ - alt: boolean; - /** - * Equivalent to . - */ - meta: boolean; - } - - interface InterceptBufferProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptFileProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptHttpProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptStreamProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface InterceptStringProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface IpcMessageEvent extends Event { - channel: string; - args: any[]; - } - - interface Item { - /** - * or files Array The path(s) to the file(s) being dragged. - */ - file: string; - /** - * The image must be non-empty on macOS. - */ - icon: NativeImage; - } - - interface JumpListSettings { - /** - * The minimum number of items that will be shown in the Jump List (for a more - * detailed description of this value see the ). - */ - minItems: number; - /** - * Array of JumpListItem objects that correspond to items that the user has - * explicitly removed from custom categories in the Jump List. These items must not - * be re-added to the Jump List in the call to app.setJumpList(), Windows will not - * display any custom category that contains any of the removed items. - */ - removedItems: JumpListItem[]; - } - - interface LoadCommitEvent extends Event { - url: string; - isMainFrame: boolean; - } - - interface LoadFileOptions { - /** - * Passed to url.format(). - */ - query?: Query; - /** - * Passed to url.format(). - */ - search?: string; - /** - * Passed to url.format(). - */ - hash?: string; - } - - interface LoadURLOptions { - /** - * An HTTP Referrer url. - */ - httpReferrer?: (string) | (Referrer); - /** - * A user agent originating the request. - */ - userAgent?: string; - /** - * Extra headers separated by "\n" - */ - extraHeaders?: string; - postData?: (UploadRawData[]) | (UploadFile[]) | (UploadBlob[]); - /** - * Base url (with trailing path separator) for files to be loaded by the data url. - * This is needed only if the specified url is a data url and needs to load other - * files. - */ - baseURLForDataURL?: string; - } - - interface LoginItemSettings { - options?: Options; - /** - * true if the app is set to open at login. - */ - openAtLogin: boolean; - /** - * true if the app is set to open as hidden at login. This setting is not available - * on . - */ - openAsHidden: boolean; - /** - * true if the app was opened at login automatically. This setting is not available - * on . - */ - wasOpenedAtLogin: boolean; - /** - * true if the app was opened as a hidden login item. This indicates that the app - * should not open any windows at startup. This setting is not available on . - */ - wasOpenedAsHidden: boolean; - /** - * true if the app was opened as a login item that should restore the state from - * the previous session. This indicates that the app should restore the windows - * that were open the last time the app was closed. This setting is not available - * on . - */ - restoreState: boolean; - } - - interface LoginItemSettingsOptions { - /** - * The executable path to compare against. Defaults to process.execPath. - */ - path?: string; - /** - * The command-line arguments to compare against. Defaults to an empty array. - */ - args?: string[]; - } - - interface MemoryDumpConfig { - } - - interface MenuItemConstructorOptions { - /** - * Will be called with click(menuItem, browserWindow, event) when the menu item is - * clicked. - */ - click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: KeyboardEvent) => void; - /** - * Can be undo, redo, cut, copy, paste, pasteAndMatchStyle, delete, selectAll, - * reload, forceReload, toggleDevTools, resetZoom, zoomIn, zoomOut, - * togglefullscreen, window, minimize, close, help, about, services, hide, - * hideOthers, unhide, quit, startSpeaking, stopSpeaking, close, minimize, zoom, - * front, appMenu, fileMenu, editMenu, viewMenu, recentDocuments, toggleTabBar, - * selectNextTab, selectPreviousTab, mergeAllWindows, clearRecentDocuments, - * moveTabToNewWindow or windowMenu Define the action of the menu item, when - * specified the click property will be ignored. See . - */ - role?: ('undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteAndMatchStyle' | 'delete' | 'selectAll' | 'reload' | 'forceReload' | 'toggleDevTools' | 'resetZoom' | 'zoomIn' | 'zoomOut' | 'togglefullscreen' | 'window' | 'minimize' | 'close' | 'help' | 'about' | 'services' | 'hide' | 'hideOthers' | 'unhide' | 'quit' | 'startSpeaking' | 'stopSpeaking' | 'close' | 'minimize' | 'zoom' | 'front' | 'appMenu' | 'fileMenu' | 'editMenu' | 'viewMenu' | 'recentDocuments' | 'toggleTabBar' | 'selectNextTab' | 'selectPreviousTab' | 'mergeAllWindows' | 'clearRecentDocuments' | 'moveTabToNewWindow' | 'windowMenu'); - /** - * Can be normal, separator, submenu, checkbox or radio. - */ - type?: ('normal' | 'separator' | 'submenu' | 'checkbox' | 'radio'); - label?: string; - sublabel?: string; - accelerator?: Accelerator; - icon?: (NativeImage) | (string); - /** - * If false, the menu item will be greyed out and unclickable. - */ - enabled?: boolean; - /** - * default is true, and when false will prevent the accelerator from triggering the - * item if the item is not visible`. - */ - acceleratorWorksWhenHidden?: boolean; - /** - * If false, the menu item will be entirely hidden. - */ - visible?: boolean; - /** - * Should only be specified for checkbox or radio type menu items. - */ - checked?: boolean; - /** - * If false, the accelerator won't be registered with the system, but it will still - * be displayed. Defaults to true. - */ - registerAccelerator?: boolean; - /** - * Should be specified for submenu type menu items. If submenu is specified, the - * type: 'submenu' can be omitted. If the value is not a then it will be - * automatically converted to one using Menu.buildFromTemplate. - */ - submenu?: (MenuItemConstructorOptions[]) | (Menu); - /** - * Unique within a single menu. If defined then it can be used as a reference to - * this item by the position attribute. - */ - id?: string; - /** - * Inserts this item before the item with the specified label. If the referenced - * item doesn't exist the item will be inserted at the end of the menu. Also - * implies that the menu item in question should be placed in the same “group” as - * the item. - */ - before?: string[]; - /** - * Inserts this item after the item with the specified label. If the referenced - * item doesn't exist the item will be inserted at the end of the menu. - */ - after?: string[]; - /** - * Provides a means for a single context menu to declare the placement of their - * containing group before the containing group of the item with the specified - * label. - */ - beforeGroupContaining?: string[]; - /** - * Provides a means for a single context menu to declare the placement of their - * containing group after the containing group of the item with the specified - * label. - */ - afterGroupContaining?: string[]; - } - - interface MessageBoxOptions { - /** - * Can be "none", "info", "error", "question" or "warning". On Windows, "question" - * displays the same icon as "info", unless you set an icon using the "icon" - * option. On macOS, both "warning" and "error" display the same warning icon. - */ - type?: string; - /** - * Array of texts for buttons. On Windows, an empty array will result in one button - * labeled "OK". - */ - buttons?: string[]; - /** - * Index of the button in the buttons array which will be selected by default when - * the message box opens. - */ - defaultId?: number; - /** - * Title of the message box, some platforms will not show it. - */ - title?: string; - /** - * Content of the message box. - */ - message: string; - /** - * Extra information of the message. - */ - detail?: string; - /** - * If provided, the message box will include a checkbox with the given label. The - * checkbox state can be inspected only when using callback. - */ - checkboxLabel?: string; - /** - * Initial checked state of the checkbox. false by default. - */ - checkboxChecked?: boolean; - icon?: NativeImage; - /** - * The index of the button to be used to cancel the dialog, via the Esc key. By - * default this is assigned to the first button with "cancel" or "no" as the label. - * If no such labeled buttons exist and this option is not set, 0 will be used as - * the return value or callback response. - */ - cancelId?: number; - /** - * On Windows Electron will try to figure out which one of the buttons are common - * buttons (like "Cancel" or "Yes"), and show the others as command links in the - * dialog. This can make the dialog appear in the style of modern Windows apps. If - * you don't like this behavior, you can set noLink to true. - */ - noLink?: boolean; - /** - * Normalize the keyboard access keys across platforms. Default is false. Enabling - * this assumes & is used in the button labels for the placement of the keyboard - * shortcut access key and labels will be converted so they work correctly on each - * platform, & characters are removed on macOS, converted to _ on Linux, and left - * untouched on Windows. For example, a button label of Vie&w will be converted to - * Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and - * Linux. - */ - normalizeAccessKeys?: boolean; - } - - interface MessageBoxReturnValue { - /** - * The index of the clicked button. - */ - response: number; - /** - * The checked state of the checkbox if checkboxLabel was set. Otherwise false. - */ - checkboxChecked: boolean; - } - - interface MessageBoxSyncOptions { - /** - * Can be "none", "info", "error", "question" or "warning". On Windows, "question" - * displays the same icon as "info", unless you set an icon using the "icon" - * option. On macOS, both "warning" and "error" display the same warning icon. - */ - type?: string; - /** - * Array of texts for buttons. On Windows, an empty array will result in one button - * labeled "OK". - */ - buttons?: string[]; - /** - * Index of the button in the buttons array which will be selected by default when - * the message box opens. - */ - defaultId?: number; - /** - * Title of the message box, some platforms will not show it. - */ - title?: string; - /** - * Content of the message box. - */ - message: string; - /** - * Extra information of the message. - */ - detail?: string; - /** - * If provided, the message box will include a checkbox with the given label. The - * checkbox state can be inspected only when using callback. - */ - checkboxLabel?: string; - /** - * Initial checked state of the checkbox. false by default. - */ - checkboxChecked?: boolean; - icon?: (NativeImage) | (string); - /** - * The index of the button to be used to cancel the dialog, via the Esc key. By - * default this is assigned to the first button with "cancel" or "no" as the label. - * If no such labeled buttons exist and this option is not set, 0 will be used as - * the return value or callback response. - */ - cancelId?: number; - /** - * On Windows Electron will try to figure out which one of the buttons are common - * buttons (like "Cancel" or "Yes"), and show the others as command links in the - * dialog. This can make the dialog appear in the style of modern Windows apps. If - * you don't like this behavior, you can set noLink to true. - */ - noLink?: boolean; - /** - * Normalize the keyboard access keys across platforms. Default is false. Enabling - * this assumes & is used in the button labels for the placement of the keyboard - * shortcut access key and labels will be converted so they work correctly on each - * platform, & characters are removed on macOS, converted to _ on Linux, and left - * untouched on Windows. For example, a button label of Vie&w will be converted to - * Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and - * Linux. - */ - normalizeAccessKeys?: boolean; - } - - interface NewWindowEvent extends Event { - url: string; - frameName: string; - /** - * Can be `default`, `foreground-tab`, `background-tab`, `new-window`, - * `save-to-disk` and `other`. - */ - disposition: ('default' | 'foreground-tab' | 'background-tab' | 'new-window' | 'save-to-disk' | 'other'); - /** - * The options which should be used for creating the new . - */ - options: Options; - } - - interface NotificationConstructorOptions { - /** - * A title for the notification, which will be shown at the top of the notification - * window when it is shown. - */ - title: string; - /** - * A subtitle for the notification, which will be displayed below the title. - */ - subtitle?: string; - /** - * The body text of the notification, which will be displayed below the title or - * subtitle. - */ - body: string; - /** - * Whether or not to emit an OS notification noise when showing the notification. - */ - silent?: boolean; - /** - * An icon to use in the notification. - */ - icon?: (string) | (NativeImage); - /** - * Whether or not to add an inline reply option to the notification. - */ - hasReply?: boolean; - /** - * The placeholder to write in the inline reply input field. - */ - replyPlaceholder?: string; - /** - * The name of the sound file to play when the notification is shown. - */ - sound?: string; - /** - * Actions to add to the notification. Please read the available actions and - * limitations in the NotificationAction documentation. - */ - actions?: NotificationAction[]; - /** - * A custom title for the close button of an alert. An empty string will cause the - * default localized text to be used. - */ - closeButtonText?: string; - } - - interface OnBeforeRedirectDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - redirectURL: string; - statusCode: number; - /** - * The server IP address that the request was actually sent to. - */ - ip?: string; - fromCache: boolean; - responseHeaders: ResponseHeaders; - } - - interface OnBeforeRedirectFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeRequestDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - uploadData: UploadData[]; - } - - interface OnBeforeRequestFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeSendHeadersDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - requestHeaders: RequestHeaders; - } - - interface OnBeforeSendHeadersFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnBeforeSendHeadersResponse { - cancel?: boolean; - /** - * When provided, request will be made with these headers. - */ - requestHeaders?: RequestHeaders; - } - - interface OnCompletedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - responseHeaders: ResponseHeaders; - fromCache: boolean; - statusCode: number; - statusLine: string; - } - - interface OnCompletedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnErrorOccurredDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - fromCache: boolean; - /** - * The error description. - */ - error: string; - } - - interface OnErrorOccurredFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnHeadersReceivedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - statusLine: string; - statusCode: number; - responseHeaders: ResponseHeaders; - } - - interface OnHeadersReceivedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnHeadersReceivedResponse { - cancel?: boolean; - /** - * When provided, the server is assumed to have responded with these headers. - */ - responseHeaders?: ResponseHeaders; - /** - * Should be provided when overriding responseHeaders to change header status - * otherwise original response header's status will be used. - */ - statusLine?: string; - } - - interface OnResponseStartedDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - responseHeaders: ResponseHeaders; - /** - * Indicates whether the response was fetched from disk cache. - */ - fromCache: boolean; - statusCode: number; - statusLine: string; - } - - interface OnResponseStartedFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OnSendHeadersDetails { - id: number; - url: string; - method: string; - webContentsId?: number; - resourceType: string; - referrer: string; - timestamp: number; - requestHeaders: RequestHeaders; - } - - interface OnSendHeadersFilter { - /** - * Array of URL patterns that will be used to filter out the requests that do not - * match the URL patterns. - */ - urls: string[]; - } - - interface OpenDevToolsOptions { - /** - * Opens the devtools with specified dock state, can be right, bottom, undocked, - * detach. Defaults to last used dock state. In undocked mode it's possible to dock - * back. In detach mode it's not. - */ - mode: ('right' | 'bottom' | 'undocked' | 'detach'); - /** - * Whether to bring the opened devtools window to the foreground. The default is - * true. - */ - activate?: boolean; - } - - interface OpenDialogOptions { - title?: string; - defaultPath?: string; - /** - * Custom label for the confirmation button, when left empty the default label will - * be used. - */ - buttonLabel?: string; - filters?: FileFilter[]; - /** - * Contains which features the dialog should use. The following values are - * supported: - */ - properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>; - /** - * Message to display above input boxes. - */ - message?: string; - /** - * Create when packaged for the Mac App Store. - */ - securityScopedBookmarks?: boolean; - } - - interface OpenDialogReturnValue { - /** - * whether or not the dialog was canceled. - */ - canceled: boolean; - /** - * An array of file paths chosen by the user. If the dialog is cancelled this will - * be an empty array. - */ - filePaths?: string[]; - /** - * An array matching the filePaths array of base64 encoded strings which contains - * security scoped bookmark data. securityScopedBookmarks must be enabled for this - * to be populated. - */ - bookmarks?: string[]; - } - - interface OpenDialogSyncOptions { - title?: string; - defaultPath?: string; - /** - * Custom label for the confirmation button, when left empty the default label will - * be used. - */ - buttonLabel?: string; - filters?: FileFilter[]; - /** - * Contains which features the dialog should use. The following values are - * supported: - */ - properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>; - /** - * Message to display above input boxes. - */ - message?: string; - /** - * Create when packaged for the Mac App Store. - */ - securityScopedBookmarks?: boolean; - } - - interface OpenExternalOptions { - /** - * true to bring the opened application to the foreground. The default is true. - */ - activate?: boolean; - /** - * The working directory. - */ - workingDirectory?: string; - } - - interface OpenExternalSyncOptions { - /** - * true to bring the opened application to the foreground. The default is true. - */ - activate?: boolean; - /** - * The working directory. - */ - workingDirectory?: string; - } - - interface PageFaviconUpdatedEvent extends Event { - /** - * Array of URLs. - */ - favicons: string[]; - } - - interface PageTitleUpdatedEvent extends Event { - title: string; - explicitSet: boolean; - } - - interface Parameters { - /** - * Specify the screen type to emulate (default: desktop): - */ - screenPosition: ('desktop' | 'mobile'); - /** - * Set the emulated screen size (screenPosition == mobile). - */ - screenSize: Size; - /** - * Position the view on the screen (screenPosition == mobile) (default: { x: 0, y: - * 0 }). - */ - viewPosition: Point; - /** - * Set the device scale factor (if zero defaults to original device scale factor) - * (default: 0). - */ - deviceScaleFactor: number; - /** - * Set the emulated view size (empty means no override) - */ - viewSize: Size; - /** - * Scale of emulated view inside available space (not in fit to view mode) - * (default: 1). - */ - scale: number; - } - - interface Payment { - /** - * The identifier of the purchased product. - */ - productIdentifier: string; - /** - * The quantity purchased. - */ - quantity: number; - } - - interface PermissionCheckHandlerDetails { - /** - * The security orign of the media check. - */ - securityOrigin: string; - /** - * The type of media access being requested, can be video, audio or unknown - */ - mediaType: ('video' | 'audio' | 'unknown'); - /** - * The last URL the requesting frame loaded - */ - requestingUrl: string; - /** - * Whether the frame making the request is the main frame - */ - isMainFrame: boolean; - } - - interface PermissionRequestHandlerDetails { - /** - * The url of the openExternal request. - */ - externalURL?: string; - /** - * The types of media access being requested, elements can be video or audio - */ - mediaTypes?: Array<'video' | 'audio'>; - /** - * The last URL the requesting frame loaded - */ - requestingUrl: string; - /** - * Whether the frame making the request is the main frame - */ - isMainFrame: boolean; - } - - interface PluginCrashedEvent extends Event { - name: string; - version: string; - } - - interface PopupOptions { - /** - * Default is the focused window. - */ - window?: BrowserWindow; - /** - * Default is the current mouse cursor position. Must be declared if y is declared. - */ - x?: number; - /** - * Default is the current mouse cursor position. Must be declared if x is declared. - */ - y?: number; - /** - * The index of the menu item to be positioned under the mouse cursor at the - * specified coordinates. Default is -1. - */ - positioningItem?: number; - /** - * Called when menu is closed. - */ - callback?: () => void; - } - - interface PrintOptions { - /** - * Don't ask user for print settings. Default is false. - */ - silent?: boolean; - /** - * Also prints the background color and image of the web page. Default is false. - */ - printBackground?: boolean; - /** - * Set the printer device name to use. Default is ''. - */ - deviceName?: string; - } - - interface PrintToPDFOptions { - /** - * Specifies the type of margins to use. Uses 0 for default margin, 1 for no - * margin, and 2 for minimum margin. - */ - marginsType?: number; - /** - * Specify page size of the generated PDF. Can be A3, A4, A5, Legal, Letter, - * Tabloid or an Object containing height and width in microns. - */ - pageSize?: (string) | (Size); - /** - * Whether to print CSS backgrounds. - */ - printBackground?: boolean; - /** - * Whether to print selection only. - */ - printSelectionOnly?: boolean; - /** - * true for landscape, false for portrait. - */ - landscape?: boolean; - } - - interface Privileges { - /** - * Default false. - */ - standard?: boolean; - /** - * Default false. - */ - secure?: boolean; - /** - * Default false. - */ - bypassCSP?: boolean; - /** - * Default false. - */ - allowServiceWorkers?: boolean; - /** - * Default false. - */ - supportFetchAPI?: boolean; - /** - * Default false. - */ - corsEnabled?: boolean; - } - - interface ProgressBarOptions { - /** - * Mode for the progress bar. Can be none, normal, indeterminate, error or paused. - */ - mode: ('none' | 'normal' | 'indeterminate' | 'error' | 'paused'); - } - - interface Provider { - /** - * . - */ - spellCheck: (words: string[], callback: (misspeltWords: string[]) => void) => void; - } - - interface ReadBookmark { - title: string; - url: string; - } - - interface RedirectRequest { - url: string; - method: string; - session?: Session; - uploadData?: UploadData; - } - - interface RegisterBufferProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterFileProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterHttpProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterStreamProtocolRequest { - url: string; - headers: Headers; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RegisterStringProtocolRequest { - url: string; - referrer: string; - method: string; - uploadData: UploadData[]; - } - - interface RelaunchOptions { - args?: string[]; - execPath?: string; - } - - interface Request { - method: string; - url: string; - referrer: string; - } - - interface ResizeOptions { - /** - * Defaults to the image's width. - */ - width?: number; - /** - * Defaults to the image's height. - */ - height?: number; - /** - * The desired quality of the resize image. Possible values are good, better or - * best. The default is best. These values express a desired quality/speed - * tradeoff. They are translated into an algorithm-specific method that depends on - * the capabilities (CPU, GPU) of the underlying platform. It is possible for all - * three methods to be mapped to the same algorithm on a given platform. - */ - quality?: string; - } - - interface ResourceUsage { - images: MemoryUsageDetails; - scripts: MemoryUsageDetails; - cssStyleSheets: MemoryUsageDetails; - xslStyleSheets: MemoryUsageDetails; - fonts: MemoryUsageDetails; - other: MemoryUsageDetails; - } - - interface Response { - cancel?: boolean; - /** - * The original request is prevented from being sent or completed and is instead - * redirected to the given URL. - */ - redirectURL?: string; - } - - interface Result { - requestId: number; - /** - * Position of the active match. - */ - activeMatchOrdinal: number; - /** - * Number of Matches. - */ - matches: number; - /** - * Coordinates of first match region. - */ - selectionArea: SelectionArea; - finalUpdate: boolean; - } - - interface SaveDialogOptions { - title?: string; - /** - * Absolute directory path, absolute file path, or file name to use by default. - */ - defaultPath?: string; - /** - * Custom label for the confirmation button, when left empty the default label will - * be used. - */ - buttonLabel?: string; - filters?: FileFilter[]; - /** - * Message to display above text fields. - */ - message?: string; - /** - * Custom label for the text displayed in front of the filename text field. - */ - nameFieldLabel?: string; - /** - * Show the tags input box, defaults to true. - */ - showsTagField?: boolean; - /** - * Create a when packaged for the Mac App Store. If this option is enabled and the - * file doesn't already exist a blank file will be created at the chosen path. - */ - securityScopedBookmarks?: boolean; - } - - interface SaveDialogReturnValue { - /** - * whether or not the dialog was canceled. - */ - canceled: boolean; - /** - * If the dialog is canceled this will be undefined. - */ - filePath?: string; - /** - * Base64 encoded string which contains the security scoped bookmark data for the - * saved file. securityScopedBookmarks must be enabled for this to be present. - */ - bookmark?: string; - } - - interface SaveDialogSyncOptions { - title?: string; - /** - * Absolute directory path, absolute file path, or file name to use by default. - */ - defaultPath?: string; - /** - * Custom label for the confirmation button, when left empty the default label will - * be used. - */ - buttonLabel?: string; - filters?: FileFilter[]; - /** - * Message to display above text fields. - */ - message?: string; - /** - * Custom label for the text displayed in front of the filename text field. - */ - nameFieldLabel?: string; - /** - * Show the tags input box, defaults to true. - */ - showsTagField?: boolean; - /** - * Create a when packaged for the Mac App Store. If this option is enabled and the - * file doesn't already exist a blank file will be created at the chosen path. - */ - securityScopedBookmarks?: boolean; - } - - interface Settings { - /** - * true to open the app at login, false to remove the app as a login item. Defaults - * to false. - */ - openAtLogin?: boolean; - /** - * true to open the app as hidden. Defaults to false. The user can edit this - * setting from the System Preferences so - * app.getLoginItemSettings().wasOpenedAsHidden should be checked when the app is - * opened to know the current value. This setting is not available on . - */ - openAsHidden?: boolean; - /** - * The executable to launch at login. Defaults to process.execPath. - */ - path?: string; - /** - * The command-line arguments to pass to the executable. Defaults to an empty - * array. Take care to wrap paths in quotes. - */ - args?: string[]; - } - - interface SourcesOptions { - /** - * An array of Strings that lists the types of desktop sources to be captured, - * available types are screen and window. - */ - types: string[]; - /** - * The size that the media source thumbnail should be scaled to. Default is 150 x - * 150. Set width or height to 0 when you do not need the thumbnails. This will - * save the processing time required for capturing the content of each window and - * screen. - */ - thumbnailSize?: Size; - /** - * Set to true to enable fetching window icons. The default value is false. When - * false the appIcon property of the sources return null. Same if a source has the - * type screen. - */ - fetchWindowIcons?: boolean; - } - - interface SystemMemoryInfo { - /** - * The total amount of physical memory in Kilobytes available to the system. - */ - total: number; - /** - * The total amount of memory not being used by applications or disk cache. - */ - free: number; - /** - * The total amount of swap memory in Kilobytes available to the system. - */ - swapTotal: number; - /** - * The free amount of swap memory in Kilobytes available to the system. - */ - swapFree: number; - } - - interface ToBitmapOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface ToDataURLOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface ToPNGOptions { - /** - * Defaults to 1.0. - */ - scaleFactor?: number; - } - - interface TouchBarButtonConstructorOptions { - /** - * Button text. - */ - label?: string; - /** - * Button background color in hex format, i.e #ABCDEF. - */ - backgroundColor?: string; - /** - * Button icon. - */ - icon?: NativeImage; - /** - * Can be left, right or overlay. - */ - iconPosition?: ('left' | 'right' | 'overlay'); - /** - * Function to call when the button is clicked. - */ - click?: () => void; - } - - interface TouchBarColorPickerConstructorOptions { - /** - * Array of hex color strings to appear as possible colors to select. - */ - availableColors?: string[]; - /** - * The selected hex color in the picker, i.e #ABCDEF. - */ - selectedColor?: string; - /** - * Function to call when a color is selected. - */ - change?: (color: string) => void; - } - - interface TouchBarConstructorOptions { - items: Array<(TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer)>; - escapeItem?: (TouchBarButton) | (TouchBarColorPicker) | (TouchBarGroup) | (TouchBarLabel) | (TouchBarPopover) | (TouchBarScrubber) | (TouchBarSegmentedControl) | (TouchBarSlider) | (TouchBarSpacer) | (null); - } - - interface TouchBarGroupConstructorOptions { - /** - * Items to display as a group. - */ - items: TouchBar; - } - - interface TouchBarLabelConstructorOptions { - /** - * Text to display. - */ - label?: string; - /** - * Hex color of text, i.e #ABCDEF. - */ - textColor?: string; - } - - interface TouchBarPopoverConstructorOptions { - /** - * Popover button text. - */ - label?: string; - /** - * Popover button icon. - */ - icon?: NativeImage; - /** - * Items to display in the popover. - */ - items?: TouchBar; - /** - * true to display a close button on the left of the popover, false to not show it. - * Default is true. - */ - showCloseButton?: boolean; - } - - interface TouchBarScrubberConstructorOptions { - /** - * An array of items to place in this scrubber. - */ - items: ScrubberItem[]; - /** - * Called when the user taps an item that was not the last tapped item. - */ - select: (selectedIndex: number) => void; - /** - * Called when the user taps any item. - */ - highlight: (highlightedIndex: number) => void; - /** - * Selected item style. Defaults to null. - */ - selectedStyle: string; - /** - * Selected overlay item style. Defaults to null. - */ - overlayStyle: string; - /** - * Defaults to false. - */ - showArrowButtons: boolean; - /** - * Defaults to free. - */ - mode: string; - /** - * Defaults to true. - */ - continuous: boolean; - } - - interface TouchBarSegmentedControlConstructorOptions { - /** - * Style of the segments: - */ - segmentStyle?: ('automatic' | 'rounded' | 'textured-rounded' | 'round-rect' | 'textured-square' | 'capsule' | 'small-square' | 'separated'); - /** - * The selection mode of the control: - */ - mode?: ('single' | 'multiple' | 'buttons'); - /** - * An array of segments to place in this control. - */ - segments: SegmentedControlSegment[]; - /** - * The index of the currently selected segment, will update automatically with user - * interaction. When the mode is multiple it will be the last selected item. - */ - selectedIndex?: number; - /** - * Called when the user selects a new segment. - */ - change: (selectedIndex: number, isSelected: boolean) => void; - } - - interface TouchBarSliderConstructorOptions { - /** - * Label text. - */ - label?: string; - /** - * Selected value. - */ - value?: number; - /** - * Minimum value. - */ - minValue?: number; - /** - * Maximum value. - */ - maxValue?: number; - /** - * Function to call when the slider is changed. - */ - change?: (newValue: number) => void; - } - - interface TouchBarSpacerConstructorOptions { - /** - * Size of spacer, possible values are: - */ - size?: ('small' | 'large' | 'flexible'); - } - - interface UpdateTargetUrlEvent extends Event { - url: string; - } - - interface UploadProgress { - /** - * Whether the request is currently active. If this is false no other properties - * will be set - */ - active: boolean; - /** - * Whether the upload has started. If this is false both current and total will be - * set to 0. - */ - started: boolean; - /** - * The number of bytes that have been uploaded so far - */ - current: number; - /** - * The number of bytes that will be uploaded this request - */ - total: number; - } - - interface Versions { - /** - * A String representing Chrome's version string. - */ - chrome?: string; - /** - * A String representing Electron's version string. - */ - electron?: string; - } - - interface VisibleOnAllWorkspacesOptions { - /** - * Sets whether the window should be visible above fullscreen windows - */ - visibleOnFullScreen?: boolean; - } - - interface WillNavigateEvent extends Event { - url: string; - } - - interface EditFlags { - /** - * Whether the renderer believes it can undo. - */ - canUndo: boolean; - /** - * Whether the renderer believes it can redo. - */ - canRedo: boolean; - /** - * Whether the renderer believes it can cut. - */ - canCut: boolean; - /** - * Whether the renderer believes it can copy - */ - canCopy: boolean; - /** - * Whether the renderer believes it can paste. - */ - canPaste: boolean; - /** - * Whether the renderer believes it can delete. - */ - canDelete: boolean; - /** - * Whether the renderer believes it can select all. - */ - canSelectAll: boolean; - } - - interface Extra { - } - - interface FoundInPageResult { - requestId: number; - /** - * Position of the active match. - */ - activeMatchOrdinal: number; - /** - * Number of Matches. - */ - matches: number; - /** - * Coordinates of first match region. - */ - selectionArea: SelectionArea; - finalUpdate: boolean; - } - - interface MediaFlags { - /** - * Whether the media element has crashed. - */ - inError: boolean; - /** - * Whether the media element is paused. - */ - isPaused: boolean; - /** - * Whether the media element is muted. - */ - isMuted: boolean; - /** - * Whether the media element has audio. - */ - hasAudio: boolean; - /** - * Whether the media element is looping. - */ - isLooping: boolean; - /** - * Whether the media element's controls are visible. - */ - isControlsVisible: boolean; - /** - * Whether the media element's controls are toggleable. - */ - canToggleControls: boolean; - /** - * Whether the media element can be rotated. - */ - canRotate: boolean; - } - - interface Options { - } - - interface Query { - } - - interface RequestHeaders { - } - - interface ResponseHeaders { - } - - interface SelectionArea { - } - - interface WebPreferences { - /** - * Whether to enable DevTools. If it is set to false, can not use - * BrowserWindow.webContents.openDevTools() to open DevTools. Default is true. - */ - devTools?: boolean; - /** - * Whether node integration is enabled. Default is false. - */ - nodeIntegration?: boolean; - /** - * Whether node integration is enabled in web workers. Default is false. More about - * this can be found in . - */ - nodeIntegrationInWorker?: boolean; - /** - * Experimental option for enabling Node.js support in sub-frames such as iframes - * and child windows. All your preloads will load for every iframe, you can use - * process.isMainFrame to determine if you are in the main frame or not. - */ - nodeIntegrationInSubFrames?: boolean; - /** - * Specifies a script that will be loaded before other scripts run in the page. - * This script will always have access to node APIs no matter whether node - * integration is turned on or off. The value should be the absolute file path to - * the script. When node integration is turned off, the preload script can - * reintroduce Node global symbols back to the global scope. See example . - */ - preload?: string; - /** - * If set, this will sandbox the renderer associated with the window, making it - * compatible with the Chromium OS-level sandbox and disabling the Node.js engine. - * This is not the same as the nodeIntegration option and the APIs available to the - * preload script are more limited. Read more about the option . This option is - * currently experimental and may change or be removed in future Electron releases. - */ - sandbox?: boolean; - /** - * Whether to enable the module. Default is true. - */ - enableRemoteModule?: boolean; - /** - * Sets the session used by the page. Instead of passing the Session object - * directly, you can also choose to use the partition option instead, which accepts - * a partition string. When both session and partition are provided, session will - * be preferred. Default is the default session. - */ - session?: Session; - /** - * Sets the session used by the page according to the session's partition string. - * If partition starts with persist:, the page will use a persistent session - * available to all pages in the app with the same partition. If there is no - * persist: prefix, the page will use an in-memory session. By assigning the same - * partition, multiple pages can share the same session. Default is the default - * session. - */ - partition?: string; - /** - * When specified, web pages with the same affinity will run in the same renderer - * process. Note that due to reusing the renderer process, certain webPreferences - * options will also be shared between the web pages even when you specified - * different values for them, including but not limited to preload, sandbox and - * nodeIntegration. So it is suggested to use exact same webPreferences for web - * pages with the same affinity. - */ - affinity?: string; - /** - * The default zoom factor of the page, 3.0 represents 300%. Default is 1.0. - */ - zoomFactor?: number; - /** - * Enables JavaScript support. Default is true. - */ - javascript?: boolean; - /** - * When false, it will disable the same-origin policy (usually using testing - * websites by people), and set allowRunningInsecureContent to true if this options - * has not been set by user. Default is true. - */ - // webSecurity?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Allow an https page to run JavaScript, CSS or plugins from http URLs. Default is - * false. - */ - // allowRunningInsecureContent?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Enables image support. Default is true. - */ - images?: boolean; - /** - * Make TextArea elements resizable. Default is true. - */ - textAreasAreResizable?: boolean; - /** - * Enables WebGL support. Default is true. - */ - webgl?: boolean; - /** - * Whether plugins should be enabled. Default is false. - */ - plugins?: boolean; - /** - * Enables Chromium's experimental features. Default is false. - */ - // experimentalFeatures?: boolean; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * Enables scroll bounce (rubber banding) effect on macOS. Default is false. - */ - scrollBounce?: boolean; - /** - * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to - * enable. The full list of supported feature strings can be found in the file. - */ - enableBlinkFeatures?: string; - /** - * A list of feature strings separated by ,, like CSSVariables,KeyboardEventKey to - * disable. The full list of supported feature strings can be found in the file. - */ - disableBlinkFeatures?: string; - /** - * Sets the default font for the font-family. - */ - defaultFontFamily?: DefaultFontFamily; - /** - * Defaults to 16. - */ - defaultFontSize?: number; - /** - * Defaults to 13. - */ - defaultMonospaceFontSize?: number; - /** - * Defaults to 0. - */ - minimumFontSize?: number; - /** - * Defaults to ISO-8859-1. - */ - defaultEncoding?: string; - /** - * Whether to throttle animations and timers when the page becomes background. This - * also affects the . Defaults to true. - */ - backgroundThrottling?: boolean; - /** - * Whether to enable offscreen rendering for the browser window. Defaults to false. - * See the for more details. - */ - offscreen?: boolean; - /** - * Whether to run Electron APIs and the specified preload script in a separate - * JavaScript context. Defaults to false. The context that the preload script runs - * in will still have full access to the document and window globals but it will - * use its own set of JavaScript builtins (Array, Object, JSON, etc.) and will be - * isolated from any changes made to the global environment by the loaded page. The - * Electron API will only be available in the preload script and not the loaded - * page. This option should be used when loading potentially untrusted remote - * content to ensure the loaded content cannot tamper with the preload script and - * any Electron APIs being used. This option uses the same technique used by . You - * can access this context in the dev tools by selecting the 'Electron Isolated - * Context' entry in the combo box at the top of the Console tab. - */ - contextIsolation?: boolean; - /** - * Whether to use native window.open(). Defaults to false. Child windows will - * always have node integration disabled unless nodeIntegrationInSubFrames is true. - * This option is currently experimental. - */ - nativeWindowOpen?: boolean; - /** - * Whether to enable the . Defaults to false. The preload script configured for the - * will have node integration enabled when it is executed so you should ensure - * remote/untrusted content is not able to create a tag with a possibly malicious - * preload script. You can use the will-attach-webview event on to strip away the - * preload script and to validate or alter the 's initial settings. - */ - webviewTag?: boolean; - /** - * A list of strings that will be appended to process.argv in the renderer process - * of this app. Useful for passing small bits of data down to renderer process - * preload scripts. - */ - additionalArguments?: string[]; - /** - * Whether to enable browser style consecutive dialog protection. Default is false. - */ - safeDialogs?: boolean; - /** - * The message to display when consecutive dialog protection is triggered. If not - * defined the default message would be used, note that currently the default - * message is in English and not localized. - */ - safeDialogsMessage?: string; - /** - * Whether dragging and dropping a file or link onto the page causes a navigation. - * Default is false. - */ - navigateOnDragDrop?: boolean; - /** - * Autoplay policy to apply to content in the window, can be - * no-user-gesture-required, user-gesture-required, - * document-user-activation-required. Defaults to no-user-gesture-required. - */ - autoplayPolicy?: ('no-user-gesture-required' | 'user-gesture-required' | 'document-user-activation-required'); - /** - * Whether to prevent the window from resizing when entering HTML Fullscreen. - * Default is false. - */ - disableHtmlFullscreenWindowResize?: boolean; - } - - interface DefaultFontFamily { - /** - * Defaults to Times New Roman. - */ - standard?: string; - /** - * Defaults to Times New Roman. - */ - serif?: string; - /** - * Defaults to Arial. - */ - sansSerif?: string; - /** - * Defaults to Courier New. - */ - monospace?: string; - /** - * Defaults to Script. - */ - cursive?: string; - /** - * Defaults to Impact. - */ - fantasy?: string; - } - -} - -declare module 'electron' { - export = Electron; -} - -interface NodeRequireFunction { - (moduleName: 'electron'): typeof Electron; -} - -interface File { - /** - * The real path to the file on the users filesystem - */ - path: string; -} - -declare module 'original-fs' { - import * as fs from 'fs'; - export = fs; -} - -interface Document { - createElement(tagName: 'webview'): Electron.WebviewTag; -} - -declare namespace NodeJS { - interface Process extends EventEmitter { - - // Docs: http://electronjs.org/docs/api/process - - // ### BEGIN VSCODE MODIFICATION ### - // /** - // * Emitted when Electron has loaded its internal initialization script and is - // * beginning to load the web page or the main script. It can be used by the preload - // * script to add removed Node global symbols back to the global scope when node - // * integration is turned off: - // */ - // on(event: 'loaded', listener: Function): this; - // once(event: 'loaded', listener: Function): this; - // addListener(event: 'loaded', listener: Function): this; - // removeListener(event: 'loaded', listener: Function): this; - // ### END VSCODE MODIFICATION ### - /** - * Causes the main thread of the current process crash. - */ - crash(): void; - getCPUUsage(): Electron.CPUUsage; - /** - * Indicates the creation time of the application. The time is represented as - * number of milliseconds since epoch. It returns null if it is unable to get the - * process creation time. - */ - getCreationTime(): (number) | (null); - /** - * Returns an object with V8 heap statistics. Note that all statistics are reported - * in Kilobytes. - */ - getHeapStatistics(): Electron.HeapStatistics; - getIOCounters(): Electron.IOCounters; - /** - * Returns an object giving memory usage statistics about the current process. Note - * that all statistics are reported in Kilobytes. This api should be called after - * app ready. Chromium does not provide residentSet value for macOS. This is - * because macOS performs in-memory compression of pages that haven't been recently - * used. As a result the resident set size value is not what one would expect. - * private memory is more representative of the actual pre-compression memory usage - * of the process on macOS. - */ - getProcessMemoryInfo(): Promise; - /** - * Returns an object giving memory usage statistics about the entire system. Note - * that all statistics are reported in Kilobytes. - */ - getSystemMemoryInfo(): Electron.SystemMemoryInfo; - /** - * Examples: Note: It returns the actual operating system version instead of kernel - * version on macOS unlike os.release(). - */ - getSystemVersion(): string; - /** - * Causes the main thread of the current process hang. - */ - hang(): void; - /** - * Sets the file descriptor soft limit to maxDescriptors or the OS hard limit, - * whichever is lower for the current process. - */ - setFdLimit(maxDescriptors: number): void; - /** - * Takes a V8 heap snapshot and saves it to filePath. - */ - takeHeapSnapshot(filePath: string): boolean; - /** - * A Boolean. When app is started by being passed as parameter to the default app, - * this property is true in the main process, otherwise it is undefined. - */ - defaultApp?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings are printed to - * stderr when formerly callback-based APIs converted to Promises are invoked using - * callbacks. Setting this to true will enable deprecation warnings. - */ - enablePromiseAPIs?: boolean; - /** - * A Boolean, true when the current renderer context is the "main" renderer frame. - * If you want the ID of the current frame you should use webFrame.routingId. - */ - isMainFrame?: boolean; - /** - * A Boolean. For Mac App Store build, this property is true, for other builds it - * is undefined. - */ - mas?: boolean; - /** - * A Boolean that controls ASAR support inside your application. Setting this to - * true will disable the support for asar archives in Node's built-in modules. - */ - noAsar?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings are printed to - * stderr. Setting this to true will silence deprecation warnings. This property is - * used instead of the --no-deprecation command line flag. - */ - noDeprecation?: boolean; - /** - * A String representing the path to the resources directory. - */ - resourcesPath?: string; - /** - * A Boolean. When the renderer process is sandboxed, this property is true, - * otherwise it is undefined. - */ - sandboxed?: boolean; - /** - * A Boolean that controls whether or not deprecation warnings will be thrown as - * exceptions. Setting this to true will throw errors for deprecations. This - * property is used instead of the --throw-deprecation command line flag. - */ - throwDeprecation?: boolean; - /** - * A Boolean that controls whether or not deprecations printed to stderr include - * their stack trace. Setting this to true will print stack traces for - * deprecations. This property is instead of the --trace-deprecation command line - * flag. - */ - traceDeprecation?: boolean; - /** - * A Boolean that controls whether or not process warnings printed to stderr - * include their stack trace. Setting this to true will print stack traces for - * process warnings (including deprecations). This property is instead of the - * --trace-warnings command line flag. - */ - traceProcessWarnings?: boolean; - /** - * A String representing the current process's type, can be "browser" (i.e. main - * process), "renderer", or "worker" (i.e. web worker). - */ - type?: string; - /** - * A Boolean. If the app is running as a Windows Store app (appx), this property is - * true, for otherwise it is undefined. - */ - windowsStore?: boolean; - } - interface ProcessVersions { - electron: string; - chrome: string; - } -} diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 95ead7a29b..2fb2268d32 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -15,6 +15,7 @@ import { cloneAndChange } from 'vs/base/common/objects'; import { escape } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; +import { renderCodicons, markdownEscapeEscapedCodicons } from 'vs/base/common/codicons'; export interface MarkdownRenderOptions extends FormattedTextRenderOptions { codeBlockRenderer?: (modeId: string, value: string) => Promise; @@ -118,7 +119,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende } }; renderer.paragraph = (text): string => { - return `

${text}

`; + return `

${markdown.supportThemeIcons ? renderCodicons(text) : text}

`; }; if (options.codeBlockRenderer) { @@ -192,7 +193,13 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende allowedSchemes.push(Schemas.command); } - const renderedMarkdown = marked.parse(markdown.value, markedOptions); + const renderedMarkdown = marked.parse( + markdown.supportThemeIcons + ? markdownEscapeEscapedCodicons(markdown.value) + : markdown.value, + markedOptions + ); + element.innerHTML = insane(renderedMarkdown, { allowedSchemes, allowedAttributes: { diff --git a/src/vs/base/browser/ui/checkbox/check-dark.svg b/src/vs/base/browser/ui/checkbox/check-dark.svg deleted file mode 100644 index 865cc83c34..0000000000 --- a/src/vs/base/browser/ui/checkbox/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/check-light.svg b/src/vs/base/browser/ui/checkbox/check-light.svg deleted file mode 100644 index e1a546660e..0000000000 --- a/src/vs/base/browser/ui/checkbox/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/base/browser/ui/checkbox/checkbox.css b/src/vs/base/browser/ui/checkbox/checkbox.css index 39b792326d..1b293fcf49 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.css +++ b/src/vs/base/browser/ui/checkbox/checkbox.css @@ -44,10 +44,7 @@ background-size: 16px !important; } -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-light.svg') center center no-repeat; -} - -.monaco-custom-checkbox.monaco-simple-checkbox.checked { - background: url('check-dark.svg') center center no-repeat; +/* hide check when unchecked */ +.monaco-custom-checkbox.monaco-simple-checkbox.unchecked:not(.checked)::before { + visibility: hidden;; } diff --git a/src/vs/base/browser/ui/checkbox/checkbox.ts b/src/vs/base/browser/ui/checkbox/checkbox.ts index f91c4fe1cb..af8b05d09d 100644 --- a/src/vs/base/browser/ui/checkbox/checkbox.ts +++ b/src/vs/base/browser/ui/checkbox/checkbox.ts @@ -192,7 +192,7 @@ export class SimpleCheckbox extends Widget { constructor(private title: string, private isChecked: boolean) { super(); - this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, actionClassName: 'monaco-simple-checkbox' }); + this.checkbox = new Checkbox({ title: this.title, isChecked: this.isChecked, actionClassName: 'monaco-simple-checkbox codicon-check' }); this.domNode = this.checkbox.domNode; diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css index a72a2128ef..c3efcaad23 100644 --- a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -395,9 +395,9 @@ .codicon-debug-breakpoint-function-unverified:before { content: "\eb87" } .codicon-debug-breakpoint-function:before { content: "\eb88" } .codicon-debug-breakpoint-function-disabled:before { content: "\eb88" } -.codicon-debug-breakpoint-stackframe-active:before { content: "\eb89" } -.codicon-debug-breakpoint-stackframe:before { content: "\eb8b" } -.codicon-debug-breakpoint-stackframe-focused:before { content: "\eb8b" } +.codicon-debug-stackframe-active:before { content: "\eb89" } +.codicon-debug-stackframe:before { content: "\eb8b" } +.codicon-debug-stackframe-focused:before { content: "\eb8b" } .codicon-debug-breakpoint-unsupported:before { content: "\eb8c" } .codicon-symbol-string:before { content: "\eb8d" } .codicon-debug-reverse-continue:before { content: "\eb8e" } diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts index 1ced08b3df..1b27d9aff3 100644 --- a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts +++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts @@ -6,16 +6,7 @@ import 'vs/css!./codicon/codicon'; import 'vs/css!./codicon/codicon-animations'; import { escape } from 'vs/base/common/strings'; - -function expand(text: string): string { - return text.replace(/\$\((([a-z0-9\-]+?)(~([a-z0-9\-]*?))?)\)/gi, (_match, _g1, name, _g3, animation) => { - return ``; - }); -} - -export function renderCodicons(label: string): string { - return expand(escape(label)); -} +import { renderCodicons } from 'vs/base/common/codicons'; export class CodiconLabel { @@ -24,7 +15,7 @@ export class CodiconLabel { ) { } set text(text: string) { - this._container.innerHTML = renderCodicons(text || ''); + this._container.innerHTML = renderCodicons(escape(text ?? '')); } set title(title: string) { diff --git a/src/vs/base/browser/ui/contextview/contextview.css b/src/vs/base/browser/ui/contextview/contextview.css index 4e60a74b52..3cba707513 100644 --- a/src/vs/base/browser/ui/contextview/contextview.css +++ b/src/vs/base/browser/ui/contextview/contextview.css @@ -5,5 +5,5 @@ .context-view { position: absolute; - z-index: 2000; -} \ No newline at end of file + z-index: 2500; +} diff --git a/src/vs/base/browser/ui/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 3620363cdd..28d1f74151 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -28,6 +28,7 @@ max-width: 90%; min-height: 75px; padding: 10px; + transform: translate3d(0px, 0px, 0px); } /** Dialog: Title Actions Row */ diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 55cdb2a3fa..f3ad63520f 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -46,6 +46,7 @@ interface ButtonMapEntry { export class Dialog extends Disposable { private element: HTMLElement | undefined; + private shadowElement: HTMLElement | undefined; private modal: HTMLElement | undefined; private buttonsContainer: HTMLElement | undefined; private messageDetailElement: HTMLElement | undefined; @@ -61,7 +62,8 @@ export class Dialog extends Disposable { constructor(private container: HTMLElement, private message: string, buttons: string[], private options: IDialogOptions) { super(); this.modal = this.container.appendChild($(`.dialog-modal-block${options.type === 'pending' ? '.dimmed' : ''}`)); - this.element = this.modal.appendChild($('.dialog-box')); + this.shadowElement = this.modal.appendChild($('.dialog-shadow')); + this.element = this.shadowElement.appendChild($('.dialog-box')); hide(this.element); // If no button is provided, default to OK @@ -249,10 +251,13 @@ export class Dialog extends Disposable { const shadowColor = style.dialogShadow ? `0 0px 8px ${style.dialogShadow}` : ''; const border = style.dialogBorder ? `1px solid ${style.dialogBorder}` : ''; + if (this.shadowElement) { + this.shadowElement.style.boxShadow = shadowColor; + } + if (this.element) { this.element.style.color = fgColor; this.element.style.backgroundColor = bgColor; - this.element.style.boxShadow = shadowColor; this.element.style.border = border; if (this.buttonGroup) { diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index c604bd4feb..7d037e250a 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import { renderCodicons } from 'vs/base/common/codicons'; import { escape } from 'vs/base/common/strings'; export interface IHighlight { @@ -65,13 +65,13 @@ export class HighlightedLabel { if (pos < highlight.start) { htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent += this.supportCodicons ? renderCodicons(substring) : escape(substring); + htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); htmlContent += ''; pos = highlight.end; } htmlContent += ''; const substring = this.text.substring(highlight.start, highlight.end); - htmlContent += this.supportCodicons ? renderCodicons(substring) : escape(substring); + htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); htmlContent += ''; pos = highlight.end; } @@ -79,7 +79,7 @@ export class HighlightedLabel { if (pos < this.text.length) { htmlContent += ''; const substring = this.text.substring(pos); - htmlContent += this.supportCodicons ? renderCodicons(substring) : escape(substring); + htmlContent += this.supportCodicons ? renderCodicons(escape(substring)) : escape(substring); htmlContent += ''; } diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index 8d08e4ae84..4838359bcf 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -593,7 +593,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { const isSelected = this.element && hasClass(this.element, 'focused'); const fgColor = isSelected && this.menuStyle.selectionForegroundColor ? this.menuStyle.selectionForegroundColor : this.menuStyle.foregroundColor; - const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : this.menuStyle.backgroundColor; + const bgColor = isSelected && this.menuStyle.selectionBackgroundColor ? this.menuStyle.selectionBackgroundColor : undefined; const border = isSelected && this.menuStyle.selectionBorderColor ? `thin solid ${this.menuStyle.selectionBorderColor}` : ''; if (this.item) { diff --git a/src/vs/base/browser/ui/tree/abstractTree.ts b/src/vs/base/browser/ui/tree/abstractTree.ts index 9a058a8643..aa15045062 100644 --- a/src/vs/base/browser/ui/tree/abstractTree.ts +++ b/src/vs/base/browser/ui/tree/abstractTree.ts @@ -1298,7 +1298,7 @@ export abstract class AbstractTree implements IDisposable onDidModelSplice(() => null, null, this.disposables); // Active nodes can change when the model changes or when focus or selection change. - // We debouce it with 0 delay since these events may fire in the same stack and we only + // We debounce it with 0 delay since these events may fire in the same stack and we only // want to run this once. It also doesn't matter if it runs on the next tick since it's only // a nice to have UI feature. onDidChangeActiveNodes.input = Event.chain(Event.any(onDidModelSplice, this.focus.onDidChange, this.selection.onDidChange)) diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts new file mode 100644 index 0000000000..fe44e44800 --- /dev/null +++ b/src/vs/base/common/codicons.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const escapeCodiconsRegex = /(? `\\${match}`); +} + +const markdownEscapedCodiconsRegex = /\\\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi; +export function markdownEscapeEscapedCodicons(text: string): string { + // Need to add an extra \ for escaping in markdown + return text.replace(markdownEscapedCodiconsRegex, match => `\\${match}`); +} + +const markdownUnescapeCodiconsRegex = /(? `$(${codicon})`); +} + +const renderCodiconsRegex = /(\\)?\$\((([a-z0-9\-]+?)(?:~([a-z0-9\-]*?))?)\)/gi; +export function renderCodicons(text: string): string { + return text.replace(renderCodiconsRegex, (_, escape, codicon, name, animation) => { + return escape + ? `$(${codicon})` + : ``; + }); +} diff --git a/src/vs/base/common/decorators.ts b/src/vs/base/common/decorators.ts index 62ffd87486..8c513d2d48 100644 --- a/src/vs/base/common/decorators.ts +++ b/src/vs/base/common/decorators.ts @@ -84,11 +84,11 @@ export function memoize(target: any, key: string, descriptor: any) { return createMemoizer()(target, key, descriptor); } -export interface IDebouceReducer { +export interface IDebounceReducer { (previousValue: T, ...args: any[]): T; } -export function debounce(delay: number, reducer?: IDebouceReducer, initialValueProvider?: () => T): Function { +export function debounce(delay: number, reducer?: IDebounceReducer, initialValueProvider?: () => T): Function { return createDecorator((fn, key) => { const timerKey = `$debounce$${key}`; const resultKey = `$debounce$result$${key}`; @@ -112,3 +112,44 @@ export function debounce(delay: number, reducer?: IDebouceReducer, initial }; }); } + +export function throttle(delay: number, reducer?: IDebounceReducer, initialValueProvider?: () => T): Function { + return createDecorator((fn, key) => { + const timerKey = `$throttle$timer$${key}`; + const resultKey = `$throttle$result$${key}`; + const lastRunKey = `$throttle$lastRun$${key}`; + const pendingKey = `$throttle$pending$${key}`; + + return function (this: any, ...args: any[]) { + if (!this[resultKey]) { + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + } + if (this[lastRunKey] === null || this[lastRunKey] === undefined) { + this[lastRunKey] = -Number.MAX_VALUE; + } + + if (reducer) { + this[resultKey] = reducer(this[resultKey], ...args); + } + + if (this[pendingKey]) { + return; + } + + const nextTime = this[lastRunKey] + delay; + if (nextTime <= Date.now()) { + this[lastRunKey] = Date.now(); + fn.apply(this, [this[resultKey]]); + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + } else { + this[pendingKey] = true; + this[timerKey] = setTimeout(() => { + this[pendingKey] = false; + this[lastRunKey] = Date.now(); + fn.apply(this, [this[resultKey]]); + this[resultKey] = initialValueProvider ? initialValueProvider() : undefined; + }, nextTime - Date.now()); + } + }; + }); +} diff --git a/src/vs/base/common/errorMessage.ts b/src/vs/base/common/errorMessage.ts index 7019fee66b..6ef618071a 100644 --- a/src/vs/base/common/errorMessage.ts +++ b/src/vs/base/common/errorMessage.ts @@ -8,15 +8,11 @@ import * as types from 'vs/base/common/types'; import * as arrays from 'vs/base/common/arrays'; function exceptionToErrorMessage(exception: any, verbose: boolean): string { - if (exception.message) { - if (verbose && (exception.stack || exception.stacktrace)) { - return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace)); - } - - return detectSystemErrorMessage(exception); + if (verbose && (exception.stack || exception.stacktrace)) { + return nls.localize('stackTrace.format', "{0}: {1}", detectSystemErrorMessage(exception), stackToString(exception.stack) || stackToString(exception.stacktrace)); } - return nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); + return detectSystemErrorMessage(exception); } function stackToString(stack: string[] | string | undefined): string | undefined { @@ -34,7 +30,7 @@ function detectSystemErrorMessage(exception: any): string { return nls.localize('nodeExceptionMessage', "A system error occurred ({0})", exception.message); } - return exception.message; + return exception.message || nls.localize('error.defaultMessage', "An unknown error occurred. Please consult the log for more details."); } /** diff --git a/src/vs/base/common/errors.ts b/src/vs/base/common/errors.ts index 9f04cd4c90..792526cdec 100644 --- a/src/vs/base/common/errors.ts +++ b/src/vs/base/common/errors.ts @@ -31,7 +31,7 @@ export class ErrorHandler { }; } - public addListener(listener: ErrorListenerCallback): ErrorListenerUnbind { + addListener(listener: ErrorListenerCallback): ErrorListenerUnbind { this.listeners.push(listener); return () => { @@ -49,21 +49,21 @@ export class ErrorHandler { this.listeners.splice(this.listeners.indexOf(listener), 1); } - public setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { + setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { this.unexpectedErrorHandler = newUnexpectedErrorHandler; } - public getUnexpectedErrorHandler(): (e: any) => void { + getUnexpectedErrorHandler(): (e: any) => void { return this.unexpectedErrorHandler; } - public onUnexpectedError(e: any): void { + onUnexpectedError(e: any): void { this.unexpectedErrorHandler(e); this.emit(e); } // For external errors, we don't want the listeners to be called - public onUnexpectedExternalError(e: any): void { + onUnexpectedExternalError(e: any): void { this.unexpectedErrorHandler(e); } } diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 655ae4a6f8..a1727ebe3b 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -5,37 +5,51 @@ import { equals } from 'vs/base/common/arrays'; import { UriComponents } from 'vs/base/common/uri'; +import { escapeCodicons, markdownUnescapeCodicons } from 'vs/base/common/codicons'; export interface IMarkdownString { readonly value: string; readonly isTrusted?: boolean; + readonly supportThemeIcons?: boolean; uris?: { [href: string]: UriComponents }; } export class MarkdownString implements IMarkdownString { + private readonly _isTrusted: boolean; + private readonly _supportThemeIcons: boolean; - private _value: string; - private _isTrusted: boolean; + constructor( + private _value: string = '', + isTrustedOrOptions: boolean | { isTrusted?: boolean, supportThemeIcons?: boolean } = false, + ) { + if (typeof isTrustedOrOptions === 'boolean') { + this._isTrusted = isTrustedOrOptions; + this._supportThemeIcons = false; + } + else { + this._isTrusted = isTrustedOrOptions.isTrusted ?? false; + this._supportThemeIcons = isTrustedOrOptions.supportThemeIcons ?? false; + } - constructor(value: string = '', isTrusted = false) { - this._value = value; - this._isTrusted = isTrusted; } get value() { return this._value; } get isTrusted() { return this._isTrusted; } + get supportThemeIcons() { return this._supportThemeIcons; } appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this._value += value + value = value .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') .replace('\n', '\n\n'); + this._value += this.supportThemeIcons ? markdownUnescapeCodicons(value) : value; return this; } appendMarkdown(value: string): MarkdownString { this._value += value; + return this; } @@ -47,6 +61,10 @@ export class MarkdownString implements IMarkdownString { this._value += '\n```\n'; return this; } + + static escapeThemeIcons(value: string): string { + return escapeCodicons(value); + } } export function isEmptyMarkdownString(oneOrMany: IMarkdownString | IMarkdownString[] | null | undefined): boolean { @@ -64,7 +82,8 @@ export function isMarkdownString(thing: any): thing is IMarkdownString { return true; } else if (thing && typeof thing === 'object') { return typeof (thing).value === 'string' - && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined); + && (typeof (thing).isTrusted === 'boolean' || (thing).isTrusted === undefined) + && (typeof (thing).supportThemeIcons === 'boolean' || (thing).supportThemeIcons === undefined); } return false; } @@ -89,7 +108,7 @@ function markdownStringEqual(a: IMarkdownString, b: IMarkdownString): boolean { } else if (!a || !b) { return false; } else { - return a.value === b.value && a.isTrusted === b.isTrusted; + return a.value === b.value && a.isTrusted === b.isTrusted && a.supportThemeIcons === b.supportThemeIcons; } } diff --git a/src/vs/base/parts/ipc/node/ipc.cp.ts b/src/vs/base/parts/ipc/node/ipc.cp.ts index 1a031debb6..813fae737f 100644 --- a/src/vs/base/parts/ipc/node/ipc.cp.ts +++ b/src/vs/base/parts/ipc/node/ipc.cp.ts @@ -227,7 +227,7 @@ export class Client implements IChannelClient, IDisposable { this.child.on('error', err => console.warn('IPC "' + this.options.serverName + '" errored with ' + err)); this.child.on('exit', (code: any, signal: any) => { - process.removeListener('exit', onExit); + process.removeListener('exit' as 'loaded', onExit); // https://github.com/electron/electron/issues/21475 this.activeRequests.forEach(r => dispose(r)); this.activeRequests.clear(); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 8f48b0d690..7f42f3c185 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -6,42 +6,104 @@ import * as assert from 'assert'; import * as marked from 'vs/base/common/marked/marked'; import { renderMarkdown } from 'vs/base/browser/markdownRenderer'; +import { MarkdownString } from 'vs/base/common/htmlContent'; suite('MarkdownRenderer', () => { - test('image rendering conforms to default', () => { - const markdown = { value: `![image](someimageurl 'caption')` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); + suite('Images', () => { + + test('image rendering conforms to default', () => { + const markdown = { value: `![image](someimageurl 'caption')` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + sanitize: true, + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image rendering conforms to default without title', () => { + const markdown = { value: `![image](someimageurl)` }; + const result: HTMLElement = renderMarkdown(markdown); + const renderer = new marked.Renderer(); + const imageFromMarked = marked(markdown.value, { + sanitize: true, + renderer + }).trim(); + assert.strictEqual(result.innerHTML, imageFromMarked); + }); + + test('image width from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); + + test('image height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); + + test('image width and height from title params', () => { + let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); + assert.strictEqual(result.innerHTML, `

image

`); + }); + }); - test('image rendering conforms to default without title', () => { - const markdown = { value: `![image](someimageurl)` }; - const result: HTMLElement = renderMarkdown(markdown); - const renderer = new marked.Renderer(); - const imageFromMarked = marked(markdown.value, { - sanitize: true, - renderer - }).trim(); - assert.strictEqual(result.innerHTML, imageFromMarked); + suite('ThemeIcons Support On', () => { + + test('render appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText('$(zap) $(dont match me)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(dont match me)

`); + }); + + test('render appendText escaped', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText(MarkdownString.escapeThemeIcons('$(zap) $(dont match me)')); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(zap) $(dont match me)

`); + }); + + test('render appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('$(zap) $(dont match me)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(dont match me)

`); + }); + + test('render appendMarkdown escaped', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown(MarkdownString.escapeThemeIcons('$(zap) $(dont match me)')); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(zap) $(dont match me)

`); + }); + }); - test('image width from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); + suite('ThemeIcons Support Off', () => { + + test('render appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendText('$(zap) $(dont match me)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(zap) $(dont match me)

`); + }); + + test('render appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendMarkdown('$(zap) $(dont match me)'); + + let result: HTMLElement = renderMarkdown(mds); + assert.strictEqual(result.innerHTML, `

$(zap) $(dont match me)

`); + }); + }); - test('image height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); - }); - - test('image width and height from title params', () => { - let result: HTMLElement = renderMarkdown({ value: `![image](someimageurl|height=200,width=100 'caption')` }); - assert.strictEqual(result.innerHTML, `

image

`); - }); }); diff --git a/src/vs/base/test/common/decorators.test.ts b/src/vs/base/test/common/decorators.test.ts index 0c73d72b58..e262debcf6 100644 --- a/src/vs/base/test/common/decorators.test.ts +++ b/src/vs/base/test/common/decorators.test.ts @@ -3,8 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as sinon from 'sinon'; import * as assert from 'assert'; -import { memoize, createMemoizer } from 'vs/base/common/decorators'; +import { memoize, createMemoizer, throttle } from 'vs/base/common/decorators'; suite('Decorators', () => { test('memoize should memoize methods', () => { @@ -100,7 +101,9 @@ suite('Decorators', () => { test('memoized property should not be enumerable', () => { class Foo { @memoize - get answer() { return 42; } + get answer() { + return 42; + } } const foo = new Foo(); @@ -112,7 +115,9 @@ suite('Decorators', () => { test('memoized property should not be writable', () => { class Foo { @memoize - get answer() { return 42; } + get answer() { + return 42; + } } const foo = new Foo(); @@ -131,7 +136,9 @@ suite('Decorators', () => { let counter = 0; class Foo { @memoizer - get answer() { return ++counter; } + get answer() { + return ++counter; + } } const foo = new Foo(); @@ -145,4 +152,49 @@ suite('Decorators', () => { assert.equal(foo.answer, 3); assert.equal(foo.answer, 3); }); + + test('throttle', () => { + const spy = sinon.spy(); + const clock = sinon.useFakeTimers(); + try { + class ThrottleTest { + private _handle: Function; + + constructor(fn: Function) { + this._handle = fn; + } + + @throttle( + 100, + (a: number, b: number) => a + b, + () => 0 + ) + report(p: number): void { + this._handle(p); + } + } + + const t = new ThrottleTest(spy); + + t.report(1); + t.report(2); + t.report(3); + assert.deepEqual(spy.args, [[1]]); + + clock.tick(200); + assert.deepEqual(spy.args, [[1], [5]]); + spy.reset(); + + t.report(4); + t.report(5); + clock.tick(50); + t.report(6); + + assert.deepEqual(spy.args, [[4]]); + clock.tick(60); + assert.deepEqual(spy.args, [[4], [11]]); + } finally { + clock.restore(); + } + }); }); diff --git a/src/vs/base/test/common/errors.test.ts b/src/vs/base/test/common/errors.test.ts index a9eeeb13c8..512d86c19c 100644 --- a/src/vs/base/test/common/errors.test.ts +++ b/src/vs/base/test/common/errors.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -16,9 +17,17 @@ suite('Errors', () => { error.detail.exception = {}; error.detail.exception.message = 'Foo Bar'; assert.strictEqual(toErrorMessage(error), 'Foo Bar'); + assert.strictEqual(toErrorMessage(error, true), 'Foo Bar'); assert(toErrorMessage()); assert(toErrorMessage(null)); assert(toErrorMessage({})); + + try { + throw new Error(); + } catch (error) { + assert.strictEqual(toErrorMessage(error), 'An unknown error occurred. Please consult the log for more details.'); + assert.ok(toErrorMessage(error, true).length > 'An unknown error occurred. Please consult the log for more details.'.length); + } }); -}); \ No newline at end of file +}); diff --git a/src/vs/base/test/common/hash.test.ts b/src/vs/base/test/common/hash.test.ts index b028bb901e..4a1a6b46a5 100644 --- a/src/vs/base/test/common/hash.test.ts +++ b/src/vs/base/test/common/hash.test.ts @@ -43,4 +43,14 @@ suite('Hash', () => { assert.notEqual(hash({ 'foo': 'bar' }), hash({ 'foo': 'bar2' })); assert.notEqual(hash({}), hash([])); }); -}); \ No newline at end of file + + test('array - unexpected collision', function () { + this.skip(); + const a = hash([undefined, undefined, undefined, undefined, undefined]); + const b = hash([undefined, undefined, 'HHHHHH', [{ line: 0, character: 0 }, { line: 0, character: 0 }], undefined]); + // console.log(a); + // console.log(b); + assert.notEqual(a, b); + }); + +}); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index 3ae0668d12..a544b7043e 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { MarkdownString } from 'vs/base/common/htmlContent'; -suite('markdownString', () => { +suite('MarkdownString', () => { test('escape', () => { @@ -16,4 +16,63 @@ suite('markdownString', () => { assert.equal(mds.value, '\\# foo\n\n\\*bar\\*'); }); + + suite('ThemeIcons', () => { + + test('escapeThemeIcons', () => { + assert.equal( + MarkdownString.escapeThemeIcons('$(zap) $(not an icon) foo$(bar)'), + '\\$(zap) $(not an icon) foo\\$(bar)' + ); + }); + + suite('Support On', () => { + + test('appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText('$(zap)'); + + assert.equal(mds.value, '$(zap)'); + }); + + test('appendText escaped', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendText(MarkdownString.escapeThemeIcons('$(zap)')); + + assert.equal(mds.value, '\\\\$\\(zap\\)'); + }); + + test('appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown('$(zap)'); + + assert.equal(mds.value, '$(zap)'); + }); + + test('appendMarkdown escaped', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: true }); + mds.appendMarkdown(MarkdownString.escapeThemeIcons('$(zap)')); + + assert.equal(mds.value, '\\$(zap)'); + }); + }); + + suite('Support Off', () => { + + test('appendText', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendText('$(zap)'); + + assert.equal(mds.value, '$\\(zap\\)'); + }); + + test('appendMarkdown', () => { + const mds = new MarkdownString(undefined, { supportThemeIcons: false }); + mds.appendMarkdown('$(zap)'); + + assert.equal(mds.value, '$(zap)'); + }); + }); + + }); }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 381e7ecf8a..08f74ee4f3 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -3,43 +3,43 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/issueReporter'; -import { shell, ipcRenderer, webFrame, clipboard } from 'electron'; -import { localize } from 'vs/nls'; -import { $ } from 'vs/base/browser/dom'; -import * as collections from 'vs/base/common/collections'; -import * as browser from 'vs/base/browser/browser'; -import { escape } from 'vs/base/common/strings'; -import product from 'vs/platform/product/common/product'; +import { clipboard, ipcRenderer, shell, webFrame } from 'electron'; import * as os from 'os'; +import * as browser from 'vs/base/browser/browser'; +import { $ } from 'vs/base/browser/dom'; +import { Button } from 'vs/base/browser/ui/button/button'; +import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import * as collections from 'vs/base/common/collections'; import { debounce } from 'vs/base/common/decorators'; -import * as platform from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; +import * as platform from 'vs/base/common/platform'; +import { escape } from 'vs/base/common/strings'; import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; -import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; -import { MainProcessService, IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; -import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; -import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; -import { CodiconLabel } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; -import { Button } from 'vs/base/browser/ui/button/button'; -import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; -import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; +import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; +import 'vs/css!./media/issueReporter'; +import { localize } from 'vs/nls'; +import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { ISettingsSearchIssueReporterData, IssueReporterData, IssueReporterExtensionData, IssueReporterFeatures, IssueReporterStyles, IssueType } from 'vs/platform/issue/node/issue'; +import { getLogLevel, ILogService } from 'vs/platform/log/common/log'; +import { FollowerLogService, LoggerChannelClient } from 'vs/platform/log/common/logIpc'; +import { SpdLogService } from 'vs/platform/log/node/spdlogService'; +import product from 'vs/platform/product/common/product'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { ITelemetryServiceConfig, TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; +import { combinedAppender, LogAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; +import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; +import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; const MAX_URL_LENGTH = 2045; @@ -226,7 +226,7 @@ export class IssueReporter extends Disposable { } if (styles.buttonHoverBackground) { - content.push(`.monaco-text-button:hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); + content.push(`.monaco-text-button:not(.disabled):hover, .monaco-text-button:focus { background-color: ${styles.buttonHoverBackground} !important; }`); } styleTag.innerHTML = content.join('\n'); @@ -432,6 +432,11 @@ export class IssueReporter extends Disposable { sendWorkbenchCommand('workbench.action.reloadWindowWithExtensionsDisabled'); }); + this.addEventListener('extensionBugsLink', 'click', (e: MouseEvent) => { + const url = (e.target).innerText; + shell.openExternal(url); + }); + this.addEventListener('disableExtensions', 'keydown', (e: Event) => { e.stopPropagation(); if ((e as KeyboardEvent).keyCode === 13 || (e as KeyboardEvent).keyCode === 32) { @@ -1029,15 +1034,63 @@ export class IssueReporter extends Disposable { const matches = extensions.filter(extension => extension.id === selectedExtensionId); if (matches.length) { this.issueReporterModel.update({ selectedExtension: matches[0] }); + this.validateSelectedExtension(); const title = (this.getElementById('issue-title')).value; this.searchExtensionIssues(title); } else { this.issueReporterModel.update({ selectedExtension: undefined }); this.clearSearchResults(); + this.validateSelectedExtension(); } }); } + + this.addEventListener('problem-source', 'change', (_) => { + this.validateSelectedExtension(); + }); + } + + private validateSelectedExtension(): void { + const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; + const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; + hide(extensionValidationMessage); + hide(extensionValidationNoUrlsMessage); + + if (!this.issueReporterModel.getData().selectedExtension) { + this.previewButton.enabled = true; + return; + } + + const hasValidGitHubUrl = this.getExtensionGitHubUrl(); + if (hasValidGitHubUrl) { + this.previewButton.enabled = true; + } else { + this.setExtensionValidationMessage(); + this.previewButton.enabled = false; + } + } + + private setExtensionValidationMessage(): void { + const extensionValidationMessage = this.getElementById('extension-selection-validation-error')!; + const extensionValidationNoUrlsMessage = this.getElementById('extension-selection-validation-error-no-url')!; + const bugsUrl = this.getExtensionBugsUrl(); + if (bugsUrl) { + show(extensionValidationMessage); + const link = this.getElementById('extensionBugsLink')!; + link.textContent = bugsUrl; + return; + } + + const extensionUrl = this.getExtensionRepositoryUrl(); + if (extensionUrl) { + show(extensionValidationMessage); + const link = this.getElementById('extensionBugsLink'); + link!.textContent = extensionUrl; + return; + } + + show(extensionValidationNoUrlsMessage); } private updateProcessInfo(state: IssueReporterModelData) { diff --git a/src/vs/code/electron-browser/issue/issueReporterPage.ts b/src/vs/code/electron-browser/issue/issueReporterPage.ts index 566c7bc45c..d5f7045839 100644 --- a/src/vs/code/electron-browser/issue/issueReporterPage.ts +++ b/src/vs/code/electron-browser/issue/issueReporterPage.ts @@ -32,6 +32,11 @@ export default (): string => ` + + diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index ad4689db44..06ea58deb2 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -50,11 +50,10 @@ import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IUserDataSyncService, IUserDataSyncStoreService, ISettingsMergeService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; -import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; -import { SettingsMergeChannelClient } from 'vs/platform/userDataSync/common/settingsSyncIpc'; +import { UserDataSyncChannel, UserDataSyncUtilServiceClient } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; @@ -63,7 +62,6 @@ import { AuthTokenService } from 'vs/platform/auth/electron-browser/authTokenSer import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc'; import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; -import { UserDataSyncUtilServiceClient } from 'vs/platform/userDataSync/common/keybindingsSyncIpc'; import { UserDataAutoSync } from 'vs/platform/userDataSync/electron-browser/userDataAutoSync'; export interface ISharedProcessConfiguration { @@ -186,8 +184,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); services.set(IAuthTokenService, new SyncDescriptor(AuthTokenService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); - const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter); - services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel)); services.set(IUserDataSyncUtilService, new UserDataSyncUtilServiceClient(server.getChannel('userDataSyncUtil', activeWindowRouter))); services.set(IUserDataSyncStoreService, new SyncDescriptor(UserDataSyncStoreService)); services.set(IUserDataSyncService, new SyncDescriptor(UserDataSyncService)); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index e264267e50..585d44ec69 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -140,8 +140,13 @@ export class CodeWindow extends Disposable implements ICodeWindow { } }; + // Apply icon to window + // Linux: always + // Windows: only when running out of sources, otherwise an icon is set by us on the executable if (isLinux) { - options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); // Windows and Mac are better off using the embedded icon(s) + options.icon = path.join(this.environmentService.appRoot, 'resources/linux/code.png'); + } else if (isWindows && !this.environmentService.isBuilt) { + options.icon = path.join(this.environmentService.appRoot, 'resources/win32/code_150x150.png'); } const windowConfig = this.configurationService.getValue('window'); diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index b795204dbd..4361d2eb8e 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -20,7 +20,7 @@ import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccess import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { withNullAsUndefined } from 'vs/base/common/types'; +import { withNullAsUndefined, assertType } from 'vs/base/common/types'; export type ServicesAccessor = InstantiationServicesAccessor; export type IEditorContributionCtor = IConstructorSignature1; @@ -311,6 +311,60 @@ export function registerDefaultLanguageCommand(id: string, handler: (model: ITex }); } +export function registerModelAndPositionCommand(id: string, handler: (model: ITextModel, position: Position, ...args: any[]) => any) { + CommandsRegistry.registerCommand(id, function (accessor, ...args) { + + const [resource, position] = args; + assertType(URI.isUri(resource)); + assertType(Position.isIPosition(position)); + + const model = accessor.get(IModelService).getModel(resource); + if (model) { + const editorPosition = Position.lift(position); + return handler(model, editorPosition, args.slice(2)); + } + + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return new Promise((resolve, reject) => { + try { + const result = handler(reference.object.textEditorModel, Position.lift(position), args.slice(2)); + resolve(result); + } catch (err) { + reject(err); + } + }).finally(() => { + reference.dispose(); + }); + }); + }); +} + +export function registerModelCommand(id: string, handler: (model: ITextModel, ...args: any[]) => any) { + CommandsRegistry.registerCommand(id, function (accessor, ...args) { + + const [resource] = args; + assertType(URI.isUri(resource)); + + const model = accessor.get(IModelService).getModel(resource); + if (model) { + return handler(model, args.slice(1)); + } + + return accessor.get(ITextModelService).createModelReference(resource).then(reference => { + return new Promise((resolve, reject) => { + try { + const result = handler(reference.object.textEditorModel, args.slice(1)); + resolve(result); + } catch (err) { + reject(err); + } + }).finally(() => { + reference.dispose(); + }); + }); + }); +} + export function registerEditorCommand(editorCommand: T): T { EditorContributionRegistry.INSTANCE.registerEditorCommand(editorCommand); return editorCommand; diff --git a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts index bcf29eb93b..84f22c0729 100644 --- a/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts +++ b/src/vs/editor/browser/viewParts/editorScrollbar/editorScrollbar.ts @@ -50,6 +50,7 @@ export class EditorScrollbar extends ViewPart { horizontalScrollbarSize: scrollbar.horizontalScrollbarSize, horizontalSliderSize: scrollbar.horizontalSliderSize, handleMouseWheel: scrollbar.handleMouseWheel, + alwaysConsumeMouseWheel: scrollbar.alwaysConsumeMouseWheel, arrowSize: scrollbar.arrowSize, mouseWheelScrollSensitivity: mouseWheelScrollSensitivity, fastScrollSensitivity: fastScrollSensitivity, diff --git a/src/vs/editor/browser/widget/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditorWidget.ts index f04e5b657b..ef030a6c53 100644 --- a/src/vs/editor/browser/widget/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditorWidget.ts @@ -417,9 +417,12 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE // Current model is the new model return; } - + const hasTextFocus = this.hasTextFocus(); const detachedModel = this._detachModel(); this._attachModel(model); + if (hasTextFocus && this.hasModel()) { + this.focus(); + } const e: editorCommon.IModelChangedEvent = { oldModelUrl: detachedModel ? detachedModel.uri : null, diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 5b7a878a5c..dc23029ce1 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -2327,6 +2327,11 @@ export interface IEditorScrollbarOptions { * Defaults to true. */ handleMouseWheel?: boolean; + /** + * Always consume mouse wheel events (always call preventDefault() and stopPropagation() on the browser events). + * Defaults to true. + */ + alwaysConsumeMouseWheel?: boolean; /** * Height in pixels for the horizontal scrollbar. * Defaults to 10 (px). @@ -2357,6 +2362,7 @@ export interface InternalEditorScrollbarOptions { readonly verticalHasArrows: boolean; readonly horizontalHasArrows: boolean; readonly handleMouseWheel: boolean; + readonly alwaysConsumeMouseWheel: boolean; readonly horizontalScrollbarSize: number; readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; @@ -2391,6 +2397,7 @@ class EditorScrollbar extends BaseEditorOption${title}
`; this._commands.set(String(i), lens.command); diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 162c710ec7..3d06bd3b0a 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -10,7 +10,7 @@ import { illegalArgument, onUnexpectedExternalError } from 'vs/base/common/error import { URI } from 'vs/base/common/uri'; import { CodeEditorStateFlag, EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from 'vs/editor/browser/core/editorState'; import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { registerLanguageCommand, ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -25,6 +25,8 @@ import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { LinkedList } from 'vs/base/common/linkedList'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; export function alertFormattingEdits(edits: ISingleEditOperation[]): void { @@ -354,11 +356,11 @@ export function getOnTypeFormattingEdits( }); } -registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) { - const { resource, range, options } = args; - if (!(resource instanceof URI) || !Range.isIRange(range)) { - throw illegalArgument(); - } +CommandsRegistry.registerCommand('_executeFormatRangeProvider', function (accessor, ...args) { + const [resource, range, options] = args; + assertType(URI.isUri(resource)); + assertType(Range.isIRange(range)); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); @@ -366,11 +368,10 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) return getDocumentRangeFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, Range.lift(range), options, CancellationToken.None); }); -registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { - const { resource, options } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } +CommandsRegistry.registerCommand('_executeFormatDocumentProvider', function (accessor, ...args) { + const [resource, options] = args; + assertType(URI.isUri(resource)); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); @@ -379,11 +380,12 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar return getDocumentFormattingEditsUntilResult(accessor.get(IEditorWorkerService), model, options, CancellationToken.None); }); -registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) { - const { resource, position, ch, options } = args; - if (!(resource instanceof URI) || !Position.isIPosition(position) || typeof ch !== 'string') { - throw illegalArgument(); - } +CommandsRegistry.registerCommand('_executeFormatOnTypeProvider', function (accessor, ...args) { + const [resource, position, ch, options] = args; + assertType(URI.isUri(resource)); + assertType(Position.isIPosition(position)); + assertType(typeof ch === 'string'); + const model = accessor.get(IModelService).getModel(resource); if (!model) { throw illegalArgument('resource'); diff --git a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts index fe0444bddd..a4249e9f42 100644 --- a/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts +++ b/src/vs/editor/contrib/gotoSymbol/goToSymbol.ts @@ -6,7 +6,7 @@ import { flatten, coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { LocationLink, DefinitionProviderRegistry, ImplementationProviderRegistry, TypeDefinitionProviderRegistry, DeclarationProviderRegistry, ProviderResult, ReferenceProviderRegistry } from 'vs/editor/common/modes'; @@ -72,8 +72,8 @@ export function getReferencesAtPosition(model: ITextModel, position: Position, c }); } -registerDefaultLanguageCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None)); -registerDefaultLanguageCommand('_executeReferenceProvider', (model, position) => getReferencesAtPosition(model, position, false, CancellationToken.None)); +registerModelAndPositionCommand('_executeDefinitionProvider', (model, position) => getDefinitionsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeDeclarationProvider', (model, position) => getDeclarationsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeImplementationProvider', (model, position) => getImplementationsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeTypeDefinitionProvider', (model, position) => getTypeDefinitionsAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeReferenceProvider', (model, position) => getReferencesAtPosition(model, position, false, CancellationToken.None)); diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts index 27c1ead87b..28c3271207 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesController.ts @@ -23,7 +23,7 @@ import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async import { getOuterEditor, PeekContext } from 'vs/editor/contrib/peekView/peekView'; import { IListService, WorkbenchListFocusContextKey } from 'vs/platform/list/browser/listService'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { KeyCode, KeyMod, KeyChord } from 'vs/base/common/keyCodes'; export const ctxReferenceSearchVisible = new RawContextKey('referenceSearchVisible', false); @@ -173,6 +173,18 @@ export abstract class ReferencesController implements editorCommon.IEditorContri }); } + changeFocusBetweenPreviewAndReferences() { + if (!this._widget) { + // can be called while still resolving... + return; + } + if (this._widget.isPreviewEditorFocused()) { + this._widget.focusOnReferenceTree(); + } else { + this._widget.focusOnPreviewEditor(); + } + } + async goToNextOrPreviousReference(fwd: boolean) { if (!this._editor.hasModel() || !this._model || !this._widget) { // can be called while still resolving... @@ -229,7 +241,7 @@ export abstract class ReferencesController implements editorCommon.IEditorContri if (this._editor === openedEditor) { // this._widget.show(range); - this._widget.focus(); + this._widget.focusOnReferenceTree(); } else { // we opened a different editor instance which means a different controller instance. @@ -278,6 +290,18 @@ function withController(accessor: ServicesAccessor, fn: (controller: ReferencesC } } +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'changePeekFocus', + weight: KeybindingWeight.WorkbenchContrib + 50, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.F2), + when: ContextKeyExpr.or(ctxReferenceSearchVisible, PeekContext.inPeekEditor), + handler(accessor) { + withController(accessor, controller => { + controller.changeFocusBetweenPreviewAndReferences(); + }); + } +}); + KeybindingsRegistry.registerCommandAndKeybindingRule({ id: 'goToNextReference', weight: KeybindingWeight.WorkbenchContrib + 50, diff --git a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts index 3c887377fc..5fd2197761 100644 --- a/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/peek/referencesWidget.ts @@ -251,10 +251,18 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super.show(where, this.layoutData.heightInLines || 18); } - focus(): void { + focusOnReferenceTree(): void { this._tree.domFocus(); } + focusOnPreviewEditor(): void { + this._preview.focus(); + } + + isPreviewEditorFocused(): boolean { + return this._preview.hasTextFocus(); + } + protected _onTitleClick(e: IMouseEvent): void { if (this._preview && this._preview.getModel()) { this._onDidSelectReference.fire({ @@ -283,7 +291,8 @@ export class ReferenceWidget extends peekView.PeekViewWidget { horizontal: 'auto', useShadows: true, verticalHasArrows: false, - horizontalHasArrows: false + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false }, overviewRulerLanes: 2, fixedOverflowWidgets: true, @@ -457,7 +466,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { dom.show(this._treeContainer); dom.show(this._previewContainer); this._splitView.layout(this._dim.width); - this.focus(); + this.focusOnReferenceTree(); // pick input and a reference to begin with return this._tree.setInput(this._model.groups.length === 1 ? this._model.groups[0] : this._model); diff --git a/src/vs/editor/contrib/hover/getHover.ts b/src/vs/editor/contrib/hover/getHover.ts index 699de92cba..9683682b62 100644 --- a/src/vs/editor/contrib/hover/getHover.ts +++ b/src/vs/editor/contrib/hover/getHover.ts @@ -6,7 +6,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; -import { registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { ITextModel } from 'vs/editor/common/model'; import { Hover, HoverProviderRegistry } from 'vs/editor/common/modes'; @@ -27,7 +27,7 @@ export function getHover(model: ITextModel, position: Position, token: Cancellat return Promise.all(promises).then(coalesce); } -registerDefaultLanguageCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeHoverProvider', (model, position) => getHover(model, position, CancellationToken.None)); function isValid(result: Hover) { const hasRange = (typeof result.range !== 'undefined'); diff --git a/src/vs/editor/contrib/hover/hoverWidgets.ts b/src/vs/editor/contrib/hover/hoverWidgets.ts index 9b3e6d9263..f1d9a3de84 100644 --- a/src/vs/editor/contrib/hover/hoverWidgets.ts +++ b/src/vs/editor/contrib/hover/hoverWidgets.ts @@ -19,7 +19,7 @@ export class ContentHoverWidget extends Widget implements IContentWidget { protected _editor: ICodeEditor; private _isVisible: boolean; private readonly _containerDomNode: HTMLElement; - private readonly _domNode: HTMLElement; + protected readonly _domNode: HTMLElement; protected _showAtPosition: Position | null; protected _showAtRange: Range | null; private _stoleFocus: boolean; diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index dd2d5bff25..79c034408a 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -13,7 +13,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor } from 'vs/editor/common/modes'; +import { DocumentColorProvider, Hover as MarkdownHover, HoverProviderRegistry, IColor, TokenizationRegistry } from 'vs/editor/common/modes'; import { getColorPresentations } from 'vs/editor/contrib/colorPicker/color'; import { ColorDetector } from 'vs/editor/contrib/colorPicker/colorDetector'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/colorPickerModel'; @@ -238,6 +238,12 @@ export class ModesContentHoverWidget extends ContentHoverWidget { this._register(editor.onDidChangeConfiguration((e) => { this._hoverOperation.setHoverTime(this._editor.getOption(EditorOption.hover).delay); })); + this._register(TokenizationRegistry.onDidChange((e) => { + if (this.isVisible && this._lastRange && this._messages.length > 0) { + this._domNode.textContent = ''; + this._renderMessages(this._lastRange, this._messages); + } + })); } dispose(): void { diff --git a/src/vs/editor/contrib/message/messageController.ts b/src/vs/editor/contrib/message/messageController.ts index 5fc613e290..e5da4cc805 100644 --- a/src/vs/editor/contrib/message/messageController.ts +++ b/src/vs/editor/contrib/message/messageController.ts @@ -176,7 +176,7 @@ class MessageWidget implements IContentWidget { } getPosition(): IContentWidgetPosition { - return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE] }; + return { position: this._position, preference: [ContentWidgetPositionPreference.ABOVE, ContentWidgetPositionPreference.BELOW] }; } } diff --git a/src/vs/editor/contrib/parameterHints/parameterHints.css b/src/vs/editor/contrib/parameterHints/parameterHints.css index 2b03b69167..623baa27b5 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHints.css +++ b/src/vs/editor/contrib/parameterHints/parameterHints.css @@ -54,6 +54,10 @@ white-space: initial; } +.monaco-editor .parameter-hints-widget .docs .markdown-docs p code { + font-family: var(--monaco-monospace-font); +} + .monaco-editor .parameter-hints-widget .docs .code { white-space: pre-wrap; } diff --git a/src/vs/editor/contrib/quickOpen/quickOpen.ts b/src/vs/editor/contrib/quickOpen/quickOpen.ts index 7b69a0970d..b899f089ff 100644 --- a/src/vs/editor/contrib/quickOpen/quickOpen.ts +++ b/src/vs/editor/contrib/quickOpen/quickOpen.ts @@ -3,17 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { illegalArgument } from 'vs/base/common/errors'; import { URI } from 'vs/base/common/uri'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { DocumentSymbol } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { CancellationToken } from 'vs/base/common/cancellation'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { OutlineModel, OutlineElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; import { values } from 'vs/base/common/collections'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { assertType } from 'vs/base/common/types'; export async function getDocumentSymbols(document: ITextModel, flat: boolean, token: CancellationToken): Promise { @@ -63,26 +63,19 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo } -registerLanguageCommand('_executeDocumentSymbolProvider', function (accessor, args) { - const { resource } = args; - if (!(resource instanceof URI)) { - throw illegalArgument('resource'); - } +CommandsRegistry.registerCommand('_executeDocumentSymbolProvider', async function (accessor, ...args) { + const [resource] = args; + assertType(URI.isUri(resource)); + const model = accessor.get(IModelService).getModel(resource); if (model) { return getDocumentSymbols(model, false, CancellationToken.None); } - return accessor.get(ITextModelService).createModelReference(resource).then(reference => { - return new Promise((resolve, reject) => { - try { - const result = getDocumentSymbols(reference.object.textEditorModel, false, CancellationToken.None); - resolve(result); - } catch (err) { - reject(err); - } - }).finally(() => { - reference.dispose(); - }); - }); + const reference = await accessor.get(ITextModelService).createModelReference(resource); + try { + return await getDocumentSymbols(reference.object.textEditorModel, false, CancellationToken.None); + } finally { + reference.dispose(); + } }); diff --git a/src/vs/editor/contrib/smartSelect/smartSelect.ts b/src/vs/editor/contrib/smartSelect/smartSelect.ts index 12a442950d..16a7673496 100644 --- a/src/vs/editor/contrib/smartSelect/smartSelect.ts +++ b/src/vs/editor/contrib/smartSelect/smartSelect.ts @@ -7,7 +7,7 @@ import * as arrays from 'vs/base/common/arrays'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor, registerDefaultLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, IActionOptions, registerEditorAction, registerEditorContribution, ServicesAccessor, registerModelCommand } from 'vs/editor/browser/editorExtensions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; @@ -302,6 +302,7 @@ export function provideSelectionRanges(model: ITextModel, positions: Position[], }); } -registerDefaultLanguageCommand('_executeSelectionRangeProvider', function (model, _position, args) { - return provideSelectionRanges(model, args.positions, CancellationToken.None); +registerModelCommand('_executeSelectionRangeProvider', function (model, ...args) { + const [positions] = args; + return provideSelectionRanges(model, positions, CancellationToken.None); }); diff --git a/src/vs/editor/contrib/suggest/suggestModel.ts b/src/vs/editor/contrib/suggest/suggestModel.ts index 43a9cae6ff..8a626d9eff 100644 --- a/src/vs/editor/contrib/suggest/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/suggestModel.ts @@ -21,6 +21,7 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; import { WordDistance } from 'vs/editor/contrib/suggest/wordDistance'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { isLowSurrogate, isHighSurrogate } from 'vs/base/common/strings'; export interface ICancelEvent { readonly retrigger: boolean; @@ -95,7 +96,7 @@ export class SuggestModel implements IDisposable { private readonly _toDispose = new DisposableStore(); private _quickSuggestDelay: number = 10; - private _triggerCharacterListener?: IDisposable; + private readonly _triggerCharacterListener = new DisposableStore(); private readonly _triggerQuickSuggest = new TimeoutTimer(); private _state: State = State.Idle; @@ -181,8 +182,7 @@ export class SuggestModel implements IDisposable { } private _updateTriggerCharacters(): void { - - dispose(this._triggerCharacterListener); + this._triggerCharacterListener.clear(); if (this._editor.getOption(EditorOption.readOnly) || !this._editor.hasModel() @@ -191,29 +191,49 @@ export class SuggestModel implements IDisposable { return; } - const supportsByTriggerCharacter: { [ch: string]: Set } = Object.create(null); + const supportsByTriggerCharacter = new Map>(); for (const support of CompletionProviderRegistry.all(this._editor.getModel())) { for (const ch of support.triggerCharacters || []) { - let set = supportsByTriggerCharacter[ch]; + let set = supportsByTriggerCharacter.get(ch); if (!set) { - set = supportsByTriggerCharacter[ch] = new Set(); + set = new Set(); set.add(getSnippetSuggestSupport()); + supportsByTriggerCharacter.set(ch, set); } set.add(support); } } - this._triggerCharacterListener = this._editor.onDidType(text => { - const lastChar = text.charAt(text.length - 1); - const supports = supportsByTriggerCharacter[lastChar]; + const checkTriggerCharacter = (text?: string) => { + + if (!text) { + // came here from the compositionEnd-event + const position = this._editor.getPosition()!; + const model = this._editor.getModel()!; + text = model.getLineContent(position.lineNumber).substr(0, position.column - 1); + } + + let lastChar = ''; + if (isLowSurrogate(text.charCodeAt(text.length - 1))) { + if (isHighSurrogate(text.charCodeAt(text.length - 2))) { + lastChar = text.substr(text.length - 2); + } + } else { + lastChar = text.charAt(text.length - 1); + } + + const supports = supportsByTriggerCharacter.get(lastChar); if (supports) { // keep existing items that where not computed by the // supports/providers that want to trigger now const items: CompletionItem[] | undefined = this._completionModel ? this._completionModel.adopt(supports) : undefined; this.trigger({ auto: true, shy: false, triggerCharacter: lastChar }, Boolean(this._completionModel), supports, items); } - }); + }; + + this._triggerCharacterListener.add(this._editor.onDidType(checkTriggerCharacter)); + this._triggerCharacterListener.add(this._editor.onCompositionEnd(checkTriggerCharacter)); } // --- trigger/retrigger/cancel suggest diff --git a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts index 262d8ba826..374e87977a 100644 --- a/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/wordHighlighter.ts @@ -11,7 +11,7 @@ import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/err import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { EditorAction, IActionOptions, registerDefaultLanguageCommand, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { EditorAction, IActionOptions, registerEditorAction, registerEditorContribution, registerModelAndPositionCommand } from 'vs/editor/browser/editorExtensions'; import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; @@ -155,7 +155,7 @@ function computeOccurencesAtPosition(model: ITextModel, selection: Selection, wo return new TextualOccurenceAtPositionRequest(model, selection, wordSeparators); } -registerDefaultLanguageCommand('_executeDocumentHighlights', (model, position) => getOccurrencesAtPosition(model, position, CancellationToken.None)); +registerModelAndPositionCommand('_executeDocumentHighlights', (model, position) => getOccurrencesAtPosition(model, position, CancellationToken.None)); class WordHighlighter { diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index 347747d311..0a55d88766 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -305,6 +305,7 @@ export class StandaloneKeybindingService extends AbstractKeybindingService { let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); if (shouldPreventDefault) { keyEvent.preventDefault(); + keyEvent.stopPropagation(); } })); } diff --git a/src/vs/editor/standalone/browser/standaloneLanguages.ts b/src/vs/editor/standalone/browser/standaloneLanguages.ts index ade01f8d33..608cc546af 100644 --- a/src/vs/editor/standalone/browser/standaloneLanguages.ts +++ b/src/vs/editor/standalone/browser/standaloneLanguages.ts @@ -424,7 +424,7 @@ export function registerCodeLensProvider(languageId: string, provider: modes.Cod */ export function registerCodeActionProvider(languageId: string, provider: CodeActionProvider): IDisposable { return modes.CodeActionProviderRegistry.register(languageId, { - provideCodeActions: (model: model.ITextModel, range: Range, context: modes.CodeActionContext, token: CancellationToken): modes.CodeActionList | Promise => { + provideCodeActions: (model: model.ITextModel, range: Range, context: modes.CodeActionContext, token: CancellationToken): modes.ProviderResult => { let markers = StaticServices.markerService.get().read({ resource: model.uri }).filter(m => { return Range.areIntersectingOrTouching(m, range); }); @@ -521,7 +521,7 @@ export interface CodeActionProvider { /** * Provide commands for the given document and range. */ - provideCodeActions(model: model.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): modes.CodeActionList | Promise; + provideCodeActions(model: model.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): modes.ProviderResult; } /** diff --git a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts index 516c85bf21..846aa15f89 100644 --- a/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts +++ b/src/vs/editor/test/common/viewLayout/editorLayoutProvider.test.ts @@ -60,6 +60,7 @@ suite('Editor ViewLayout - EditorLayoutProvider', () => { verticalHasArrows: input.verticalScrollbarHasArrows, horizontalHasArrows: false, handleMouseWheel: EditorOptions.scrollbar.defaultValue.handleMouseWheel, + alwaysConsumeMouseWheel: true, horizontalScrollbarSize: input.horizontalScrollbarHeight, horizontalSliderSize: EditorOptions.scrollbar.defaultValue.horizontalSliderSize, verticalScrollbarSize: input.verticalScrollbarWidth, diff --git a/src/vs/loader.js b/src/vs/loader.js index d522e400fb..ea6d638a50 100644 --- a/src/vs/loader.js +++ b/src/vs/loader.js @@ -728,7 +728,7 @@ var AMDLoader; var result = compileWrapper.apply(this.exports, args); // cached data aftermath that._handleCachedData(script, scriptSource, cachedDataPath, !options.cachedData, moduleManager); - that._verifyCachedData(script, scriptSource, cachedDataPath, hashData); + that._verifyCachedData(script, scriptSource, cachedDataPath, hashData, moduleManager); return result; }; }; @@ -775,7 +775,7 @@ var AMDLoader; var scriptOpts = { filename: vmScriptPathOrUri_1, cachedData: cachedData }; var script = _this._createAndEvalScript(moduleManager, scriptSource, scriptOpts, callback, errorback); _this._handleCachedData(script, scriptSource, cachedDataPath_1, wantsCachedData_1 && !cachedData, moduleManager); - _this._verifyCachedData(script, scriptSource, cachedDataPath_1, hashData); + _this._verifyCachedData(script, scriptSource, cachedDataPath_1, hashData, moduleManager); }); } }; @@ -906,7 +906,7 @@ var AMDLoader; }); } }; - NodeScriptLoader.prototype._verifyCachedData = function (script, scriptSource, cachedDataPath, hashData) { + NodeScriptLoader.prototype._verifyCachedData = function (script, scriptSource, cachedDataPath, hashData, moduleManager) { var _this = this; if (!hashData) { // nothing to do @@ -922,8 +922,8 @@ var AMDLoader; // for violations of this contract. var hashDataNow = _this._crypto.createHash('md5').update(scriptSource, 'utf8').digest(); if (!hashData.equals(hashDataNow)) { - console.warn("FAILED TO VERIFY CACHED DATA. Deleting '" + cachedDataPath + "' now, but a RESTART IS REQUIRED"); - _this._fs.unlink(cachedDataPath, function (err) { return console.error("FAILED to unlink: '" + cachedDataPath + "'", err); }); + moduleManager.getConfig().onError(new Error("FAILED TO VERIFY CACHED DATA, deleting stale '" + cachedDataPath + "' now, but a RESTART IS REQUIRED")); + _this._fs.unlink(cachedDataPath, function (err) { return moduleManager.getConfig().onError(err); }); } }, Math.ceil(5000 * (1 + Math.random()))); }; diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 55316f5507..e9d6a524f7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -380,6 +380,7 @@ declare namespace monaco { export interface IMarkdownString { readonly value: string; readonly isTrusted?: boolean; + readonly supportThemeIcons?: boolean; uris?: { [href: string]: UriComponents; }; @@ -3363,6 +3364,11 @@ declare namespace monaco.editor { * Defaults to true. */ handleMouseWheel?: boolean; + /** + * Always consume mouse wheel events (always call preventDefault() and stopPropagation() on the browser events). + * Defaults to true. + */ + alwaysConsumeMouseWheel?: boolean; /** * Height in pixels for the horizontal scrollbar. * Defaults to 10 (px). @@ -3393,6 +3399,7 @@ declare namespace monaco.editor { readonly verticalHasArrows: boolean; readonly horizontalHasArrows: boolean; readonly handleMouseWheel: boolean; + readonly alwaysConsumeMouseWheel: boolean; readonly horizontalScrollbarSize: number; readonly horizontalSliderSize: number; readonly verticalScrollbarSize: number; @@ -4522,7 +4529,7 @@ declare namespace monaco.languages { /** * Provide commands for the given document and range. */ - provideCodeActions(model: editor.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): CodeActionList | Promise; + provideCodeActions(model: editor.ITextModel, range: Range, context: CodeActionContext, token: CancellationToken): ProviderResult; } /** @@ -5521,9 +5528,9 @@ declare namespace monaco.languages { } export interface ResourceFileEdit { - oldUri: Uri; - newUri: Uri; - options: { + oldUri?: Uri; + newUri?: Uri; + options?: { overwrite?: boolean; ignoreIfNotExists?: boolean; ignoreIfExists?: boolean; diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 019ed8e6fd..18dd21c0a1 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -796,6 +796,7 @@ export interface IFilesConfiguration { eol: string; enableTrash: boolean; hotExit: string; + preventSaveConflicts: boolean; }; } diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 338c095afa..06b4670087 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -484,15 +484,15 @@ export class DiskFileSystemProvider extends Disposable implements //#region File Watching - private _onDidWatchErrorOccur: Emitter = this._register(new Emitter()); - readonly onDidErrorOccur: Event = this._onDidWatchErrorOccur.event; + private _onDidWatchErrorOccur = this._register(new Emitter()); + readonly onDidErrorOccur = this._onDidWatchErrorOccur.event; private _onDidChangeFile = this._register(new Emitter()); - get onDidChangeFile(): Event { return this._onDidChangeFile.event; } + readonly onDidChangeFile = this._onDidChangeFile.event; private recursiveWatcher: WindowsWatcherService | UnixWatcherService | NsfwWatcherService | undefined; private recursiveFoldersToWatch: { path: string, excludes: string[] }[] = []; - private recursiveWatchRequestDelayer: ThrottledDelayer = this._register(new ThrottledDelayer(0)); + private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer(0)); private recursiveWatcherLogLevelListener: IDisposable | undefined; diff --git a/src/vs/platform/files/test/files.test.ts b/src/vs/platform/files/test/files.test.ts index 811bffd4ae..326d53e55c 100644 --- a/src/vs/platform/files/test/files.test.ts +++ b/src/vs/platform/files/test/files.test.ts @@ -42,7 +42,7 @@ suite('Files', () => { assert.strictEqual(true, r1.gotDeleted()); }); - function testIsEqual(testMethod: (pA: string | undefined, pB: string, ignoreCase: boolean) => boolean): void { + function testIsEqual(testMethod: (pA: string, pB: string, ignoreCase: boolean) => boolean): void { // corner cases assert(testMethod('', '', true)); @@ -136,7 +136,7 @@ suite('Files', () => { test('isEqualOrParent (ignorecase)', function () { // same assertions apply as with isEqual() - testIsEqual(isEqualOrParent); + testIsEqual(isEqualOrParent); // if (isWindows) { assert(isEqualOrParent('c:\\some\\path', 'c:\\', true)); @@ -182,4 +182,4 @@ suite('Files', () => { assert(!isEqualOrParent('foo/bar/test.ts', 'foo/BAR/test.', true)); } }); -}); \ No newline at end of file +}); diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index b9c0fbb2f6..d6e64ae482 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -22,7 +22,7 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Registry } from 'vs/platform/registry/common/platform'; -import { attachListStyler, computeStyles, defaultListStyles, IColorMapping, attachStyler } from 'vs/platform/theme/common/styler'; +import { attachListStyler, computeStyles, defaultListStyles, IColorMapping } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; import { ObjectTree, IObjectTreeOptions, ICompressibleTreeRenderer, CompressibleObjectTree, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree'; @@ -287,7 +287,7 @@ export class WorkbenchList extends List { this.disposables.add((listService as ListService).register(this)); if (options.overrideStyles) { - this.disposables.add(attachStyler(themeService, options.overrideStyles, this)); + this.disposables.add(attachListStyler(this, themeService, options.overrideStyles)); } this.disposables.add(this.onSelectionChange(() => { @@ -368,7 +368,7 @@ export class WorkbenchPagedList extends PagedList { this.disposables.add((listService as ListService).register(this)); if (options.overrideStyles) { - this.disposables.add(attachStyler(themeService, options.overrideStyles, this)); + this.disposables.add(attachListStyler(this, themeService, options.overrideStyles)); } this.registerListeners(); @@ -1044,7 +1044,7 @@ class WorkbenchTreeInternals { this.disposables.push( this.contextKeyService, (listService as ListService).register(tree), - overrideStyles ? attachStyler(themeService, overrideStyles, tree) : Disposable.None, + overrideStyles ? attachListStyler(tree, themeService, overrideStyles) : Disposable.None, tree.onDidChangeSelection(() => { const selection = tree.getSelection(); const focus = tree.getFocus(); diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index fb6098b8a6..97fb4fd6a0 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -102,7 +102,7 @@ export interface IProductConfiguration { readonly portable?: string; - readonly extensionKind?: { readonly [extensionId: string]: ExtensionKind | ExtensionKind[]; }; + readonly extensionKind?: { readonly [extensionId: string]: ExtensionKind[]; }; readonly extensionAllowedProposedApi?: readonly string[]; readonly msftInternalDomains?: string[]; diff --git a/src/vs/platform/remote/common/tunnel.ts b/src/vs/platform/remote/common/tunnel.ts index 5689ad5ff0..6a081c2c9e 100644 --- a/src/vs/platform/remote/common/tunnel.ts +++ b/src/vs/platform/remote/common/tunnel.ts @@ -11,8 +11,9 @@ export const ITunnelService = createDecorator('tunnelService'); export interface RemoteTunnel { readonly tunnelRemotePort: number; + readonly tunnelRemoteHost: string; readonly tunnelLocalPort: number; - readonly localAddress?: string; + readonly localAddress: string; dispose(): void; } diff --git a/src/vs/platform/sign/node/signService.ts b/src/vs/platform/sign/node/signService.ts index addf63a110..ed68540eec 100644 --- a/src/vs/platform/sign/node/signService.ts +++ b/src/vs/platform/sign/node/signService.ts @@ -5,11 +5,19 @@ import { ISignService } from 'vs/platform/sign/common/sign'; +/* tslint:disable */ + +declare module vsda { + export class signer { + sign(arg: any): any; + } +} + export class SignService implements ISignService { _serviceBrand: undefined; - private vsda(): Promise { - return import('vsda'); + private vsda(): Promise { + return new Promise((resolve, reject) => require(['vsda'], resolve, reject)); } async sign(value: string): Promise { @@ -20,9 +28,8 @@ export class SignService implements ISignService { return signer.sign(value); } } catch (e) { - console.error('signer.sign: ' + e); + // ignore errors silently } - return value; } } diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 3adffe6173..d0a8945f65 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -11,16 +11,16 @@ import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { IStateService } from 'vs/platform/state/node/state'; import { ILogService } from 'vs/platform/log/common/log'; -type StorageDatebase = { [key: string]: any; }; +type StorageDatabase = { [key: string]: any; }; export class FileStorage { - private _database: StorageDatebase | null = null; + private _database: StorageDatabase | null = null; private lastFlushedSerializedDatabase: string | null = null; constructor(private dbPath: string, private onError: (error: Error) => void) { } - private get database(): StorageDatebase { + private get database(): StorageDatabase { if (!this._database) { this._database = this.loadSync(); } @@ -42,7 +42,7 @@ export class FileStorage { this._database = database; } - private loadSync(): StorageDatebase { + private loadSync(): StorageDatabase { try { this.lastFlushedSerializedDatabase = fs.readFileSync(this.dbPath).toString(); @@ -56,7 +56,7 @@ export class FileStorage { } } - private async loadAsync(): Promise { + private async loadAsync(): Promise { try { this.lastFlushedSerializedDatabase = (await readFile(this.dbPath)).toString(); diff --git a/src/vs/platform/userDataSync/common/extensionsMerge.ts b/src/vs/platform/userDataSync/common/extensionsMerge.ts new file mode 100644 index 0000000000..3b17eafee5 --- /dev/null +++ b/src/vs/platform/userDataSync/common/extensionsMerge.ts @@ -0,0 +1,169 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { values, keys } from 'vs/base/common/map'; +import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync'; +import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { startsWith } from 'vs/base/common/strings'; + +export interface IMergeResult { + added: ISyncExtension[]; + removed: IExtensionIdentifier[]; + updated: ISyncExtension[]; + remote: ISyncExtension[] | null; +} + +export function merge(localExtensions: ISyncExtension[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null, skippedExtensions: ISyncExtension[], ignoredExtensions: string[]): IMergeResult { + const added: ISyncExtension[] = []; + const removed: IExtensionIdentifier[] = []; + const updated: ISyncExtension[] = []; + + if (!remoteExtensions) { + return { + added, + removed, + updated, + remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase())) + }; + } + + const uuids: Map = new Map(); + const addUUID = (identifier: IExtensionIdentifier) => { if (identifier.uuid) { uuids.set(identifier.id.toLowerCase(), identifier.uuid); } }; + localExtensions.forEach(({ identifier }) => addUUID(identifier)); + remoteExtensions.forEach(({ identifier }) => addUUID(identifier)); + if (lastSyncExtensions) { + lastSyncExtensions.forEach(({ identifier }) => addUUID(identifier)); + } + + const addExtensionToMap = (map: Map, extension: ISyncExtension) => { + const uuid = extension.identifier.uuid || uuids.get(extension.identifier.id.toLowerCase()); + const key = uuid ? `uuid:${uuid}` : `id:${extension.identifier.id.toLowerCase()}`; + map.set(key, extension); + return map; + }; + const localExtensionsMap = localExtensions.reduce(addExtensionToMap, new Map()); + const remoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); + const newRemoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); + const lastSyncExtensionsMap = lastSyncExtensions ? lastSyncExtensions.reduce(addExtensionToMap, new Map()) : null; + const skippedExtensionsMap = skippedExtensions.reduce(addExtensionToMap, new Map()); + const ignoredExtensionsSet = ignoredExtensions.reduce((set, id) => { + const uuid = uuids.get(id.toLowerCase()); + return set.add(uuid ? `uuid:${uuid}` : `id:${id.toLowerCase()}`); + }, new Set()); + + const localToRemote = compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); + if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { + // No changes found between local and remote. + return { added: [], removed: [], updated: [], remote: null }; + } + + const baseToLocal = compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet); + const baseToRemote = compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); + + const massageSyncExtension = (extension: ISyncExtension, key: string): ISyncExtension => { + const massagedExtension: ISyncExtension = { + identifier: { + id: extension.identifier.id, + uuid: startsWith(key, 'uuid:') ? key.substring('uuid:'.length) : undefined + }, + enabled: extension.enabled, + }; + if (extension.version) { + massagedExtension.version = extension.version; + } + return massagedExtension; + }; + + // Remotely removed extension. + for (const key of values(baseToRemote.removed)) { + const e = localExtensionsMap.get(key); + if (e) { + removed.push(e.identifier); + } + } + + // Remotely added extension + for (const key of values(baseToRemote.added)) { + // Got added in local + if (baseToLocal.added.has(key)) { + // Is different from local to remote + if (localToRemote.updated.has(key)) { + updated.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); + } + } else { + // Add to local + added.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); + } + } + + // Remotely updated extensions + for (const key of values(baseToRemote.updated)) { + // If updated in local + if (baseToLocal.updated.has(key)) { + // Is different from local to remote + if (localToRemote.updated.has(key)) { + // update it in local + updated.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); + } + } + } + + // Locally added extensions + for (const key of values(baseToLocal.added)) { + // Not there in remote + if (!baseToRemote.added.has(key)) { + newRemoteExtensionsMap.set(key, massageSyncExtension(localExtensionsMap.get(key)!, key)); + } + } + + // Locally updated extensions + for (const key of values(baseToLocal.updated)) { + // If removed in remote + if (baseToRemote.removed.has(key)) { + continue; + } + + // If not updated in remote + if (!baseToRemote.updated.has(key)) { + newRemoteExtensionsMap.set(key, massageSyncExtension(localExtensionsMap.get(key)!, key)); + } + } + + // Locally removed extensions + for (const key of values(baseToLocal.removed)) { + // If not skipped and not updated in remote + if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) { + newRemoteExtensionsMap.delete(key); + } + } + + const remoteChanges = compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set()); + const remote = remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0 ? values(newRemoteExtensionsMap) : null; + return { added, removed, updated, remote }; +} + +function compare(from: Map | null, to: Map, ignoredExtensions: Set): { added: Set, removed: Set, updated: Set } { + const fromKeys = from ? keys(from).filter(key => !ignoredExtensions.has(key)) : []; + const toKeys = keys(to).filter(key => !ignoredExtensions.has(key)); + const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const updated: Set = new Set(); + + for (const key of fromKeys) { + if (removed.has(key)) { + continue; + } + const fromExtension = from!.get(key)!; + const toExtension = to.get(key); + if (!toExtension + || fromExtension.enabled !== toExtension.enabled + || fromExtension.version !== toExtension.version + ) { + updated.add(key); + } + } + + return { added, removed, updated }; +} diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index efb3f52e0b..8e2bf6fc2f 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -13,12 +13,11 @@ import { joinPath } from 'vs/base/common/resources'; import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType, IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -import { keys, values } from 'vs/base/common/map'; -import { startsWith } from 'vs/base/common/strings'; import { IFileService } from 'vs/platform/files/common/files'; import { Queue } from 'vs/base/common/async'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; +import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; export interface ISyncPreviewResult { readonly added: ISyncExtension[]; @@ -135,8 +134,14 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser const localExtensions = await this.getLocalExtensions(); - this.logService.trace('Extensions: Merging remote extensions with local extensions...'); - const { added, removed, updated, remote } = this.merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions); + if (remoteExtensions) { + this.logService.trace('Extensions: Merging remote extensions with local extensions...'); + } else { + this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.'); + } + + const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; + const { added, removed, updated, remote } = merge(localExtensions, remoteExtensions, lastSyncExtensions, skippedExtensions, ignoredExtensions); if (!added.length && !removed.length && !updated.length && !remote) { this.logService.trace('Extensions: No changes found during synchronizing extensions.'); @@ -162,160 +167,6 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser } } - /** - * Merge Strategy: - * - If remote does not exist, merge with local (First time sync) - * - Overwrite local with remote changes. Removed, Added, Updated. - * - Update remote with those local extension which are newly added or updated or removed and untouched in remote. - */ - private merge(localExtensions: ISyncExtension[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null, skippedExtensions: ISyncExtension[]): { added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], remote: ISyncExtension[] | null } { - const ignoredExtensions = this.configurationService.getValue('sync.ignoredExtensions') || []; - // First time sync - if (!remoteExtensions) { - this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.'); - return { added: [], removed: [], updated: [], remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase())) }; - } - - const uuids: Map = new Map(); - const addUUID = (identifier: IExtensionIdentifier) => { if (identifier.uuid) { uuids.set(identifier.id.toLowerCase(), identifier.uuid); } }; - localExtensions.forEach(({ identifier }) => addUUID(identifier)); - remoteExtensions.forEach(({ identifier }) => addUUID(identifier)); - if (lastSyncExtensions) { - lastSyncExtensions.forEach(({ identifier }) => addUUID(identifier)); - } - - const addExtensionToMap = (map: Map, extension: ISyncExtension) => { - const uuid = extension.identifier.uuid || uuids.get(extension.identifier.id.toLowerCase()); - const key = uuid ? `uuid:${uuid}` : `id:${extension.identifier.id.toLowerCase()}`; - map.set(key, extension); - return map; - }; - const localExtensionsMap = localExtensions.reduce(addExtensionToMap, new Map()); - const remoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); - const newRemoteExtensionsMap = remoteExtensions.reduce(addExtensionToMap, new Map()); - const lastSyncExtensionsMap = lastSyncExtensions ? lastSyncExtensions.reduce(addExtensionToMap, new Map()) : null; - const skippedExtensionsMap = skippedExtensions.reduce(addExtensionToMap, new Map()); - const ignoredExtensionsSet = ignoredExtensions.reduce((set, id) => { - const uuid = uuids.get(id.toLowerCase()); - return set.add(uuid ? `uuid:${uuid}` : `id:${id.toLowerCase()}`); - }, new Set()); - - const localToRemote = this.compare(localExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet); - if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { - // No changes found between local and remote. - return { added: [], removed: [], updated: [], remote: null }; - } - - const added: ISyncExtension[] = []; - const removed: IExtensionIdentifier[] = []; - const updated: ISyncExtension[] = []; - - const baseToLocal = lastSyncExtensionsMap ? this.compare(lastSyncExtensionsMap, localExtensionsMap, ignoredExtensionsSet) : { added: keys(localExtensionsMap).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - const baseToRemote = lastSyncExtensionsMap ? this.compare(lastSyncExtensionsMap, remoteExtensionsMap, ignoredExtensionsSet) : { added: keys(remoteExtensionsMap).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - - const massageSyncExtension = (extension: ISyncExtension, key: string): ISyncExtension => { - return { - identifier: { - id: extension.identifier.id, - uuid: startsWith(key, 'uuid:') ? key.substring('uuid:'.length) : undefined - }, - enabled: extension.enabled, - version: extension.version - }; - }; - - // Remotely removed extension. - for (const key of values(baseToRemote.removed)) { - const e = localExtensionsMap.get(key); - if (e) { - removed.push(e.identifier); - } - } - - // Remotely added extension - for (const key of values(baseToRemote.added)) { - // Got added in local - if (baseToLocal.added.has(key)) { - // Is different from local to remote - if (localToRemote.updated.has(key)) { - updated.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); - } - } else { - // Add to local - added.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); - } - } - - // Remotely updated extensions - for (const key of values(baseToRemote.updated)) { - // If updated in local - if (baseToLocal.updated.has(key)) { - // Is different from local to remote - if (localToRemote.updated.has(key)) { - // update it in local - updated.push(massageSyncExtension(remoteExtensionsMap.get(key)!, key)); - } - } - } - - // Locally added extensions - for (const key of values(baseToLocal.added)) { - // Not there in remote - if (!baseToRemote.added.has(key)) { - newRemoteExtensionsMap.set(key, massageSyncExtension(localExtensionsMap.get(key)!, key)); - } - } - - // Locally updated extensions - for (const key of values(baseToLocal.updated)) { - // If removed in remote - if (baseToRemote.removed.has(key)) { - continue; - } - - // If not updated in remote - if (!baseToRemote.updated.has(key)) { - newRemoteExtensionsMap.set(key, massageSyncExtension(localExtensionsMap.get(key)!, key)); - } - } - - // Locally removed extensions - for (const key of values(baseToLocal.removed)) { - // If not skipped and not updated in remote - if (!skippedExtensionsMap.has(key) && !baseToRemote.updated.has(key)) { - newRemoteExtensionsMap.delete(key); - } - } - - const remoteChanges = this.compare(remoteExtensionsMap, newRemoteExtensionsMap, new Set()); - const remote = remoteChanges.added.size > 0 || remoteChanges.updated.size > 0 || remoteChanges.removed.size > 0 ? values(newRemoteExtensionsMap) : null; - return { added, removed, updated, remote }; - } - - private compare(from: Map, to: Map, ignoredExtensions: Set): { added: Set, removed: Set, updated: Set } { - const fromKeys = keys(from).filter(key => !ignoredExtensions.has(key)); - const toKeys = keys(to).filter(key => !ignoredExtensions.has(key)); - const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); - const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); - const updated: Set = new Set(); - - for (const key of fromKeys) { - if (removed.has(key)) { - continue; - } - const fromExtension = from.get(key)!; - const toExtension = to.get(key); - if (!toExtension - || fromExtension.enabled !== toExtension.enabled - || fromExtension.version !== toExtension.version - ) { - updated.add(key); - } - } - - return { added, removed, updated }; - } - private async updateLocalExtensions(added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], skippedExtensions: ISyncExtension[]): Promise { const removeFromSkipped: IExtensionIdentifier[] = []; const addToSkipped: ISyncExtension[] = []; diff --git a/src/vs/platform/userDataSync/common/keybindingsSync.ts b/src/vs/platform/userDataSync/common/keybindingsSync.ts index 2b57fe82d8..8e173d109d 100644 --- a/src/vs/platform/userDataSync/common/keybindingsSync.ts +++ b/src/vs/platform/userDataSync/common/keybindingsSync.ts @@ -19,6 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CancellationToken } from 'vs/base/common/cancellation'; import { OS, OperatingSystem } from 'vs/base/common/platform'; import { isUndefined } from 'vs/base/common/types'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; interface ISyncContent { mac?: string; @@ -217,7 +218,7 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser || lastSyncContent !== remoteContent // Remote has forwarded ) { this.logService.trace('Keybindings: Merging remote keybindings with local keybindings...'); - const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.keybindingsResource); + const formattingOptions = await this.getFormattingOptions(); const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService); // Sync only if there are changes if (result.hasChanges) { @@ -243,6 +244,14 @@ export class KeybindingsSynchroniser extends Disposable implements ISynchroniser return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } + private _formattingOptions: Promise | undefined = undefined; + private getFormattingOptions(): Promise { + if (!this._formattingOptions) { + this._formattingOptions = this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.keybindingsResource); + } + return this._formattingOptions; + } + private async getLocalContent(): Promise { try { return await this.fileService.readFile(this.environmentService.keybindingsResource); diff --git a/src/vs/platform/userDataSync/common/keybindingsSyncIpc.ts b/src/vs/platform/userDataSync/common/keybindingsSyncIpc.ts deleted file mode 100644 index 02b96500f3..0000000000 --- a/src/vs/platform/userDataSync/common/keybindingsSyncIpc.ts +++ /dev/null @@ -1,45 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; -import { IStringDictionary } from 'vs/base/common/collections'; -import { URI } from 'vs/base/common/uri'; -import { FormattingOptions } from 'vs/base/common/jsonFormatter'; - -export class UserDataSycnUtilServiceChannel implements IServerChannel { - - constructor(private readonly service: IUserDataSyncUtilService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]); - case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0])); - } - throw new Error('Invalid call'); - } -} - -export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { - - _serviceBrand: undefined; - - constructor(private readonly channel: IChannel) { - } - - async resolveUserBindings(userbindings: string[]): Promise> { - return this.channel.call('resolveUserKeybindings', [userbindings]); - } - - async resolveFormattingOptions(file: URI): Promise { - return this.channel.call('resolveFormattingOptions', [file]); - } - -} diff --git a/src/vs/platform/userDataSync/common/settingsMerge.ts b/src/vs/platform/userDataSync/common/settingsMerge.ts new file mode 100644 index 0000000000..5274945839 --- /dev/null +++ b/src/vs/platform/userDataSync/common/settingsMerge.ts @@ -0,0 +1,191 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as objects from 'vs/base/common/objects'; +import { parse, findNodeAtLocation, parseTree, Node } from 'vs/base/common/json'; +import { setProperty } from 'vs/base/common/jsonEdit'; +import { values } from 'vs/base/common/map'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; +import * as contentUtil from 'vs/platform/userDataSync/common/content'; + +export function computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[], formattingOptions: FormattingOptions): string { + if (ignoredSettings.length) { + const remote = parse(remoteContent); + const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); + for (const key of ignoredSettings) { + if (ignored.has(key)) { + localContent = contentUtil.edit(localContent, [key], remote[key], formattingOptions); + } + } + } + return localContent; +} + +export function merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[], formattingOptions: FormattingOptions): { mergeContent: string, hasChanges: boolean, hasConflicts: boolean } { + const local = parse(localContent); + const remote = parse(remoteContent); + const base = baseContent ? parse(baseContent) : null; + const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); + + const localToRemote = compare(local, remote, ignored); + if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { + // No changes found between local and remote. + return { mergeContent: localContent, hasChanges: false, hasConflicts: false }; + } + + const conflicts: Set = new Set(); + const baseToLocal = base ? compare(base, local, ignored) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToRemote = base ? compare(base, remote, ignored) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + let mergeContent = localContent; + + // Removed settings in Local + for (const key of values(baseToLocal.removed)) { + // Got updated in remote + if (baseToRemote.updated.has(key)) { + conflicts.add(key); + } + } + + // Removed settings in Remote + for (const key of values(baseToRemote.removed)) { + if (conflicts.has(key)) { + continue; + } + // Got updated in local + if (baseToLocal.updated.has(key)) { + conflicts.add(key); + } else { + mergeContent = contentUtil.edit(mergeContent, [key], undefined, formattingOptions); + } + } + + // Added settings in Local + for (const key of values(baseToLocal.added)) { + if (conflicts.has(key)) { + continue; + } + // Got added in remote + if (baseToRemote.added.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } + } + + // Added settings in remote + for (const key of values(baseToRemote.added)) { + if (conflicts.has(key)) { + continue; + } + // Got added in local + if (baseToLocal.added.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } else { + mergeContent = contentUtil.edit(mergeContent, [key], remote[key], formattingOptions); + } + } + + // Updated settings in Local + for (const key of values(baseToLocal.updated)) { + if (conflicts.has(key)) { + continue; + } + // Got updated in remote + if (baseToRemote.updated.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } + } + + // Updated settings in Remote + for (const key of values(baseToRemote.updated)) { + if (conflicts.has(key)) { + continue; + } + // Got updated in local + if (baseToLocal.updated.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } else { + mergeContent = contentUtil.edit(mergeContent, [key], remote[key], formattingOptions); + } + } + + if (conflicts.size > 0) { + const conflictNodes: { key: string, node: Node | undefined }[] = []; + const tree = parseTree(mergeContent); + const eol = formattingOptions.eol!; + for (const key of values(conflicts)) { + const node = findNodeAtLocation(tree, [key]); + conflictNodes.push({ key, node }); + } + conflictNodes.sort((a, b) => { + if (a.node && b.node) { + return b.node.offset - a.node.offset; + } + return a.node ? 1 : -1; + }); + const lastNode = tree.children ? tree.children[tree.children.length - 1] : undefined; + for (const { key, node } of conflictNodes) { + const remoteEdit = setProperty(`{${eol}\t${eol}}`, [key], remote[key], { tabSize: 4, insertSpaces: false, eol: eol })[0]; + const remoteContent = remoteEdit ? `${remoteEdit.content.substring(remoteEdit.offset + remoteEdit.length + 1)},${eol}` : ''; + if (node) { + // Updated in Local and Remote with different value + const localStartOffset = contentUtil.getLineStartOffset(mergeContent, eol, node.parent!.offset); + const localEndOffset = contentUtil.getLineEndOffset(mergeContent, eol, node.offset + node.length); + mergeContent = mergeContent.substring(0, localStartOffset) + + `<<<<<<< local${eol}` + + mergeContent.substring(localStartOffset, localEndOffset) + + `${eol}=======${eol}${remoteContent}>>>>>>> remote` + + mergeContent.substring(localEndOffset); + } else { + // Removed in Local, but updated in Remote + if (lastNode) { + const localStartOffset = contentUtil.getLineEndOffset(mergeContent, eol, lastNode.offset + lastNode.length); + mergeContent = mergeContent.substring(0, localStartOffset) + + `${eol}<<<<<<< local${eol}=======${eol}${remoteContent}>>>>>>> remote` + + mergeContent.substring(localStartOffset); + } else { + const localStartOffset = tree.offset + 1; + mergeContent = mergeContent.substring(0, localStartOffset) + + `${eol}<<<<<<< local${eol}=======${eol}${remoteContent}>>>>>>> remote${eol}` + + mergeContent.substring(localStartOffset); + } + } + } + } + + return { mergeContent, hasChanges: true, hasConflicts: conflicts.size > 0 }; +} + +function compare(from: IStringDictionary, to: IStringDictionary, ignored: Set): { added: Set, removed: Set, updated: Set } { + const fromKeys = Object.keys(from).filter(key => !ignored.has(key)); + const toKeys = Object.keys(to).filter(key => !ignored.has(key)); + const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const updated: Set = new Set(); + + for (const key of fromKeys) { + if (removed.has(key)) { + continue; + } + const value1 = from[key]; + const value2 = to[key]; + if (!objects.equals(value1, value2)) { + updated.add(key); + } + } + + return { added, removed, updated }; +} diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index a9bd2cd61f..b94999ed40 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files'; -import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, ISettingsMergeService, IUserDataSyncStoreService, DEFAULT_IGNORED_SETTINGS, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, IUserDataSyncStoreService, DEFAULT_IGNORED_SETTINGS, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { VSBuffer } from 'vs/base/common/buffer'; import { parse, ParseError } from 'vs/base/common/json'; import { localize } from 'vs/nls'; @@ -17,6 +17,8 @@ import { joinPath, dirname } from 'vs/base/common/resources'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { startsWith } from 'vs/base/common/strings'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { computeRemoteContent, merge } from 'vs/platform/userDataSync/common/settingsMerge'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; interface ISyncPreviewResult { readonly fileContent: IFileContent | null; @@ -46,8 +48,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { @IFileService private readonly fileService: IFileService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, - @ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService, @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, + @IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService, @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); @@ -148,7 +150,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { await this.writeToLocal(content, fileContent); } if (hasRemoteChanged) { - const remoteContent = remoteUserData.content ? await this.settingsMergeService.computeRemoteContent(content, remoteUserData.content, this.getIgnoredSettings(content)) : content; + const formatUtils = await this.getFormattingOptions(); + const remoteContent = remoteUserData.content ? computeRemoteContent(content, remoteUserData.content, this.getIgnoredSettings(content), formatUtils) : content; this.logService.info('Settings: Updating remote settings'); const ref = await this.writeToRemote(remoteContent, remoteUserData.ref); remoteUserData = { ref, content }; @@ -205,7 +208,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { || lastSyncData.content !== remoteContent // Remote has forwarded ) { this.logService.trace('Settings: Merging remote settings with local settings...'); - const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings()); + const formatUtils = await this.getFormattingOptions(); + const result = merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings(), formatUtils); // Sync only if there are changes if (result.hasChanges) { hasLocalChanged = result.mergeContent !== localContent; @@ -230,6 +234,14 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; } + private _formattingOptions: Promise | undefined = undefined; + private getFormattingOptions(): Promise { + if (!this._formattingOptions) { + this._formattingOptions = this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.settingsResource); + } + return this._formattingOptions; + } + private getIgnoredSettings(settingsContent?: string): string[] { let value: string[] = []; if (settingsContent) { diff --git a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts b/src/vs/platform/userDataSync/common/settingsSyncIpc.ts deleted file mode 100644 index 392eab5da1..0000000000 --- a/src/vs/platform/userDataSync/common/settingsSyncIpc.ts +++ /dev/null @@ -1,42 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync'; - -export class SettingsMergeChannel implements IServerChannel { - - constructor(private readonly service: ISettingsMergeService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(context: any, command: string, args?: any): Promise { - switch (command) { - case 'merge': return this.service.merge(args[0], args[1], args[2], args[3]); - case 'computeRemoteContent': return this.service.computeRemoteContent(args[0], args[1], args[2]); - } - throw new Error('Invalid call'); - } -} - -export class SettingsMergeChannelClient implements ISettingsMergeService { - - _serviceBrand: undefined; - - constructor(private readonly channel: IChannel) { - } - - merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { - return this.channel.call('merge', [localContent, remoteContent, baseContent, ignoredSettings]); - } - - computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise { - return this.channel.call('computeRemoteContent', [localContent, remoteContent, ignoredSettings]); - } - -} diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 683627ecca..6d62118c4b 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -177,18 +177,6 @@ export interface IUserDataSyncService extends ISynchroniser { removeExtension(identifier: IExtensionIdentifier): Promise; } -export const ISettingsMergeService = createDecorator('ISettingsMergeService'); - -export interface ISettingsMergeService { - - _serviceBrand: undefined; - - merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }>; - - computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise; - -} - export const IUserDataSyncUtilService = createDecorator('IUserDataSyncUtilService'); export interface IUserDataSyncUtilService { diff --git a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts index 0a3e193c95..f800257dab 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncIpc.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncIpc.ts @@ -3,9 +3,12 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; -import { IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; +import { URI } from 'vs/base/common/uri'; +import { IStringDictionary } from 'vs/base/common/collections'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; export class UserDataSyncChannel implements IServerChannel { @@ -30,3 +33,38 @@ export class UserDataSyncChannel implements IServerChannel { throw new Error('Invalid call'); } } + +export class UserDataSycnUtilServiceChannel implements IServerChannel { + + constructor(private readonly service: IUserDataSyncUtilService) { } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]); + case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0])); + } + throw new Error('Invalid call'); + } +} + +export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService { + + _serviceBrand: undefined; + + constructor(private readonly channel: IChannel) { + } + + async resolveUserBindings(userbindings: string[]): Promise> { + return this.channel.call('resolveUserKeybindings', [userbindings]); + } + + async resolveFormattingOptions(file: URI): Promise { + return this.channel.call('resolveFormattingOptions', [file]); + } + +} + diff --git a/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts new file mode 100644 index 0000000000..0e4e7d9147 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/extensionsMerge.test.ts @@ -0,0 +1,500 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ISyncExtension } from 'vs/platform/userDataSync/common/userDataSync'; +import { merge } from 'vs/platform/userDataSync/common/extensionsMerge'; + +suite('ExtensionsMerge - No Conflicts', () => { + + test('merge returns local extension if remote does not exist', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, null, null, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, localExtensions); + }); + + test('merge returns local extension if remote does not exist with ignored extensions', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, null, null, [], ['a']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge returns local extension if remote does not exist with ignored extensions (ignore case)', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, null, null, [], ['A']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge returns local extension if remote does not exist with skipped extensions', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const skippedExtension: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, null, null, skippedExtension, []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge returns local extension if remote does not exist with skipped and ignored extensions', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const skippedExtension: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, null, null, skippedExtension, ['a']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when there is no base', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, enabled: true }, { identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when there is no base and with ignored extensions', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], ['a']); + + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, enabled: true }, { identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when remote is moved forwarded', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, enabled: true }, { identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }, { id: 'd', uuid: 'd' }]); + assert.deepEqual(actual.updated, []); + assert.equal(actual.remote, null); + }); + + test('merge local and remote extensions when remote moved forwarded with ignored extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a']); + + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, enabled: true }, { identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); + assert.deepEqual(actual.updated, []); + assert.equal(actual.remote, null); + }); + + test('merge local and remote extensions when remote is moved forwarded with skipped extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'b', uuid: 'b' }, enabled: true }, { identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); + assert.deepEqual(actual.updated, []); + assert.equal(actual.remote, null); + }); + + test('merge local and remote extensions when remote is moved forwarded with skipped and ignored extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['b']); + + assert.deepEqual(actual.added, [{ identifier: { id: 'c', uuid: 'c' }, enabled: true }]); + assert.deepEqual(actual.removed, [{ id: 'd', uuid: 'd' }]); + assert.deepEqual(actual.updated, []); + assert.equal(actual.remote, null); + }); + + test('merge local and remote extensions when local is moved forwarded', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, localExtensions); + }); + + test('merge local and remote extensions when local is moved forwarded with ignored settings', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['b']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, [ + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]); + }); + + test('merge local and remote extensions when local is moved forwarded with skipped extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when local is moved forwarded with skipped and ignored extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['c']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when both moved forwarded', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, enabled: true }]); + assert.deepEqual(actual.removed, [{ id: 'a', uuid: 'a' }]); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when both moved forwarded with ignored extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, [], ['a', 'e']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when both moved forwarded with skipped extensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'e', uuid: 'e' }, enabled: true }]); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge local and remote extensions when both moved forwarded with skipped and ignoredextensions', async () => { + const baseExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const skippedExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + ]; + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'e', uuid: 'e' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, baseExtensions, skippedExtensions, ['e']); + + assert.deepEqual(actual.added, []); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + test('merge when remote extension has no uuid and different extension id case', async () => { + const localExtensions: ISyncExtension[] = [ + { identifier: { id: 'a', uuid: 'a' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + const remoteExtensions: ISyncExtension[] = [ + { identifier: { id: 'A' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + ]; + const expected: ISyncExtension[] = [ + { identifier: { id: 'A' }, enabled: true }, + { identifier: { id: 'd', uuid: 'd' }, enabled: true }, + { identifier: { id: 'b', uuid: 'b' }, enabled: true }, + { identifier: { id: 'c', uuid: 'c' }, enabled: true }, + ]; + + const actual = merge(localExtensions, remoteExtensions, null, [], []); + + assert.deepEqual(actual.added, [{ identifier: { id: 'd', uuid: 'd' }, enabled: true }]); + assert.deepEqual(actual.removed, []); + assert.deepEqual(actual.updated, []); + assert.deepEqual(actual.remote, expected); + }); + + +}); diff --git a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts index 59c706f40f..dcff11676a 100644 --- a/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts +++ b/src/vs/platform/userDataSync/test/common/keybindingsMerge.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge'; import { IStringDictionary } from 'vs/base/common/collections'; -import { OperatingSystem, OS } from 'vs/base/common/platform'; import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { URI } from 'vs/base/common/uri'; @@ -441,7 +440,7 @@ suite('KeybindingsMerge - No Conflicts', () => { }); -suite.skip('KeybindingsMerge - Conflicts', () => { +suite('KeybindingsMerge - Conflicts', () => { test('merge when local and remote with one entry but different value', async () => { const localContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]); @@ -730,6 +729,6 @@ class MockUserDataSyncUtilService implements IUserDataSyncUtilService { } async resolveFormattingOptions(file?: URI): Promise { - return { eol: OS === OperatingSystem.Windows ? '\r\n' : '\n', insertSpaces: false, tabSize: 4 }; + return { eol: '\n', insertSpaces: false, tabSize: 4 }; } } diff --git a/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts new file mode 100644 index 0000000000..e2e792b841 --- /dev/null +++ b/src/vs/platform/userDataSync/test/common/settingsMerge.test.ts @@ -0,0 +1,547 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { merge, computeRemoteContent } from 'vs/platform/userDataSync/common/settingsMerge'; + +const formattingOptions = { eol: '\n', insertSpaces: false, tabSize: 4 }; + +suite('SettingsMerge - No Conflicts', () => { + + test('merge when local and remote are same with one entry', async () => { + const localContent = stringify({ 'a': 1 }); + const remoteContent = stringify({ 'a': 1 }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when local and remote are same with multiple entries', async () => { + const localContent = stringify({ + 'a': 1, + 'b': 2 + }); + const remoteContent = stringify({ + 'a': 1, + 'b': 2 + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when local and remote are same with multiple entries in different order', async () => { + const localContent = stringify({ + 'b': 2, + 'a': 1, + }); + const remoteContent = stringify({ + 'a': 1, + 'b': 2 + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when local and remote are same with different base content', async () => { + const localContent = stringify({ + 'b': 2, + 'a': 1, + }); + const baseContent = stringify({ + 'a': 2, + 'b': 1 + }); + const remoteContent = stringify({ + 'a': 1, + 'b': 2 + }); + const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when a new entry is added to remote', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({ + 'a': 1, + 'b': 2 + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, remoteContent); + }); + + test('merge when multiple new entries are added to remote', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({ + 'b': 2, + 'a': 1, + 'c': 3, + }); + const expected = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, expected); + }); + + test('merge when multiple new entries are added to remote from base and local has not changed', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({ + 'b': 2, + 'a': 1, + 'c': 3, + }); + const expected = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + }); + const actual = merge(localContent, remoteContent, localContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, expected); + }); + + test('merge when an entry is removed from remote from base and local has not changed', async () => { + const localContent = stringify({ + 'a': 1, + 'b': 2, + }); + const remoteContent = stringify({ + 'a': 1, + }); + const actual = merge(localContent, remoteContent, localContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, remoteContent); + }); + + test('merge when all entries are removed from base and local has not changed', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({}); + const actual = merge(localContent, remoteContent, localContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.deepEqual(JSON.parse(actual.mergeContent), {}); + }); + + test('merge when an entry is updated in remote from base and local has not changed', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({ + 'a': 2 + }); + const actual = merge(localContent, remoteContent, localContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, remoteContent); + }); + + test('merge when remote has moved forwareded with multiple changes and local stays with base', async () => { + const localContent = stringify({ + 'a': 1, + }); + const remoteContent = stringify({ + 'a': 2, + 'b': 1, + 'c': 3, + 'd': 4, + }); + const actual = merge(localContent, remoteContent, localContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, remoteContent); + }); + + test('merge when a new entries are added to local', async () => { + const localContent = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + 'd': 4, + }); + const remoteContent = stringify({ + 'a': 1, + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when multiple new entries are added to local from base and remote is not changed', async () => { + const localContent = stringify({ + 'a': 2, + 'b': 1, + 'c': 3, + 'd': 4, + }); + const remoteContent = stringify({ + 'a': 1, + }); + const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when an entry is removed from local from base and remote has not changed', async () => { + const localContent = stringify({ + 'a': 1, + 'c': 2 + }); + const remoteContent = stringify({ + 'a': 2, + 'b': 1, + 'c': 3, + 'd': 4, + }); + const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when an entry is updated in local from base and remote has not changed', async () => { + const localContent = stringify({ + 'a': 1, + 'c': 2 + }); + const remoteContent = stringify({ + 'a': 2, + 'c': 2, + }); + const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('merge when local has moved forwarded with multiple changes and remote stays with base', async () => { + const localContent = stringify({ + 'a': 2, + 'b': 1, + 'c': 3, + 'd': 4, + }); + const remoteContent = stringify({ + 'a': 1, + }); + const actual = merge(localContent, remoteContent, remoteContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + +}); + +suite('SettingsMerge - Conflicts', () => { + + test('merge when local and remote with one entry but different value', async () => { + const localContent = stringify({ + 'a': 1 + }); + const remoteContent = stringify({ + 'a': 2 + }); + const actual = merge(localContent, remoteContent, null, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(actual.hasConflicts); + assert.equal(actual.mergeContent, + `{ +<<<<<<< local + "a": 1 +======= + "a": 2, +>>>>>>> remote +}`); + }); + + test('merge when the entry is removed in remote but updated in local and a new entry is added in remote', async () => { + const baseContent = stringify({ + 'a': 1 + }); + const localContent = stringify({ + 'a': 2 + }); + const remoteContent = stringify({ + 'b': 2 + }); + const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(actual.hasConflicts); + assert.equal(actual.mergeContent, + `{ +<<<<<<< local + "a": 2, +======= +>>>>>>> remote + "b": 2 +}`); + }); + + test('merge with single entry and local is empty', async () => { + const baseContent = stringify({ + 'a': 1 + }); + const localContent = stringify({}); + const remoteContent = stringify({ + 'a': 2 + }); + const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(actual.hasConflicts); + assert.equal(actual.mergeContent, + `{ +<<<<<<< local +======= + "a": 2, +>>>>>>> remote +}`); + }); + + test('merge when local and remote has moved forwareded with conflicts', async () => { + const baseContent = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + 'd': 4, + }); + const localContent = stringify({ + 'a': 2, + 'c': 3, + 'd': 5, + 'e': 4, + 'f': 1, + }); + const remoteContent = stringify({ + 'b': 3, + 'c': 3, + 'd': 6, + 'e': 5, + }); + const actual = merge(localContent, remoteContent, baseContent, [], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(actual.hasConflicts); + assert.equal(actual.mergeContent, + `{ +<<<<<<< local + "a": 2, +======= +>>>>>>> remote + "c": 3, +<<<<<<< local + "d": 5, +======= + "d": 6, +>>>>>>> remote +<<<<<<< local + "e": 4, +======= + "e": 5, +>>>>>>> remote + "f": 1 +<<<<<<< local +======= + "b": 3, +>>>>>>> remote +}`); + }); + +}); + +suite('SettingsMerge - Ignored Settings', () => { + + test('ignored setting is not merged when changed in local and remote', async () => { + const localContent = stringify({ 'a': 1 }); + const remoteContent = stringify({ 'a': 2 }); + const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged when changed in local and remote from base', async () => { + const baseContent = stringify({ 'a': 0 }); + const localContent = stringify({ 'a': 1 }); + const remoteContent = stringify({ 'a': 2 }); + const actual = merge(localContent, remoteContent, baseContent, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged when added in remote', async () => { + const localContent = stringify({}); + const remoteContent = stringify({ 'a': 1 }); + const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged when added in remote from base', async () => { + const localContent = stringify({ 'b': 2 }); + const remoteContent = stringify({ 'a': 1, 'b': 2 }); + const actual = merge(localContent, remoteContent, localContent, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged when removed in remote', async () => { + const localContent = stringify({ 'a': 1 }); + const remoteContent = stringify({}); + const actual = merge(localContent, remoteContent, null, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged when removed in remote from base', async () => { + const localContent = stringify({ 'a': 2 }); + const remoteContent = stringify({}); + const actual = merge(localContent, remoteContent, localContent, ['a'], formattingOptions); + assert.ok(!actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, localContent); + }); + + test('ignored setting is not merged with other changes without conflicts', async () => { + const baseContent = stringify({ + 'a': 2, + 'b': 2, + 'c': 3, + 'd': 4, + 'e': 5, + }); + const localContent = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + }); + const remoteContent = stringify({ + 'a': 3, + 'b': 3, + 'd': 4, + 'e': 6, + }); + const expectedContent = stringify({ + 'a': 1, + 'b': 3, + }); + const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], formattingOptions); + assert.ok(actual.hasChanges); + assert.ok(!actual.hasConflicts); + assert.equal(actual.mergeContent, expectedContent); + }); + + test('ignored setting is not merged with other changes conflicts', async () => { + const baseContent = stringify({ + 'a': 2, + 'b': 2, + 'c': 3, + 'd': 4, + 'e': 5, + }); + const localContent = stringify({ + 'a': 1, + 'b': 4, + 'c': 3, + 'd': 5, + }); + const remoteContent = stringify({ + 'a': 3, + 'b': 3, + 'e': 6, + }); + const actual = merge(localContent, remoteContent, baseContent, ['a', 'e'], formattingOptions); + //'{\n\t"a": 1,\n\n<<<<<<< local\t"b": 4,\n=======\n\t"b": 3,\n>>>>>>> remote' + //'{\n\t"a": 1,\n<<<<<<< local\n\t"b": 4,\n=======\n\t"b": 3,\n>>>>>>> remote\n<<<<<<< local\n\t"d": 5\n=======\n>>>>>>> remote\n}' + assert.ok(actual.hasChanges); + assert.ok(actual.hasChanges); + assert.ok(actual.hasConflicts); + assert.equal(actual.mergeContent, + `{ + "a": 1, +<<<<<<< local + "b": 4, +======= + "b": 3, +>>>>>>> remote +<<<<<<< local + "d": 5 +======= +>>>>>>> remote +}`); + }); + +}); + +suite('SettingsMerge - Compute Remote Content', () => { + + test('local content is returned when there are no ignored settings', async () => { + const localContent = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + }); + const remoteContent = stringify({ + 'a': 3, + 'b': 3, + 'd': 4, + 'e': 6, + }); + const actual = computeRemoteContent(localContent, remoteContent, [], formattingOptions); + assert.equal(actual, localContent); + }); + + test('ignored settings are not updated from remote content', async () => { + const localContent = stringify({ + 'a': 1, + 'b': 2, + 'c': 3, + }); + const remoteContent = stringify({ + 'a': 3, + 'b': 3, + 'd': 4, + 'e': 6, + }); + const expected = stringify({ + 'a': 3, + 'b': 2, + 'c': 3, + }); + const actual = computeRemoteContent(localContent, remoteContent, ['a'], formattingOptions); + assert.equal(actual, expected); + }); + +}); + +function stringify(value: any): string { + return JSON.stringify(value, null, '\t'); +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index c5a370841e..e6b3584c96 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -91,7 +91,7 @@ export interface IWindowSettings { titleBarStyle: 'native' | 'custom'; autoDetectHighContrast: boolean; menuBarVisibility: MenuBarVisibility; - newWindowDimensions: 'default' | 'inherit' | 'maximized' | 'fullscreen'; + newWindowDimensions: 'default' | 'inherit' | 'offset' | 'maximized' | 'fullscreen'; nativeTabs: boolean; nativeFullScreen: boolean; enableMenuBarMnemonics: boolean; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index f0612bd763..8c5fcbbad4 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1381,7 +1381,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic // Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen let allowFullscreen: boolean; if (state.hasDefaultState) { - allowFullscreen = (windowConfig?.newWindowDimensions && ['fullscreen', 'inherit'].indexOf(windowConfig.newWindowDimensions) >= 0); + allowFullscreen = (windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0); } // Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore @@ -1576,7 +1576,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic } else if (windowConfig.newWindowDimensions === 'fullscreen') { state.mode = WindowMode.Fullscreen; ensureNoOverlap = false; - } else if (windowConfig.newWindowDimensions === 'inherit' && lastActive) { + } else if ((windowConfig.newWindowDimensions === 'inherit' || windowConfig.newWindowDimensions === 'offset') && lastActive) { const lastActiveState = lastActive.serializeWindowState(); if (lastActiveState.mode === WindowMode.Fullscreen) { state.mode = WindowMode.Fullscreen; // only take mode (fixes https://github.com/Microsoft/vscode/issues/19331) @@ -1584,7 +1584,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic state = lastActiveState; } - ensureNoOverlap = false; + ensureNoOverlap = state.mode !== WindowMode.Fullscreen && windowConfig.newWindowDimensions === 'offset'; } } diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 2af0214a4d..6a418ec92a 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -2341,6 +2341,12 @@ declare module 'vscode' { */ export class MarkdownString { + /** + * Escapes any [ThemeIcons](#ThemeIcon), e.g. `$(zap)`, in the string. + * @param value A string. + */ + static escapeThemeIcons(value: string): string; + /** * The markdown string. */ @@ -2356,8 +2362,9 @@ declare module 'vscode' { * Creates a new markdown string with the given value. * * @param value Optional, initial value. + * @param options Optional, options to specify whether [ThemeIcons](#ThemeIcon) are supported within the [`MarkdownString`](#MarkdownString). */ - constructor(value?: string); + constructor(value?: string, options?: { supportThemeIcons?: boolean }); /** * Appends and escapes the given string to this markdown string. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index f15a39e735..9bf2d04326 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -16,7 +16,7 @@ declare module 'vscode' { - //#region Alex - resolvers + //#region Alex - resolvers, AlexR - ports export interface RemoteAuthorityResolverContext { resolveAttempt: number; @@ -33,7 +33,30 @@ declare module 'vscode' { extensionHostEnv?: { [key: string]: string | null }; } - export type ResolverResult = ResolvedAuthority & ResolvedOptions; + export interface TunnelOptions { + remote: { port: number, host: string }; + localPort?: number; + name?: string; + } + + export interface Tunnel extends Disposable { + remote: { port: number, host: string }; + localAddress: string; + } + + /** + * Used as part of the ResolverResult if the extension has any candidate, published, or forwarded ports. + */ + export interface TunnelInformation { + /** + * Tunnels that are detected by the extension. The remotePort is used for display purposes. + * The localAddress should be the complete local address(ex. localhost:1234) for connecting to the port. Tunnels provided through + * detected are read-only from the forwarded ports UI. + */ + detectedTunnels?: { remote: { port: number, host: string }, localAddress: string }[]; + } + + export type ResolverResult = ResolvedAuthority & ResolvedOptions & TunnelInformation; export class RemoteAuthorityResolverError extends Error { static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError; @@ -44,6 +67,20 @@ declare module 'vscode' { export interface RemoteAuthorityResolver { resolve(authority: string, context: RemoteAuthorityResolverContext): ResolverResult | Thenable; + /** + * Can be optionally implemented if the extension can forward ports better than the core. + * When not implemented, the core will use its default forwarding logic. + * When implemented, the core will use this to forward ports. + */ + forwardPort?(tunnelOptions: TunnelOptions): Thenable; + } + + export namespace workspace { + /** + * Forwards a port. Currently only works for a remote host of localhost. + * @param forward The `localPort` is a suggestion only. If that port is not available another will be chosen. + */ + export function makeTunnel(forward: TunnelOptions): Thenable; } export interface ResourceLabelFormatter { @@ -133,12 +170,15 @@ declare module 'vscode' { */ export interface SemanticTokensProvider { /** - * A file can contain many tokens, perhaps even hundreds of thousands tokens. Therefore, to improve - * the memory consumption around describing semantic tokens, we have decided to avoid allocating objects - * and we have decided to represent tokens from a file as an array of integers. + * A file can contain many tokens, perhaps even hundreds of thousands of tokens. Therefore, to improve + * the memory consumption around describing semantic tokens, we have decided to avoid allocating an object + * for each token and we represent tokens from a file as an array of integers. Furthermore, the position + * of each token is expressed relative to the token before it because most tokens remain stable relative to + * each other when edits are made in a file. * * - * In short, each token takes 5 integers to represent, so a specific token i in the file consists of the following fields: + * --- + * In short, each token takes 5 integers to represent, so a specific token `i` in the file consists of the following fields: * - at index `5*i` - `deltaLine`: token line number, relative to the previous token * - at index `5*i+1` - `deltaStart`: token start character, relative to the previous token (relative to 0 or the previous token's start if they are on the same line) * - at index `5*i+2` - `length`: the length of the token. A token cannot be multiline. @@ -146,87 +186,119 @@ declare module 'vscode' { * - at index `5*i+4` - `tokenModifiers`: each set bit will be looked up in `SemanticTokensLegend.tokenModifiers` * * + * + * --- + * ### How to encode tokens + * * Here is an example for encoding a file with 3 tokens: * ``` - * [ { line: 2, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] }, + * { line: 2, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] }, * { line: 2, startChar: 10, length: 4, tokenType: "types", tokenModifiers: [] }, - * { line: 5, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } ] + * { line: 5, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } * ``` * * 1. First of all, a legend must be devised. This legend must be provided up-front and capture all possible token types. - * For this example, we will choose the following legend which is passed in when registering the provider: + * For this example, we will choose the following legend which must be passed in when registering the provider: * ``` - * { tokenTypes: ['', 'properties', 'types', 'classes'], - * tokenModifiers: ['', 'private', 'static'] } + * tokenTypes: ['properties', 'types', 'classes'], + * tokenModifiers: ['private', 'static'] * ``` * - * 2. The first transformation is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked + * 2. The first transformation step is to encode `tokenType` and `tokenModifiers` as integers using the legend. Token types are looked * up by index, so a `tokenType` value of `1` means `tokenTypes[1]`. Multiple token modifiers can be set by using bit flags, - * so a `tokenModifier` value of `6` is first viewed as binary `0b110`, which means `[tokenModifiers[1], tokenModifiers[2]]` because - * bits 1 and 2 are set. Using this legend, the tokens now are: + * so a `tokenModifier` value of `3` is first viewed as binary `0b00000011`, which means `[tokenModifiers[0], tokenModifiers[1]]` because + * bits 0 and 1 are set. Using this legend, the tokens now are: * ``` - * [ { line: 2, startChar: 5, length: 3, tokenType: 1, tokenModifiers: 6 }, // 6 is 0b110 - * { line: 2, startChar: 10, length: 4, tokenType: 2, tokenModifiers: 0 }, - * { line: 5, startChar: 2, length: 7, tokenType: 3, tokenModifiers: 0 } ] + * { line: 2, startChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { line: 2, startChar: 10, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { line: 5, startChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } * ``` * - * 3. Then, we will encode each token relative to the previous token in the file: + * 3. The next steps is to encode each token relative to the previous token in the file. In this case, the second token + * is on the same line as the first token, so the `startChar` of the second token is made relative to the `startChar` + * of the first token, so it will be `10 - 5`. The third token is on a different line than the second token, so the + * `startChar` of the third token will not be altered: * ``` - * [ { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 1, tokenModifiers: 6 }, - * // this token is on the same line as the first one, so the startChar is made relative - * { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 2, tokenModifiers: 0 }, - * // this token is on a different line than the second one, so the startChar remains unchanged - * { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 3, tokenModifiers: 0 } ] + * { deltaLine: 2, deltaStartChar: 5, length: 3, tokenType: 0, tokenModifiers: 3 }, + * { deltaLine: 0, deltaStartChar: 5, length: 4, tokenType: 1, tokenModifiers: 0 }, + * { deltaLine: 3, deltaStartChar: 2, length: 7, tokenType: 2, tokenModifiers: 0 } * ``` * - * 4. Finally, the integers are organized in a single array, which is a memory friendly representation: + * 4. Finally, the last step is to inline each of the 5 fields for a token in a single array, which is a memory friendly representation: * ``` * // 1st token, 2nd token, 3rd token - * [ 2,5,3,1,6, 0,5,4,2,0, 3,2,7,3,0 ] + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] * ``` * - * In principle, each call to `provideSemanticTokens` expects a complete representations of the semantic tokens. - * It is possible to simply return all the tokens at each call. * - * But oftentimes, a small edit in the file will result in a small change to the above delta-based represented tokens. - * (In fact, that is why the above tokens are delta-encoded relative to their corresponding previous tokens). - * In such a case, if VS Code passes in the previous result id, it is possible for an advanced tokenization provider - * to return a delta to the integers array. * - * To continue with the previous example, suppose a new line has been pressed at the beginning of the file, such that - * all the tokens are now one line lower, and that a new token has appeared since the last result on line 4. - * For example, the tokens might look like: + * --- + * ### How tokens change when the document changes + * + * Let's look at how tokens might change. + * + * Continuing with the above example, suppose a new line was inserted at the top of the file. + * That would make all the tokens move down by one line (notice how the line has changed for each one): * ``` - * [ { line: 3, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] }, + * { line: 3, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] }, + * { line: 3, startChar: 10, length: 4, tokenType: "types", tokenModifiers: [] }, + * { line: 6, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } + * ``` + * The integer encoding of the tokens does not change substantially because of the delta-encoding of positions: + * ``` + * // 1st token, 2nd token, 3rd token + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * ``` + * It is possible to express these new tokens in terms of an edit applied to the previous tokens: + * ``` + * [ 2,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * + * edit: { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3 + * ``` + * + * Furthermore, let's assume that a new token has appeared on line 4: + * ``` + * { line: 3, startChar: 5, length: 3, tokenType: "properties", tokenModifiers: ["private", "static"] }, * { line: 3, startChar: 10, length: 4, tokenType: "types", tokenModifiers: [] }, * { line: 4, startChar: 3, length: 5, tokenType: "properties", tokenModifiers: ["static"] }, - * { line: 6, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } ] + * { line: 6, startChar: 2, length: 7, tokenType: "classes", tokenModifiers: [] } + * ``` + * The integer encoding of the tokens is: + * ``` + * // 1st token, 2nd token, 3rd token, 4th token + * [ 3,5,3,0,3, 0,5,4,1,0, 1,3,5,0,2, 2,2,7,2,0, ] + * ``` + * Again, it is possible to express these new tokens in terms of an edit applied to the previous tokens: + * ``` + * [ 3,5,3,0,3, 0,5,4,1,0, 3,2,7,2,0 ] + * [ 3,5,3,0,3, 0,5,4,1,0, 1,3,5,0,2, 2,2,7,2,0, ] + * + * edit: { start: 10, deleteCount: 1, data: [1,3,5,0,2,2] } // replace integer at offset 10 with [1,3,5,0,2,2] * ``` * - * The integer encoding of all new tokens would be: - * ``` - * [ 3,5,3,1,6, 0,5,4,2,0, 1,3,5,1,2, 2,2,7,3,0 ] - * ``` * - * A smart tokens provider can return a `resultId` to `SemanticTokens`. Then, if the editor still has in memory the previous - * result, the editor will pass in options the previous result id at `SemanticTokensRequestOptions.previousResultId`. Only when - * the editor passes in the previous result id, it is safe and smart for a smart tokens provider can compute a diff from the - * previous result to the new result. * - * *NOTE*: It is illegal to return `SemanticTokensEdits` if `options.previousResultId` is not set! + * --- + * ### When to return `SemanticTokensEdits` * - * ``` - * [ 2,5,3,1,6, 0,5,4,2,0, 3,2,7,3,0 ] - * [ 3,5,3,1,6, 0,5,4,2,0, 1,3,5,1,2, 2,2,7,3,0 ] - * ``` - * and return as simple integer edits the diff: - * ``` - * { edits: [ - * { start: 0, deleteCount: 1, data: [3] } // replace integer at offset 0 with 3 - * { start: 10, deleteCount: 1, data: [1,3,5,1,2,2] } // replace integer at offset 10 with [1,3,5,1,2,2] - * ]} - * ``` - * All indices expressed in the returned diff represent indices in the old result array, so they all refer to the previous result state. + * When doing edits, it is possible that multiple edits occur until VS Code decides to invoke the semantic tokens provider. + * In principle, each call to `provideSemanticTokens` can return a full representations of the semantic tokens, and that would + * be a perfectly reasonable semantic tokens provider implementation. + * + * However, when having a language server running in a separate process, transferring all the tokens between processes + * might be slow, so VS Code allows to return the new tokens expressed in terms of multiple edits applied to the previous + * tokens. + * + * To clearly define what "previous tokens" means, it is possible to return a `resultId` with the semantic tokens. If the + * editor still has in memory the previous result, the editor will pass in options the previous `resultId` at + * `SemanticTokensRequestOptions.previousResultId`. Only when the editor passes in the previous `resultId`, it is allowed + * that a semantic tokens provider returns the new tokens expressed as edits to be applied to the previous result. Even in this + * case, the semantic tokens provider needs to return a new `resultId` that will identify these new tokens as a basis + * for the next request. + * + * *NOTE 1*: It is illegal to return `SemanticTokensEdits` if `options.previousResultId` is not set. + * *NOTE 2*: All edits in `SemanticTokensEdits` contain indices in the old integers array, so they all refer to the previous result state. */ provideSemanticTokens(document: TextDocument, options: SemanticTokensRequestOptions, token: CancellationToken): ProviderResult; } @@ -1056,7 +1128,7 @@ declare module 'vscode' { } //#endregion - //#region Ben - status bar item with ID and Name + //#region Status bar item with ID and Name: https://github.com/microsoft/vscode/issues/74972 export namespace window { @@ -1104,26 +1176,6 @@ declare module 'vscode' { //#endregion - //#region Ben - extension auth flow (desktop+web) - - export interface AppUriOptions { - payload?: { - path?: string; - query?: string; - fragment?: string; - }; - } - - export namespace env { - - /** - * @deprecated use `vscode.env.asExternalUri` instead. - */ - export function createAppUri(options?: AppUriOptions): Thenable; - } - - //#endregion - //#region Custom editors: https://github.com/microsoft/vscode/issues/77131 /** @@ -1279,10 +1331,28 @@ declare module 'vscode' { /** * Marks that the code action cannot currently be applied. * - * This should be a human readable description of why the code action is currently disabled. Disabled code actions - * will be surfaced in the refactor UI but cannot be applied. + * Disabled code actions will be surfaced in the refactor UI but cannot be applied. */ - disabled?: string; + disabled?: { + /** + * Human readable description of why the code action is currently disabled. + * + * This is displayed in the UI. + */ + reason: string; + }; + } + + //#endregion + + //#region Allow theme icons in hovers: https://github.com/microsoft/vscode/issues/84695 + + export interface MarkdownString { + + /** + * Indicates that this markdown string can contain [ThemeIcons](#ThemeIcon), e.g. `$(zap)`. + */ + readonly supportThemeIcons?: boolean; } //#endregion diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 623b6d42af..9c02edb1eb 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -56,6 +56,7 @@ import './mainThreadWorkspace'; import './mainThreadComments'; // import './mainThreadTask'; {{SQL CARBON EDIT}} @anthonydresser comment out task import './mainThreadLabelService'; +import './mainThreadTunnelService'; import 'vs/workbench/api/common/apiCommands'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts index a2dc74aaab..c57c4d475b 100644 --- a/src/vs/workbench/api/browser/mainThreadDiagnostics.ts +++ b/src/vs/workbench/api/browser/mainThreadDiagnostics.ts @@ -33,7 +33,7 @@ export class MainThreadDiagnostics implements MainThreadDiagnosticsShape { this._activeOwners.clear(); } - private _forwardMarkers(resources: URI[]): void { + private _forwardMarkers(resources: readonly URI[]): void { const data: [UriComponents, IMarkerData[]][] = []; for (const resource of resources) { data.push([ diff --git a/src/vs/workbench/api/browser/mainThreadOutputService.ts b/src/vs/workbench/api/browser/mainThreadOutputService.ts index 6953d67357..27ad1fb6ff 100644 --- a/src/vs/workbench/api/browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/browser/mainThreadOutputService.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Registry } from 'vs/platform/registry/common/platform'; -import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/contrib/output/common/output'; +import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID } from 'vs/workbench/contrib/output/common/output'; +import { Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../common/extHost.protocol'; diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts new file mode 100644 index 0000000000..bf4a9de2e8 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MainThreadTunnelServiceShape, IExtHostContext, MainContext, ExtHostContext, ExtHostTunnelServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { TunnelOptions, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; + +@extHostNamedCustomer(MainContext.MainThreadTunnelService) +export class MainThreadTunnelService implements MainThreadTunnelServiceShape { + // @ts-ignore + private readonly _proxy: ExtHostTunnelServiceShape; + + constructor( + extHostContext: IExtHostContext, + @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService + ) { + this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTunnelService); + } + + async $openTunnel(tunnelOptions: TunnelOptions): Promise { + const tunnel = await this.remoteExplorerService.forward(tunnelOptions.remote.port, tunnelOptions.localPort, tunnelOptions.name); + if (tunnel) { + return { remote: { host: tunnel.tunnelRemoteHost, port: tunnel.tunnelRemotePort }, localAddress: tunnel.localAddress }; + } + return undefined; + } + + async $closeTunnel(remotePort: number): Promise { + return this.remoteExplorerService.close(remotePort); + } + + $addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): Promise { + return Promise.resolve(this.remoteExplorerService.addDetected(tunnels)); + } + + dispose(): void { + // + } +} diff --git a/src/vs/workbench/api/browser/mainThreadUrls.ts b/src/vs/workbench/api/browser/mainThreadUrls.ts index 6f266d61bb..4bee3e110f 100644 --- a/src/vs/workbench/api/browser/mainThreadUrls.ts +++ b/src/vs/workbench/api/browser/mainThreadUrls.ts @@ -72,16 +72,6 @@ export class MainThreadUrls implements MainThreadUrlsShape { return this.urlService.create(uri); } - async $proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial }): Promise { - const payload: Partial = options && options.payload ? options.payload : Object.create(null); - - // we define the authority to be the extension ID to ensure - // that the Uri gets routed back to the extension properly. - payload.authority = extensionId.value; - - return this.urlService.create(payload); - } - dispose(): void { this.handlers.forEach(({ disposable }) => disposable.dispose()); this.handlers.clear(); diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 2eeba105ba..6fcd443fc0 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -22,9 +22,8 @@ import { VIEWLET_ID as DEBUG } from 'vs/workbench/contrib/debug/common/debug'; import { VIEWLET_ID as REMOTE } from 'vs/workbench/contrib/remote/common/remote.contribution'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; -import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction, Viewlet } from 'vs/workbench/browser/viewlet'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -37,6 +36,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; export interface IUserFriendlyViewsContainerDescriptor { id: string; @@ -315,8 +315,24 @@ class ViewsExtensionHandler implements IWorkbenchContribution { viewContainer = this.viewContainersRegistry.registerViewContainer(id, true, extensionId); - // Register as viewlet - class CustomViewlet extends ViewContainerViewlet { + class CustomViewPaneContainer extends ViewPaneContainer { + constructor( + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IStorageService protected storageService: IStorageService, + @IConfigurationService configurationService: IConfigurationService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService, + ) { + super(id, `${id}.state`, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); + } + } + + // Register a viewlet + class CustomViewlet extends Viewlet { constructor( @IConfigurationService configurationService: IConfigurationService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -329,9 +345,10 @@ class ViewsExtensionHandler implements IWorkbenchContribution { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(id, `${id}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(id, instantiationService.createInstance(CustomViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); } } + const viewletDescriptor = ViewletDescriptor.create( CustomViewlet, id, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 30c266c720..2f7ddaa796 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -68,6 +68,7 @@ import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransf import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { find } from 'vs/base/common/arrays'; +import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -87,6 +88,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const rpcProtocol = accessor.get(IExtHostRpcService); const extHostStorage = accessor.get(IExtHostStorage); const extHostLogService = accessor.get(ILogService); + const extHostTunnelService = accessor.get(IExtHostTunnelService); // register addressable instances rpcProtocol.set(ExtHostContext.ExtHostLogService, extHostLogService); @@ -94,6 +96,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I rpcProtocol.set(ExtHostContext.ExtHostConfiguration, extHostConfiguration); rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService); rpcProtocol.set(ExtHostContext.ExtHostStorage, extHostStorage); + rpcProtocol.set(ExtHostContext.ExtHostTunnelService, extHostTunnelService); // automatically create and register addressable instances const extHostDecorations = rpcProtocol.set(ExtHostContext.ExtHostDecorations, accessor.get(IExtHostDecorations)); @@ -228,10 +231,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get appName() { return initData.environment.appName; }, get appRoot() { return initData.environment.appRoot!.fsPath; }, get uriScheme() { return initData.environment.appUriScheme; }, - createAppUri(options?) { - checkProposedApiEnabled(extension); - return extHostUrls.proposedCreateAppUri(extension.identifier, options); - }, get logLevel() { checkProposedApiEnabled(extension); return typeConverters.LogLevel.to(extHostLogService.getLevel()); @@ -506,7 +505,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostStatusBar.setStatusBarMessage(text, timeoutOrThenable); }, withScmProgress(task: (progress: vscode.Progress) => Thenable) { - console.warn(`[Deprecation Warning] function 'withScmProgress' is deprecated and should no longer be used. Use 'withProgress' instead.`); return extHostProgress.withProgress(extension, { location: extHostTypes.ProgressLocation.SourceControl }, (progress, token) => task({ report(n: number) { /*noop*/ } })); }, withProgress(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable) { @@ -565,7 +563,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I get rootPath() { if (extension.isUnderDevelopment && !warnedRootPathDeprecated) { warnedRootPathDeprecated = true; - console.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`); + extHostLogService.warn(`[Deprecation Warning] 'workspace.rootPath' is deprecated and should no longer be used. Please use 'workspace.workspaceFolders' instead. More details: https://aka.ms/vscode-eliminating-rootpath`); } return extHostWorkspace.getPath(); @@ -721,6 +719,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, onWillRenameFiles: (listener: (e: vscode.FileWillRenameEvent) => any, thisArg?: any, disposables?: vscode.Disposable[]) => { return extHostFileSystemEvent.getOnWillRenameFileEvent(extension)(listener, thisArg, disposables); + }, + makeTunnel: (forward: vscode.TunnelOptions) => { + checkProposedApiEnabled(extension); + return extHostTunnelService.makeTunnel(forward); } }; diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 40ae774638..3e38d688ab 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -47,6 +47,7 @@ import { createExtHostContextProxyIdentifier as createExtId, createMainContextPr import * as search from 'vs/workbench/services/search/common/search'; import { SaveReason } from 'vs/workbench/common/editor'; import { ExtensionActivationReason } from 'vs/workbench/api/common/extHostExtensionActivator'; +import { TunnelOptions, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService'; // {{SQL CARBON EDIT}} import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views'; @@ -90,6 +91,7 @@ export interface IInitData { telemetryInfo: ITelemetryInfo; logLevel: LogLevel; logsLocation: URI; + logFile: URI; autoStart: boolean; remote: { isRemote: boolean; authority: string | undefined; }; uiKind: UIKind; @@ -614,7 +616,6 @@ export interface MainThreadUrlsShape extends IDisposable { $registerUriHandler(handle: number, extensionId: ExtensionIdentifier): Promise; $unregisterUriHandler(handle: number): Promise; $createAppUri(uri: UriComponents): Promise; - $proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: { payload?: Partial; }): Promise; } export interface ExtHostUrlsShape { @@ -781,6 +782,12 @@ export interface MainThreadWindowShape extends IDisposable { $asExternalUri(uri: UriComponents, options: IOpenUriOptions): Promise; } +export interface MainThreadTunnelServiceShape extends IDisposable { + $openTunnel(tunnelOptions: TunnelOptions): Promise; + $closeTunnel(remotePort: number): Promise; + $addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): Promise; +} + // -- extension host export interface ExtHostCommandsShape { @@ -1395,6 +1402,11 @@ export interface ExtHostStorageShape { $acceptValue(shared: boolean, key: string, value: object | undefined): void; } + +export interface ExtHostTunnelServiceShape { + +} + // --- proxy identifiers export const MainContext = { @@ -1435,7 +1447,8 @@ export const MainContext = { MainThreadSearch: createMainId('MainThreadSearch'), MainThreadTask: createMainId('MainThreadTask'), MainThreadWindow: createMainId('MainThreadWindow'), - MainThreadLabelService: createMainId('MainThreadLabelService') + MainThreadLabelService: createMainId('MainThreadLabelService'), + MainThreadTunnelService: createMainId('MainThreadTunnelService') }; export const ExtHostContext = { @@ -1469,5 +1482,6 @@ export const ExtHostContext = { ExtHostStorage: createMainId('ExtHostStorage'), ExtHostUrls: createExtId('ExtHostUrls'), ExtHostOutputService: createMainId('ExtHostOutputService'), - ExtHosLabelService: createMainId('ExtHostLabelService') + ExtHosLabelService: createMainId('ExtHostLabelService'), + ExtHostTunnelService: createMainId('ExtHostTunnelService') }; diff --git a/src/vs/workbench/api/common/extHostApiCommands.ts b/src/vs/workbench/api/common/extHostApiCommands.ts index 188180394c..7dc9312688 100644 --- a/src/vs/workbench/api/common/extHostApiCommands.ts +++ b/src/vs/workbench/api/common/extHostApiCommands.ts @@ -27,6 +27,7 @@ export class ApiCommandArgument { static readonly Uri = new ApiCommandArgument('uri', 'Uri of a text document', v => URI.isUri(v), v => v); static readonly Position = new ApiCommandArgument('position', 'A position in a text document', v => types.Position.isPosition(v), typeConverters.Position.from); + static readonly Range = new ApiCommandArgument('range', 'A range in a text document', v => types.Range.isRange(v), typeConverters.Range.from); static readonly CallHierarchyItem = new ApiCommandArgument('item', 'A call hierarchy item', v => v instanceof types.CallHierarchyItem, typeConverters.CallHierarchyItem.to); @@ -42,7 +43,7 @@ export class ApiCommandResult { constructor( readonly description: string, - readonly convert: (v: V) => O + readonly convert: (v: V, apiArgs: any[]) => O ) { } } @@ -68,7 +69,7 @@ export class ApiCommand { }); const internalResult = await commands.executeCommand(this.internalId, ...internalArgs); - return this.result.convert(internalResult); + return this.result.convert(internalResult, apiArgs); }, undefined, this._getCommandHandlerDesc()); } @@ -83,6 +84,123 @@ export class ApiCommand { const newCommands: ApiCommand[] = [ + // -- document highlights + new ApiCommand( + 'vscode.executeDocumentHighlights', '_executeDocumentHighlights', 'Execute document highlight provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.', tryMapWith(typeConverters.DocumentHighlight.to)) + ), + // -- document symbols + new ApiCommand( + 'vscode.executeDocumentSymbolProvider', '_executeDocumentSymbolProvider', 'Execute document symbol provider.', + [ApiCommandArgument.Uri], + new ApiCommandResult('A promise that resolves to an array of DocumentHighlight-instances.', (value, apiArgs) => { + + if (isFalsyOrEmpty(value)) { + return undefined; + } + class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol { + static to(symbol: modes.DocumentSymbol): MergedInfo { + const res = new MergedInfo( + symbol.name, + typeConverters.SymbolKind.to(symbol.kind), + symbol.containerName || '', + new types.Location(apiArgs[0], typeConverters.Range.to(symbol.range)) + ); + res.detail = symbol.detail; + res.range = res.location.range; + res.selectionRange = typeConverters.Range.to(symbol.selectionRange); + res.children = symbol.children ? symbol.children.map(MergedInfo.to) : []; + return res; + } + + detail!: string; + range!: vscode.Range; + selectionRange!: vscode.Range; + children!: vscode.DocumentSymbol[]; + containerName!: string; + } + return value.map(MergedInfo.to); + + }) + ), + // -- formatting + new ApiCommand( + 'vscode.executeFormatDocumentProvider', '_executeFormatDocumentProvider', 'Execute document format provider.', + [ApiCommandArgument.Uri, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)], + new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to)) + ), + new ApiCommand( + 'vscode.executeFormatRangeProvider', '_executeFormatRangeProvider', 'Execute range format provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Range, new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)], + new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to)) + ), + new ApiCommand( + 'vscode.executeFormatOnTypeProvider', '_executeFormatOnTypeProvider', 'Execute format on type provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position, new ApiCommandArgument('ch', 'Trigger character', v => typeof v === 'string', v => v), new ApiCommandArgument('options', 'Formatting options', _ => true, v => v)], + new ApiCommandResult('A promise that resolves to an array of TextEdits.', tryMapWith(typeConverters.TextEdit.to)) + ), + // -- go to symbol (definition, type definition, declaration, impl, references) + new ApiCommand( + 'vscode.executeDefinitionProvider', '_executeDefinitionProvider', 'Execute all definition provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to)) + ), + new ApiCommand( + 'vscode.executeTypeDefinitionProvider', '_executeTypeDefinitionProvider', 'Execute all type definition providers.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to)) + ), + new ApiCommand( + 'vscode.executeDeclarationProvider', '_executeDeclarationProvider', 'Execute all declaration providers.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to)) + ), + new ApiCommand( + 'vscode.executeImplementationProvider', '_executeImplementationProvider', 'Execute all implementation providers.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to)) + ), + new ApiCommand( + 'vscode.executeReferenceProvider', '_executeReferenceProvider', 'Execute all reference providers.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Location-instances.', tryMapWith(typeConverters.location.to)) + ), + // -- hover + new ApiCommand( + 'vscode.executeHoverProvider', '_executeHoverProvider', 'Execute all hover provider.', + [ApiCommandArgument.Uri, ApiCommandArgument.Position], + new ApiCommandResult('A promise that resolves to an array of Hover-instances.', tryMapWith(typeConverters.Hover.to)) + ), + // -- selection range + new ApiCommand( + 'vscode.executeSelectionRangeProvider', '_executeSelectionRangeProvider', 'Execute selection range provider.', + [ApiCommandArgument.Uri, new ApiCommandArgument('position', 'A positions in a text document', v => Array.isArray(v) && v.every(v => types.Position.isPosition(v)), v => v.map(typeConverters.Position.from))], + new ApiCommandResult('A promise that resolves to an array of ranges.', result => { + return result.map(ranges => { + let node: types.SelectionRange | undefined; + for (const range of ranges.reverse()) { + node = new types.SelectionRange(typeConverters.Range.to(range), node); + } + return node!; + }); + }) + ), + // -- symbol search + new ApiCommand( + 'vscode.executeWorkspaceSymbolProvider', '_executeWorkspaceSymbolProvider', 'Execute all workspace symbol provider.', + [new ApiCommandArgument('query', 'Search string', v => typeof v === 'string', v => v)], + new ApiCommandResult<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][], types.SymbolInformation[]>('A promise that resolves to an array of SymbolInformation-instances.', value => { + const result: types.SymbolInformation[] = []; + if (Array.isArray(value)) { + for (let tuple of value) { + result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to)); + } + } + return result; + }) + ), + // --- call hierarchy new ApiCommand( 'vscode.prepareCallHierarchy', '_executePrepareCallHierarchy', 'Prepare call hierarchy at a position inside a document', [ApiCommandArgument.Uri, ApiCommandArgument.Position], @@ -121,68 +239,6 @@ export class ExtHostApiCommands { } registerCommands() { - this._register('vscode.executeWorkspaceSymbolProvider', this._executeWorkspaceSymbolProvider, { - description: 'Execute all workspace symbol provider.', - args: [{ name: 'query', description: 'Search string', constraint: String }], - returns: 'A promise that resolves to an array of SymbolInformation-instances.' - - }); - this._register('vscode.executeDefinitionProvider', this._executeDefinitionProvider, { - description: 'Execute all definition provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position of a symbol', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Location-instances.' - }); - this._register('vscode.executeDeclarationProvider', this._executeDeclaraionProvider, { - description: 'Execute all declaration provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position of a symbol', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Location-instances.' - }); - this._register('vscode.executeTypeDefinitionProvider', this._executeTypeDefinitionProvider, { - description: 'Execute all type definition providers.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position of a symbol', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Location-instances.' - }); - this._register('vscode.executeImplementationProvider', this._executeImplementationProvider, { - description: 'Execute all implementation providers.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position of a symbol', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Location-instance.' - }); - this._register('vscode.executeHoverProvider', this._executeHoverProvider, { - description: 'Execute all hover provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position of a symbol', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Hover-instances.' - }); - this._register('vscode.executeDocumentHighlights', this._executeDocumentHighlights, { - description: 'Execute document highlight provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of DocumentHighlight-instances.' - }); - this._register('vscode.executeReferenceProvider', this._executeReferenceProvider, { - description: 'Execute reference provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position } - ], - returns: 'A promise that resolves to an array of Location-instances.' - }); this._register('vscode.executeDocumentRenameProvider', this._executeDocumentRenameProvider, { description: 'Execute rename provider.', args: [ @@ -201,13 +257,6 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to SignatureHelp.' }); - this._register('vscode.executeDocumentSymbolProvider', this._executeDocumentSymbolProvider, { - description: 'Execute document symbol provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI } - ], - returns: 'A promise that resolves to an array of SymbolInformation and DocumentSymbol instances.' - }); this._register('vscode.executeCompletionItemProvider', this._executeCompletionItemProvider, { description: 'Execute completion item provider.', args: [ @@ -235,33 +284,7 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of CodeLens-instances.' }); - this._register('vscode.executeFormatDocumentProvider', this._executeFormatDocumentProvider, { - description: 'Execute document format provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'options', description: 'Formatting options' } - ], - returns: 'A promise that resolves to an array of TextEdits.' - }); - this._register('vscode.executeFormatRangeProvider', this._executeFormatRangeProvider, { - description: 'Execute range format provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'range', description: 'Range in a text document', constraint: types.Range }, - { name: 'options', description: 'Formatting options' } - ], - returns: 'A promise that resolves to an array of TextEdits.' - }); - this._register('vscode.executeFormatOnTypeProvider', this._executeFormatOnTypeProvider, { - description: 'Execute document format provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'position', description: 'Position in a text document', constraint: types.Position }, - { name: 'ch', description: 'Character that got typed', constraint: String }, - { name: 'options', description: 'Formatting options' } - ], - returns: 'A promise that resolves to an array of TextEdits.' - }); + this._register('vscode.executeLinkProvider', this._executeDocumentLinkProvider, { description: 'Execute document link provider.', args: [ @@ -284,14 +307,6 @@ export class ExtHostApiCommands { ], returns: 'A promise that resolves to an array of ColorPresentation objects.' }); - this._register('vscode.executeSelectionRangeProvider', this._executeSelectionRangeProvider, { - description: 'Execute selection range provider.', - args: [ - { name: 'uri', description: 'Uri of a text document', constraint: URI }, - { name: 'positions', description: 'Positions in a text document', constraint: Array.isArray } - ], - returns: 'A promise that resolves to an array of ranges.' - }); // ----------------------------------------------------------------- // The following commands are registered on both sides separately. @@ -362,87 +377,6 @@ export class ExtHostApiCommands { this._disposables.add(disposable); } - /** - * Execute workspace symbol provider. - * - * @param query Search string to match query symbol names - * @return A promise that resolves to an array of symbol information. - */ - private _executeWorkspaceSymbolProvider(query: string): Promise { - return this._commands.executeCommand<[search.IWorkspaceSymbolProvider, search.IWorkspaceSymbol[]][]>('_executeWorkspaceSymbolProvider', { query }).then(value => { - const result: types.SymbolInformation[] = []; - if (Array.isArray(value)) { - for (let tuple of value) { - result.push(...tuple[1].map(typeConverters.WorkspaceSymbol.to)); - } - } - return result; - }); - } - - private _executeDefinitionProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeDefinitionProvider', args) - .then(tryMapWith(typeConverters.location.to)); - } - - private _executeDeclaraionProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeDeclarationProvider', args) - .then(tryMapWith(typeConverters.location.to)); - } - - private _executeTypeDefinitionProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeTypeDefinitionProvider', args) - .then(tryMapWith(typeConverters.location.to)); - } - - private _executeImplementationProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeImplementationProvider', args) - .then(tryMapWith(typeConverters.location.to)); - } - - private _executeHoverProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeHoverProvider', args) - .then(tryMapWith(typeConverters.Hover.to)); - } - - private _executeDocumentHighlights(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeDocumentHighlights', args) - .then(tryMapWith(typeConverters.DocumentHighlight.to)); - } - - private _executeReferenceProvider(resource: URI, position: types.Position): Promise { - const args = { - resource, - position: position && typeConverters.Position.from(position) - }; - return this._commands.executeCommand('_executeReferenceProvider', args) - .then(tryMapWith(typeConverters.location.to)); - } - private _executeDocumentRenameProvider(resource: URI, position: types.Position, newName: string): Promise { const args = { resource, @@ -502,24 +436,6 @@ export class ExtHostApiCommands { }); } - private _executeSelectionRangeProvider(resource: URI, positions: types.Position[]): Promise { - const pos = positions.map(typeConverters.Position.from); - const args = { - resource, - position: pos[0], - positions: pos - }; - return this._commands.executeCommand('_executeSelectionRangeProvider', args).then(result => { - return result.map(ranges => { - let node: types.SelectionRange | undefined; - for (const range of ranges.reverse()) { - node = new types.SelectionRange(typeConverters.Range.to(range), node); - } - return node!; - }); - }); - } - private _executeColorPresentationProvider(color: types.Color, context: { uri: URI, range: types.Range; }): Promise { const args = { resource: context.uri, @@ -534,38 +450,6 @@ export class ExtHostApiCommands { }); } - private _executeDocumentSymbolProvider(resource: URI): Promise { - const args = { - resource - }; - return this._commands.executeCommand('_executeDocumentSymbolProvider', args).then((value): vscode.SymbolInformation[] | undefined => { - if (isFalsyOrEmpty(value)) { - return undefined; - } - class MergedInfo extends types.SymbolInformation implements vscode.DocumentSymbol { - static to(symbol: modes.DocumentSymbol): MergedInfo { - const res = new MergedInfo( - symbol.name, - typeConverters.SymbolKind.to(symbol.kind), - symbol.containerName || '', - new types.Location(resource, typeConverters.Range.to(symbol.range)) - ); - res.detail = symbol.detail; - res.range = res.location.range; - res.selectionRange = typeConverters.Range.to(symbol.selectionRange); - res.children = symbol.children ? symbol.children.map(MergedInfo.to) : []; - return res; - } - - detail!: string; - range!: vscode.Range; - selectionRange!: vscode.Range; - children!: vscode.DocumentSymbol[]; - containerName!: string; - } - return value.map(MergedInfo.to); - }); - } private _executeCodeActionProvider(resource: URI, rangeOrSelection: types.Range | types.Selection, kind?: string): Promise<(vscode.CodeAction | vscode.Command | undefined)[] | undefined> { const args = { @@ -610,36 +494,6 @@ export class ExtHostApiCommands { } - private _executeFormatDocumentProvider(resource: URI, options: vscode.FormattingOptions): Promise { - const args = { - resource, - options - }; - return this._commands.executeCommand('_executeFormatDocumentProvider', args) - .then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text))); - } - - private _executeFormatRangeProvider(resource: URI, range: types.Range, options: vscode.FormattingOptions): Promise { - const args = { - resource, - range: typeConverters.Range.from(range), - options - }; - return this._commands.executeCommand('_executeFormatRangeProvider', args) - .then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text))); - } - - private _executeFormatOnTypeProvider(resource: URI, position: types.Position, ch: string, options: vscode.FormattingOptions): Promise { - const args = { - resource, - position: typeConverters.Position.from(position), - ch, - options - }; - return this._commands.executeCommand('_executeFormatOnTypeProvider', args) - .then(tryMapWith(edit => new types.TextEdit(typeConverters.Range.to(edit.range), edit.text))); - } - private _executeDocumentLinkProvider(resource: URI): Promise { return this._commands.executeCommand('_executeLinkProvider', resource) .then(tryMapWith(typeConverters.DocumentLink.to)); diff --git a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts index b8bcf2973a..d8d6b7ff1e 100644 --- a/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts +++ b/src/vs/workbench/api/common/extHostDocumentsAndEditors.ts @@ -13,6 +13,7 @@ import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ExtHostTextEditor } from 'vs/workbench/api/common/extHostTextEditor'; import * as typeConverters from 'vs/workbench/api/common/extHostTypeConverters'; +import { ILogService } from 'vs/platform/log/common/log'; export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsShape { @@ -35,6 +36,7 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha constructor( @IExtHostRpcService private readonly _extHostRpc: IExtHostRpcService, + @ILogService private readonly _logService: ILogService ) { } $acceptDocumentsAndEditorsDelta(delta: IDocumentsAndEditorsDelta): void { @@ -92,8 +94,9 @@ export class ExtHostDocumentsAndEditors implements ExtHostDocumentsAndEditorsSha const documentData = this._documents.get(resource.toString())!; const editor = new ExtHostTextEditor( - this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), data.id, + this._extHostRpc.getProxy(MainContext.MainThreadTextEditors), + this._logService, documentData, data.selections.map(typeConverters.Selection.to), data.options, diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index a406a9082c..f672a68a52 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -32,6 +32,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; interface ITestRunner { /** Old test runner API, as exported from `vscode/lib/testrunner` */ @@ -76,6 +77,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio protected readonly _extHostWorkspace: ExtHostWorkspace; protected readonly _extHostConfiguration: ExtHostConfiguration; protected readonly _logService: ILogService; + protected readonly _extHostTunnelService: IExtHostTunnelService; protected readonly _mainThreadWorkspaceProxy: MainThreadWorkspaceShape; protected readonly _mainThreadTelemetryProxy: MainThreadTelemetryShape; @@ -104,7 +106,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio @IExtHostConfiguration extHostConfiguration: IExtHostConfiguration, @ILogService logService: ILogService, @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtensionStoragePaths storagePath: IExtensionStoragePaths + @IExtensionStoragePaths storagePath: IExtensionStoragePaths, + @IExtHostTunnelService extHostTunnelService: IExtHostTunnelService ) { this._hostUtils = hostUtils; this._extHostContext = extHostContext; @@ -113,6 +116,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio this._extHostWorkspace = extHostWorkspace; this._extHostConfiguration = extHostConfiguration; this._logService = logService; + this._extHostTunnelService = extHostTunnelService; this._disposables = new DisposableStore(); this._mainThreadWorkspaceProxy = this._extHostContext.getProxy(MainContext.MainThreadWorkspace); @@ -652,6 +656,8 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio extensionHostEnv: result.extensionHostEnv }; + await this._extHostTunnelService.addDetected(result.detectedTunnels); + return { type: 'ok', value: { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 865ba8e216..cb383daefc 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -389,7 +389,7 @@ class CodeActionAdapter { edit: candidate.edit && typeConvert.WorkspaceEdit.from(candidate.edit), kind: candidate.kind && candidate.kind.value, isPreferred: candidate.isPreferred, - disabled: candidate.disabled + disabled: candidate.disabled?.reason }); } } @@ -740,11 +740,16 @@ class SuggestAdapter { private _cache = new Cache('CompletionItem'); private _disposables = new Map(); + private _didWarnMust: boolean = false; + private _didWarnShould: boolean = false; + constructor( private readonly _documents: ExtHostDocuments, private readonly _commands: CommandsConverter, private readonly _provider: vscode.CompletionItemProvider, - private readonly _logService: ILogService + private readonly _logService: ILogService, + private readonly _telemetry: extHostProtocol.MainThreadTelemetryShape, + private readonly _extensionId: ExtensionIdentifier ) { } provideCompletionItems(resource: URI, position: IPosition, context: modes.CompletionContext, token: CancellationToken): Promise { @@ -809,12 +814,37 @@ class SuggestAdapter { return Promise.resolve(undefined); } + const _mustNotChange = SuggestAdapter._mustNotChangeHash(item); + const _mayNotChange = SuggestAdapter._mayNotChangeHash(item); + return asPromise(() => this._provider.resolveCompletionItem!(item, token)).then(resolvedItem => { if (!resolvedItem) { return undefined; } + type BlameExtension = { + extensionId: string; + kind: string + }; + + type BlameExtensionMeta = { + extensionId: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + kind: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth' }; + }; + + if (!this._didWarnMust && _mustNotChange !== SuggestAdapter._mustNotChangeHash(resolvedItem)) { + this._logService.warn(`[${this._extensionId.value}] INVALID result from 'resolveCompletionItem', extension MUST NOT change any of: label, sortText, filterText, insertText, or textEdit`); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'must' }); + this._didWarnMust = true; + } + + if (!this._didWarnShould && _mayNotChange !== SuggestAdapter._mayNotChangeHash(resolvedItem)) { + this._logService.info(`[${this._extensionId.value}] UNSAVE result from 'resolveCompletionItem', extension SHOULD NOT change any of: additionalTextEdits, or command`); + this._telemetry.$publicLog2('resolveCompletionItem/invalid', { extensionId: this._extensionId.value, kind: 'should' }); + this._didWarnShould = true; + } + const pos = typeConvert.Position.to(position); return this._convertCompletionItem(resolvedItem, pos, id); }); @@ -908,6 +938,16 @@ class SuggestAdapter { private static _isValidRangeForCompletion(range: vscode.Range, position: vscode.Position): boolean { return range.isSingleLine || range.start.line === position.line; } + + private static _mustNotChangeHash(item: vscode.CompletionItem) { + const args = [item.label, item.sortText, item.filterText, item.insertText, item.range, item.range2]; + const res = JSON.stringify(args); + return res; + } + + private static _mayNotChangeHash(item: vscode.CompletionItem) { + return JSON.stringify([item.additionalTextEdits, item.command]); + } } class SignatureHelpAdapter { @@ -1253,7 +1293,8 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF private static _handlePool: number = 0; private readonly _uriTransformer: IURITransformer | null; - private _proxy: extHostProtocol.MainThreadLanguageFeaturesShape; + private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape; + private readonly _telemetryShape: extHostProtocol.MainThreadTelemetryShape; private _documents: ExtHostDocuments; private _commands: ExtHostCommands; private _diagnostics: ExtHostDiagnostics; @@ -1270,6 +1311,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF ) { this._uriTransformer = uriTransformer; this._proxy = mainContext.getProxy(extHostProtocol.MainContext.MainThreadLanguageFeatures); + this._telemetryShape = mainContext.getProxy(extHostProtocol.MainContext.MainThreadTelemetry); this._documents = documents; this._commands = commands; this._diagnostics = diagnostics; @@ -1584,7 +1626,7 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF // --- suggestion registerCompletionItemProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.CompletionItemProvider, triggerCharacters: string[]): vscode.Disposable { - const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService), extension); + const handle = this._addNewAdapter(new SuggestAdapter(this._documents, this._commands.converter, provider, this._logService, this._telemetryShape, extension.identifier), extension); this._proxy.$registerSuggestSupport(handle, this._transformDocumentSelector(selector), triggerCharacters, SuggestAdapter.supportsResolving(provider), extension.identifier); return this._createDisposable(handle); } diff --git a/src/vs/workbench/api/common/extHostProgress.ts b/src/vs/workbench/api/common/extHostProgress.ts index 5bb2571d6c..d1e42212e6 100644 --- a/src/vs/workbench/api/common/extHostProgress.ts +++ b/src/vs/workbench/api/common/extHostProgress.ts @@ -9,7 +9,7 @@ import { ProgressLocation } from './extHostTypeConverters'; import { Progress, IProgressStep } from 'vs/platform/progress/common/progress'; import { localize } from 'vs/nls'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; -import { debounce } from 'vs/base/common/decorators'; +import { throttle } from 'vs/base/common/decorators'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; export class ExtHostProgress implements ExtHostProgressShape { @@ -85,7 +85,7 @@ class ProgressCallback extends Progress { super(p => this.throttledReport(p)); } - @debounce(100, (result: IProgressStep, currentValue: IProgressStep) => mergeProgress(result, currentValue), () => Object.create(null)) + @throttle(100, (result: IProgressStep, currentValue: IProgressStep) => mergeProgress(result, currentValue), () => Object.create(null)) throttledReport(p: IProgressStep): void { this._proxy.$progressReport(this._handle, p); } diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index bce13601a7..a1f3400a7b 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -14,6 +14,7 @@ import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; import * as vscode from 'vscode'; +import { ILogService } from 'vs/platform/log/common/log'; export class TextEditorDecorationType implements vscode.TextEditorDecorationType { @@ -133,23 +134,11 @@ export class TextEditorEdit { } } - -function deprecated(name: string, message: string = 'Refer to the documentation for further details.') { - return (target: Object, key: string, descriptor: TypedPropertyDescriptor) => { - const originalMethod = descriptor.value; - descriptor.value = function (...args: any[]) { - console.warn(`[Deprecation Warning] method '${name}' is deprecated and should no longer be used. ${message}`); - return originalMethod.apply(this, args); - }; - - return descriptor; - }; -} - export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { private _proxy: MainThreadTextEditorsShape; private _id: string; + private _logService: ILogService; private _tabSize!: number; private _indentSize!: number; @@ -157,10 +146,11 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { private _cursorStyle!: TextEditorCursorStyle; private _lineNumbers!: TextEditorLineNumbersStyle; - constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration) { + constructor(proxy: MainThreadTextEditorsShape, id: string, source: IResolvedTextEditorConfiguration, logService: ILogService) { this._proxy = proxy; this._id = id; this._accept(source); + this._logService = logService; } public _accept(source: IResolvedTextEditorConfiguration): void { @@ -207,7 +197,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { // reflect the new tabSize value immediately this._tabSize = tabSize; } - warnOnError(this._proxy.$trySetOptions(this._id, { + this._warnOnError(this._proxy.$trySetOptions(this._id, { tabSize: tabSize })); } @@ -248,7 +238,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { // reflect the new indentSize value immediately this._indentSize = indentSize; } - warnOnError(this._proxy.$trySetOptions(this._id, { + this._warnOnError(this._proxy.$trySetOptions(this._id, { indentSize: indentSize })); } @@ -274,7 +264,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { // reflect the new insertSpaces value immediately this._insertSpaces = insertSpaces; } - warnOnError(this._proxy.$trySetOptions(this._id, { + this._warnOnError(this._proxy.$trySetOptions(this._id, { insertSpaces: insertSpaces })); } @@ -289,7 +279,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return; } this._cursorStyle = value; - warnOnError(this._proxy.$trySetOptions(this._id, { + this._warnOnError(this._proxy.$trySetOptions(this._id, { cursorStyle: value })); } @@ -304,7 +294,7 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { return; } this._lineNumbers = value; - warnOnError(this._proxy.$trySetOptions(this._id, { + this._warnOnError(this._proxy.$trySetOptions(this._id, { lineNumbers: TypeConverters.TextEditorLineNumbersStyle.from(value) })); } @@ -369,15 +359,17 @@ export class ExtHostTextEditorOptions implements vscode.TextEditorOptions { } if (hasUpdate) { - warnOnError(this._proxy.$trySetOptions(this._id, bulkConfigurationUpdate)); + this._warnOnError(this._proxy.$trySetOptions(this._id, bulkConfigurationUpdate)); } } + + private _warnOnError(promise: Promise): void { + promise.catch(err => this._logService.warn(err)); + } } export class ExtHostTextEditor implements vscode.TextEditor { - private readonly _proxy: MainThreadTextEditorsShape; - private readonly _id: string; private readonly _documentData: ExtHostDocumentData; private _selections: Selection[]; @@ -387,18 +379,17 @@ export class ExtHostTextEditor implements vscode.TextEditor { private _disposed: boolean = false; private _hasDecorationsForKey: { [key: string]: boolean; }; - get id(): string { return this._id; } - constructor( - proxy: MainThreadTextEditorsShape, id: string, document: ExtHostDocumentData, + readonly id: string, + private readonly _proxy: MainThreadTextEditorsShape, + private readonly _logService: ILogService, + document: ExtHostDocumentData, selections: Selection[], options: IResolvedTextEditorConfiguration, visibleRanges: Range[], viewColumn: vscode.ViewColumn | undefined ) { - this._proxy = proxy; - this._id = id; this._documentData = document; this._selections = selections; - this._options = new ExtHostTextEditorOptions(this._proxy, this._id, options); + this._options = new ExtHostTextEditorOptions(this._proxy, this.id, options, _logService); this._visibleRanges = visibleRanges; this._viewColumn = viewColumn; this._hasDecorationsForKey = Object.create(null); @@ -409,12 +400,12 @@ export class ExtHostTextEditor implements vscode.TextEditor { this._disposed = true; } - @deprecated('TextEditor.show') show(column: vscode.ViewColumn) { - this._proxy.$tryShowEditor(this._id, TypeConverters.ViewColumn.from(column)); + show(column: vscode.ViewColumn) { + this._proxy.$tryShowEditor(this.id, TypeConverters.ViewColumn.from(column)); } - @deprecated('TextEditor.hide') hide() { - this._proxy.$tryHideEditor(this._id); + hide() { + this._proxy.$tryHideEditor(this.id); } // ---- the document @@ -515,7 +506,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { () => { if (TypeConverters.isDecorationOptionsArr(ranges)) { return this._proxy.$trySetDecorations( - this._id, + this.id, decorationType.key, TypeConverters.fromRangeOrRangeWithMessage(ranges) ); @@ -529,7 +520,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { _ranges[4 * i + 3] = range.end.character + 1; } return this._proxy.$trySetDecorationsFast( - this._id, + this.id, decorationType.key, _ranges ); @@ -541,7 +532,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { revealRange(range: Range, revealType: vscode.TextEditorRevealType): void { this._runOnProxy( () => this._proxy.$tryRevealRange( - this._id, + this.id, TypeConverters.Range.from(range), (revealType || TextEditorRevealType.Default) ) @@ -550,7 +541,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { private _trySetSelection(): Promise { const selection = this._selections.map(TypeConverters.Selection.from); - return this._runOnProxy(() => this._proxy.$trySetSelections(this._id, selection)); + return this._runOnProxy(() => this._proxy.$trySetSelections(this.id, selection)); } _acceptSelections(selections: Selection[]): void { @@ -616,7 +607,7 @@ export class ExtHostTextEditor implements vscode.TextEditor { }; }); - return this._proxy.$tryApplyEdits(this._id, editData.documentVersionId, edits, { + return this._proxy.$tryApplyEdits(this.id, editData.documentVersionId, edits, { setEndOfLine: typeof editData.setEndOfLine === 'number' ? TypeConverters.EndOfLine.from(editData.setEndOfLine) : undefined, undoStopBefore: editData.undoStopBefore, undoStopAfter: editData.undoStopAfter @@ -650,27 +641,22 @@ export class ExtHostTextEditor implements vscode.TextEditor { } } - return this._proxy.$tryInsertSnippet(this._id, snippet.value, ranges, options); + return this._proxy.$tryInsertSnippet(this.id, snippet.value, ranges, options); } // ---- util private _runOnProxy(callback: () => Promise): Promise { if (this._disposed) { - console.warn('TextEditor is closed/disposed'); + this._logService.warn('TextEditor is closed/disposed'); return Promise.resolve(undefined); } return callback().then(() => this, err => { if (!(err instanceof Error && err.name === 'DISPOSED')) { - console.warn(err); + this._logService.warn(err); } return null; }); } } -function warnOnError(promise: Promise): void { - promise.then(undefined, (err) => { - console.warn(err); - }); -} diff --git a/src/vs/workbench/api/common/extHostTunnelService.ts b/src/vs/workbench/api/common/extHostTunnelService.ts new file mode 100644 index 0000000000..ddca398ef8 --- /dev/null +++ b/src/vs/workbench/api/common/extHostTunnelService.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtHostTunnelServiceShape, MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import * as vscode from 'vscode'; +import { Disposable } from 'vs/base/common/lifecycle'; + +export interface TunnelOptions { + remote: { port: number, host: string }; + localPort?: number; + name?: string; + closeable?: boolean; +} + +export interface TunnelDto { + remote: { port: number, host: string }; + localAddress: string; +} + +export interface IExtHostTunnelService extends ExtHostTunnelServiceShape { + readonly _serviceBrand: undefined; + makeTunnel(forward: TunnelOptions): Promise; + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise; +} + +export const IExtHostTunnelService = createDecorator('IExtHostTunnelService'); + + +export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService { + readonly _serviceBrand: undefined; + private readonly _proxy: MainThreadTunnelServiceShape; + + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService + ) { + super(); + this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService); + } + async makeTunnel(forward: TunnelOptions): Promise { + const tunnel = await this._proxy.$openTunnel(forward); + if (tunnel) { + const disposableTunnel: vscode.Tunnel = { + remote: tunnel.remote, + localAddress: tunnel.localAddress, + dispose: () => { + return this._proxy.$closeTunnel(tunnel.remote.port); + } + }; + this._register(disposableTunnel); + return disposableTunnel; + } + return undefined; + } + + async addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): Promise { + if (tunnels) { + return this._proxy.$addDetected(tunnels); + } + } + +} + diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6ea563c076..e58d532d13 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -309,7 +309,7 @@ export namespace MarkdownString { } export function to(value: htmlContent.IMarkdownString): vscode.MarkdownString { - return new htmlContent.MarkdownString(value.value, value.isTrusted); + return new htmlContent.MarkdownString(value.value, { isTrusted: value.isTrusted, supportThemeIcons: value.supportThemeIcons }); } export function fromStrict(value: string | types.MarkdownString): undefined | string | htmlContent.IMarkdownString { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 931c01e33b..6a3d96b196 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -14,6 +14,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import * as vscode from 'vscode'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { markdownUnescapeCodicons, escapeCodicons } from 'vs/base/common/codicons'; function es5ClassCompat(target: Function): any { ///@ts-ignore @@ -1231,21 +1232,26 @@ export class MarkdownString { value: string; isTrusted?: boolean; + readonly supportThemeIcons?: boolean; - constructor(value?: string) { - this.value = value || ''; + constructor(value?: string, { supportThemeIcons }: { supportThemeIcons?: boolean } = {}) { + this.value = value ?? ''; + this.supportThemeIcons = supportThemeIcons ?? false; } appendText(value: string): MarkdownString { // escape markdown syntax tokens: http://daringfireball.net/projects/markdown/syntax#backslash - this.value += value + value = value .replace(/[\\`*_{}[\]()#+\-.!]/g, '\\$&') .replace('\n', '\n\n'); + this.value += this.supportThemeIcons ? markdownUnescapeCodicons(value) : value; + return this; } appendMarkdown(value: string): MarkdownString { this.value += value; + return this; } @@ -1257,6 +1263,10 @@ export class MarkdownString { this.value += '\n```\n'; return this; } + + static escapeThemeIcons(value: string): string { + return escapeCodicons(value); + } } @es5ClassCompat diff --git a/src/vs/workbench/api/common/extHostUrls.ts b/src/vs/workbench/api/common/extHostUrls.ts index 5ea42ebb96..bcd081c9d3 100644 --- a/src/vs/workbench/api/common/extHostUrls.ts +++ b/src/vs/workbench/api/common/extHostUrls.ts @@ -59,8 +59,4 @@ export class ExtHostUrls implements ExtHostUrlsShape { async createAppUri(uri: URI): Promise { return URI.revive(await this._proxy.$createAppUri(uri)); } - - async proposedCreateAppUri(extensionId: ExtensionIdentifier, options?: vscode.AppUriOptions): Promise { - return URI.revive(await this._proxy.$proposedCreateAppUri(extensionId, options)); - } } diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 9ea4c749b4..cc5d6519d7 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -339,11 +339,6 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac if (folders.length === 0) { return undefined; } - - if (folders.length > 1) { - return undefined; - } - // #54483 @Joh Why are we still using fsPath? return folders[0].uri.fsPath; } diff --git a/src/vs/workbench/api/node/extHost.services.ts b/src/vs/workbench/api/node/extHost.services.ts index deeb5c441d..86fdb68a46 100644 --- a/src/vs/workbench/api/node/extHost.services.ts +++ b/src/vs/workbench/api/node/extHost.services.ts @@ -26,6 +26,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService'; +import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; // register singleton services registerSingleton(ILogService, ExtHostLogService); @@ -42,3 +43,4 @@ registerSingleton(IExtHostSearch, NativeExtHostSearch); registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths); registerSingleton(IExtHostExtensionService, ExtHostExtensionService); registerSingleton(IExtHostStorage, ExtHostStorage); +registerSingleton(IExtHostTunnelService, ExtHostTunnelService); diff --git a/src/vs/workbench/api/node/extHostLogService.ts b/src/vs/workbench/api/node/extHostLogService.ts index e71d0284ac..a41b257e11 100644 --- a/src/vs/workbench/api/node/extHostLogService.ts +++ b/src/vs/workbench/api/node/extHostLogService.ts @@ -3,31 +3,21 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import { join } from 'vs/base/common/path'; import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log'; import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { Schemas } from 'vs/base/common/network'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; -import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; +import { dirname } from 'vs/base/common/resources'; export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape { constructor( @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtHostOutputService extHostOutputService: IExtHostOutputService ) { - if (initData.logsLocation.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); } - super(new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel)); - - // Register an output channel for exthost log - extHostOutputService.createOutputChannelFromLogFile( - initData.remote.isRemote ? localize('remote extension host Log', "Remote Extension Host") : localize('extension host Log', "Extension Host"), - URI.file(join(initData.logsLocation.fsPath, `${ExtensionHostLogFileName}.log`)) - ); + if (initData.logFile.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); } + super(new SpdLogService(ExtensionHostLogFileName, dirname(initData.logFile).fsPath, initData.logLevel)); } $setLevel(level: LogLevel): void { diff --git a/src/vs/workbench/api/node/extHostOutputService.ts b/src/vs/workbench/api/node/extHostOutputService.ts index 7a5e4ccecb..0fdc425eef 100644 --- a/src/vs/workbench/api/node/extHostOutputService.ts +++ b/src/vs/workbench/api/node/extHostOutputService.ts @@ -14,6 +14,7 @@ import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputSe import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { MutableDisposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel { @@ -55,6 +56,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService { constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, + @ILogService private readonly logService: ILogService, @IExtHostInitDataService initData: IExtHostInitDataService, ) { super(extHostRpc); @@ -90,7 +92,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService { return new ExtHostOutputChannelBackedByFile(name, appender, this._proxy); } catch (error) { // Do not crash if logger cannot be created - console.log(error); + this.logService.error(error); return new ExtHostPushOutputChannel(name, this._proxy); } } diff --git a/src/vs/workbench/api/worker/extHostLogService.ts b/src/vs/workbench/api/worker/extHostLogService.ts index 951a849cbe..d558cc2bf4 100644 --- a/src/vs/workbench/api/worker/extHostLogService.ts +++ b/src/vs/workbench/api/worker/extHostLogService.ts @@ -6,12 +6,8 @@ import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log'; import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; -import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; -import { joinPath } from 'vs/base/common/resources'; -import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { UriComponents } from 'vs/base/common/uri'; -import { localize } from 'vs/nls'; export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape { @@ -23,14 +19,11 @@ export class ExtHostLogService extends AbstractLogService implements ILogService constructor( @IExtHostRpcService rpc: IExtHostRpcService, @IExtHostInitDataService initData: IExtHostInitDataService, - @IExtHostOutputService extHostOutputService: IExtHostOutputService ) { super(); - const logFile = joinPath(initData.logsLocation, `${ExtensionHostLogFileName}.log`); this._proxy = rpc.getProxy(MainContext.MainThreadLog); - this._logFile = logFile.toJSON(); + this._logFile = initData.logFile.toJSON(); this.setLevel(initData.logLevel); - extHostOutputService.createOutputChannelFromLogFile(localize('name', "Worker Extension Host"), logFile); } $setLevel(level: LogLevel): void { diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 80f25c474d..f58ad3a6ca 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -39,6 +39,7 @@ export interface IDraggedResource { } export class DraggedEditorIdentifier { + constructor(private _identifier: IEditorIdentifier) { } get identifier(): IEditorIdentifier { @@ -47,6 +48,7 @@ export class DraggedEditorIdentifier { } export class DraggedEditorGroupIdentifier { + constructor(private _identifier: GroupIdentifier) { } get identifier(): GroupIdentifier { @@ -56,13 +58,17 @@ export class DraggedEditorGroupIdentifier { export interface IDraggedEditor extends IDraggedResource { backupResource?: URI; + encoding?: string; + mode?: string; viewState?: IEditorViewState; } export interface ISerializedDraggedEditor { resource: string; backupResource?: string; - viewState: IEditorViewState | null; + encoding?: string; + mode?: string; + viewState?: IEditorViewState; } export const CodeDataTransfers = { @@ -86,7 +92,9 @@ export function extractResources(e: DragEvent, externalOnly?: boolean): Array ({ resource: untitledOrFileResource.resource, + encoding: (untitledOrFileResource as IDraggedEditor).encoding, + mode: (untitledOrFileResource as IDraggedEditor).mode, options: { pinned: true, index: targetIndex, @@ -234,7 +244,7 @@ export class ResourcesDropHandler { // Untitled: always ensure that we open a new untitled for each file we drop if (droppedDirtyEditor.resource.scheme === Schemas.untitled) { - droppedDirtyEditor.resource = this.untitledTextEditorService.createOrGet().getResource(); + droppedDirtyEditor.resource = this.untitledTextEditorService.createOrGet(undefined, droppedDirtyEditor.mode, undefined, droppedDirtyEditor.encoding).getResource(); } // Return early if the resource is already dirty in target or opened already @@ -342,6 +352,7 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: // Editors: enables cross window DND of tabs into the editor area const textFileService = accessor.get(ITextFileService); + const untitledTextEditorService = accessor.get(IUntitledTextEditorService); const backupFileService = accessor.get(IBackupFileService); const editorService = accessor.get(IEditorService); @@ -349,24 +360,42 @@ export function fillResourceDataTransfers(accessor: ServicesAccessor, resources: files.forEach(file => { // Try to find editor view state from the visible editors that match given resource - let viewState: IEditorViewState | null = null; + let viewState: IEditorViewState | undefined = undefined; const textEditorWidgets = editorService.visibleTextEditorWidgets; for (const textEditorWidget of textEditorWidgets) { if (isCodeEditor(textEditorWidget)) { const model = textEditorWidget.getModel(); if (model?.uri?.toString() === file.resource.toString()) { - viewState = textEditorWidget.saveViewState(); + viewState = withNullAsUndefined(textEditorWidget.saveViewState()); break; } } } + // Try to find encoding and mode from text model + let encoding: string | undefined = undefined; + let mode: string | undefined = undefined; + if (untitledTextEditorService.exists(file.resource)) { + const model = untitledTextEditorService.createOrGet(file.resource); + encoding = model.getEncoding(); + mode = model.getMode(); + } else { + const model = textFileService.models.get(file.resource); + if (model) { + encoding = model.getEncoding(); + mode = model.textEditorModel?.getModeId(); + } + } + + // If the resource is dirty, send over its backup + // resource to restore dirty state + let backupResource: string | undefined = undefined; + if (textFileService.isDirty(file.resource)) { + backupResource = backupFileService.toBackupResource(file.resource).toString(); + } + // Add as dragged editor - draggedEditors.push({ - resource: file.resource.toString(), - backupResource: textFileService.isDirty(file.resource) ? backupFileService.toBackupResource(file.resource).toString() : undefined, - viewState - }); + draggedEditors.push({ resource: file.resource.toString(), backupResource, viewState, encoding, mode }); }); if (draggedEditors.length) { diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 250533cc42..a1148223f7 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -109,18 +109,18 @@ body.web { cursor: pointer; } -.monaco-workbench.monaco-font-aliasing-antialiased { +.monaco-workbench.mac.monaco-font-aliasing-antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -.monaco-workbench.monaco-font-aliasing-none { +.monaco-workbench.mac.monaco-font-aliasing-none { -webkit-font-smoothing: none; -moz-osx-font-smoothing: unset; } @media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) { - .monaco-workbench.monaco-font-aliasing-auto { + .monaco-workbench.mac.monaco-font-aliasing-auto { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/src/vs/workbench/browser/panecomposite.ts b/src/vs/workbench/browser/panecomposite.ts new file mode 100644 index 0000000000..44f3572986 --- /dev/null +++ b/src/vs/workbench/browser/panecomposite.ts @@ -0,0 +1,85 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Dimension } from 'vs/base/browser/dom'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IView } from 'vs/workbench/common/views'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { Composite } from 'vs/workbench/browser/composite'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ViewPaneContainer } from './parts/views/viewPaneContainer'; +import { IPaneComposite } from 'vs/workbench/common/panecomposite'; +import { IAction, IActionViewItem } from 'vs/base/common/actions'; + +export class PaneComposite extends Composite implements IPaneComposite { + constructor(id: string, + protected readonly viewPaneContainer: ViewPaneContainer, + @ITelemetryService + telemetryService: ITelemetryService, + @IStorageService + protected storageService: IStorageService, + @IInstantiationService + protected instantiationService: IInstantiationService, + @IThemeService + themeService: IThemeService, + @IContextMenuService + protected contextMenuService: IContextMenuService, + @IExtensionService + protected extensionService: IExtensionService, + @IWorkspaceContextService + protected contextService: IWorkspaceContextService) { + super(id, telemetryService, themeService, storageService); + + this._register(this.viewPaneContainer.onTitleAreaUpdate(() => this.updateTitleArea())); + } + create(parent: HTMLElement): void { + this.viewPaneContainer.create(parent); + } + setVisible(visible: boolean): void { + super.setVisible(visible); + this.viewPaneContainer.setVisible(visible); + } + layout(dimension: Dimension): void { + this.viewPaneContainer.layout(dimension); + } + getOptimalWidth(): number { + return this.viewPaneContainer.getOptimalWidth(); + } + openView(id: string, focus?: boolean): IView { + return this.viewPaneContainer.openView(id, focus); + } + + getViewPaneContainer(): ViewPaneContainer { + return this.viewPaneContainer; + } + + getContextMenuActions(): ReadonlyArray { + return this.viewPaneContainer.getContextMenuActions(); + } + + getActions(): ReadonlyArray { + return this.viewPaneContainer.getActions(); + } + + getSecondaryActions(): ReadonlyArray { + return this.viewPaneContainer.getSecondaryActions(); + } + + getActionViewItem(action: IAction): IActionViewItem | undefined { + return this.viewPaneContainer.getActionViewItem(action); + } + + getTitle(): string { + return this.viewPaneContainer.getTitle(); + } + + saveState(): void { + super.saveState(); + } +} diff --git a/src/vs/workbench/browser/parts/editor/binaryEditor.ts b/src/vs/workbench/browser/parts/editor/binaryEditor.ts index c59a36eba8..2a9f8b8b34 100644 --- a/src/vs/workbench/browser/parts/editor/binaryEditor.ts +++ b/src/vs/workbench/browser/parts/editor/binaryEditor.ts @@ -3,8 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/binaryeditor'; import * as nls from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; @@ -12,11 +13,10 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer'; import { URI } from 'vs/base/common/uri'; -import { Dimension, size, clearNode } from 'vs/base/browser/dom'; +import { Dimension, size, clearNode, append, addDisposableListener, EventType, $ } from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { dispose } from 'vs/base/common/lifecycle'; +import { dispose, IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { assertIsDefined, assertAllDefined } from 'vs/base/common/types'; @@ -31,11 +31,11 @@ export interface IOpenCallbacks { */ export abstract class BaseBinaryResourceEditor extends BaseEditor { - private readonly _onMetadataChanged: Emitter = this._register(new Emitter()); - readonly onMetadataChanged: Event = this._onMetadataChanged.event; + private readonly _onMetadataChanged = this._register(new Emitter()); + readonly onMetadataChanged = this._onMetadataChanged.event; - private readonly _onDidOpenInPlace: Emitter = this._register(new Emitter()); - readonly onDidOpenInPlace: Event = this._onDidOpenInPlace.event; + private readonly _onDidOpenInPlace = this._register(new Emitter()); + readonly onDidOpenInPlace = this._onDidOpenInPlace.event; private callbacks: IOpenCallbacks; private metadata: string | undefined; @@ -160,3 +160,122 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor { super.dispose(); } } +export interface IResourceDescriptor { + readonly resource: URI; + readonly name: string; + readonly size?: number; + readonly etag?: string; + readonly mime: string; +} + +class BinarySize { + static readonly KB = 1024; + static readonly MB = BinarySize.KB * BinarySize.KB; + static readonly GB = BinarySize.MB * BinarySize.KB; + static readonly TB = BinarySize.GB * BinarySize.KB; + + static formatSize(size: number): string { + if (size < BinarySize.KB) { + return nls.localize('sizeB', "{0}B", size); + } + + if (size < BinarySize.MB) { + return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); + } + + if (size < BinarySize.GB) { + return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); + } + + if (size < BinarySize.TB) { + return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); + } + + return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); + } +} + +interface ResourceViewerContext extends IDisposable { + layout?(dimension: Dimension): void; +} + +interface ResourceViewerDelegate { + openInternalClb(uri: URI): void; + openExternalClb?(uri: URI): void; + metadataClb(meta: string): void; +} + +class ResourceViewer { + + private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally + + static show( + descriptor: IResourceDescriptor, + container: HTMLElement, + scrollbar: DomScrollableElement, + delegate: ResourceViewerDelegate, + ): ResourceViewerContext { + + // Ensure CSS class + container.className = 'monaco-binary-resource-editor'; + + // Large Files + if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) { + return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate); + } + + // Seemingly Binary Files + return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, delegate); + } +} + +class FileTooLargeFileView { + static create( + container: HTMLElement, + descriptorSize: number, + scrollbar: DomScrollableElement, + delegate: ResourceViewerDelegate + ) { + const size = BinarySize.formatSize(descriptorSize); + delegate.metadataClb(size); + + clearNode(container); + + const label = document.createElement('span'); + label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size); + container.appendChild(label); + + scrollbar.scanDomNode(); + + return Disposable.None; + } +} + +class FileSeemsBinaryFileView { + static create( + container: HTMLElement, + descriptor: IResourceDescriptor, + scrollbar: DomScrollableElement, + delegate: ResourceViewerDelegate + ) { + delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : ''); + + clearNode(container); + + const disposables = new DisposableStore(); + + const label = document.createElement('p'); + label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding."); + container.appendChild(label); + + const link = append(label, $('a.embedded-link')); + link.setAttribute('role', 'button'); + link.textContent = nls.localize('openAsText', "Do you want to open it anyway?"); + + disposables.add(addDisposableListener(link, EventType.CLICK, () => delegate.openInternalClb(descriptor.resource))); + + scrollbar.scanDomNode(); + + return disposables; + } +} diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts index 923b0e0a38..346bb15018 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbs.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbs.ts @@ -124,11 +124,6 @@ Registry.as(Extensions.Configuration).registerConfigurat type: 'boolean', default: true }, - // 'breadcrumbs.useQuickPick': { - // description: localize('useQuickPick', "Use quick pick instead of breadcrumb-pickers."), - // type: 'boolean', - // default: false - // }, 'breadcrumbs.filePath': { description: localize('filepath', "Controls whether and how file paths are shown in the breadcrumbs view."), type: 'string', @@ -155,6 +150,7 @@ Registry.as(Extensions.Configuration).registerConfigurat description: localize('symbolSortOrder', "Controls how symbols are sorted in the breadcrumbs outline view."), type: 'string', default: 'position', + overridable: true, enum: ['position', 'name', 'type'], enumDescriptions: [ localize('symbolSortOrder.position', "Show symbol outline in file position order."), diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts index 89ed66d0a4..4932a14929 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsPicker.ts @@ -15,7 +15,7 @@ import { basename, dirname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/breadcrumbscontrol'; import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; import { FileKind, IFileService, IFileStat } from 'vs/platform/files/common/files'; import { IConstructorSignature1, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { WorkbenchDataTree, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -429,6 +429,7 @@ export class BreadcrumbsFilePicker extends BreadcrumbsPicker { export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { protected readonly _symbolSortOrder: BreadcrumbsConfig<'position' | 'name' | 'type'>; + protected _outlineComparator: OutlineItemComparator; constructor( parent: HTMLElement, @@ -438,6 +439,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { ) { super(parent, instantiationService, themeService, configurationService); this._symbolSortOrder = BreadcrumbsConfig.SymbolSortOrder.bindTo(this._configurationService); + this._outlineComparator = new OutlineItemComparator(); } protected _createTree(container: HTMLElement) { @@ -452,7 +454,7 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { collapseByDefault: true, expandOnlyOnTwistieClick: true, multipleSelectionSupport: false, - sorter: new OutlineItemComparator(this._getOutlineItemCompareType()), + sorter: this._outlineComparator, identityProvider: new OutlineIdentityProvider(), keyboardNavigationLabelProvider: new OutlineNavigationLabelProvider(), filter: this._instantiationService.createInstance(OutlineFilter, 'breadcrumbs') @@ -471,6 +473,13 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { const tree = this._tree as WorkbenchDataTree; tree.setInput(model); + const textModel = model.textModel; + const overrideConfiguration = { + resource: textModel.uri, + overrideIdentifier: textModel.getLanguageIdentifier().language + }; + this._outlineComparator.type = this._getOutlineItemCompareType(overrideConfiguration); + if (element !== model) { tree.reveal(element, 0.5); tree.setFocus([element], this._fakeEvent); @@ -486,8 +495,8 @@ export class BreadcrumbsOutlinePicker extends BreadcrumbsPicker { } } - private _getOutlineItemCompareType(): OutlineSortOrder { - switch (this._symbolSortOrder.getValue()) { + private _getOutlineItemCompareType(overrideConfiguration?: IConfigurationOverrides): OutlineSortOrder { + switch (this._symbolSortOrder.getValue(overrideConfiguration)) { case 'name': return OutlineSortOrder.ByName; case 'type': diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 8be11b4334..549c3e5d1a 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -422,12 +422,14 @@ editorCommands.setup(); if (isMacintosh) { MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { command: { id: NavigateBackwardsAction.ID, title: NavigateBackwardsAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/back-tb.png')) } }, - group: 'navigation' + group: 'navigation', + order: 0 }); MenuRegistry.appendMenuItem(MenuId.TouchBarContext, { command: { id: NavigateForwardAction.ID, title: NavigateForwardAction.LABEL, icon: { dark: URI.parse(require.toUrl('vs/workbench/browser/parts/editor/media/forward-tb.png')) } }, - group: 'navigation' + group: 'navigation', + order: 1 }); } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index f6f62a016b..6cccd0f0aa 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/editorgroupview'; import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; -import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor, SaveReason } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor, SaveReason, SaveContext } from 'vs/workbench/common/editor'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom'; @@ -73,30 +73,30 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region events - private readonly _onDidFocus: Emitter = this._register(new Emitter()); - readonly onDidFocus: Event = this._onDidFocus.event; + private readonly _onDidFocus = this._register(new Emitter()); + readonly onDidFocus = this._onDidFocus.event; - private readonly _onWillDispose: Emitter = this._register(new Emitter()); - readonly onWillDispose: Event = this._onWillDispose.event; + private readonly _onWillDispose = this._register(new Emitter()); + readonly onWillDispose = this._onWillDispose.event; - private readonly _onDidGroupChange: Emitter = this._register(new Emitter()); - readonly onDidGroupChange: Event = this._onDidGroupChange.event; + private readonly _onDidGroupChange = this._register(new Emitter()); + readonly onDidGroupChange = this._onDidGroupChange.event; - private readonly _onWillOpenEditor: Emitter = this._register(new Emitter()); - readonly onWillOpenEditor: Event = this._onWillOpenEditor.event; + private readonly _onWillOpenEditor = this._register(new Emitter()); + readonly onWillOpenEditor = this._onWillOpenEditor.event; - private readonly _onDidOpenEditorFail: Emitter = this._register(new Emitter()); - readonly onDidOpenEditorFail: Event = this._onDidOpenEditorFail.event; + private readonly _onDidOpenEditorFail = this._register(new Emitter()); + readonly onDidOpenEditorFail = this._onDidOpenEditorFail.event; - private readonly _onWillCloseEditor: Emitter = this._register(new Emitter()); - readonly onWillCloseEditor: Event = this._onWillCloseEditor.event; + private readonly _onWillCloseEditor = this._register(new Emitter()); + readonly onWillCloseEditor = this._onWillCloseEditor.event; - private readonly _onDidCloseEditor: Emitter = this._register(new Emitter()); - readonly onDidCloseEditor: Event = this._onDidCloseEditor.event; + private readonly _onDidCloseEditor = this._register(new Emitter()); + readonly onDidCloseEditor = this._onDidCloseEditor.event; //#endregion - private _group: EditorGroup; + private readonly _group: EditorGroup; private _disposed = false; private active: boolean | undefined; @@ -115,9 +115,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private editorContainer: HTMLElement; private editorControl: EditorControl; - private disposedEditorsWorker: RunOnceWorker; + private readonly disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0)); - private mapEditorToPendingConfirmation: Map> = new Map>(); + private readonly mapEditorToPendingConfirmation = new Map>(); constructor( private accessor: IEditorGroupsAccessor, @@ -146,8 +146,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this._group = this._register(instantiationService.createInstance(EditorGroup, undefined)); } - this.disposedEditorsWorker = this._register(new RunOnceWorker(editors => this.handleDisposedEditors(editors), 0)); - //#region create() { // Container @@ -1312,7 +1310,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Otherwise, handle accordingly switch (res) { case ConfirmResult.SAVE: - const result = await editor.save(this._group.id, { reason: SaveReason.EXPLICIT }); + const result = await editor.save(this._group.id, { reason: SaveReason.EXPLICIT, context: SaveContext.EDITOR_CLOSE }); return !result; case ConfirmResult.DONT_SAVE: diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 986000f6bf..428ea6dac8 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -121,7 +121,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro private _partOptions: IEditorPartOptions; - private groupViews: Map = new Map(); + private readonly groupViews = new Map(); private mostRecentActiveGroups: GroupIdentifier[] = []; private container: HTMLElement | undefined; diff --git a/src/vs/workbench/browser/parts/editor/media/resourceviewer.css b/src/vs/workbench/browser/parts/editor/media/binaryeditor.css similarity index 74% rename from src/vs/workbench/browser/parts/editor/media/resourceviewer.css rename to src/vs/workbench/browser/parts/editor/media/binaryeditor.css index 507b3f6be9..57bc96d509 100644 --- a/src/vs/workbench/browser/parts/editor/media/resourceviewer.css +++ b/src/vs/workbench/browser/parts/editor/media/binaryeditor.css @@ -3,17 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-resource-viewer:focus { +.monaco-binary-resource-editor:focus { outline: none !important; } -.monaco-resource-viewer { +.monaco-binary-resource-editor { padding: 5px 0 0 10px; box-sizing: border-box; } -.monaco-resource-viewer .embedded-link, -.monaco-resource-viewer .embedded-link:hover { +.monaco-binary-resource-editor .embedded-link, +.monaco-binary-resource-editor .embedded-link:hover { cursor: pointer; text-decoration: underline; margin-left: 5px; diff --git a/src/vs/workbench/browser/parts/editor/resourceViewer.ts b/src/vs/workbench/browser/parts/editor/resourceViewer.ts deleted file mode 100644 index 1fa96e6f9a..0000000000 --- a/src/vs/workbench/browser/parts/editor/resourceViewer.ts +++ /dev/null @@ -1,144 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as DOM from 'vs/base/browser/dom'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { URI } from 'vs/base/common/uri'; -import 'vs/css!./media/resourceviewer'; -import * as nls from 'vs/nls'; -import { ICssStyleCollector, ITheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { IMAGE_PREVIEW_BORDER } from 'vs/workbench/common/theme'; - -export interface IResourceDescriptor { - readonly resource: URI; - readonly name: string; - readonly size?: number; - readonly etag?: string; - readonly mime: string; -} - -class BinarySize { - static readonly KB = 1024; - static readonly MB = BinarySize.KB * BinarySize.KB; - static readonly GB = BinarySize.MB * BinarySize.KB; - static readonly TB = BinarySize.GB * BinarySize.KB; - - static formatSize(size: number): string { - if (size < BinarySize.KB) { - return nls.localize('sizeB', "{0}B", size); - } - - if (size < BinarySize.MB) { - return nls.localize('sizeKB', "{0}KB", (size / BinarySize.KB).toFixed(2)); - } - - if (size < BinarySize.GB) { - return nls.localize('sizeMB', "{0}MB", (size / BinarySize.MB).toFixed(2)); - } - - if (size < BinarySize.TB) { - return nls.localize('sizeGB', "{0}GB", (size / BinarySize.GB).toFixed(2)); - } - - return nls.localize('sizeTB', "{0}TB", (size / BinarySize.TB).toFixed(2)); - } -} - -export interface ResourceViewerContext extends IDisposable { - layout?(dimension: DOM.Dimension): void; -} - -interface ResourceViewerDelegate { - openInternalClb(uri: URI): void; - openExternalClb?(uri: URI): void; - metadataClb(meta: string): void; -} - -registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { - const borderColor = theme.getColor(IMAGE_PREVIEW_BORDER); - collector.addRule(`.monaco-resource-viewer.image img { border : 1px solid ${borderColor ? borderColor.toString() : ''}; }`); -}); - -/** - * Helper to actually render the given resource into the provided container. Will adjust scrollbar (if provided) automatically based on loading - * progress of the binary resource. - */ -export class ResourceViewer { - - private static readonly MAX_OPEN_INTERNAL_SIZE = BinarySize.MB * 200; // max size until we offer an action to open internally - - static show( - descriptor: IResourceDescriptor, - container: HTMLElement, - scrollbar: DomScrollableElement, - delegate: ResourceViewerDelegate, - ): ResourceViewerContext { - - // Ensure CSS class - container.className = 'monaco-resource-viewer'; - - // Large Files - if (typeof descriptor.size === 'number' && descriptor.size > ResourceViewer.MAX_OPEN_INTERNAL_SIZE) { - return FileTooLargeFileView.create(container, descriptor.size, scrollbar, delegate); - } - - // Seemingly Binary Files - else { - return FileSeemsBinaryFileView.create(container, descriptor, scrollbar, delegate); - } - } -} - -class FileTooLargeFileView { - static create( - container: HTMLElement, - descriptorSize: number, - scrollbar: DomScrollableElement, - delegate: ResourceViewerDelegate - ) { - const size = BinarySize.formatSize(descriptorSize); - delegate.metadataClb(size); - - DOM.clearNode(container); - - const label = document.createElement('span'); - label.textContent = nls.localize('nativeFileTooLargeError', "The file is not displayed in the editor because it is too large ({0}).", size); - container.appendChild(label); - - scrollbar.scanDomNode(); - - return Disposable.None; - } -} - -class FileSeemsBinaryFileView { - static create( - container: HTMLElement, - descriptor: IResourceDescriptor, - scrollbar: DomScrollableElement, - delegate: ResourceViewerDelegate - ) { - delegate.metadataClb(typeof descriptor.size === 'number' ? BinarySize.formatSize(descriptor.size) : ''); - - DOM.clearNode(container); - - const disposables = new DisposableStore(); - - const label = document.createElement('p'); - label.textContent = nls.localize('nativeBinaryError', "The file is not displayed in the editor because it is either binary or uses an unsupported text encoding."); - container.appendChild(label); - - const link = DOM.append(label, DOM.$('a.embedded-link')); - link.setAttribute('role', 'button'); - link.textContent = nls.localize('openAsText', "Do you want to open it anyway?"); - - disposables.add(DOM.addDisposableListener(link, DOM.EventType.CLICK, () => delegate.openInternalClb(descriptor.resource))); - - scrollbar.scanDomNode(); - - return disposables; - } -} diff --git a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css index d25fc80a93..31ce912533 100644 --- a/src/vs/workbench/browser/parts/notifications/media/notificationsList.css +++ b/src/vs/workbench/browser/parts/notifications/media/notificationsList.css @@ -18,7 +18,6 @@ opacity: 0; position: absolute; line-height: 22px; - margin: 10px 0; /* 10px top and bottom */ word-wrap: break-word; /* never overflow long words, but break to next line */ } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index 9c328e3b7e..5b5b49baf1 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -85,7 +85,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate .menubar { /* move menubar above drag region as negative z-index on drag region cause greyscale AA */ - z-index: 2000; + z-index: 2500; } .monaco-workbench.linux .part.titlebar > .window-title { diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index 4a0c521ebd..f4e235c1c5 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -29,7 +29,7 @@ import { dirname, basename } from 'vs/base/common/resources'; import { LIGHT, FileThemeIcon, FolderThemeIcon, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { FileKind } from 'vs/platform/files/common/files'; import { WorkbenchAsyncDataTree, TreeResourceNavigator2 } from 'vs/platform/list/browser/listService'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { localize } from 'vs/nls'; import { timeout } from 'vs/base/common/async'; import { textLinkForeground, textCodeBlockBackground, focusBorder, listFilterMatchHighlight, listFilterMatchHighlightBorder } from 'vs/platform/theme/common/colorRegistry'; @@ -43,7 +43,7 @@ import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; import { isFalsyOrWhitespace } from 'vs/base/common/strings'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -export class CustomTreeViewPane extends ViewletPane { +export class CustomTreeViewPane extends ViewPane { private treeView: ITreeView; @@ -55,7 +55,7 @@ export class CustomTreeViewPane extends ViewletPane { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title }, keybindingService, contextMenuService, configurationService, contextKeyService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); diff --git a/src/vs/workbench/browser/parts/views/paneViewlet.ts b/src/vs/workbench/browser/parts/views/paneViewlet.ts deleted file mode 100644 index 1a445057df..0000000000 --- a/src/vs/workbench/browser/parts/views/paneViewlet.ts +++ /dev/null @@ -1,469 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import 'vs/css!./media/paneviewlet'; -import * as nls from 'vs/nls'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; -import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; -import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme'; -import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom'; -import { IDisposable, combinedDisposable } from 'vs/base/common/lifecycle'; -import { firstIndex } from 'vs/base/common/arrays'; -import { IAction, IActionRunner } from 'vs/base/common/actions'; -import { IActionViewItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { prepareActions } from 'vs/workbench/browser/actions'; -import { Viewlet, ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; -import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { PaneView, IPaneViewOptions, IPaneOptions, Pane } from 'vs/base/browser/ui/splitview/paneview'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IView, FocusedViewContext } from 'vs/workbench/common/views'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { assertIsDefined } from 'vs/base/common/types'; - -export interface IPaneColors extends IColorMapping { - dropBackground?: ColorIdentifier; - headerForeground?: ColorIdentifier; - headerBackground?: ColorIdentifier; - headerBorder?: ColorIdentifier; -} - -export interface IViewletPaneOptions extends IPaneOptions { - actionRunner?: IActionRunner; - id: string; - title: string; - showActionsAlways?: boolean; -} - -export abstract class ViewletPane extends Pane implements IView { - - private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; - - private _onDidFocus = this._register(new Emitter()); - readonly onDidFocus: Event = this._onDidFocus.event; - - private _onDidBlur = this._register(new Emitter()); - readonly onDidBlur: Event = this._onDidBlur.event; - - private _onDidChangeBodyVisibility = this._register(new Emitter()); - readonly onDidChangeBodyVisibility: Event = this._onDidChangeBodyVisibility.event; - - protected _onDidChangeTitleArea = this._register(new Emitter()); - readonly onDidChangeTitleArea: Event = this._onDidChangeTitleArea.event; - - private focusedViewContextKey: IContextKey; - - private _isVisible: boolean = false; - readonly id: string; - title: string; - - protected actionRunner?: IActionRunner; - protected toolbar?: ToolBar; - private readonly showActionsAlways: boolean = false; - private headerContainer?: HTMLElement; - private titleContainer?: HTMLElement; - protected twistiesContainer?: HTMLElement; - - constructor( - options: IViewletPaneOptions, - @IKeybindingService protected keybindingService: IKeybindingService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IConfigurationService protected readonly configurationService: IConfigurationService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - super(options); - - this.id = options.id; - this.title = options.title; - this.actionRunner = options.actionRunner; - this.showActionsAlways = !!options.showActionsAlways; - this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); - } - - setVisible(visible: boolean): void { - if (this._isVisible !== visible) { - this._isVisible = visible; - - if (this.isExpanded()) { - this._onDidChangeBodyVisibility.fire(visible); - } - } - } - - isVisible(): boolean { - return this._isVisible; - } - - isBodyVisible(): boolean { - return this._isVisible && this.isExpanded(); - } - - setExpanded(expanded: boolean): boolean { - const changed = super.setExpanded(expanded); - if (changed) { - this._onDidChangeBodyVisibility.fire(expanded); - } - - return changed; - } - - render(): void { - super.render(); - - const focusTracker = trackFocus(this.element); - this._register(focusTracker); - this._register(focusTracker.onDidFocus(() => { - this.focusedViewContextKey.set(this.id); - this._onDidFocus.fire(); - })); - this._register(focusTracker.onDidBlur(() => { - this.focusedViewContextKey.reset(); - this._onDidBlur.fire(); - })); - } - - protected renderHeader(container: HTMLElement): void { - this.headerContainer = container; - - this.renderTwisties(container); - - this.renderHeaderTitle(container, this.title); - - const actions = append(container, $('.actions')); - toggleClass(actions, 'show', this.showActionsAlways); - this.toolbar = new ToolBar(actions, this.contextMenuService, { - orientation: ActionsOrientation.HORIZONTAL, - actionViewItemProvider: action => this.getActionViewItem(action), - ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), - getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), - actionRunner: this.actionRunner - }); - - this._register(this.toolbar); - this.setActions(); - - const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewletPane.AlwaysShowActionsConfig)); - this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this)); - this.updateActionsVisibility(); - } - - protected renderTwisties(container: HTMLElement): void { - this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right')); - } - - protected renderHeaderTitle(container: HTMLElement, title: string): void { - this.titleContainer = append(container, $('h3.title', undefined, title)); - } - - protected updateTitle(title: string): void { - if (this.titleContainer) { - this.titleContainer.textContent = title; - } - this.title = title; - this._onDidChangeTitleArea.fire(); - } - - focus(): void { - if (this.element) { - this.element.focus(); - this._onDidFocus.fire(); - } - } - - private setActions(): void { - if (this.toolbar) { - this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); - this.toolbar.context = this.getActionsContext(); - } - } - - private updateActionsVisibility(): void { - if (!this.headerContainer) { - return; - } - const shouldAlwaysShowActions = this.configurationService.getValue('workbench.view.alwaysShowHeaderActions'); - toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions); - } - - protected updateActions(): void { - this.setActions(); - this._onDidChangeTitleArea.fire(); - } - - getActions(): IAction[] { - return []; - } - - getSecondaryActions(): IAction[] { - return []; - } - - getActionViewItem(action: IAction): IActionViewItem | undefined { - return undefined; - } - - getActionsContext(): unknown { - return undefined; - } - - getOptimalWidth(): number { - return 0; - } - - saveState(): void { - // Subclasses to implement for saving state - } -} - -export interface IViewsViewletOptions extends IPaneViewOptions { - showHeaderInTitleWhenSingleView: boolean; -} - -interface IViewletPaneItem { - pane: ViewletPane; - disposable: IDisposable; -} - -export class PaneViewlet extends Viewlet { - - private lastFocusedPane: ViewletPane | undefined; - private paneItems: IViewletPaneItem[] = []; - private paneview?: PaneView; - - get onDidSashChange(): Event { - return assertIsDefined(this.paneview).onDidSashChange; - } - - protected get panes(): ViewletPane[] { - return this.paneItems.map(i => i.pane); - } - - protected get length(): number { - return this.paneItems.length; - } - - constructor( - id: string, - private options: IViewsViewletOptions, - @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @ITelemetryService telemetryService: ITelemetryService, - @IThemeService themeService: IThemeService, - @IStorageService storageService: IStorageService - ) { - super(id, configurationService, layoutService, telemetryService, themeService, storageService); - } - - create(parent: HTMLElement): void { - super.create(parent); - this.paneview = this._register(new PaneView(parent, this.options)); - this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewletPane, to as ViewletPane))); - this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); - } - - private showContextMenu(event: StandardMouseEvent): void { - for (const paneItem of this.paneItems) { - // Do not show context menu if target is coming from inside pane views - if (isAncestor(event.target, paneItem.pane.element)) { - return; - } - } - - event.stopPropagation(); - event.preventDefault(); - - let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => this.getContextMenuActions() - }); - } - - getTitle(): string { - let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; - - if (this.isSingleView()) { - const paneItemTitle = this.paneItems[0].pane.title; - title = paneItemTitle ? `${title}: ${paneItemTitle}` : title; - } - - return title; - } - - getActions(): IAction[] { - if (this.isSingleView()) { - return this.paneItems[0].pane.getActions(); - } - - return []; - } - - getSecondaryActions(): IAction[] { - if (this.isSingleView()) { - return this.paneItems[0].pane.getSecondaryActions(); - } - - return []; - } - - getActionViewItem(action: IAction): IActionViewItem | undefined { - if (this.isSingleView()) { - return this.paneItems[0].pane.getActionViewItem(action); - } - - return super.getActionViewItem(action); - } - - focus(): void { - super.focus(); - - if (this.lastFocusedPane) { - this.lastFocusedPane.focus(); - } else if (this.paneItems.length > 0) { - for (const { pane: pane } of this.paneItems) { - if (pane.isExpanded()) { - pane.focus(); - return; - } - } - } - } - - layout(dimension: Dimension): void { - if (this.paneview) { - this.paneview.layout(dimension.height, dimension.width); - } - } - - getOptimalWidth(): number { - const sizes = this.paneItems - .map(paneItem => paneItem.pane.getOptimalWidth() || 0); - - return Math.max(...sizes); - } - - addPanes(panes: { pane: ViewletPane, size: number, index?: number; }[]): void { - const wasSingleView = this.isSingleView(); - - for (const { pane: pane, size, index } of panes) { - this.addPane(pane, size, index); - } - - this.updateViewHeaders(); - if (this.isSingleView() !== wasSingleView) { - this.updateTitleArea(); - } - } - - private addPane(pane: ViewletPane, size: number, index = this.paneItems.length - 1): void { - const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane); - const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => { - if (this.isSingleView()) { - this.updateTitleArea(); - } - }); - const onDidChange = pane.onDidChange(() => { - if (pane === this.lastFocusedPane && !pane.isExpanded()) { - this.lastFocusedPane = undefined; - } - }); - - const paneStyler = attachStyler(this.themeService, { - headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND, - headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND, - headerBorder: SIDE_BAR_SECTION_HEADER_BORDER, - dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND - }, pane); - const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange); - const paneItem: IViewletPaneItem = { pane: pane, disposable }; - - this.paneItems.splice(index, 0, paneItem); - assertIsDefined(this.paneview).addPane(pane, size, index); - } - - removePanes(panes: ViewletPane[]): void { - const wasSingleView = this.isSingleView(); - - panes.forEach(pane => this.removePane(pane)); - - this.updateViewHeaders(); - if (wasSingleView !== this.isSingleView()) { - this.updateTitleArea(); - } - } - - private removePane(pane: ViewletPane): void { - const index = firstIndex(this.paneItems, i => i.pane === pane); - - if (index === -1) { - return; - } - - if (this.lastFocusedPane === pane) { - this.lastFocusedPane = undefined; - } - - assertIsDefined(this.paneview).removePane(pane); - const [paneItem] = this.paneItems.splice(index, 1); - paneItem.disposable.dispose(); - - } - - movePane(from: ViewletPane, to: ViewletPane): void { - const fromIndex = firstIndex(this.paneItems, item => item.pane === from); - const toIndex = firstIndex(this.paneItems, item => item.pane === to); - - if (fromIndex < 0 || fromIndex >= this.paneItems.length) { - return; - } - - if (toIndex < 0 || toIndex >= this.paneItems.length) { - return; - } - - const [paneItem] = this.paneItems.splice(fromIndex, 1); - this.paneItems.splice(toIndex, 0, paneItem); - - assertIsDefined(this.paneview).movePane(from, to); - } - - resizePane(pane: ViewletPane, size: number): void { - assertIsDefined(this.paneview).resizePane(pane, size); - } - - getPaneSize(pane: ViewletPane): number { - return assertIsDefined(this.paneview).getPaneSize(pane); - } - - protected updateViewHeaders(): void { - if (this.isSingleView()) { - this.paneItems[0].pane.setExpanded(true); - this.paneItems[0].pane.headerVisible = false; - } else { - this.paneItems.forEach(i => i.pane.headerVisible = true); - } - } - - protected isSingleView(): boolean { - return this.options.showHeaderInTitleWhenSingleView && this.paneItems.length === 1; - } - - dispose(): void { - super.dispose(); - this.paneItems.forEach(i => i.disposable.dispose()); - if (this.paneview) { - this.paneview.dispose(); - } - } -} diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts new file mode 100644 index 0000000000..2981a681fb --- /dev/null +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -0,0 +1,759 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/paneviewlet'; +import * as nls from 'vs/nls'; +import { Event, Emitter } from 'vs/base/common/event'; +import { ColorIdentifier } from 'vs/platform/theme/common/colorRegistry'; +import { attachStyler, IColorMapping } from 'vs/platform/theme/common/styler'; +import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND, SIDE_BAR_SECTION_HEADER_FOREGROUND, SIDE_BAR_SECTION_HEADER_BACKGROUND, SIDE_BAR_SECTION_HEADER_BORDER } from 'vs/workbench/common/theme'; +import { append, $, trackFocus, toggleClass, EventType, isAncestor, Dimension, addDisposableListener } from 'vs/base/browser/dom'; +import { IDisposable, combinedDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { firstIndex } from 'vs/base/common/arrays'; +import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions'; +import { IActionViewItem, ActionsOrientation, Separator } from 'vs/base/browser/ui/actionbar/actionbar'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { prepareActions } from 'vs/workbench/browser/actions'; +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { PaneView, IPaneViewOptions, IPaneOptions, Pane, DefaultPaneDndController } from 'vs/base/browser/ui/splitview/paneview'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { Extensions as ViewContainerExtensions, IView, FocusedViewContext, IViewContainersRegistry, IViewDescriptor } from 'vs/workbench/common/views'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { assertIsDefined } from 'vs/base/common/types'; +import { PersistentContributableViewsModel, IAddedViewDescriptorRef, IViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; +import { Component } from 'vs/workbench/common/component'; +import { Extensions, ViewletRegistry } from 'vs/workbench/browser/viewlet'; + +export interface IPaneColors extends IColorMapping { + dropBackground?: ColorIdentifier; + headerForeground?: ColorIdentifier; + headerBackground?: ColorIdentifier; + headerBorder?: ColorIdentifier; +} + +export interface IViewPaneOptions extends IPaneOptions { + actionRunner?: IActionRunner; + id: string; + title: string; + showActionsAlways?: boolean; +} + +export abstract class ViewPane extends Pane implements IView { + + private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions'; + + private _onDidFocus = this._register(new Emitter()); + readonly onDidFocus: Event = this._onDidFocus.event; + + private _onDidBlur = this._register(new Emitter()); + readonly onDidBlur: Event = this._onDidBlur.event; + + private _onDidChangeBodyVisibility = this._register(new Emitter()); + readonly onDidChangeBodyVisibility: Event = this._onDidChangeBodyVisibility.event; + + protected _onDidChangeTitleArea = this._register(new Emitter()); + readonly onDidChangeTitleArea: Event = this._onDidChangeTitleArea.event; + + private focusedViewContextKey: IContextKey; + + private _isVisible: boolean = false; + readonly id: string; + title: string; + + protected actionRunner?: IActionRunner; + protected toolbar?: ToolBar; + private readonly showActionsAlways: boolean = false; + private headerContainer?: HTMLElement; + private titleContainer?: HTMLElement; + protected twistiesContainer?: HTMLElement; + + constructor( + options: IViewPaneOptions, + @IKeybindingService protected keybindingService: IKeybindingService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IConfigurationService protected readonly configurationService: IConfigurationService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(options); + + this.id = options.id; + this.title = options.title; + this.actionRunner = options.actionRunner; + this.showActionsAlways = !!options.showActionsAlways; + this.focusedViewContextKey = FocusedViewContext.bindTo(contextKeyService); + } + + setVisible(visible: boolean): void { + if (this._isVisible !== visible) { + this._isVisible = visible; + + if (this.isExpanded()) { + this._onDidChangeBodyVisibility.fire(visible); + } + } + } + + isVisible(): boolean { + return this._isVisible; + } + + isBodyVisible(): boolean { + return this._isVisible && this.isExpanded(); + } + + setExpanded(expanded: boolean): boolean { + const changed = super.setExpanded(expanded); + if (changed) { + this._onDidChangeBodyVisibility.fire(expanded); + } + + return changed; + } + + render(): void { + super.render(); + + const focusTracker = trackFocus(this.element); + this._register(focusTracker); + this._register(focusTracker.onDidFocus(() => { + this.focusedViewContextKey.set(this.id); + this._onDidFocus.fire(); + })); + this._register(focusTracker.onDidBlur(() => { + this.focusedViewContextKey.reset(); + this._onDidBlur.fire(); + })); + } + + protected renderHeader(container: HTMLElement): void { + this.headerContainer = container; + + this.renderTwisties(container); + + this.renderHeaderTitle(container, this.title); + + const actions = append(container, $('.actions')); + toggleClass(actions, 'show', this.showActionsAlways); + this.toolbar = new ToolBar(actions, this.contextMenuService, { + orientation: ActionsOrientation.HORIZONTAL, + actionViewItemProvider: action => this.getActionViewItem(action), + ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title), + getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id), + actionRunner: this.actionRunner + }); + + this._register(this.toolbar); + this.setActions(); + + const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewPane.AlwaysShowActionsConfig)); + this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this)); + this.updateActionsVisibility(); + } + + protected renderTwisties(container: HTMLElement): void { + this.twistiesContainer = append(container, $('.twisties.codicon.codicon-chevron-right')); + } + + protected renderHeaderTitle(container: HTMLElement, title: string): void { + this.titleContainer = append(container, $('h3.title', undefined, title)); + } + + protected updateTitle(title: string): void { + if (this.titleContainer) { + this.titleContainer.textContent = title; + } + this.title = title; + this._onDidChangeTitleArea.fire(); + } + + focus(): void { + if (this.element) { + this.element.focus(); + this._onDidFocus.fire(); + } + } + + private setActions(): void { + if (this.toolbar) { + this.toolbar.setActions(prepareActions(this.getActions()), prepareActions(this.getSecondaryActions()))(); + this.toolbar.context = this.getActionsContext(); + } + } + + private updateActionsVisibility(): void { + if (!this.headerContainer) { + return; + } + const shouldAlwaysShowActions = this.configurationService.getValue('workbench.view.alwaysShowHeaderActions'); + toggleClass(this.headerContainer, 'actions-always-visible', shouldAlwaysShowActions); + } + + protected updateActions(): void { + this.setActions(); + this._onDidChangeTitleArea.fire(); + } + + getActions(): IAction[] { + return []; + } + + getSecondaryActions(): IAction[] { + return []; + } + + getActionViewItem(action: IAction): IActionViewItem | undefined { + return undefined; + } + + getActionsContext(): unknown { + return undefined; + } + + getOptimalWidth(): number { + return 0; + } + + saveState(): void { + // Subclasses to implement for saving state + } +} + +export interface IViewPaneContainerOptions extends IPaneViewOptions { + showHeaderInTitleWhenSingleView: boolean; +} + +interface IViewPaneItem { + pane: ViewPane; + disposable: IDisposable; +} + +export class ViewPaneContainer extends Component implements IViewPaneContainer { + + private lastFocusedPane: ViewPane | undefined; + private paneItems: IViewPaneItem[] = []; + private paneview?: PaneView; + + private visible: boolean = false; + + private areExtensionsReady: boolean = false; + + private didLayout = false; + private dimension: Dimension | undefined; + + protected actionRunner: IActionRunner | undefined; + + private readonly visibleViewsCountFromCache: number | undefined; + private readonly visibleViewsStorageId: string; + protected readonly viewsModel: PersistentContributableViewsModel; + private viewDisposables: IDisposable[] = []; + + private readonly _onTitleAreaUpdate: Emitter = this._register(new Emitter()); + readonly onTitleAreaUpdate: Event = this._onTitleAreaUpdate.event; + + private readonly _onDidChangeVisibility = this._register(new Emitter()); + readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + + + get onDidSashChange(): Event { + return assertIsDefined(this.paneview).onDidSashChange; + } + + protected get panes(): ViewPane[] { + return this.paneItems.map(i => i.pane); + } + + protected get length(): number { + return this.paneItems.length; + } + + constructor( + id: string, + viewPaneContainerStateStorageId: string, + private options: IViewPaneContainerOptions, + @IInstantiationService protected instantiationService: IInstantiationService, + @IConfigurationService protected configurationService: IConfigurationService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @ITelemetryService protected telemetryService: ITelemetryService, + @IExtensionService protected extensionService: IExtensionService, + @IThemeService protected themeService: IThemeService, + @IStorageService protected storageService: IStorageService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService + ) { + + super(id, themeService, storageService); + + const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(id); + if (!container) { + throw new Error('Could not find container'); + } + + // Use default pane dnd controller if not specified + if (!this.options.dnd) { + this.options.dnd = new DefaultPaneDndController(); + } + + this.visibleViewsStorageId = `${id}.numberOfVisibleViews`; + this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined); + this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); + this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewPaneContainerStateStorageId)); + } + + create(parent: HTMLElement): void { + // super.create(parent); + this.paneview = this._register(new PaneView(parent, this.options)); + this._register(this.paneview.onDidDrop(({ from, to }) => this.movePane(from as ViewPane, to as ViewPane))); + this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); + + this._register(this.onDidSashChange(() => this.saveViewSizes())); + this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); + this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); + const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { + const size = this.viewsModel.getSize(viewDescriptor.id); + const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); + return ({ viewDescriptor, index, size, collapsed }); + }); + if (addedViews.length) { + this.onDidAddViews(addedViews); + } + + // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + this.areExtensionsReady = true; + if (this.panes.length) { + this.updateTitleArea(); + this.updateViewHeaders(); + } + }); + + this.focus(); + } + + getTitle(): string { + let title = Registry.as(Extensions.Viewlets).getViewlet(this.getId()).name; + + if (this.isSingleView()) { + const paneItemTitle = this.paneItems[0].pane.title; + title = paneItemTitle ? `${title}: ${paneItemTitle}` : title; + } + + return title; + } + + private showContextMenu(event: StandardMouseEvent): void { + for (const paneItem of this.paneItems) { + // Do not show context menu if target is coming from inside pane views + if (isAncestor(event.target, paneItem.pane.element)) { + return; + } + } + + event.stopPropagation(); + event.preventDefault(); + + let anchor: { x: number, y: number; } = { x: event.posx, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.getContextMenuActions() + }); + } + + getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] { + const result: IAction[] = []; + if (viewDescriptor) { + result.push({ + id: `${viewDescriptor.id}.removeView`, + label: nls.localize('hideView', "Hide"), + enabled: viewDescriptor.canToggleVisibility, + run: () => this.toggleViewVisibility(viewDescriptor.id) + }); + } + + const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => ({ + id: `${viewDescriptor.id}.toggleVisibility`, + label: viewDescriptor.name, + checked: this.viewsModel.isVisible(viewDescriptor.id), + enabled: viewDescriptor.canToggleVisibility, + run: () => this.toggleViewVisibility(viewDescriptor.id) + })); + + if (result.length && viewToggleActions.length) { + result.push(new Separator()); + } + + result.push(...viewToggleActions); + return result; + } + + getActions(): IAction[] { + if (this.isSingleView()) { + return this.paneItems[0].pane.getActions(); + } + + return []; + } + + getSecondaryActions(): IAction[] { + if (this.isSingleView()) { + return this.paneItems[0].pane.getSecondaryActions(); + } + + return []; + } + + getActionViewItem(action: IAction): IActionViewItem | undefined { + if (this.isSingleView()) { + return this.paneItems[0].pane.getActionViewItem(action); + } + + return undefined; + } + + focus(): void { + if (this.lastFocusedPane) { + this.lastFocusedPane.focus(); + } else if (this.paneItems.length > 0) { + for (const { pane: pane } of this.paneItems) { + if (pane.isExpanded()) { + pane.focus(); + return; + } + } + } + } + + layout(dimension: Dimension): void { + if (this.paneview) { + this.paneview.layout(dimension.height, dimension.width); + } + + this.dimension = dimension; + if (this.didLayout) { + this.saveViewSizes(); + } else { + this.didLayout = true; + this.restoreViewSizes(); + } + } + + getOptimalWidth(): number { + const additionalMargin = 16; + const optimalWidth = Math.max(...this.panes.map(view => view.getOptimalWidth() || 0)); + return optimalWidth + additionalMargin; + } + + addPanes(panes: { pane: ViewPane, size: number, index?: number; }[]): void { + const wasSingleView = this.isSingleView(); + + for (const { pane: pane, size, index } of panes) { + this.addPane(pane, size, index); + } + + this.updateViewHeaders(); + if (this.isSingleView() !== wasSingleView) { + this.updateTitleArea(); + } + } + + setVisible(visible: boolean): void { + if (this.visible !== !!visible) { + this.visible = visible; + + this._onDidChangeVisibility.fire(visible); + } + + this.panes.filter(view => view.isVisible() !== visible) + .map((view) => view.setVisible(visible)); + } + + isVisible(): boolean { + return this.visible; + } + + protected updateTitleArea(): void { + this._onTitleAreaUpdate.fire(); + + } + + protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane { + return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewPane; + } + + getView(id: string): ViewPane | undefined { + return this.panes.filter(view => view.id === id)[0]; + } + + private saveViewSizes(): void { + // Save size only when the layout has happened + if (this.didLayout) { + for (const view of this.panes) { + this.viewsModel.setSize(view.id, this.getPaneSize(view)); + } + } + } + + private restoreViewSizes(): void { + // Restore sizes only when the layout has happened + if (this.didLayout) { + let initialSizes; + for (let i = 0; i < this.viewsModel.visibleViewDescriptors.length; i++) { + const pane = this.panes[i]; + const viewDescriptor = this.viewsModel.visibleViewDescriptors[i]; + const size = this.viewsModel.getSize(viewDescriptor.id); + + if (typeof size === 'number') { + this.resizePane(pane, size); + } else { + initialSizes = initialSizes ? initialSizes : this.computeInitialSizes(); + this.resizePane(pane, initialSizes.get(pane.id) || 200); + } + } + } + } + + private computeInitialSizes(): Map { + const sizes: Map = new Map(); + if (this.dimension) { + const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0); + for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) { + sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight); + } + } + return sizes; + } + + saveState(): void { + this.panes.forEach((view) => view.saveState()); + this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE); + } + + private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { + event.stopPropagation(); + event.preventDefault(); + + const actions: IAction[] = this.getContextMenuActions(viewDescriptor); + + let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => actions + }); + } + + openView(id: string, focus?: boolean): IView { + if (focus) { + this.focus(); + } + let view = this.getView(id); + if (!view) { + this.toggleViewVisibility(id); + } + view = this.getView(id)!; + view.setExpanded(true); + if (focus) { + view.focus(); + } + return view; + } + + protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { + const panesToAdd: { pane: ViewPane, size: number, index: number }[] = []; + for (const { viewDescriptor, collapsed, index, size } of added) { + const pane = this.createView(viewDescriptor, + { + id: viewDescriptor.id, + title: viewDescriptor.name, + actionRunner: this.getActionRunner(), + expanded: !collapsed + }); + + pane.render(); + const contextMenuDisposable = addDisposableListener(pane.draggableElement, 'contextmenu', e => { + e.stopPropagation(); + e.preventDefault(); + this.onContextMenu(new StandardMouseEvent(e), viewDescriptor); + }); + + const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => { + this.viewsModel.setCollapsed(viewDescriptor.id, collapsed); + }); + + this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable)); + panesToAdd.push({ pane, size: size || pane.minimumSize, index }); + } + + this.addPanes(panesToAdd); + this.restoreViewSizes(); + + const panes: ViewPane[] = []; + for (const { pane } of panesToAdd) { + pane.setVisible(this.isVisible()); + panes.push(pane); + } + return panes; + } + + getActionRunner(): IActionRunner { + if (!this.actionRunner) { + this.actionRunner = new ActionRunner(); + } + + return this.actionRunner; + } + + private onDidRemoveViews(removed: IViewDescriptorRef[]): void { + removed = removed.sort((a, b) => b.index - a.index); + const panesToRemove: ViewPane[] = []; + for (const { index } of removed) { + const [disposable] = this.viewDisposables.splice(index, 1); + disposable.dispose(); + panesToRemove.push(this.panes[index]); + } + this.removePanes(panesToRemove); + dispose(panesToRemove); + } + + protected toggleViewVisibility(viewId: string): void { + const visible = !this.viewsModel.isVisible(viewId); + type ViewsToggleVisibilityClassification = { + viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + }; + this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible }); + this.viewsModel.setVisible(viewId, visible); + } + + + private addPane(pane: ViewPane, size: number, index = this.paneItems.length - 1): void { + const onDidFocus = pane.onDidFocus(() => this.lastFocusedPane = pane); + const onDidChangeTitleArea = pane.onDidChangeTitleArea(() => { + if (this.isSingleView()) { + this.updateTitleArea(); + } + }); + const onDidChange = pane.onDidChange(() => { + if (pane === this.lastFocusedPane && !pane.isExpanded()) { + this.lastFocusedPane = undefined; + } + }); + + // TODO@sbatten Styling is viewlet specific, must fix + const paneStyler = attachStyler(this.themeService, { + headerForeground: SIDE_BAR_SECTION_HEADER_FOREGROUND, + headerBackground: SIDE_BAR_SECTION_HEADER_BACKGROUND, + headerBorder: SIDE_BAR_SECTION_HEADER_BORDER, + dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND + }, pane); + const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange); + const paneItem: IViewPaneItem = { pane: pane, disposable }; + + this.paneItems.splice(index, 0, paneItem); + assertIsDefined(this.paneview).addPane(pane, size, index); + } + + removePanes(panes: ViewPane[]): void { + const wasSingleView = this.isSingleView(); + + panes.forEach(pane => this.removePane(pane)); + + this.updateViewHeaders(); + if (wasSingleView !== this.isSingleView()) { + this.updateTitleArea(); + } + } + + private removePane(pane: ViewPane): void { + const index = firstIndex(this.paneItems, i => i.pane === pane); + + if (index === -1) { + return; + } + + if (this.lastFocusedPane === pane) { + this.lastFocusedPane = undefined; + } + + assertIsDefined(this.paneview).removePane(pane); + const [paneItem] = this.paneItems.splice(index, 1); + paneItem.disposable.dispose(); + + } + + movePane(from: ViewPane, to: ViewPane): void { + const fromIndex = firstIndex(this.paneItems, item => item.pane === from); + const toIndex = firstIndex(this.paneItems, item => item.pane === to); + + const fromViewDescriptor = this.viewsModel.visibleViewDescriptors[fromIndex]; + const toViewDescriptor = this.viewsModel.visibleViewDescriptors[toIndex]; + + if (fromIndex < 0 || fromIndex >= this.paneItems.length) { + return; + } + + if (toIndex < 0 || toIndex >= this.paneItems.length) { + return; + } + + const [paneItem] = this.paneItems.splice(fromIndex, 1); + this.paneItems.splice(toIndex, 0, paneItem); + + assertIsDefined(this.paneview).movePane(from, to); + + this.viewsModel.move(fromViewDescriptor.id, toViewDescriptor.id); + } + + resizePane(pane: ViewPane, size: number): void { + assertIsDefined(this.paneview).resizePane(pane, size); + } + + getPaneSize(pane: ViewPane): number { + return assertIsDefined(this.paneview).getPaneSize(pane); + } + + protected updateViewHeaders(): void { + if (this.isSingleView()) { + this.paneItems[0].pane.setExpanded(true); + this.paneItems[0].pane.headerVisible = false; + } else { + this.paneItems.forEach(i => i.pane.headerVisible = true); + } + } + + protected isSingleView(): boolean { + if (!(this.options.showHeaderInTitleWhenSingleView && this.paneItems.length === 1)) { + return false; + } + if (!this.areExtensionsReady) { + if (this.visibleViewsCountFromCache === undefined) { + return false; + } + // Check in cache so that view do not jump. See #29609 + return this.visibleViewsCountFromCache === 1; + } + return true; + } + + dispose(): void { + super.dispose(); + this.paneItems.forEach(i => i.disposable.dispose()); + if (this.paneview) { + this.paneview.dispose(); + } + } +} + + diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 2fc68eda07..bf11afe5af 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -4,330 +4,30 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { dispose, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { IAction } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -import { firstIndex } from 'vs/base/common/arrays'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IViewDescriptor, IViewsViewlet, IViewContainersRegistry, Extensions as ViewContainerExtensions, IView } from 'vs/workbench/common/views'; +import { IViewDescriptor } from 'vs/workbench/common/views'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } from 'vs/platform/storage/common/storage'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { PaneViewlet, ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; -import { DefaultPaneDndController } from 'vs/base/browser/ui/splitview/paneview'; +import { ViewPaneContainer, ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { WorkbenchTree, IListService } from 'vs/platform/list/browser/listService'; import { IWorkbenchThemeService, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; import { Event } from 'vs/base/common/event'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { localize } from 'vs/nls'; -import { IAddedViewDescriptorRef, IViewDescriptorRef, PersistentContributableViewsModel } from 'vs/workbench/browser/parts/views/views'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { MementoObject } from 'vs/workbench/common/memento'; +import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; -export interface IViewletViewOptions extends IViewletPaneOptions { - viewletState: MementoObject; +export interface IViewletViewOptions extends IViewPaneOptions { } -export abstract class ViewContainerViewlet extends PaneViewlet implements IViewsViewlet { - - private readonly viewletState: MementoObject; - private didLayout = false; - private dimension: DOM.Dimension | undefined; - private areExtensionsReady: boolean = false; - - private readonly visibleViewsCountFromCache: number | undefined; - private readonly visibleViewsStorageId: string; - protected readonly viewsModel: PersistentContributableViewsModel; - private viewDisposables: IDisposable[] = []; - - constructor( - id: string, - viewletStateStorageId: string, - showHeaderInTitleWhenSingleView: boolean, - @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService, - @IStorageService protected storageService: IStorageService, - @IInstantiationService protected instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IExtensionService protected extensionService: IExtensionService, - @IWorkspaceContextService protected contextService: IWorkspaceContextService - ) { - super(id, { showHeaderInTitleWhenSingleView, dnd: new DefaultPaneDndController() }, configurationService, layoutService, contextMenuService, telemetryService, themeService, storageService); - - const container = Registry.as(ViewContainerExtensions.ViewContainersRegistry).get(id); - if (!container) { - throw new Error('Could not find container'); - } - this.viewsModel = this._register(this.instantiationService.createInstance(PersistentContributableViewsModel, container, viewletStateStorageId)); - this.viewletState = this.getMemento(StorageScope.WORKSPACE); - - this.visibleViewsStorageId = `${id}.numberOfVisibleViews`; - this.visibleViewsCountFromCache = this.storageService.getNumber(this.visibleViewsStorageId, StorageScope.WORKSPACE, undefined); - this._register(toDisposable(() => this.viewDisposables = dispose(this.viewDisposables))); - } - - create(parent: HTMLElement): void { - super.create(parent); - this._register(this.onDidSashChange(() => this.saveViewSizes())); - this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); - this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); - const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { - const size = this.viewsModel.getSize(viewDescriptor.id); - const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); - return ({ viewDescriptor, index, size, collapsed }); - }); - if (addedViews.length) { - this.onDidAddViews(addedViews); - } - - // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 - this.extensionService.whenInstalledExtensionsRegistered().then(() => { - this.areExtensionsReady = true; - if (this.panes.length) { - this.updateTitleArea(); - this.updateViewHeaders(); - } - }); - - this.focus(); - } - - getContextMenuActions(viewDescriptor?: IViewDescriptor): IAction[] { - const result: IAction[] = []; - if (viewDescriptor) { - result.push({ - id: `${viewDescriptor.id}.removeView`, - label: localize('hideView', "Hide"), - enabled: viewDescriptor.canToggleVisibility, - run: () => this.toggleViewVisibility(viewDescriptor.id) - }); - } - const viewToggleActions = this.viewsModel.viewDescriptors.map(viewDescriptor => ({ - id: `${viewDescriptor.id}.toggleVisibility`, - label: viewDescriptor.name, - checked: this.viewsModel.isVisible(viewDescriptor.id), - enabled: viewDescriptor.canToggleVisibility, - run: () => this.toggleViewVisibility(viewDescriptor.id) - })); - - if (result.length && viewToggleActions.length) { - result.push(new Separator()); - } - result.push(...viewToggleActions); - - const parentActions = this.getViewletContextMenuActions(); - if (result.length && parentActions.length) { - result.push(new Separator()); - } - result.push(...parentActions); - - return result; - } - - protected getViewletContextMenuActions() { - return super.getContextMenuActions(); - } - - setVisible(visible: boolean): void { - super.setVisible(visible); - this.panes.filter(view => view.isVisible() !== visible) - .map((view) => view.setVisible(visible)); - } - - openView(id: string, focus?: boolean): IView { - if (focus) { - this.focus(); - } - let view = this.getView(id); - if (!view) { - this.toggleViewVisibility(id); - } - view = this.getView(id)!; - view.setExpanded(true); - if (focus) { - view.focus(); - } - return view; - } - - movePane(from: ViewletPane, to: ViewletPane): void { - const fromIndex = firstIndex(this.panes, pane => pane === from); - const toIndex = firstIndex(this.panes, pane => pane === to); - const fromViewDescriptor = this.viewsModel.visibleViewDescriptors[fromIndex]; - const toViewDescriptor = this.viewsModel.visibleViewDescriptors[toIndex]; - - super.movePane(from, to); - this.viewsModel.move(fromViewDescriptor.id, toViewDescriptor.id); - } - - layout(dimension: DOM.Dimension): void { - super.layout(dimension); - this.dimension = dimension; - if (this.didLayout) { - this.saveViewSizes(); - } else { - this.didLayout = true; - this.restoreViewSizes(); - } - } - - getOptimalWidth(): number { - const additionalMargin = 16; - const optimalWidth = Math.max(...this.panes.map(view => view.getOptimalWidth() || 0)); - return optimalWidth + additionalMargin; - } - - protected isSingleView(): boolean { - if (!super.isSingleView()) { - return false; - } - if (!this.areExtensionsReady) { - if (this.visibleViewsCountFromCache === undefined) { - return false; - } - // Check in cache so that view do not jump. See #29609 - return this.visibleViewsCountFromCache === 1; - } - return true; - } - - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane { - return (this.instantiationService as any).createInstance(viewDescriptor.ctorDescriptor.ctor, ...(viewDescriptor.ctorDescriptor.arguments || []), options) as ViewletPane; - } - - protected getView(id: string): ViewletPane | undefined { - return this.panes.filter(view => view.id === id)[0]; - } - - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { - const panesToAdd: { pane: ViewletPane, size: number, index: number }[] = []; - for (const { viewDescriptor, collapsed, index, size } of added) { - const pane = this.createView(viewDescriptor, - { - id: viewDescriptor.id, - title: viewDescriptor.name, - actionRunner: this.getActionRunner(), - expanded: !collapsed, - viewletState: this.viewletState - }); - pane.render(); - const contextMenuDisposable = DOM.addDisposableListener(pane.draggableElement, 'contextmenu', e => { - e.stopPropagation(); - e.preventDefault(); - this.onContextMenu(new StandardMouseEvent(e), viewDescriptor); - }); - - const collapseDisposable = Event.latch(Event.map(pane.onDidChange, () => !pane.isExpanded()))(collapsed => { - this.viewsModel.setCollapsed(viewDescriptor.id, collapsed); - }); - - this.viewDisposables.splice(index, 0, combinedDisposable(contextMenuDisposable, collapseDisposable)); - panesToAdd.push({ pane, size: size || pane.minimumSize, index }); - } - - this.addPanes(panesToAdd); - this.restoreViewSizes(); - - const panes: ViewletPane[] = []; - for (const { pane } of panesToAdd) { - pane.setVisible(this.isVisible()); - panes.push(pane); - } - return panes; - } - - private onDidRemoveViews(removed: IViewDescriptorRef[]): void { - removed = removed.sort((a, b) => b.index - a.index); - const panesToRemove: ViewletPane[] = []; - for (const { index } of removed) { - const [disposable] = this.viewDisposables.splice(index, 1); - disposable.dispose(); - panesToRemove.push(this.panes[index]); - } - this.removePanes(panesToRemove); - dispose(panesToRemove); - } - - private onContextMenu(event: StandardMouseEvent, viewDescriptor: IViewDescriptor): void { - event.stopPropagation(); - event.preventDefault(); - - const actions: IAction[] = this.getContextMenuActions(viewDescriptor); - - let anchor: { x: number, y: number } = { x: event.posx, y: event.posy }; - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => actions - }); - } - - protected toggleViewVisibility(viewId: string): void { - const visible = !this.viewsModel.isVisible(viewId); - type ViewsToggleVisibilityClassification = { - viewId: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - visible: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - }; - this.telemetryService.publicLog2<{ viewId: String, visible: boolean }, ViewsToggleVisibilityClassification>('views.toggleVisibility', { viewId, visible }); - this.viewsModel.setVisible(viewId, visible); - } - - private saveViewSizes(): void { - // Save size only when the layout has happened - if (this.didLayout) { - for (const view of this.panes) { - this.viewsModel.setSize(view.id, this.getPaneSize(view)); - } - } - } - - private restoreViewSizes(): void { - // Restore sizes only when the layout has happened - if (this.didLayout) { - let initialSizes; - for (let i = 0; i < this.viewsModel.visibleViewDescriptors.length; i++) { - const pane = this.panes[i]; - const viewDescriptor = this.viewsModel.visibleViewDescriptors[i]; - const size = this.viewsModel.getSize(viewDescriptor.id); - - if (typeof size === 'number') { - this.resizePane(pane, size); - } else { - initialSizes = initialSizes ? initialSizes : this.computeInitialSizes(); - this.resizePane(pane, initialSizes.get(pane.id) || 200); - } - } - } - } - - private computeInitialSizes(): Map { - const sizes: Map = new Map(); - if (this.dimension) { - const totalWeight = this.viewsModel.visibleViewDescriptors.reduce((totalWeight, { weight }) => totalWeight + (weight || 20), 0); - for (const viewDescriptor of this.viewsModel.visibleViewDescriptors) { - sizes.set(viewDescriptor.id, this.dimension.height * (viewDescriptor.weight || 20) / totalWeight); - } - } - return sizes; - } - - protected saveState(): void { - this.panes.forEach((view) => view.saveState()); - this.storageService.store(this.visibleViewsStorageId, this.length, StorageScope.WORKSPACE); - - super.saveState(); - } -} - -export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { +export abstract class FilterViewPaneContainer extends ViewPaneContainer { private constantViewDescriptors: Map = new Map(); private allViews: Map> = new Map(); private filterValue: string | undefined; @@ -345,7 +45,8 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { @IExtensionService extensionService: IExtensionService, @IWorkspaceContextService contextService: IWorkspaceContextService ) { - super(viewletId, `${viewletId}.state`, false, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + + super(viewletId, `${viewletId}.state`, { showHeaderInTitleWhenSingleView: false }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); this._register(onDidChangeFilterValue(newFilterValue => { this.filterValue = newFilterValue; this.onFilterChanged(newFilterValue); @@ -397,7 +98,7 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { })); result.push(...viewToggleActions); - const parentActions = this.getViewletContextMenuActions(); + const parentActions = super.getContextMenuActions(); if (viewToggleActions.length && parentActions.length) { result.push(new Separator()); } @@ -423,8 +124,8 @@ export abstract class FilterViewContainerViewlet extends ViewContainerViewlet { return views; } - onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { - const panes: ViewletPane[] = super.onDidAddViews(added); + onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { + const panes: ViewPane[] = super.onDidAddViews(added); for (let i = 0; i < added.length; i++) { if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) { panes[i].setExpanded(false); diff --git a/src/vs/workbench/browser/viewlet.ts b/src/vs/workbench/browser/viewlet.ts index bd04fcd729..75cd20a8cc 100644 --- a/src/vs/workbench/browser/viewlet.ts +++ b/src/vs/workbench/browser/viewlet.ts @@ -9,45 +9,57 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Action, IAction } from 'vs/base/common/actions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewlet } from 'vs/workbench/common/viewlet'; -import { Composite, CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; -import { IConstructorSignature0, BrandedService } from 'vs/platform/instantiation/common/instantiation'; +import { CompositeDescriptor, CompositeRegistry } from 'vs/workbench/browser/composite'; +import { IConstructorSignature0, IInstantiationService, BrandedService } from 'vs/platform/instantiation/common/instantiation'; import { ToggleSidebarVisibilityAction, ToggleSidebarPositionAction } from 'vs/workbench/browser/actions/layoutActions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { URI } from 'vs/base/common/uri'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree'; import { AbstractTree } from 'vs/base/browser/ui/tree/abstractTree'; import { assertIsDefined } from 'vs/base/common/types'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { PaneComposite } from 'vs/workbench/browser/panecomposite'; +import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; -export abstract class Viewlet extends Composite implements IViewlet { +export abstract class Viewlet extends PaneComposite implements IViewlet { constructor(id: string, - protected configurationService: IConfigurationService, - private layoutService: IWorkbenchLayoutService, - telemetryService: ITelemetryService, - themeService: IThemeService, - storageService: IStorageService + viewPaneContainer: ViewPaneContainer, + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService ) { - super(id, telemetryService, themeService, storageService); - } - - getOptimalWidth(): number | undefined { - return undefined; + super(id, viewPaneContainer, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); } getContextMenuActions(): IAction[] { + const parentActions = [...super.getContextMenuActions()]; + if (parentActions.length) { + parentActions.push(new Separator()); + } + const toggleSidebarPositionAction = new ToggleSidebarPositionAction(ToggleSidebarPositionAction.ID, ToggleSidebarPositionAction.getLabel(this.layoutService), this.layoutService, this.configurationService); - return [toggleSidebarPositionAction, - { - id: ToggleSidebarVisibilityAction.ID, - label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), - enabled: true, - run: () => this.layoutService.setSideBarHidden(true) - }]; + return [...parentActions, toggleSidebarPositionAction, + { + id: ToggleSidebarVisibilityAction.ID, + label: nls.localize('compositePart.hideSideBarLabel', "Hide Side Bar"), + enabled: true, + run: () => this.layoutService.setSideBarHidden(true) + }]; } } @@ -64,6 +76,7 @@ export class ViewletDescriptor extends CompositeDescriptor { order?: number, iconUrl?: URI ): ViewletDescriptor { + return new ViewletDescriptor(ctor as IConstructorSignature0, id, name, cssClass, order, iconUrl); } diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index ef1be41590..67cd07b62e 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -13,7 +13,7 @@ import { getZoomLevel, isFirefox, isSafari, isChrome } from 'vs/base/browser/bro import { mark } from 'vs/base/common/performance'; import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { Registry } from 'vs/platform/registry/common/platform'; -import { isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; +import { isWindows, isLinux, isWeb, isNative, isMacintosh } from 'vs/base/common/platform'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { IEditorInputFactoryRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; @@ -252,6 +252,10 @@ export class Workbench extends Layout { private fontAliasing: 'default' | 'antialiased' | 'none' | 'auto' | undefined; private setFontAliasing(configurationService: IConfigurationService) { + if (!isMacintosh) { + return; // macOS only + } + const aliasing = configurationService.getValue<'default' | 'antialiased' | 'none' | 'auto'>('workbench.fontAliasing'); if (this.fontAliasing === aliasing) { return; diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 7d1a8e5d33..9669e39527 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -290,6 +290,15 @@ export const enum SaveReason { WINDOW_CHANGE = 4 } +export const enum SaveContext { + + /** + * Indicates that the editor is saved because it + * is being closed by the user. + */ + EDITOR_CLOSE = 1, +} + export interface ISaveOptions { /** @@ -297,6 +306,11 @@ export interface ISaveOptions { */ reason?: SaveReason; + /** + * Additional information about the context of the save. + */ + context?: SaveContext; + /** * Forces to load the contents of the working copy * again even if the working copy is not dirty. @@ -398,11 +412,6 @@ 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. */ @@ -553,10 +562,10 @@ export abstract class TextEditorInput extends EditorInput { } saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - return this.doSaveAs(group, () => this.textFileService.saveAs(this.resource, undefined, options)); + return this.doSaveAs(group, options, () => this.textFileService.saveAs(this.resource, undefined, options)); } - protected async doSaveAs(group: GroupIdentifier, saveRunnable: () => Promise, replaceAllEditors?: boolean): Promise { + protected async doSaveAs(group: GroupIdentifier, options: ISaveOptions | undefined, saveRunnable: () => Promise, replaceAllEditors?: boolean): Promise { // Preserve view state by opening the editor first. In addition // this allows the user to review the contents of the editor. @@ -574,7 +583,9 @@ export abstract class TextEditorInput extends EditorInput { // Replace editor preserving viewstate (either across all groups or // only selected group) if the target is different from the current resource - if (!isEqual(target, this.resource)) { + // and if the editor is not being saved because it is being closed + // (because in that case we do not want to open a different editor anyway) + if (options?.context !== SaveContext.EDITOR_CLOSE && !isEqual(target, this.resource)) { const replacement = this.editorService.createInput({ resource: target }); const targetGroups = replaceAllEditors ? this.editorGroupService.groups.map(group => group.id) : [group]; for (const group of targetGroups) { diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroup.ts index 4690a6c364..329e33a729 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroup.ts @@ -483,13 +483,12 @@ export class EditorGroup extends Disposable { private splice(index: number, del: boolean, editor?: EditorInput): void { const editorToDeleteOrReplace = this.editors[index]; - const args: (number | EditorInput)[] = [index, del ? 1 : 0]; - if (editor) { - args.push(editor); - } - // Perform on editors array - this.editors.splice.apply(this.editors, args); + if (editor) { + this.editors.splice(index, del ? 1 : 0, editor); + } else { + this.editors.splice(index, del ? 1 : 0); + } // Add if (!del && editor) { diff --git a/src/vs/workbench/common/editor/untitledTextEditorInput.ts b/src/vs/workbench/common/editor/untitledTextEditorInput.ts index 2e00a3d554..f24cff8336 100644 --- a/src/vs/workbench/common/editor/untitledTextEditorInput.ts +++ b/src/vs/workbench/common/editor/untitledTextEditorInput.ts @@ -163,7 +163,7 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin } save(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - return this.doSaveAs(group, async () => { + return this.doSaveAs(group, options, async () => { // With associated file path, save to the path that is // associated. Make sure to convert the result using @@ -182,7 +182,7 @@ export class UntitledTextEditorInput extends TextEditorInput implements IEncodin } saveAs(group: GroupIdentifier, options?: ITextFileSaveOptions): Promise { - return this.doSaveAs(group, () => this.textFileService.saveAs(this.resource, undefined, options), true /* replace editor across all groups */); + return this.doSaveAs(group, options, () => this.textFileService.saveAs(this.resource, undefined, options), true /* replace editor across all groups */); } async revert(options?: IRevertOptions): Promise { diff --git a/src/vs/workbench/common/panecomposite.ts b/src/vs/workbench/common/panecomposite.ts new file mode 100644 index 0000000000..475d98a427 --- /dev/null +++ b/src/vs/workbench/common/panecomposite.ts @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IView } from 'vs/workbench/common/views'; +import { IComposite } from 'vs/workbench/common/composite'; +import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; + +export interface IPaneComposite extends IComposite { + openView(id: string, focus?: boolean): IView; + getViewPaneContainer(): IViewPaneContainer; + saveState(): void; +} diff --git a/src/typings/vsda.d.ts b/src/vs/workbench/common/viewPaneContainer.ts similarity index 51% rename from src/typings/vsda.d.ts rename to src/vs/workbench/common/viewPaneContainer.ts index 5a0ca7212e..72bdb4ac4b 100644 --- a/src/typings/vsda.d.ts +++ b/src/vs/workbench/common/viewPaneContainer.ts @@ -3,8 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module 'vsda' { - export class signer { - sign(arg: any): any; - } +import { IAction, IActionViewItem } from 'vs/base/common/actions'; + +export interface IViewPaneContainer { + setVisible(visible: boolean): void; + isVisible(): boolean; + focus(): void; + getActions(): IAction[]; + getSecondaryActions(): IAction[]; + getActionViewItem(action: IAction): IActionViewItem | undefined; + saveState(): void; } diff --git a/src/vs/workbench/common/viewlet.ts b/src/vs/workbench/common/viewlet.ts index fffa1da6f4..eb1a608f43 100644 --- a/src/vs/workbench/common/viewlet.ts +++ b/src/vs/workbench/common/viewlet.ts @@ -3,14 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IComposite } from 'vs/workbench/common/composite'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IPaneComposite } from 'vs/workbench/common/panecomposite'; export const SideBarVisibleContext = new RawContextKey('sideBarVisible', false); export const SidebarFocusContext = new RawContextKey('sideBarFocus', false); export const ActiveViewletContext = new RawContextKey('activeViewlet', ''); -export interface IViewlet extends IComposite { +export interface IViewlet extends IPaneComposite { /** * Returns the minimal width needed to avoid any content horizontal truncation diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 203f11eeae..9a134628b0 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -175,7 +175,8 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { horizontal: 'auto', useShadows: true, verticalHasArrows: false, - horizontalHasArrows: false + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false }, overviewRulerLanes: 2, fixedOverflowWidgets: true, diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts index 38141efdcd..4b05fdd1a6 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectTMScopes/inspectTMScopes.ts @@ -266,10 +266,10 @@ class InspectTMScopesWidget extends Disposable implements IContentWidget { result += `font style${this._fontStyleToString(metadata.fontStyle)}`; result += `foreground${Color.Format.CSS.formatHexA(metadata.foreground)}`; result += `background${Color.Format.CSS.formatHexA(metadata.background)}`; - if (metadata.background.isOpaque() && metadata.foreground.isOpaque()) { - result += `contrast ratio${metadata.background.getContrastRatio(metadata.foreground).toFixed(2)}`; + if (metadata.background.isOpaque()) { + result += `contrast ratio${metadata.background.getContrastRatio(metadata.foreground.makeOpaque(metadata.background)).toFixed(2)}`; } else { - result += 'Contrast ratio cannot be precise for colors that use transparency'; + result += 'Contrast ratio cannot be precise for background colors that use transparency'; } result += ``; diff --git a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts index b8675d897f..d481811fc0 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/toggleMultiCursorModifier.ts @@ -48,6 +48,8 @@ class MultiCursorModifierContextKeyController implements IWorkbenchContribution @IContextKeyService contextKeyService: IContextKeyService ) { this._multiCursorModifier = multiCursorModifier.bindTo(contextKeyService); + + this._update(); configurationService.onDidChangeConfiguration((e) => { if (e.affectsConfiguration('editor.multiCursorModifier')) { this._update(); diff --git a/src/vs/workbench/contrib/customEditor/browser/commands.ts b/src/vs/workbench/contrib/customEditor/browser/commands.ts index 8cbf25001b..1749132453 100644 --- a/src/vs/workbench/contrib/customEditor/browser/commands.ts +++ b/src/vs/workbench/contrib/customEditor/browser/commands.ts @@ -12,15 +12,17 @@ import * as nls from 'vs/nls'; import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IListService } from 'vs/platform/list/browser/listService'; -import { IEditorCommandsContext } from 'vs/workbench/common/editor'; +import { IEditorCommandsContext, IEditorInput } from 'vs/workbench/common/editor'; import { CONTEXT_FOCUSED_CUSTOM_EDITOR_IS_EDITABLE, CONTEXT_HAS_CUSTOM_EDITORS, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExplorerService } from 'vs/workbench/contrib/files/common/files'; +import { defaultEditorId } from 'vs/workbench/contrib/customEditor/browser/customEditors'; +import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; const viewCategory = nls.localize('viewCategory', "View"); @@ -172,3 +174,62 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, { model.redo(); } }).register(); + +(new class ToggleCustomEditorCommand extends Command { + public static readonly ID = 'editor.action.customEditor.toggle'; + + constructor() { + super({ + id: ToggleCustomEditorCommand.ID, + precondition: CONTEXT_HAS_CUSTOM_EDITORS, + }); + } + + public runCommand(accessor: ServicesAccessor): void { + const editorService = accessor.get(IEditorService); + const activeControl = editorService.activeControl; + if (!activeControl) { + return; + } + + const activeGroup = activeControl.group; + const activeEditor = activeControl.input; + + if (!activeEditor) { + return; + } + + const targetResource = activeEditor.getResource(); + if (!targetResource) { + return; + } + + const customEditorService = accessor.get(ICustomEditorService); + const activeCustomEditor = customEditorService.activeCustomEditor; + + let toggleView = defaultEditorId; + if (!activeCustomEditor) { + const viewIDs = customEditorService.getContributedCustomEditors(targetResource); + if (viewIDs && viewIDs.length) { + toggleView = viewIDs[0].id; + } + else { + return; + } + } + + let replInput: IEditorInput; + if (toggleView === defaultEditorId) { + const instantiationService = accessor.get(IInstantiationService); + replInput = instantiationService.createInstance(FileEditorInput, targetResource, undefined, undefined); + } + else { + replInput = customEditorService.createInput(targetResource, toggleView, activeGroup); + } + + editorService.replaceEditors([{ + editor: activeEditor, + replacement: replInput, + }], activeGroup); + } +}).register(); diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 3cf5721aaa..cc27d7158a 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -15,7 +15,7 @@ import { IEditorModel, ITextEditorOptions } from 'vs/platform/editor/common/edit 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 { GroupIdentifier, IEditorInput, IRevertOptions, ISaveOptions, Verbosity, SaveContext } from 'vs/workbench/common/editor'; import { ICustomEditorModel, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; @@ -128,9 +128,11 @@ export class CustomFileEditorInput extends LazilyResolvedWebviewEditorInput { return false; } - const replacement = this.handleMove(groupId, target) || this.instantiationService.createInstance(FileEditorInput, target, undefined, undefined); + if (options?.context !== SaveContext.EDITOR_CLOSE) { + const replacement = this.handleMove(groupId, target) || this.instantiationService.createInstance(FileEditorInput, target, undefined, undefined); + await this.editorService.replaceEditors([{ editor: this, replacement, options: { pinned: true } }], groupId); + } - await this.editorService.replaceEditors([{ editor: this, replacement, options: { pinned: true } }], groupId); return true; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 3bb8106a46..a1adcf99c9 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -29,7 +29,7 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { CustomFileEditorInput } from './customEditorInput'; -const defaultEditorId = 'default'; +export const defaultEditorId = 'default'; const defaultEditorInfo = new CustomEditorInfo({ id: defaultEditorId, diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 55a9eccb58..213a2e6f1c 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -34,6 +34,8 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { isSafari } from 'vs/base/browser/browser'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; const $ = dom.$; @@ -52,18 +54,19 @@ const breakpointHelperDecoration: IModelDecorationOptions = { function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray, debugService: IDebugService, debugSettings: IDebugConfiguration): { range: Range; options: IModelDecorationOptions; }[] { const result: { range: Range; options: IModelDecorationOptions; }[] = []; breakpoints.forEach((breakpoint) => { - if (breakpoint.lineNumber <= model.getLineCount()) { - const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber); - const range = model.validateRange( - breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) - : new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688 - ); - - result.push({ - options: getBreakpointDecorationOptions(model, breakpoint, debugService, debugSettings), - range - }); + if (breakpoint.lineNumber > model.getLineCount()) { + return; } + const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber); + const range = model.validateRange( + breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) + : new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688 + ); + + result.push({ + options: getBreakpointDecorationOptions(model, breakpoint, debugService, debugSettings), + range + }); }); return result; @@ -111,8 +114,14 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio const positions = await session.breakpointsLocations(model.uri, lineNumber); if (positions.length > 1) { // Do not render candidates if there is only one, since it is already covered by the line breakpoint + const firstColumn = model.getLineFirstNonWhitespaceColumn(lineNumber); positions.forEach(p => { const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1); + if (p.column <= firstColumn) { + // Do not render candidates on the start of the line. + return; + } + const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop(); if (breakpointAtPosition && breakpointAtPosition.inlineWidget) { // Space already occupied, do not render candidate. @@ -622,4 +631,65 @@ class InlineBreakpointWidget implements IContentWidget, IDisposable { } } +registerThemingParticipant((theme, collector) => { + const debugIconBreakpointColor = theme.getColor(debugIconBreakpointForeground); + if (debugIconBreakpointColor) { + collector.addRule(` + .monaco-workbench .codicon-debug-breakpoint, + .monaco-workbench .codicon-debug-breakpoint-conditional, + .monaco-workbench .codicon-debug-breakpoint-log, + .monaco-workbench .codicon-debug-breakpoint-function, + .monaco-workbench .codicon-debug-breakpoint-data, + .monaco-workbench .codicon-debug-breakpoint-unsupported, + .monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']), + .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe-focused::after, + .monaco-workbench .codicon-debug-breakpoint.codicon-debug-stackframe::after { + color: ${debugIconBreakpointColor} !important; + } + `); + } + + const debugIconBreakpointDisabledColor = theme.getColor(debugIconBreakpointDisabledForeground); + if (debugIconBreakpointDisabledColor) { + collector.addRule(` + .monaco-workbench .codicon[class*='-disabled'] { + color: ${debugIconBreakpointDisabledColor} !important; + } + `); + } + + const debugIconBreakpointUnverifiedColor = theme.getColor(debugIconBreakpointUnverifiedForeground); + if (debugIconBreakpointUnverifiedColor) { + collector.addRule(` + .monaco-workbench .codicon[class*='-unverified'] { + color: ${debugIconBreakpointUnverifiedColor} !important; + } + `); + } + + const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground); + if (debugIconBreakpointCurrentStackframeForegroundColor) { + collector.addRule(` + .monaco-workbench .codicon-debug-stackframe { + color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important; + } + `); + } + + const debugIconBreakpointStackframeFocusedColor = theme.getColor(debugIconBreakpointStackframeForeground); + if (debugIconBreakpointStackframeFocusedColor) { + collector.addRule(` + .monaco-workbench .codicon-debug-stackframe-focused { + color: ${debugIconBreakpointStackframeFocusedColor} !important; + } + `); + } +}); + +const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', { dark: '#E51400', light: '#E51400', hc: '#E51400' }, nls.localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); +const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpointDisabledForeground', { dark: '#848484', light: '#848484', hc: '#848484' }, nls.localize('debugIcon.breakpointDisabledForeground', 'Icon color for disabled breakpoints.')); +const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', { dark: '#848484', light: '#848484', hc: '#848484' }, nls.localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.')); +const debugIconBreakpointCurrentStackframeForeground = registerColor('debugIcon.breakpointCurrentStackframeForeground', { dark: '#FFCC00', light: '#FFCC00', hc: '#FFCC00' }, nls.localize('debugIcon.breakpointCurrentStackframeForeground', 'Icon color for the current breakpoint stack frame.')); +const debugIconBreakpointStackframeForeground = registerColor('debugIcon.breakpointStackframeForeground', { dark: '#89D185', light: '#89D185', hc: '#89D185' }, nls.localize('debugIcon.breakpointStackframeForeground', 'Icon color for all breakpoint stack frames.')); + registerEditorContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID, BreakpointEditorContribution); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 6269b164e2..0b6f033fbc 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -28,7 +28,7 @@ import { attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -45,7 +45,7 @@ function createCheckbox(): HTMLInputElement { return checkbox; } -export class BreakpointsView extends ViewletPane { +export class BreakpointsView extends ViewPane { private static readonly MAX_VISIBLE_FILES = 9; private list!: WorkbenchList; @@ -63,7 +63,7 @@ export class BreakpointsView extends ViewletPane { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('breakpointsSection', "Breakpoints Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.minimumBodySize = this.maximumBodySize = this.getExpandedBodySize(); this._register(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts new file mode 100644 index 0000000000..b88f170c9d --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -0,0 +1,160 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Constants } from 'vs/base/common/uint'; +import { Range } from 'vs/editor/common/core/range'; +import { TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; +import { IDebugService, IStackFrame } from 'vs/workbench/contrib/debug/common/debug'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; + +const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + +class CallStackEditorContribution implements IEditorContribution { + private toDispose: IDisposable[] = []; + private decorationIds: string[] = []; + private topStackFrameRange: Range | undefined; + + constructor( + private readonly editor: ICodeEditor, + @IDebugService private readonly debugService: IDebugService, + ) { + const setDecorations = () => this.decorationIds = this.editor.deltaDecorations(this.decorationIds, this.createCallStackDecorations()); + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => { + setDecorations(); + })); + this.toDispose.push(this.editor.onDidChangeModel(e => { + if (e.newModelUrl) { + setDecorations(); + } + })); + } + + private createCallStackDecorations(): IModelDeltaDecoration[] { + const focusedStackFrame = this.debugService.getViewModel().focusedStackFrame; + const decorations: IModelDeltaDecoration[] = []; + this.debugService.getModel().getSessions().forEach(s => { + s.getAllThreads().forEach(t => { + if (t.stopped) { + let candidateStackFrame = t === focusedStackFrame?.thread ? focusedStackFrame : undefined; + if (!candidateStackFrame) { + const callStack = t.getCallStack(); + if (callStack.length) { + candidateStackFrame = callStack[0]; + } + } + + if (candidateStackFrame && candidateStackFrame.source.uri.toString() === this.editor.getModel()?.uri.toString()) { + decorations.push(...this.createDecorationsForStackFrame(candidateStackFrame)); + } + } + }); + }); + + return decorations; + } + + private createDecorationsForStackFrame(stackFrame: IStackFrame): IModelDeltaDecoration[] { + // only show decorations for the currently focused thread. + const result: IModelDeltaDecoration[] = []; + const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); + const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1); + + // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, + // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). + const callStack = stackFrame.thread.getCallStack(); + if (callStack && callStack.length && stackFrame === callStack[0]) { + result.push({ + options: CallStackEditorContribution.TOP_STACK_FRAME_MARGIN, + range + }); + + result.push({ + options: CallStackEditorContribution.TOP_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + + if (this.topStackFrameRange && this.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && this.topStackFrameRange.startColumn !== stackFrame.range.startColumn) { + result.push({ + options: CallStackEditorContribution.TOP_STACK_FRAME_INLINE_DECORATION, + range: columnUntilEOLRange + }); + } + this.topStackFrameRange = columnUntilEOLRange; + } else { + result.push({ + options: CallStackEditorContribution.FOCUSED_STACK_FRAME_MARGIN, + range + }); + + result.push({ + options: CallStackEditorContribution.FOCUSED_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + } + + return result; + } + + // editor decorations + + static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. + private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'codicon-debug-stackframe', + stickiness + }; + + private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'codicon-debug-stackframe-focused', + stickiness + }; + + private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-top-stack-frame-line', + stickiness + }; + + private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = { + beforeContentClassName: 'debug-top-stack-frame-column' + }; + + private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-focused-stack-frame-line', + stickiness + }; + + dispose(): void { + this.editor.deltaDecorations(this.decorationIds, []); + this.toDispose = dispose(this.toDispose); + } +} + +registerThemingParticipant((theme, collector) => { + const topStackFrame = theme.getColor(topStackFrameColor); + if (topStackFrame) { + collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); + collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); + } + + const focusedStackFrame = theme.getColor(focusedStackFrameColor); + if (focusedStackFrame) { + collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); + } +}); + +const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); +const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); + +registerEditorContribution('editor.contrib.callStack', CallStackEditorContribution); diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 59beb62354..48944a053b 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -18,7 +18,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ILabelService } from 'vs/platform/label/common/label'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -53,8 +53,7 @@ function getContext(element: CallStackItem | null): any { } : undefined; } -export class CallStackView extends ViewletPane { - +export class CallStackView extends ViewPane { private pauseMessage!: HTMLSpanElement; private pauseMessageLabel!: HTMLSpanElement; private onCallStackChangeScheduler: RunOnceScheduler; @@ -79,7 +78,7 @@ export class CallStackView extends ViewletPane { @IMenuService menuService: IMenuService, @IContextKeyService readonly contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('callstackSection', "Call Stack Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.callStackItemType = CONTEXT_CALLSTACK_ITEM_TYPE.bindTo(contextKeyService); this.contributedContextMenu = menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); @@ -818,7 +817,7 @@ class StopAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(STOP_ID, getContext(this.session)); + return this.commandService.executeCommand(STOP_ID, undefined, getContext(this.session)); } } @@ -832,7 +831,7 @@ class DisconnectAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(DISCONNECT_ID, getContext(this.session)); + return this.commandService.executeCommand(DISCONNECT_ID, undefined, getContext(this.session)); } } @@ -846,7 +845,7 @@ class RestartAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(RESTART_SESSION_ID, getContext(this.session)); + return this.commandService.executeCommand(RESTART_SESSION_ID, undefined, getContext(this.session)); } } @@ -860,7 +859,7 @@ class StepOverAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(STEP_OVER_ID, getContext(this.thread)); + return this.commandService.executeCommand(STEP_OVER_ID, undefined, getContext(this.thread)); } } @@ -874,7 +873,7 @@ class StepIntoAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(STEP_INTO_ID, getContext(this.thread)); + return this.commandService.executeCommand(STEP_INTO_ID, undefined, getContext(this.thread)); } } @@ -888,7 +887,7 @@ class StepOutAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(STEP_OUT_ID, getContext(this.thread)); + return this.commandService.executeCommand(STEP_OUT_ID, undefined, getContext(this.thread)); } } @@ -902,7 +901,7 @@ class PauseAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(PAUSE_ID, getContext(this.thread)); + return this.commandService.executeCommand(PAUSE_ID, undefined, getContext(this.thread)); } } @@ -916,6 +915,6 @@ class ContinueAction extends Action { } public run(): Promise { - return this.commandService.executeCommand(CONTINUE_ID, getContext(this.thread)); + return this.commandService.executeCommand(CONTINUE_ID, undefined, getContext(this.thread)); } } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 8a69cf8789..e7b6f4a05d 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -35,7 +35,6 @@ import { IViewsRegistry, Extensions as ViewExtensions } from 'vs/workbench/commo import { isMacintosh } from 'vs/base/common/platform'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { URI } from 'vs/base/common/uri'; -import { DebugViewlet } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { DebugQuickOpenHandler } from 'vs/workbench/contrib/debug/browser/debugQuickOpen'; import { DebugStatusContribution } from 'vs/workbench/contrib/debug/browser/debugStatus'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; @@ -47,7 +46,7 @@ import { WatchExpressionsView } from 'vs/workbench/contrib/debug/browser/watchEx import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'; import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; -import { DebugCallStackContribution } from 'vs/workbench/contrib/debug/browser/debugCallStackContribution'; +import { DebugViewlet } from 'vs/workbench/contrib/debug/browser/debugViewlet'; import { StartView } from 'vs/workbench/contrib/debug/browser/startView'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -122,7 +121,6 @@ const registry = Registry.as(WorkbenchActionRegistryEx registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View")); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugCallStackContribution, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts b/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts deleted file mode 100644 index c84401a109..0000000000 --- a/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts +++ /dev/null @@ -1,242 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Constants } from 'vs/base/common/uint'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { localize } from 'vs/nls'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; - -interface IDebugEditorModelData { - model: ITextModel; - currentStackDecorations: string[]; - topStackFrameRange: Range | undefined; -} - -const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; - -export class DebugCallStackContribution implements IWorkbenchContribution { - private modelDataMap = new Map(); - private toDispose: IDisposable[] = []; - - constructor( - @IModelService private readonly modelService: IModelService, - @IDebugService private readonly debugService: IDebugService, - ) { - this.registerListeners(); - } - - private registerListeners(): void { - this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); - this.modelService.getModels().forEach(model => this.onModelAdded(model)); - this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this)); - - this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame())); - this.toDispose.push(this.debugService.onDidChangeState(state => { - if (state === State.Inactive) { - this.modelDataMap.forEach(modelData => { - modelData.topStackFrameRange = undefined; - }); - } - })); - } - - private onModelAdded(model: ITextModel): void { - const modelUriStr = model.uri.toString(); - const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr)); - - this.modelDataMap.set(modelUriStr, { - model: model, - currentStackDecorations: currentStackDecorations, - topStackFrameRange: undefined - }); - } - - private onModelRemoved(model: ITextModel): void { - const modelUriStr = model.uri.toString(); - const data = this.modelDataMap.get(modelUriStr); - if (data) { - this.modelDataMap.delete(modelUriStr); - } - } - - private onFocusStackFrame(): void { - this.modelDataMap.forEach((modelData, uri) => { - modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri)); - }); - } - - private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] { - const result: IModelDeltaDecoration[] = []; - const stackFrame = this.debugService.getViewModel().focusedStackFrame; - if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) { - return result; - } - - // only show decorations for the currently focused thread. - const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); - const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1); - - // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, - // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). - const callStack = stackFrame.thread.getCallStack(); - if (callStack && callStack.length && stackFrame === callStack[0]) { - result.push({ - options: DebugCallStackContribution.TOP_STACK_FRAME_MARGIN, - range - }); - - result.push({ - options: DebugCallStackContribution.TOP_STACK_FRAME_DECORATION, - range: columnUntilEOLRange - }); - - const modelData = this.modelDataMap.get(modelUriStr); - if (modelData) { - if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) { - result.push({ - options: DebugCallStackContribution.TOP_STACK_FRAME_INLINE_DECORATION, - range: columnUntilEOLRange - }); - } - modelData.topStackFrameRange = columnUntilEOLRange; - } - } else { - result.push({ - options: DebugCallStackContribution.FOCUSED_STACK_FRAME_MARGIN, - range - }); - - result.push({ - options: DebugCallStackContribution.FOCUSED_STACK_FRAME_DECORATION, - range: columnUntilEOLRange - }); - } - - return result; - } - - // editor decorations - - static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; - // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. - private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-breakpoint-stackframe', - stickiness - }; - - private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'codicon-debug-breakpoint-stackframe-focused', - stickiness - }; - - private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { - isWholeLine: true, - inlineClassName: 'debug-remove-token-colors', - className: 'debug-top-stack-frame-line', - stickiness - }; - - private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = { - beforeContentClassName: 'debug-top-stack-frame-column' - }; - - private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { - isWholeLine: true, - inlineClassName: 'debug-remove-token-colors', - className: 'debug-focused-stack-frame-line', - stickiness - }; - - dispose(): void { - this.modelDataMap.forEach(modelData => { - modelData.model.deltaDecorations(modelData.currentStackDecorations, []); - }); - this.toDispose = dispose(this.toDispose); - - this.modelDataMap.clear(); - } -} - -registerThemingParticipant((theme, collector) => { - const topStackFrame = theme.getColor(topStackFrameColor); - if (topStackFrame) { - collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); - collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); - } - - const focusedStackFrame = theme.getColor(focusedStackFrameColor); - if (focusedStackFrame) { - collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); - } - - const debugIconBreakpointColor = theme.getColor(debugIconBreakpointForeground); - if (debugIconBreakpointColor) { - collector.addRule(` - .monaco-workbench .codicon-debug-breakpoint, - .monaco-workbench .codicon-debug-breakpoint-conditional, - .monaco-workbench .codicon-debug-breakpoint-log, - .monaco-workbench .codicon-debug-breakpoint-function, - .monaco-workbench .codicon-debug-breakpoint-data, - .monaco-workbench .codicon-debug-breakpoint-unsupported, - .monaco-workbench .codicon-debug-hint:not([class*='codicon-debug-breakpoint']), - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-breakpoint-stackframe-focused::after, - .monaco-workbench .codicon-debug-breakpoint.codicon-debug-breakpoint-stackframe::after { - color: ${debugIconBreakpointColor} !important; - } - `); - } - - const debugIconBreakpointDisabledColor = theme.getColor(debugIconBreakpointDisabledForeground); - if (debugIconBreakpointDisabledColor) { - collector.addRule(` - .monaco-workbench .codicon[class*='-disabled'] { - color: ${debugIconBreakpointDisabledColor} !important; - } - `); - } - - const debugIconBreakpointUnverifiedColor = theme.getColor(debugIconBreakpointUnverifiedForeground); - if (debugIconBreakpointUnverifiedColor) { - collector.addRule(` - .monaco-workbench .codicon[class*='-unverified'] { - color: ${debugIconBreakpointUnverifiedColor} !important; - } - `); - } - - const debugIconBreakpointCurrentStackframeForegroundColor = theme.getColor(debugIconBreakpointCurrentStackframeForeground); - if (debugIconBreakpointCurrentStackframeForegroundColor) { - collector.addRule(` - .monaco-workbench .codicon-debug-breakpoint-stackframe { - color: ${debugIconBreakpointCurrentStackframeForegroundColor} !important; - } - `); - } - - const debugIconBreakpointStackframeFocusedColor = theme.getColor(debugIconBreakpointStackframeForeground); - if (debugIconBreakpointStackframeFocusedColor) { - collector.addRule(` - .monaco-workbench .codicon-debug-breakpoint-stackframe-focused { - color: ${debugIconBreakpointStackframeFocusedColor} !important; - } - `); - } - -}); - -const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); -const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); - -const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', { dark: '#E51400', light: '#E51400', hc: '#E51400' }, localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); -const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpointDisabledForeground', { dark: '#848484', light: '#848484', hc: '#848484' }, localize('debugIcon.breakpointDisabledForeground', 'Icon color for disabled breakpoints.')); -const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', { dark: '#848484', light: '#848484', hc: '#848484' }, localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.')); -const debugIconBreakpointCurrentStackframeForeground = registerColor('debugIcon.breakpointCurrentStackframeForeground', { dark: '#FFCC00', light: '#FFCC00', hc: '#FFCC00' }, localize('debugIcon.breakpointCurrentStackframeForeground', 'Icon color for the current breakpoint stack frame.')); -const debugIconBreakpointStackframeForeground = registerColor('debugIcon.breakpointStackframeForeground', { dark: '#89D185', light: '#89D185', hc: '#89D185' }, localize('debugIcon.breakpointStackframeForeground', 'Icon color for all breakpoint stack frames.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index e4827854f6..d080256948 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -11,7 +11,7 @@ import { IListService } from 'vs/platform/list/browser/listService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IDebugService, IEnablement, CONTEXT_BREAKPOINTS_FOCUSED, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_VARIABLES_FOCUSED, EDITOR_CONTRIBUTION_ID, IDebugEditorContribution, CONTEXT_IN_DEBUG_MODE, CONTEXT_EXPRESSION_SELECTED, CONTEXT_BREAKPOINT_SELECTED, IConfig, IStackFrame, IThread, IDebugSession, CONTEXT_DEBUG_STATE, REPL_ID, IDebugConfiguration, CONTEXT_JUMP_TO_CURSOR_SUPPORTED } from 'vs/workbench/contrib/debug/common/debug'; import { Expression, Variable, Breakpoint, FunctionBreakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; -import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; @@ -120,7 +120,7 @@ export function registerCommands(): void { // Same for stackFrame commands and session commands. CommandsRegistry.registerCommand({ id: COPY_STACK_TRACE_ID, - handler: async (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const textResourcePropertiesService = accessor.get(ITextResourcePropertiesService); const clipboardService = accessor.get(IClipboardService); let frame = getFrame(accessor.get(IDebugService), context); @@ -133,21 +133,21 @@ export function registerCommands(): void { CommandsRegistry.registerCommand({ id: REVERSE_CONTINUE_ID, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, thread => thread.reverseContinue()); } }); CommandsRegistry.registerCommand({ id: STEP_BACK_ID, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, thread => thread.stepBack()); } }); CommandsRegistry.registerCommand({ id: TERMINATE_THREAD_ID, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, thread => thread.terminate()); } }); @@ -206,7 +206,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.F5, when: CONTEXT_IN_DEBUG_MODE, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const debugService = accessor.get(IDebugService); let session: IDebugSession | undefined; if (isSessionContext(context)) { @@ -230,7 +230,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F10, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, (thread: IThread) => thread.next()); } }); @@ -240,7 +240,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging primary: KeyCode.F11, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn()); } }); @@ -250,7 +250,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift | KeyCode.F11, when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'), - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, (thread: IThread) => thread.stepOut()); } }); @@ -260,7 +260,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F6, when: CONTEXT_DEBUG_STATE.isEqualTo('running'), - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, thread => thread.pause()); } }); @@ -279,7 +279,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.Shift | KeyCode.F5, when: CONTEXT_IN_DEBUG_MODE, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const debugService = accessor.get(IDebugService); let session: IDebugSession | undefined; if (isSessionContext(context)) { @@ -301,7 +301,7 @@ export function registerCommands(): void { CommandsRegistry.registerCommand({ id: RESTART_FRAME_ID, - handler: async (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: async (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { const debugService = accessor.get(IDebugService); let frame = getFrame(debugService, context); if (frame) { @@ -315,7 +315,7 @@ export function registerCommands(): void { weight: KeybindingWeight.WorkbenchContrib, primary: KeyCode.F5, when: CONTEXT_IN_DEBUG_MODE, - handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => { + handler: (accessor: ServicesAccessor, _: string, context: CallStackContext | unknown) => { getThreadAndRun(accessor, context, thread => thread.continue()); } }); @@ -430,9 +430,13 @@ export function registerCommands(): void { const focused = listService.lastFocusedList; if (focused) { - const elements = focused.getFocus(); + let elements = focused.getFocus(); if (Array.isArray(elements) && elements[0] instanceof Expression) { - debugService.removeWatchExpressions(elements[0].getId()); + const selection = focused.getSelection(); + if (selection && selection.indexOf(elements[0]) >= 0) { + elements = selection; + } + elements.forEach((e: Expression) => debugService.removeWatchExpressions(e.getId())); } } } @@ -470,7 +474,7 @@ export function registerCommands(): void { primary: undefined, handler: async (accessor) => { const viewletService = accessor.get(IViewletService); - const viewlet = await viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) as IExtensionsViewlet; + const viewlet = (await viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true))?.getViewPaneContainer() as IExtensionsViewPaneContainer; viewlet.search('tag:debuggers @sort:installs'); viewlet.focus(); } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index af56a6c7f2..e9375bd487 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -157,8 +157,8 @@ class SelectionToReplAction extends EditorAction { constructor() { super({ id: 'editor.debug.action.selectionToRepl', - label: nls.localize('debugEvaluate', "Debug: Evaluate"), - alias: 'Debug: Evaluate', + label: nls.localize('evaluateInDebugConsole', "Evaluate in Debug Console"), + alias: 'Evaluate', precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), contextMenuOpts: { group: 'debug', @@ -187,8 +187,8 @@ class SelectionToWatchExpressionsAction extends EditorAction { constructor() { super({ id: 'editor.debug.action.selectionToWatch', - label: nls.localize('debugAddToWatch', "Debug: Add to Watch"), - alias: 'Debug: Add to Watch', + label: nls.localize('addToWatch', "Add to Watch"), + alias: 'Add to Watch', precondition: ContextKeyExpr.and(EditorContextKeys.hasNonEmptySelection, CONTEXT_IN_DEBUG_MODE, EditorContextKeys.editorTextFocus), contextMenuOpts: { group: 'debug', diff --git a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts index b19bd936e2..e0217e892d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugViewlet.ts +++ b/src/vs/workbench/contrib/debug/browser/debugViewlet.ts @@ -8,7 +8,6 @@ import * as nls from 'vs/nls'; import { IAction } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY } from 'vs/workbench/contrib/debug/common/debug'; import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems'; @@ -26,20 +25,38 @@ import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IMenu, MenuId, IMenuService, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { TogglePanelAction } from 'vs/workbench/browser/panel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; import { StartView } from 'vs/workbench/contrib/debug/browser/startView'; -export class DebugViewlet extends ViewContainerViewlet { +// Register a lightweight viewlet responsible for making the container +export class DebugViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(DebugViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class DebugViewPaneContainer extends ViewPaneContainer { private startDebugActionViewItem: StartDebugActionViewItem | undefined; private progressResolve: (() => void) | undefined; - private breakpointView: ViewletPane | undefined; + private breakpointView: ViewPane | undefined; private paneListeners = new Map(); private debugToolBarMenu: IMenu | undefined; private disposeOnTitleUpdate: IDisposable | undefined; @@ -62,12 +79,12 @@ export class DebugViewlet extends ViewContainerViewlet { @IContextKeyService private readonly contextKeyService: IContextKeyService, @INotificationService private readonly notificationService: INotificationService ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(VIEWLET_ID, `${VIEWLET_ID}.state`, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state))); this._register(this.debugService.onDidNewSession(() => this.updateToolBar())); this._register(this.contextKeyService.onDidChangeContext(e => { - if (e.affectsSome(new Set(CONTEXT_DEBUG_UX_KEY))) { + if (e.affectsSome(new Set([CONTEXT_DEBUG_UX_KEY]))) { this.updateTitleArea(); } })); @@ -198,7 +215,7 @@ export class DebugViewlet extends ViewContainerViewlet { } } - addPanes(panes: { pane: ViewletPane, size: number, index?: number }[]): void { + addPanes(panes: { pane: ViewPane, size: number, index?: number }[]): void { super.addPanes(panes); for (const { pane: pane } of panes) { @@ -212,7 +229,7 @@ export class DebugViewlet extends ViewContainerViewlet { } } - removePanes(panes: ViewletPane[]): void { + removePanes(panes: ViewPane[]): void { super.removePanes(panes); for (const pane of panes) { dispose(this.paneListeners.get(pane.id)); diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index 19d3434cb8..21e1bb98f7 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -65,11 +65,6 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise { - if (!this.workspaceProvider.payload) { - // TODO@Ben remove me once environment is adopted - return this.openExtensionDevelopmentHostWindowLegacy(args); - } - // Find out which workspace to open debug window on let debugWorkspace: IWorkspace = undefined; const folderUriArg = this.findArgument('folder-uri', args); @@ -117,66 +112,6 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient i }); } - private openExtensionDevelopmentHostWindowLegacy(args: string[]): Promise { - // we pass the "args" as query parameters of the URL - - let newAddress = `${document.location.origin}${document.location.pathname}?`; - let gotFolder = false; - - const addQueryParameter = (key: string, value: string) => { - const lastChar = newAddress.charAt(newAddress.length - 1); - if (lastChar !== '?' && lastChar !== '&') { - newAddress += '&'; - } - newAddress += `${key}=${encodeURIComponent(value)}`; - }; - - const findArgument = (key: string) => { - for (let a of args) { - const k = `--${key}=`; - if (a.indexOf(k) === 0) { - return a.substr(k.length); - } - } - return undefined; - }; - - const f = findArgument('folder-uri'); - if (f) { - const u = URI.parse(f); - gotFolder = true; - addQueryParameter('folder', u.path); - } - if (!gotFolder) { - // request empty window - addQueryParameter('ew', 'true'); - } - - const ep = findArgument('extensionDevelopmentPath'); - if (ep) { - addQueryParameter('extensionDevelopmentPath', ep); - } - - const etp = findArgument('extensionTestsPath'); - if (etp) { - addQueryParameter('extensionTestsPath', etp); - } - - const di = findArgument('debugId'); - if (di) { - addQueryParameter('debugId', di); - } - - const ibe = findArgument('inspect-brk-extensions'); - if (ibe) { - addQueryParameter('inspect-brk-extensions', ibe); - } - - window.open(newAddress); - - return Promise.resolve(); - } - private findArgument(key: string, args: string[]): string | undefined { for (const a of args) { const k = `--${key}=`; diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index 3062b04218..cd380278f7 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -131,6 +131,8 @@ export class LinkDetector { } const options = { selection: { startLineNumber: lineNumber, startColumn: columnNumber } }; this.decorateLink(link, () => this.editorService.openEditor({ resource: uri, options })); + }).catch(() => { + // If the uri can not be resolved we should not spam the console with error, remain quite #86587 }); return link; } diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index d7a87ebdef..3e151554f4 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { normalize, isAbsolute, posix } from 'vs/base/common/path'; -import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -388,7 +388,7 @@ class SessionTreeItem extends BaseTreeItem { } } -export class LoadedScriptsView extends ViewletPane { +export class LoadedScriptsView extends ViewPane { private treeContainer!: HTMLElement; private loadedScriptsItemType: IContextKey; @@ -411,7 +411,7 @@ export class LoadedScriptsView extends ViewletPane { @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('loadedScriptsSection', "Loaded Scripts Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index 608b8f2696..1577043466 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -16,8 +16,8 @@ align-items: center; } -.codicon-debug-breakpoint.codicon-debug-breakpoint-stackframe-focused::after, -.codicon-debug-breakpoint.codicon-debug-breakpoint-stackframe::after { +.codicon-debug-breakpoint.codicon-debug-stackframe-focused::after, +.codicon-debug-breakpoint.codicon-debug-stackframe::after { content: "\eb8a"; position: absolute; } diff --git a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css index dc27c4abd9..1d5efc5fa2 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css +++ b/src/vs/workbench/contrib/debug/browser/media/debugViewlet.css @@ -108,7 +108,7 @@ } .debug-viewlet .disabled { - opacity: 0.35; + opacity: 0.65; } /* Call stack */ diff --git a/src/vs/workbench/contrib/debug/browser/startView.ts b/src/vs/workbench/contrib/debug/browser/startView.ts index 676125c8b0..9b220e3888 100644 --- a/src/vs/workbench/contrib/debug/browser/startView.ts +++ b/src/vs/workbench/contrib/debug/browser/startView.ts @@ -11,7 +11,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { localize } from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -21,11 +20,13 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { equals } from 'vs/base/common/arrays'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; const $ = dom.$; -export class StartView extends ViewletPane { + +export class StartView extends ViewPane { static ID = 'workbench.debug.startView'; static LABEL = localize('start', "Start"); @@ -34,6 +35,7 @@ export class StartView extends ViewletPane { private runButton!: Button; private firstMessageContainer!: HTMLElement; private secondMessageContainer!: HTMLElement; + private clickElement: HTMLElement | undefined; private debuggerLabels: string[] | undefined = undefined; constructor( @@ -49,7 +51,7 @@ export class StartView extends ViewletPane { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IFileDialogService private readonly dialogService: IFileDialogService ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this._register(editorService.onDidActiveEditorChange(() => this.updateView())); this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(() => this.updateView())); } @@ -74,13 +76,13 @@ export class StartView extends ViewletPane { const setSecondMessage = () => { secondMessageElement.textContent = localize('specifyHowToRun', "To futher configure Debug and Run"); - const clickElement = this.createClickElement(localize('configure', " create a launch.json file."), () => this.commandService.executeCommand(ConfigureAction.ID)); - this.secondMessageContainer.appendChild(clickElement); + this.clickElement = this.createClickElement(localize('configure', " create a launch.json file."), () => this.commandService.executeCommand(ConfigureAction.ID)); + this.secondMessageContainer.appendChild(this.clickElement); }; const setSecondMessageWithFolder = () => { secondMessageElement.textContent = localize('noLaunchConfiguration', "To futher configure Debug and Run, "); - const clickElement = this.createClickElement(localize('openFolder', " open a folder"), () => this.dialogService.pickFolderAndOpen({ forceNewWindow: false })); - this.secondMessageContainer.appendChild(clickElement); + this.clickElement = this.createClickElement(localize('openFolder', " open a folder"), () => this.dialogService.pickFolderAndOpen({ forceNewWindow: false })); + this.secondMessageContainer.appendChild(this.clickElement); const moreText = $('span.moreText'); moreText.textContent = localize('andconfigure', " and create a launch.json file."); @@ -104,8 +106,8 @@ export class StartView extends ViewletPane { } if (!enabled && emptyWorkbench) { - const clickElement = this.createClickElement(localize('openFile', "Open a file"), () => this.dialogService.pickFileAndOpen({ forceNewWindow: false })); - this.firstMessageContainer.appendChild(clickElement); + this.clickElement = this.createClickElement(localize('openFile', "Open a file"), () => this.dialogService.pickFileAndOpen({ forceNewWindow: false })); + this.firstMessageContainer.appendChild(this.clickElement); const firstMessageElement = $('span'); this.firstMessageContainer.appendChild(firstMessageElement); firstMessageElement.textContent = localize('canBeDebuggedOrRun', " which can be debugged or run."); @@ -161,6 +163,10 @@ export class StartView extends ViewletPane { } focus(): void { - this.runButton.focus(); + if (this.debugButton.enabled) { + this.debugButton.focus(); + } else if (this.clickElement) { + this.clickElement.focus(); + } } } diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index e44008bb4d..694cbdf6cc 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -17,7 +17,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { CopyValueAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, ITreeMouseEvent, ITreeContextMenuEvent, IAsyncDataSource } from 'vs/base/browser/ui/tree/tree'; @@ -37,7 +37,7 @@ let forgetScopes = true; export const variableSetEmitter = new Emitter(); -export class VariablesView extends ViewletPane { +export class VariablesView extends ViewPane { private onFocusStackFrameScheduler: RunOnceScheduler; private needsRefresh = false; @@ -54,7 +54,7 @@ export class VariablesView extends ViewletPane { @IClipboardService private readonly clipboardService: IClipboardService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('variablesSection', "Variables Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); // Use scheduler to prevent unnecessary flashing this.onFocusStackFrameScheduler = new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index b2b34fd35d..bd75a68698 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -18,7 +18,7 @@ import { IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { renderExpressionValue, renderViewTree, IInputBoxOptions, AbstractExpressionsRenderer, IExpressionTemplateData } from 'vs/workbench/contrib/debug/browser/baseDebugView'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; @@ -34,7 +34,7 @@ import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; const MAX_VALUE_RENDER_LENGTH_IN_VIEWLET = 1024; -export class WatchExpressionsView extends ViewletPane { +export class WatchExpressionsView extends ViewPane { private onWatchExpressionsUpdatedScheduler: RunOnceScheduler; private needsRefresh = false; @@ -49,7 +49,7 @@ export class WatchExpressionsView extends ViewletPane { @IConfigurationService configurationService: IConfigurationService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('watchExpressionsSection', "Watch Expressions Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.onWatchExpressionsUpdatedScheduler = new RunOnceScheduler(() => { this.needsRefresh = false; diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index a5735393e9..d8f0bec270 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -20,12 +20,12 @@ import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IDisposable } from 'vs/base/common/lifecycle'; -import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; -import { Registry } from 'vs/platform/registry/common/platform'; import { TaskIdentifier } from 'vs/workbench/contrib/tasks/common/tasks'; import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainer } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; export const VIEWLET_ID = 'workbench.view.debug'; export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); diff --git a/src/vs/workbench/contrib/debug/common/debugModel.ts b/src/vs/workbench/contrib/debug/common/debugModel.ts index 3c6f583726..d2948904fb 100644 --- a/src/vs/workbench/contrib/debug/common/debugModel.ts +++ b/src/vs/workbench/contrib/debug/common/debugModel.ts @@ -122,7 +122,7 @@ export class ExpressionContainer implements IExpressionContainer { new Variable(this.session, this.threadId, this, v.variablesReference, v.name, v.evaluateName, v.value, v.namedVariables, v.indexedVariables, v.presentationHint, v.type)) : []; } catch (e) { - return [new Variable(this.session, this.threadId, this, 0, e.message, e.message, '', 0, 0, { kind: 'virtual' }, undefined, false)]; + return [new Variable(this.session, this.threadId, this, 0, '', undefined, e.message, 0, 0, { kind: 'virtual' }, undefined, false)]; } } diff --git a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts index d1bb982aa6..6770187a15 100644 --- a/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts +++ b/src/vs/workbench/contrib/experiments/browser/experimentalPrompt.ts @@ -7,7 +7,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { INotificationService, Severity, IPromptChoice } from 'vs/platform/notification/common/notification'; import { IExperimentService, IExperiment, ExperimentActionType, IExperimentActionPromptProperties, IExperimentActionPromptCommand, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { Disposable } from 'vs/base/common/lifecycle'; import { language } from 'vs/base/common/platform'; @@ -71,7 +71,7 @@ export class ExperimentalPrompts extends Disposable implements IWorkbenchContrib this.openerService.open(URI.parse(command.externalLink)); } else if (command.curatedExtensionsKey && Array.isArray(command.curatedExtensionsList)) { this.viewletService.openViewlet('workbench.view.extensions', true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { if (viewlet) { viewlet.search('curated:' + command.curatedExtensionsKey); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 9e10aa8cc3..7de3448014 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -23,8 +23,8 @@ import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; -import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, IExtension, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions'; -import { /*RatingsWidget, InstallCountWidget,*/ RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, IExtension, ExtensionContainers } from 'vs/workbench/contrib/extensions/common/extensions'; +import { /*RatingsWidget, InstallCountWidget, */RemoteBadgeWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { EditorOptions } from 'vs/workbench/common/editor'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { CombinedInstallAction, UpdateAction, ExtensionEditorDropDownAction, ReloadAction, MaliciousStatusLabelAction, IgnoreExtensionRecommendationAction, UndoIgnoreExtensionRecommendationAction, EnableDropDownAction, DisableDropDownAction, StatusLabelAction, SetFileIconThemeAction, SetColorThemeAction, RemoteInstallAction, ExtensionToolTipAction, SystemDisabledWarningAction, LocalInstallAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; @@ -61,7 +61,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { TokenizationRegistry } from 'vs/editor/common/modes'; import { generateTokensCSSForColorMap } from 'vs/editor/common/modes/supports/tokenization'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { ExtensionsViewlet } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; +import { ExtensionsViewlet, ExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; function removeEmbeddedSVGs(documentContent: string): string { const newDocument = new DOMParser().parseFromString(documentContent, 'text/html'); @@ -367,7 +367,7 @@ export class ExtensionEditor extends BaseEditor { this.transientDisposables.add(this.onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}#review-details`)))); this.transientDisposables.add(this.onClick(template.publisher, () => { this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => viewlet.search(`publisher:"${extension.publisherDisplayName}"`)); })); @@ -393,7 +393,7 @@ export class ExtensionEditor extends BaseEditor { } this.transientDisposables.add(this.onClick(template.publisher, () => { this.viewletService.openViewlet(VIEWLET_ID, true) - .then((viewlet: ExtensionsViewlet) => viewlet.search(`publisher:"${extension.publisherDisplayName}"`)); + .then((viewlet: ExtensionsViewlet) => (viewlet.getViewPaneContainer() as ExtensionsViewPaneContainer).search(`publisher:"${extension.publisherDisplayName}"`)); })); } // {{SQL CARBON EDIT}} - End diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index 81e355586c..de53b6cd1b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -23,7 +23,7 @@ import * as Constants from 'sql/workbench/contrib/extensions/common/constants'; import Severity from 'vs/base/common/severity'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey, /*IExtensionsViewlet, IExtensionsWorkbenchService,*/ EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey/*, IExtensionsViewPaneContainer, IExtensionsWorkbenchService*/, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { flatten, distinct, shuffle, coalesce, firstIndex } from 'vs/base/common/arrays'; @@ -49,6 +49,7 @@ import { platform, env as processEnv } from 'vs/base/common/process'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}} import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +// import { Schemas } from 'vs/base/common/network'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); @@ -712,7 +713,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe */ /*private promptFiletypeBasedRecommendations(model: ITextModel): void { {{SQL CARBON EDIT}} comment out for no unused const uri = model.uri; - if (!uri || !this.fileService.canHandleResource(uri)) { + const supportedSchemes = [Schemas.untitled, Schemas.file, Schemas.vscodeRemote]; + if (!uri || supportedSchemes.indexOf(uri.scheme) === -1) { return; } @@ -903,7 +905,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe *//* this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension: fileExtension }); this.viewletService.openViewlet('workbench.view.extensions', true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(`ext:${fileExtension}`); viewlet.focus(); diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 858c08582c..35c4bd3dd0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -12,7 +12,7 @@ import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManag import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { VIEWLET_ID, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService'; @@ -24,7 +24,7 @@ import { import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { ExtensionEditor } from 'vs/workbench/contrib/extensions/browser/extensionEditor'; -import { StatusUpdater, ExtensionsViewlet, MaliciousExtensionChecker, ExtensionsViewletViewsContribution } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; +import { StatusUpdater, MaliciousExtensionChecker, ExtensionsViewletViewsContribution, ExtensionsViewlet } from 'vs/workbench/contrib/extensions/browser/extensionsViewlet'; import { IQuickOpenRegistry, Extensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import * as jsonContributionRegistry from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index bef9cdcedc..255ad72696 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -13,8 +13,7 @@ import * as json from 'vs/base/common/json'; import { ActionViewItem, Separator, IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionbar'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { dispose, Disposable } from 'vs/base/common/lifecycle'; -// {{SQL CARBON EDIT}} -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewlet, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { ExtensionsLabel, IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -1100,7 +1099,7 @@ export class CheckForUpdatesAction extends Action { } this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => viewlet.search('')); this.notificationService.info(msgAvailableExtensions); @@ -1507,7 +1506,7 @@ export class ShowEnabledExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@enabled '); viewlet.focus(); @@ -1530,7 +1529,7 @@ export class ShowInstalledExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@installed '); viewlet.focus(); @@ -1553,7 +1552,7 @@ export class ShowDisabledExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@disabled '); viewlet.focus(); @@ -1584,7 +1583,7 @@ export class ClearExtensionsInputAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(''); viewlet.focus(); @@ -1607,7 +1606,7 @@ export class ShowBuiltInExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@builtin '); viewlet.focus(); @@ -1630,7 +1629,7 @@ export class ShowOutdatedExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@outdated '); viewlet.focus(); @@ -1653,7 +1652,7 @@ export class ShowPopularExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@sort:installs '); viewlet.focus(); @@ -1676,7 +1675,7 @@ export class ShowRecommendedExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@recommended ', true); viewlet.focus(); @@ -1710,7 +1709,7 @@ export class InstallWorkspaceRecommendedExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@recommended '); viewlet.focus(); @@ -1766,7 +1765,7 @@ export class InstallRecommendedExtensionAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(`@id:${this.extensionId}`); viewlet.focus(); @@ -1848,7 +1847,7 @@ export class ShowRecommendedKeymapExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@recommended:keymaps '); viewlet.focus(); @@ -1871,7 +1870,7 @@ export class ShowLanguageExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@category:"programming languages" @sort:installs '); viewlet.focus(); @@ -1894,7 +1893,7 @@ export class ShowAzureExtensionsAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search('@sort:installs azure '); viewlet.focus(); @@ -1932,7 +1931,7 @@ export class ChangeSortAction extends Action { run(): Promise { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(this.query.toString()); viewlet.focus(); @@ -3236,7 +3235,7 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsForL const viewletService = accessor.get(IViewletService); return viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(`ext:${fileExtension.replace(/^\./, '')}`); viewlet.focus(); @@ -3247,7 +3246,7 @@ CommandsRegistry.registerCommand('workbench.extensions.action.showExtensionsWith const viewletService = accessor.get(IViewletService); return viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { const query = extensionIds .map(id => `@id:${id}`) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsQuickOpen.ts b/src/vs/workbench/contrib/extensions/browser/extensionsQuickOpen.ts index 04d01ad9c1..ddee94ab5d 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsQuickOpen.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsQuickOpen.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { IAutoFocus, Mode, IModel } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenEntry, QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler } from 'vs/workbench/browser/quickopen'; -import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsViewPaneContainer, VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IExtensionGalleryService, IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { INotificationService } from 'vs/platform/notification/common/notification'; @@ -50,7 +50,7 @@ export class ExtensionsHandler extends QuickOpenHandler { const label = nls.localize('manage', "Press Enter to manage your extensions."); const action = () => { this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(''); viewlet.focus(); @@ -97,7 +97,7 @@ export class GalleryExtensionsHandler extends QuickOpenHandler { const label = nls.localize('install', "Press Enter to install '{0}' from the Marketplace.", text); const action = () => { return this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => viewlet.search(`@id:${text}`)) .then(() => this.extensionsService.installFromGallery(galleryExtension)) .then(undefined, err => this.notificationService.error(err)); @@ -116,7 +116,7 @@ export class GalleryExtensionsHandler extends QuickOpenHandler { const label = nls.localize('searchFor', "Press Enter to search for '{0}' in the Marketplace.", text); const action = () => { this.viewletService.openViewlet(VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(text); viewlet.focus(); @@ -136,4 +136,4 @@ export class GalleryExtensionsHandler extends QuickOpenHandler { getAutoFocus(searchValue: string): IAutoFocus { return { autoFocusFirstEntry: true }; } -} \ No newline at end of file +} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index f4a075850e..b6c400abb8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -18,7 +18,7 @@ import { append, $, addClass, toggleClass, Dimension } from 'vs/base/browser/dom import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IExtensionsWorkbenchService, IExtensionsViewlet, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey, VIEW_CONTAINER } from '../common/extensions'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, AutoUpdateConfigurationKey, ShowRecommendationsOnlyOnDemandKey, CloseExtensionDetailsOnViewChangeKey, VIEW_CONTAINER } from '../common/extensions'; import { ShowEnabledExtensionsAction, ShowInstalledExtensionsAction, ShowRecommendedExtensionsAction, /*ShowPopularExtensionsAction,*/ ShowDisabledExtensionsAction, ShowOutdatedExtensionsAction, ClearExtensionsInputAction, ChangeSortAction, UpdateAllAction, CheckForUpdatesAction, DisableAllAction, EnableAllAction, @@ -46,7 +46,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { SuggestEnabledInput, attachSuggestEnabledInputBoxStyler } from 'vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput'; import { alert } from 'vs/base/browser/ui/aria/aria'; @@ -54,10 +54,10 @@ import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { RemoteNameContext } from 'vs/workbench/browser/contextkeys'; import { ILabelService } from 'vs/platform/label/common/label'; import { MementoObject } from 'vs/workbench/common/memento'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; const NonEmptyWorkspaceContext = new RawContextKey('nonEmptyWorkspace', false); const DefaultViewsContext = new RawContextKey('defaultExtensionViews', true); @@ -318,7 +318,23 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio } -export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensionsViewlet { +export class ExtensionsViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(ExtensionsViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IExtensionsViewPaneContainer { private readonly _onSearchChange: Emitter = this._register(new Emitter()); private readonly onSearchChange: EventOf = this._onSearchChange.event; @@ -358,7 +374,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(VIEWLET_ID, `${VIEWLET_ID}.state`, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); this.searchDelayer = new Delayer(500); this.nonEmptyWorkspaceContextKey = NonEmptyWorkspaceContext.bindTo(contextKeyService); @@ -508,7 +524,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio return this.searchBox ? this.searchBox.getValue().replace(/@category/g, 'category').replace(/@tag:/g, 'tag:').replace(/@ext:/g, 'ext:') : ''; } - protected saveState(): void { + saveState(): void { const value = this.searchBox ? this.searchBox.getValue() : ''; if (ExtensionsListView.isLocalExtensionsQuery(value)) { this.searchViewletState['query.value'] = value; @@ -537,7 +553,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio ))).then(() => undefined); } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { + protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { const addedViews = super.onDidAddViews(added); this.progress(Promise.all(addedViews.map(addedView => (addedView).show(this.normalizedQuery()) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 48b57f60ab..8f78e1a28a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -32,7 +32,7 @@ import { InstallWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRe import { WorkbenchPagedList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { distinct, coalesce, firstIndex } from 'vs/base/common/arrays'; import { IExperimentService, IExperiment, ExperimentActionType } from 'vs/workbench/contrib/experiments/common/experimentService'; @@ -73,7 +73,7 @@ export interface ExtensionsListViewOptions extends IViewletViewOptions { class ExtensionListViewWarning extends Error { } -export class ExtensionsListView extends ViewletPane { +export class ExtensionsListView extends ViewPane { protected readonly server: IExtensionManagementServer | undefined; private bodyTemplate: { @@ -106,7 +106,7 @@ export class ExtensionsListView extends ViewletPane { @IProductService protected readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: options.title, showActionsAlways: true }, keybindingService, contextMenuService, configurationService, contextKeyService); this.server = options.server; } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index 2010480bd5..92d360ae15 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -3,26 +3,26 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IPager } from 'vs/base/common/paging'; import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { EnablementState, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; -import { Registry } from 'vs/platform/registry/common/platform'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Disposable } from 'vs/base/common/lifecycle'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; +import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; +import { Extensions as ViewContainerExtensions, ViewContainer, IViewContainersRegistry } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; export const VIEWLET_ID = 'workbench.view.extensions'; export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); export const EXTENSIONS_CONFIG = '.azuredatastudio/extensions.json'; -export interface IExtensionsViewlet extends IViewlet { +export interface IExtensionsViewPaneContainer extends IViewPaneContainer { search(text: string, refresh?: boolean): void; } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index f85d69efb1..8c680a864a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -112,7 +112,7 @@ export class ExtensionHostProfileService extends Disposable implements IExtensio return null; } - const inspectPort = await this._extensionService.getInspectPort(false); + const inspectPort = await this._extensionService.getInspectPort(true); if (!inspectPort) { return this._dialogService.confirm({ type: 'info', diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 237120bf10..120fde9bea 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -38,7 +38,8 @@ import { randomPort } from 'vs/base/node/ports'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ILabelService } from 'vs/platform/label/common/label'; -import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import { renderCodicons } from 'vs/base/common/codicons'; +import { escape } from 'vs/base/common/strings'; import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { SlowExtensionAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions'; @@ -365,31 +366,31 @@ export class RuntimeExtensionsEditor extends BaseEditor { if (this._extensionHostProfileService.getUnresponsiveProfile(element.description.identifier)) { const el = $('span'); - el.innerHTML = renderCodicons(` $(alert) Unresponsive`); + el.innerHTML = renderCodicons(escape(` $(alert) Unresponsive`)); el.title = nls.localize('unresponsive.title', "Extension has caused the extension host to freeze."); data.msgContainer.appendChild(el); } if (isNonEmptyArray(element.status.runtimeErrors)) { const el = $('span'); - el.innerHTML = renderCodicons(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`); + el.innerHTML = renderCodicons(escape(`$(bug) ${nls.localize('errors', "{0} uncaught errors", element.status.runtimeErrors.length)}`)); data.msgContainer.appendChild(el); } if (element.status.messages && element.status.messages.length > 0) { const el = $('span'); - el.innerHTML = renderCodicons(`$(alert) ${element.status.messages[0].message}`); + el.innerHTML = renderCodicons(escape(`$(alert) ${element.status.messages[0].message}`)); data.msgContainer.appendChild(el); } if (element.description.extensionLocation.scheme !== 'file') { const el = $('span'); - el.innerHTML = renderCodicons(`$(remote) ${element.description.extensionLocation.authority}`); + el.innerHTML = renderCodicons(escape(`$(remote) ${element.description.extensionLocation.authority}`)); data.msgContainer.appendChild(el); const hostLabel = this._labelService.getHostLabel(REMOTE_HOST_SCHEME, this._environmentService.configuration.remoteAuthority); if (hostLabel) { - el.innerHTML = renderCodicons(`$(remote) ${hostLabel}`); + el.innerHTML = renderCodicons(escape(`$(remote) ${hostLabel}`)); } } diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index a2a6f01f69..a5548221d1 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -94,7 +94,7 @@ export class FeedbackDropdown extends Dropdown { return { x: position.left + position.width, // center above the container - y: position.top - 9, // above status bar + y: position.top - 26, // above status bar and beak width: position.width, height: position.height }; diff --git a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts index 3260eb613a..4dd56a201e 100644 --- a/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts +++ b/src/vs/workbench/contrib/files/browser/editors/binaryFileEditor.ts @@ -16,7 +16,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IOpenerService } from 'vs/platform/opener/common/opener'; /** - * An implementation of editor for binary files like images. + * An implementation of editor for binary files that cannot be displayed. */ export class BinaryFileEditor extends BaseBinaryResourceEditor { @@ -39,7 +39,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor { telemetryService, themeService, environmentService, - storageService, + storageService ); } diff --git a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts index d0326a0a15..dc0b2f1aab 100644 --- a/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts +++ b/src/vs/workbench/contrib/files/browser/editors/fileEditorTracker.ts @@ -8,7 +8,7 @@ import { URI } from 'vs/base/common/uri'; import * as resources from 'vs/base/common/resources'; import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { toResource, SideBySideEditorInput, IWorkbenchEditorConfiguration, SideBySideEditor as SideBySideEditorChoice } from 'vs/workbench/common/editor'; -import { ITextFileService, ITextFileEditorModel, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; +import { ITextFileService, TextFileModelChangeEvent, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; @@ -19,24 +19,21 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ResourceMap } from 'vs/base/common/map'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { ResourceQueue, timeout } from 'vs/base/common/async'; -import { onUnexpectedError } from 'vs/base/common/errors'; +import { timeout } from 'vs/base/common/async'; import { withNullAsUndefined } from 'vs/base/common/types'; -import { EditorActivation, ITextEditorOptions } from 'vs/platform/editor/common/editor'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; // {{SQL CARBON EDIT}} import { QueryEditorInput } from 'sql/workbench/contrib/query/common/queryEditorInput'; export class FileEditorTracker extends Disposable implements IWorkbenchContribution { - private closeOnFileDelete: boolean | undefined; - private modelLoadQueue = new ResourceQueue(); - private activeOutOfWorkspaceWatchers = new ResourceMap(); + private readonly activeOutOfWorkspaceWatchers = new ResourceMap(); + + private closeOnFileDelete: boolean = false; constructor( @IEditorService private readonly editorService: IEditorService, @@ -47,7 +44,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut @IEnvironmentService private readonly environmentService: IEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IHostService private readonly hostService: IHostService + @IHostService private readonly hostService: IHostService, + @ICodeEditorService private readonly codeEditorService: ICodeEditorService ) { super(); @@ -67,44 +65,20 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // Open editors from dirty text file models this._register(this.textFileService.models.onModelsDirty(e => this.onTextFilesDirty(e))); - // Editor changing - this._register(this.editorService.onDidVisibleEditorsChange(() => this.handleOutOfWorkspaceWatchers())); + // Out of workspace file watchers + this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidVisibleEditorsChange())); // Update visible editors when focus is gained this._register(this.hostService.onDidChangeFocus(e => this.onWindowFocusChange(e))); - // Lifecycle - this.lifecycleService.onShutdown(this.dispose, this); - // Configuration this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue()))); + + // Lifecycle + this.lifecycleService.onShutdown(this.dispose, this); } - private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { - if (typeof configuration.workbench?.editor?.closeOnFileDelete === 'boolean') { - this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete; - } else { - this.closeOnFileDelete = false; // default - } - } - - private onWindowFocusChange(focused: boolean): void { - if (focused) { - // the window got focus and we use this as a hint that files might have been changed outside - // of this window. since file events can be unreliable, we queue a load for models that - // are visible in any editor. since this is a fast operation in the case nothing has changed, - // we tolerate the additional work. - distinct( - coalesce(this.editorService.visibleEditors - .map(editorInput => { - const resource = toResource(editorInput, { supportSideBySide: SideBySideEditorChoice.MASTER }); - return resource ? this.textFileService.models.get(resource) : undefined; - })) - .filter(model => !model.isDirty()), - m => m.resource.toString() - ).forEach(model => this.queueModelLoad(model)); - } - } + //#region Handle deletes and moves in opened editors // Note: there is some duplication with the other file event handler below. Since we cannot always rely on the disk events // carrying all necessary data in all environments, we also use the file operation events to make sure operations are handled. @@ -114,7 +88,7 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut // Handle moves specially when file is opened if (e.isOperation(FileOperation.MOVE)) { - this.handleMovedFileInOpenedEditors(e.resource, e.target.resource); + this.handleMovedFileInOpenedFileEditors(e.resource, e.target.resource); } // Handle deletes @@ -123,21 +97,99 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut } } - private onFileChanges(e: FileChangesEvent): void { + private handleMovedFileInOpenedFileEditors(oldResource: URI, newResource: URI): void { + this.editorGroupService.groups.forEach(group => { + group.editors.forEach(editor => { + if (editor instanceof FileEditorInput || editor instanceof QueryEditorInput) { // {{SQL CARBON EDIT}} #TODO we can remove this edit by just implementing handlemove - // Handle updates - if (e.gotAdded() || e.gotUpdated()) { - this.handleUpdates(e); + // Update Editor if file (or any parent of the input) got renamed or moved + const resource = editor.getResource(); + if (resources.isEqualOrParent(resource, oldResource)) { + let reopenFileResource: URI; + if (oldResource.toString() === resource.toString()) { + reopenFileResource = newResource; // file got moved + } else { + const index = this.getIndexOfPath(resource.path, oldResource.path, resources.hasToIgnoreCase(resource)); + reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved + } + + let encoding: string | undefined = undefined; + let mode: string | undefined = undefined; + + const model = this.textFileService.models.get(resource); + if (model) { + encoding = model.getEncoding(); + mode = model.textEditorModel?.getModeId(); + } + + this.editorService.replaceEditors([{ + editor: { resource }, + replacement: { + resource: reopenFileResource, + encoding, + mode, + options: { + preserveFocus: true, + pinned: group.isPinned(editor), + index: group.getIndexOfEditor(editor), + inactive: !group.isActive(editor), + viewState: this.getViewStateFor(oldResource, group) + } + }, + }], group); + } + } + }); + }); + } + + private getIndexOfPath(path: string, candidate: string, ignoreCase: boolean): number { + if (candidate.length > path.length) { + return -1; } - // Handle deletes + if (path === candidate) { + return 0; + } + + if (ignoreCase) { + path = path.toLowerCase(); + candidate = candidate.toLowerCase(); + } + + return path.indexOf(candidate); + } + + private getViewStateFor(resource: URI, group: IEditorGroup): IEditorViewState | undefined { + const editors = this.editorService.visibleControls; + + for (const editor of editors) { + if (editor?.input && editor.group === group) { + const editorResource = editor.input.getResource(); + if (editorResource && resource.toString() === editorResource.toString()) { + const control = editor.getControl(); + if (isCodeEditor(control)) { + return withNullAsUndefined(control.saveViewState()); + } + } + } + } + + return undefined; + } + + //#endregion + + //#region File Changes: Close editors of deleted files + + private onFileChanges(e: FileChangesEvent): void { if (e.gotDeleted()) { this.handleDeletes(e, true); } } private handleDeletes(arg1: URI | FileChangesEvent, isExternal: boolean, movedTo?: URI): void { - const nonDirtyFileEditors = this.getOpenedFileEditors(false /* non-dirty only */); + const nonDirtyFileEditors = this.getNonDirtyFileEditors(); nonDirtyFileEditors.forEach(async editor => { const resource = editor.getResource(); @@ -188,14 +240,12 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut }); } - // {{SQL CARBON EDIT}} - Support FileEditorInput or QueryInput - private getOpenedFileEditors(dirtyState: boolean): (FileEditorInput | QueryEditorInput)[] { - const editors: (FileEditorInput | QueryEditorInput)[] = []; + private getNonDirtyFileEditors(): (FileEditorInput | QueryEditorInput)[] { // {{SQL CARBON EDIT}} - Support FileEditorInput or QueryInput + const editors: (FileEditorInput | QueryEditorInput)[] = []; // {{SQL CARBON EDIT}} - Support FileEditorInput or QueryInput this.editorService.editors.forEach(editor => { - // {{SQL CARBON EDIT}} - Support FileEditorInput or QueryInput - if (editor instanceof FileEditorInput || editor instanceof QueryEditorInput) { - if (!!editor.isDirty() === dirtyState) { + if (editor instanceof FileEditorInput || editor instanceof QueryEditorInput) { // {{SQL CARBON EDIT}} - Support FileEditorInput or QueryInput + if (!editor.isDirty()) { editors.push(editor); } } else if (editor instanceof SideBySideEditorInput) { @@ -203,13 +253,13 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut const details = editor.details; if (master instanceof FileEditorInput) { - if (!!master.isDirty() === dirtyState) { + if (!master.isDirty()) { editors.push(master); } } if (details instanceof FileEditorInput) { - if (!!details.isDirty() === dirtyState) { + if (!details.isDirty()) { editors.push(details); } } @@ -219,185 +269,25 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut return editors; } - private handleMovedFileInOpenedEditors(oldResource: URI, newResource: URI): void { - this.editorGroupService.groups.forEach(group => { - group.editors.forEach(editor => { - const resource = editor.getResource(); - if (resource && (editor instanceof FileEditorInput || editor instanceof QueryEditorInput || editor.handleMove)) { // {{SQL CARBON EDIT}} #TODO we can remove this edit by just implementing handlemove + //#endregion - // Update Editor if file (or any parent of the input) got renamed or moved - if (resources.isEqualOrParent(resource, oldResource)) { - let reopenFileResource: URI; - if (oldResource.toString() === resource.toString()) { - reopenFileResource = newResource; // file got moved - } else { - const index = this.getIndexOfPath(resource.path, oldResource.path, resources.hasToIgnoreCase(resource)); - reopenFileResource = resources.joinPath(newResource, resource.path.substr(index + oldResource.path.length + 1)); // parent folder got moved - } + //#region Text File Dirty: Ensure every dirty text file is opened in an editor - 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: { - ...options, - viewState: this.getViewStateFor(oldResource, group) - } - }, - }], group); - } - } - }); - }); - } - - private getIndexOfPath(path: string, candidate: string, ignoreCase: boolean): number { - if (candidate.length > path.length) { - return -1; - } - - if (path === candidate) { - return 0; - } - - if (ignoreCase) { - path = path.toLowerCase(); - candidate = candidate.toLowerCase(); - } - - return path.indexOf(candidate); - } - - private getViewStateFor(resource: URI, group: IEditorGroup): IEditorViewState | undefined { - const editors = this.editorService.visibleControls; - - for (const editor of editors) { - if (editor?.input && editor.group === group) { - const editorResource = editor.input.getResource(); - if (editorResource && resource.toString() === editorResource.toString()) { - const control = editor.getControl(); - if (isCodeEditor(control)) { - return withNullAsUndefined(control.saveViewState()); - } - } - } - } - - return undefined; - } - - private handleUpdates(e: FileChangesEvent): void { - - // Handle updates to text models - this.handleUpdatesToTextModels(e); - - // Handle updates to visible binary editors - this.handleUpdatesToVisibleBinaryEditors(e); - } - - private handleUpdatesToTextModels(e: FileChangesEvent): void { - - // Collect distinct (saved) models to update. - // - // Note: we also consider the added event because it could be that a file was added - // and updated right after. - distinct(coalesce([...e.getUpdated(), ...e.getAdded()] - .map(u => this.textFileService.models.get(u.resource))) - .filter(model => model && !model.isDirty()), m => m.resource.toString()) - .forEach(model => this.queueModelLoad(model)); - } - - private queueModelLoad(model: ITextFileEditorModel): void { - - // Load model to update (use a queue to prevent accumulation of loads - // when the load actually takes long. At most we only want the queue - // to have a size of 2 (1 running load and 1 queued load). - const queue = this.modelLoadQueue.queueFor(model.resource); - if (queue.size <= 1) { - queue.queue(() => model.load().then(undefined, onUnexpectedError)); - } - } - - private handleUpdatesToVisibleBinaryEditors(e: FileChangesEvent): void { - const editors = this.editorService.visibleControls; - editors.forEach(editor => { - const resource = editor.input ? toResource(editor.input, { supportSideBySide: SideBySideEditorChoice.MASTER }) : undefined; - - // Support side-by-side binary editors too - let isBinaryEditor = false; - if (editor instanceof SideBySideEditor) { - const masterEditor = editor.getMasterEditor(); - isBinaryEditor = masterEditor?.getId() === BINARY_FILE_EDITOR_ID; - } else { - isBinaryEditor = editor.getId() === BINARY_FILE_EDITOR_ID; - } - - // Binary editor that should reload from event - if (resource && editor.input && isBinaryEditor && (e.contains(resource, FileChangeType.UPDATED) || e.contains(resource, FileChangeType.ADDED))) { - this.editorService.openEditor(editor.input, { forceReload: true, preserveFocus: true, activation: EditorActivation.PRESERVE }, editor.group); - } - }); - } - - private handleOutOfWorkspaceWatchers(): void { - const visibleOutOfWorkspacePaths = new ResourceMap(); - coalesce(this.editorService.visibleEditors.map(editorInput => { - return toResource(editorInput, { supportSideBySide: SideBySideEditorChoice.MASTER }); - })).filter(resource => { - return this.fileService.canHandleResource(resource) && !this.contextService.isInsideWorkspace(resource); - }).forEach(resource => { - visibleOutOfWorkspacePaths.set(resource, resource); - }); - - // Handle no longer visible out of workspace resources - this.activeOutOfWorkspaceWatchers.keys().forEach(resource => { - if (!visibleOutOfWorkspacePaths.get(resource)) { - dispose(this.activeOutOfWorkspaceWatchers.get(resource)); - this.activeOutOfWorkspaceWatchers.delete(resource); - } - }); - - // Handle newly visible out of workspace resources - visibleOutOfWorkspacePaths.forEach(resource => { - if (!this.activeOutOfWorkspaceWatchers.get(resource)) { - const disposable = this.fileService.watch(resource); - this.activeOutOfWorkspaceWatchers.set(resource, disposable); - } - }); - } - - private onTextFilesDirty(e: readonly TextFileModelChangeEvent[]): void { + private onTextFilesDirty(events: ReadonlyArray): void { // If files become dirty but are not opened, we open it in the background unless there are pending to be saved - this.doOpenDirtyResources(distinct(e.filter(e => { + this.doOpenDirtyResourcesInBackground(distinct(events.filter(({ resource }) => { // Only dirty models that are not PENDING_SAVE - const model = this.textFileService.models.get(e.resource); + const model = this.textFileService.models.get(resource); const shouldOpen = model?.isDirty() && !model.hasState(ModelState.PENDING_SAVE); // Only if not open already - return shouldOpen && !this.editorService.isOpen({ resource: e.resource }); - }).map(e => e.resource), r => r.toString())); + return shouldOpen && !this.editorService.isOpen({ resource }); + }).map(event => event.resource), resource => resource.toString())); } - private doOpenDirtyResources(resources: URI[]): void { - - // Open + private doOpenDirtyResourcesInBackground(resources: URI[]): void { this.editorService.openEditors(resources.map(resource => { return { resource, @@ -406,6 +296,91 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut })); } + //#endregion + + //#region Visible Editors Change: Install file watchers for out of workspace resources that became visible + + private onDidVisibleEditorsChange(): void { + const visibleOutOfWorkspaceResources = new ResourceMap(); + + for (const editor of this.editorService.visibleEditors) { + const resources = distinct(coalesce([ + toResource(editor, { supportSideBySide: SideBySideEditorChoice.MASTER }), + toResource(editor, { supportSideBySide: SideBySideEditorChoice.DETAILS }) + ]), resource => resource.toString()); + + for (const resource of resources) { + if (this.fileService.canHandleResource(resource) && !this.contextService.isInsideWorkspace(resource)) { + visibleOutOfWorkspaceResources.set(resource, resource); + } + } + } + + // Handle no longer visible out of workspace resources + this.activeOutOfWorkspaceWatchers.keys().forEach(resource => { + if (!visibleOutOfWorkspaceResources.get(resource)) { + dispose(this.activeOutOfWorkspaceWatchers.get(resource)); + this.activeOutOfWorkspaceWatchers.delete(resource); + } + }); + + // Handle newly visible out of workspace resources + visibleOutOfWorkspaceResources.forEach(resource => { + if (!this.activeOutOfWorkspaceWatchers.get(resource)) { + const disposable = this.fileService.watch(resource); + this.activeOutOfWorkspaceWatchers.set(resource, disposable); + } + }); + } + + //#endregion + + //#region Window Focus Change: Update visible code editors when focus is gained + + private onWindowFocusChange(focused: boolean): void { + if (focused) { + // the window got focus and we use this as a hint that files might have been changed outside + // of this window. since file events can be unreliable, we queue a load for models that + // are visible in any editor. since this is a fast operation in the case nothing has changed, + // we tolerate the additional work. + distinct( + coalesce(this.codeEditorService.listCodeEditors() + .map(codeEditor => { + const resource = codeEditor.getModel()?.uri; + if (!resource) { + return undefined; + } + + const model = this.textFileService.models.get(resource); + if (!model) { + return undefined; + } + + if (model.isDirty()) { + return undefined; + } + + return model; + })), + model => model.resource.toString() + ).forEach(model => model.load()); + } + } + + //#endregion + + //#region Configuration Change + + private onConfigurationUpdated(configuration: IWorkbenchEditorConfiguration): void { + if (typeof configuration.workbench?.editor?.closeOnFileDelete === 'boolean') { + this.closeOnFileDelete = configuration.workbench.editor.closeOnFileDelete; + } else { + this.closeOnFileDelete = false; // default + } + } + + //#endregion + dispose(): void { super.dispose(); diff --git a/src/vs/workbench/contrib/files/browser/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts similarity index 91% rename from src/vs/workbench/contrib/files/browser/textFileSaveErrorHandler.ts rename to src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 045cac2555..8f93c6f0d6 100644 --- a/src/vs/workbench/contrib/files/browser/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -21,9 +21,7 @@ import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorIn import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { TextFileContentProvider } from 'vs/workbench/contrib/files/common/files'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; -import { IModelService } from 'vs/editor/common/services/modelService'; import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands'; -import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -33,6 +31,8 @@ import { Event } from 'vs/base/common/event'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { isWindows } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; +import { SaveReason } from 'vs/workbench/common/editor'; export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext'; export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution'; @@ -126,9 +126,12 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa // Otherwise show the message that will lead the user into the save conflict editor. else { - message = nls.localize('staleSaveError', "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents.", basename(resource)); + message = nls.localize('staleSaveError', "Failed to save '{0}': The content of the file is newer. Please compare your version with the file contents or overwrite the content of the file with your changes.", basename(resource)); primaryActions.push(this.instantiationService.createInstance(ResolveSaveConflictAction, model)); + primaryActions.push(this.instantiationService.createInstance(SaveIgnoreModifiedSinceAction, model)); + + secondaryActions.push(this.instantiationService.createInstance(ConfigureSaveConflictAction)); } } @@ -278,7 +281,8 @@ class SaveElevatedAction extends Action { if (!this.model.isDisposed()) { this.model.save({ writeElevated: true, - overwriteReadonly: this.triedToMakeWriteable + overwriteReadonly: this.triedToMakeWriteable, + reason: SaveReason.EXPLICIT }); } @@ -296,17 +300,48 @@ class OverwriteReadonlyAction extends Action { run(): Promise { if (!this.model.isDisposed()) { - this.model.save({ overwriteReadonly: true }); + this.model.save({ overwriteReadonly: true, reason: SaveReason.EXPLICIT }); } return Promise.resolve(true); } } +class SaveIgnoreModifiedSinceAction extends Action { + + constructor( + private model: ITextFileEditorModel + ) { + super('workbench.files.action.saveIgnoreModifiedSince', nls.localize('overwrite', "Overwrite")); + } + + run(): Promise { + if (!this.model.isDisposed()) { + this.model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }); + } + + return Promise.resolve(true); + } +} + +class ConfigureSaveConflictAction extends Action { + + constructor( + @IPreferencesService private readonly preferencesService: IPreferencesService + ) { + super('workbench.files.action.configureSaveConflict', nls.localize('configure', "Configure")); + } + + run(): Promise { + this.preferencesService.openSettings(undefined, 'files.preventSaveConflicts'); + + return Promise.resolve(true); + } +} + export const acceptLocalChangesCommand = async (accessor: ServicesAccessor, resource: URI) => { const editorService = accessor.get(IEditorService); const resolverService = accessor.get(ITextModelService); - const modelService = accessor.get(IModelService); const control = editorService.activeControl; if (!control) { @@ -318,18 +353,11 @@ export const acceptLocalChangesCommand = async (accessor: ServicesAccessor, reso const reference = await resolverService.createModelReference(resource); const model = reference.object as IResolvedTextFileEditorModel; - const localModelSnapshot = model.createSnapshot(); clearPendingResolveSaveConflictMessages(); // hide any previously shown message about how to use these actions - // Revert to be able to save - await model.revert(); - - // Restore user value (without loosing undo stack) - modelService.updateModel(model.textEditorModel, createTextBufferFactoryFromSnapshot(localModelSnapshot)); - // Trigger save - await model.save(); + await model.save({ ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }); // Reopen file input await editorService.openEditor({ resource: model.resource }, group); diff --git a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts index cd242f385d..d35a1226f2 100644 --- a/src/vs/workbench/contrib/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/contrib/files/browser/explorerViewlet.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/explorerviewlet'; import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { VIEWLET_ID, ExplorerViewletVisibleContext, IFilesConfiguration, OpenEditorsVisibleContext, VIEW_CONTAINER } from 'vs/workbench/contrib/files/common/files'; -import { ViewContainerViewlet, IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ExplorerView } from 'vs/workbench/contrib/files/browser/views/explorerView'; import { EmptyView } from 'vs/workbench/contrib/files/browser/views/emptyView'; @@ -29,11 +29,12 @@ import { DelegatingEditorService } from 'vs/workbench/services/editor/browser/ed import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditor } from 'vs/workbench/common/editor'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Registry } from 'vs/platform/registry/common/platform'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { withUndefinedAsNull } from 'vs/base/common/types'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; export class ExplorerViewletViewsContribution extends Disposable implements IWorkbenchContribution { @@ -146,7 +147,23 @@ export class ExplorerViewletViewsContribution extends Disposable implements IWor } } -export class ExplorerViewlet extends ViewContainerViewlet { +export class ExplorerViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(ExplorerViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class ExplorerViewPaneContainer extends ViewPaneContainer { private static readonly EXPLORER_VIEWS_STATE = 'workbench.explorer.views.state'; @@ -165,7 +182,8 @@ export class ExplorerViewlet extends ViewContainerViewlet { @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService ) { - super(VIEWLET_ID, ExplorerViewlet.EXPLORER_VIEWS_STATE, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + + super(VIEWLET_ID, ExplorerViewPaneContainer.EXPLORER_VIEWS_STATE, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); this.viewletVisibleContextKey = ExplorerViewletVisibleContext.bindTo(contextKeyService); @@ -177,7 +195,7 @@ export class ExplorerViewlet extends ViewContainerViewlet { DOM.addClass(parent, 'explorer-viewlet'); } - protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewletPane { + protected createView(viewDescriptor: IViewDescriptor, options: IViewletViewOptions): ViewPane { if (viewDescriptor.id === ExplorerView.ID) { // Create a delegating editor service for the explorer to be able to delay the refresh in the opened // editors view above. This is a workaround for being able to double click on a file to make it pinned diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index fdc0634308..718790d953 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, DOWNLOAD_LABEL, GlobalNewUntitledPlainFileAction } from 'vs/workbench/contrib/files/browser/fileActions'; -import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/textFileSaveErrorHandler'; +import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 46456e5ddd..4b054af598 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -18,7 +18,7 @@ import { VIEWLET_ID, IExplorerService, IFilesConfiguration } from 'vs/workbench/ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IFileService } from 'vs/platform/files/common/files'; import { toResource, SideBySideEditor } from 'vs/workbench/common/editor'; -import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -673,7 +673,7 @@ export class CollapseExplorerView extends Action { } async run(): Promise { - const explorerViewlet = await this.viewletService.openViewlet(VIEWLET_ID) as ExplorerViewlet; + const explorerViewlet = (await this.viewletService.openViewlet(VIEWLET_ID))?.getViewPaneContainer() as ExplorerViewPaneContainer; const explorerView = explorerViewlet.getExplorerView(); if (explorerView) { explorerView.collapseAll(); diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index c9ed8e6d55..5b745dd64f 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -12,7 +12,7 @@ import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiati import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ExplorerFocusCondition, TextFileContentProvider, VIEWLET_ID, IExplorerService, ExplorerCompressedFocusContext, ExplorerCompressedFirstFocusContext, ExplorerCompressedLastFocusContext, FilesExplorerFocusCondition } from 'vs/workbench/contrib/files/common/files'; -import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { IListService } from 'vs/platform/list/browser/listService'; @@ -294,7 +294,7 @@ CommandsRegistry.registerCommand({ const explorerService = accessor.get(IExplorerService); const uri = getResourceForCommand(resource, accessor.get(IListService), accessor.get(IEditorService)); - const viewlet = await viewletService.openViewlet(VIEWLET_ID, false) as ExplorerViewlet; + const viewlet = (await viewletService.openViewlet(VIEWLET_ID, false))?.getViewPaneContainer() as ExplorerViewPaneContainer; if (uri && contextService.isInsideWorkspace(uri)) { const explorerView = viewlet.getExplorerView(); @@ -514,7 +514,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const explorer = viewlet as ExplorerViewlet; + const explorer = viewlet.getViewPaneContainer() as ExplorerViewPaneContainer; const view = explorer.getExplorerView(); view.previousCompressedStat(); } @@ -533,7 +533,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const explorer = viewlet as ExplorerViewlet; + const explorer = viewlet.getViewPaneContainer() as ExplorerViewPaneContainer; const view = explorer.getExplorerView(); view.nextCompressedStat(); } @@ -552,7 +552,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const explorer = viewlet as ExplorerViewlet; + const explorer = viewlet.getViewPaneContainer() as ExplorerViewPaneContainer; const view = explorer.getExplorerView(); view.firstCompressedStat(); } @@ -571,7 +571,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule({ return; } - const explorer = viewlet as ExplorerViewlet; + const explorer = viewlet.getViewPaneContainer() as ExplorerViewPaneContainer; const view = explorer.getExplorerView(); view.lastCompressedStat(); } diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 5cd844e60d..2ecd21d9a3 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -16,7 +16,7 @@ import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactory import { AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { FileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/fileEditorTracker'; -import { TextFileSaveErrorHandler } from 'vs/workbench/contrib/files/browser/textFileSaveErrorHandler'; +import { TextFileSaveErrorHandler } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { BinaryFileEditor } from 'vs/workbench/contrib/files/browser/editors/binaryFileEditor'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -25,7 +25,7 @@ import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry' import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import * as platform from 'vs/base/common/platform'; -import { ExplorerViewlet, ExplorerViewletViewsContribution } from 'vs/workbench/contrib/files/browser/explorerViewlet'; +import { ExplorerViewletViewsContribution, ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -334,10 +334,16 @@ configurationRegistry.registerConfiguration({ 'markdownDescription': nls.localize('maxMemoryForLargeFilesMB', "Controls the memory available to Azure Data Studio after restart when trying to open large files. Same effect as specifying `--max-memory=NEWSIZE` on the command line."), // {{SQL CARBON EDIT}} Change product name to ADS included: platform.isNative }, + 'files.preventSaveConflicts': { + 'type': 'boolean', + 'description': nls.localize('files.preventSaveConflicts', "When enabled, will prevent to save a file that has been changed since it was last edited. Instead, a diff editor is provided to compare the changes and accept or revert them. This setting should only be disabled if you frequently encounter save conflict errors and may result in data loss if used without caution."), + 'default': true, + 'scope': ConfigurationScope.RESOURCE + }, 'files.simpleDialog.enable': { 'type': 'boolean', 'description': nls.localize('files.simpleDialog.enable', "Enables the simple file dialog. The simple file dialog replaces the system file dialog when enabled."), - 'default': false, + 'default': false } } }); diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 3cea362ef5..d44673863b 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -16,7 +16,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ResourcesDropHandler, DragAndDropObserver } from 'vs/workbench/browser/dnd'; import { listDropBackground } from 'vs/platform/theme/common/colorRegistry'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; @@ -26,7 +26,7 @@ import { Schemas } from 'vs/base/common/network'; import { isWeb } from 'vs/base/common/platform'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -export class EmptyView extends ViewletPane { +export class EmptyView extends ViewPane { static readonly ID: string = 'workbench.explorer.emptyView'; static readonly NAME = nls.localize('noWorkspace', "No Folder Opened"); @@ -46,7 +46,7 @@ export class EmptyView extends ViewletPane { @ILabelService private labelService: ILabelService, @IContextKeyService contextKeyService: IContextKeyService ) { - super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.setLabels())); this._register(this.labelService.onDidChangeFormatters(() => this.setLabels())); } diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index bbf7f2dc87..c60d674365 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -27,7 +27,7 @@ import { IDecorationsService } from 'vs/workbench/services/decorations/browser/d import { TreeResourceNavigator2, WorkbenchCompressibleAsyncDataTree } from 'vs/platform/list/browser/listService'; import { DelayedDragHandler } from 'vs/base/browser/dnd'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { IViewletPaneOptions, ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { ILabelService } from 'vs/platform/label/common/label'; import { ExplorerDelegate, ExplorerDataSource, FilesRenderer, ICompressedNavigationController, FilesFilter, FileSorter, FileDragAndDrop, ExplorerCompressionDelegate, isCompressedFolderName } from 'vs/workbench/contrib/files/browser/views/explorerViewer'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -64,7 +64,7 @@ interface IExplorerViewStyles { listDropBackground?: Color; } -export class ExplorerView extends ViewletPane { +export class ExplorerView extends ViewPane { static readonly ID: string = 'workbench.explorer.fileView'; static readonly TREE_VIEW_STATE_STORAGE_KEY: string = 'workbench.explorer.treeViewState'; @@ -92,7 +92,7 @@ export class ExplorerView extends ViewletPane { private actions: IAction[] | undefined; constructor( - options: IViewletPaneOptions, + options: IViewPaneOptions, @IContextMenuService contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @@ -112,7 +112,7 @@ export class ExplorerView extends ViewletPane { @IClipboardService private clipboardService: IClipboardService, @IFileService private readonly fileService: IFileService ) { - super({ ...(options as IViewletPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); + super({ ...(options as IViewPaneOptions), id: ExplorerView.ID, ariaHeaderLabel: nls.localize('explorerSection', "Files Explorer Section") }, keybindingService, contextMenuService, configurationService, contextKeyService); this.resourceContext = instantiationService.createInstance(ResourceContextKey); this._register(this.resourceContext); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 2186296fbd..0ce59dfb44 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -138,22 +138,30 @@ export class CompressedNavigationController implements ICompressedNavigationCont static ID = 0; private _index: number; - readonly labels: HTMLElement[]; + private _labels!: HTMLElement[]; + private _updateLabelDisposable: IDisposable; get index(): number { return this._index; } get count(): number { return this.items.length; } get current(): ExplorerItem { return this.items[this._index]!; } get currentId(): string { return `${this.id}_${this.index}`; } + get labels(): HTMLElement[] { return this._labels; } private _onDidChange = new Emitter(); readonly onDidChange = this._onDidChange.event; constructor(private id: string, readonly items: ExplorerItem[], templateData: IFileTemplateData) { this._index = items.length - 1; - this.labels = Array.from(templateData.container.querySelectorAll('.label-name')) as HTMLElement[]; - for (let i = 0; i < items.length; i++) { - this.labels[i].setAttribute('aria-label', items[i].name); + this.updateLabels(templateData); + this._updateLabelDisposable = templateData.label.onDidRender(() => this.updateLabels(templateData)); + } + + private updateLabels(templateData: IFileTemplateData): void { + this._labels = Array.from(templateData.container.querySelectorAll('.label-name')) as HTMLElement[]; + + for (let i = 0; i < this.items.length; i++) { + this.labels[i].setAttribute('aria-label', this.items[i].name); } DOM.addClass(this.labels[this._index], 'active'); @@ -205,6 +213,7 @@ export class CompressedNavigationController implements ICompressedNavigationCont dispose(): void { this._onDidChange.dispose(); + this._updateLabelDisposable.dispose(); } } @@ -601,7 +610,7 @@ export class FileSorter implements ITreeSorter { } } -const fileOverwriteConfirm = (name: string) => { +const getFileOverwriteConfirm = (name: string) => { return { message: localize('confirmOverwrite', "A file or folder with the name '{0}' already exists in the destination folder. Do you want to replace it?", name), detail: localize('irreversible', "This action is irreversible!"), @@ -844,7 +853,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const name = file.name; if (typeof name === 'string' && event.target?.result instanceof ArrayBuffer) { if (target.getChild(name)) { - const { confirmed } = await this.dialogService.confirm(fileOverwriteConfirm(name)); + const { confirmed } = await this.dialogService.confirm(getFileOverwriteConfirm(name)); if (!confirmed) { return; } @@ -917,18 +926,16 @@ export class FileDragAndDrop implements ITreeDragAndDrop { }); } - const filtered = resources.filter(resource => targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase())); - const resourceExists = filtered.length >= 1; - if (resourceExists) { - const confirmationResult = await this.dialogService.confirm(fileOverwriteConfirm(basename(filtered[0]))); - if (!confirmationResult.confirmed) { - return; - } - } - // Run add in sequence const addPromisesFactory: ITask>[] = []; - resources.forEach(resource => { + await Promise.all(resources.map(async resource => { + if (targetNames.has(!hasToIgnoreCase(resource) ? basename(resource) : basename(resource).toLowerCase())) { + const confirmationResult = await this.dialogService.confirm(getFileOverwriteConfirm(basename(resource))); + if (!confirmationResult.confirmed) { + return; + } + } + addPromisesFactory.push(async () => { const sourceFile = resource; const targetFile = joinPath(target.resource, basename(sourceFile)); @@ -947,7 +954,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { this.editorService.openEditor({ resource: stat.resource, options: { pinned: true } }); } }); - }); + })); await sequence(addPromisesFactory); } @@ -1044,13 +1051,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { } catch (error) { // Conflict if ((error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { - const confirm: IConfirmation = { - message: localize('confirmOverwriteMessage', "'{0}' already exists in the destination folder. Do you want to replace it?", source.name), - detail: localize('irreversible', "This action is irreversible!"), - primaryButton: localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"), - type: 'warning' - }; - + const confirm = getFileOverwriteConfirm(source.name); // Move with overwrite if the user confirms const { confirmed } = await this.dialogService.confirm(confirm); if (confirmed) { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 5146e1ed6e..05a6e1539a 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -33,7 +33,7 @@ import { IMenuService, MenuId, IMenu } from 'vs/platform/actions/common/actions' import { DirtyEditorContext, OpenEditorsGroupContext, ReadonlyEditorContext as ReadonlyEditorContext } from 'vs/workbench/contrib/files/browser/fileCommands'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { ResourcesDropHandler, fillResourceDataTransfers, CodeDataTransfers, containsDragType } from 'vs/workbench/browser/dnd'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd'; import { memoize } from 'vs/base/common/decorators'; @@ -41,12 +41,13 @@ import { ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/browser import { URI } from 'vs/base/common/uri'; import { withUndefinedAsNull } from 'vs/base/common/types'; import { isWeb } from 'vs/base/common/platform'; -import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; +import { IWorkingCopyService, IWorkingCopy, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; const $ = dom.$; -export class OpenEditorsView extends ViewletPane { +export class OpenEditorsView extends ViewPane { private static readonly DEFAULT_VISIBLE_OPEN_EDITORS = 9; static readonly ID = 'workbench.explorer.openEditorsView'; @@ -76,10 +77,11 @@ export class OpenEditorsView extends ViewletPane { @IThemeService private readonly themeService: IThemeService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IMenuService private readonly menuService: IMenuService, - @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService + @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService ) { super({ - ...(options as IViewletPaneOptions), + ...(options as IViewPaneOptions), ariaHeaderLabel: nls.localize({ key: 'openEditosrSection', comment: ['Open is an adjective'] }, "Open Editors Section"), }, keybindingService, contextMenuService, configurationService, contextKeyService); @@ -100,7 +102,7 @@ export class OpenEditorsView extends ViewletPane { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChange(e))); // Handle dirty counter - this._register(this.workingCopyService.onDidChangeDirty(() => this.updateDirtyIndicator())); + this._register(this.workingCopyService.onDidChangeDirty(c => this.updateDirtyIndicator(c))); } private registerUpdateEvents(): void { @@ -414,7 +416,14 @@ export class OpenEditorsView extends ViewletPane { this.maximumBodySize = this.getMaxExpandedBodySize(); } - private updateDirtyIndicator(): void { + private updateDirtyIndicator(workingCopy?: IWorkingCopy): void { + if (workingCopy) { + const gotDirty = workingCopy.isDirty(); + if (gotDirty && !!(workingCopy.capabilities & WorkingCopyCapabilities.AutoSave) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) { + return; // do not indicate dirty of working copies that are auto saved after short delay + } + } + let dirty = this.workingCopyService.dirtyCount; if (dirty === 0) { dom.addClass(this.dirtyCountElement, 'hidden'); diff --git a/src/vs/workbench/contrib/files/common/workspaceWatcher.ts b/src/vs/workbench/contrib/files/common/workspaceWatcher.ts index c04f44c6c1..e4eb6349cd 100644 --- a/src/vs/workbench/contrib/files/common/workspaceWatcher.ts +++ b/src/vs/workbench/contrib/files/common/workspaceWatcher.ts @@ -17,7 +17,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; export class WorkspaceWatcher extends Disposable { - private watches = new ResourceMap(); + private readonly watches = new ResourceMap(); constructor( @IFileService private readonly fileService: FileService, diff --git a/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts b/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts index 21c86acddb..a6b311c3dd 100644 --- a/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts +++ b/src/vs/workbench/contrib/format/browser/showExtensionQuery.ts @@ -4,12 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; export function showExtensionQuery(viewletService: IViewletService, query: string) { return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { if (viewlet) { - (viewlet as IExtensionsViewlet).search(query); + (viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query); } }); } diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 603250e515..0d68a7bc41 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -21,7 +21,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; -import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/browser/minimalTranslations'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -140,7 +140,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo run: () => { logUserReaction('search'); this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { viewlet.search(`tag:lp-${locale}`); viewlet.focus(); @@ -200,7 +200,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo private installExtension(extension: IGalleryExtension): Promise { return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID) - .then(viewlet => viewlet as IExtensionsViewlet) + .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => viewlet.search(`@id:${extension.identifier.id}`)) .then(() => this.extensionManagementService.installFromGallery(extension)) .then(() => undefined, err => this.notificationService.error(err)); diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index d88c6a5fef..f0de6be8be 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -13,7 +13,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { language } from 'vs/base/common/platform'; import { firstIndex } from 'vs/base/common/arrays'; -import { IExtensionsViewlet, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsViewPaneContainer, VIEWLET_ID as EXTENSIONS_VIEWLET_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -60,7 +60,8 @@ export class ConfigureLocaleAction extends Action { if (selectedLanguage === languageOptions[languageOptions.length - 1]) { return this.viewletService.openViewlet(EXTENSIONS_VIEWLET_ID, true) - .then((viewlet: IExtensionsViewlet) => { + .then(viewlet => viewlet?.getViewPaneContainer()) + .then((viewlet: IExtensionsViewPaneContainer) => { viewlet.search('@category:"language packs"'); viewlet.focus(); }); diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index a1dbd1ca95..d2b83e2c89 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -14,7 +14,7 @@ import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IFileService, FileChangeType, whenProviderRegistered } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/services/output/common/output'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { dirname } from 'vs/base/common/resources'; diff --git a/src/vs/workbench/contrib/outline/browser/outlinePane.ts b/src/vs/workbench/contrib/outline/browser/outlinePane.ts index 066171f163..9dd1f8d58b 100644 --- a/src/vs/workbench/contrib/outline/browser/outlinePane.ts +++ b/src/vs/workbench/contrib/outline/browser/outlinePane.ts @@ -34,7 +34,7 @@ import { WorkbenchDataTree } from 'vs/platform/list/browser/listService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { attachProgressBarStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { CollapseAction } from 'vs/workbench/browser/viewlet'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -233,7 +233,7 @@ class OutlineViewState { } } -export class OutlinePane extends ViewletPane { +export class OutlinePane extends ViewPane { private _disposables = new Array(); diff --git a/src/vs/workbench/contrib/output/browser/logViewer.ts b/src/vs/workbench/contrib/output/browser/logViewer.ts index d75c6574db..e9bb1585e7 100644 --- a/src/vs/workbench/contrib/output/browser/logViewer.ts +++ b/src/vs/workbench/contrib/output/browser/logViewer.ts @@ -14,7 +14,8 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { URI } from 'vs/base/common/uri'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; -import { LOG_SCHEME, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; +import { LOG_SCHEME } from 'vs/workbench/contrib/output/common/output'; +import { IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts index 6d09db645a..fb4806a3db 100644 --- a/src/vs/workbench/contrib/output/browser/outputActions.ts +++ b/src/vs/workbench/contrib/output/browser/outputActions.ts @@ -6,7 +6,8 @@ import * as nls from 'vs/nls'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IAction, Action } from 'vs/base/common/actions'; -import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor, IFileOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; +import { IOutputService, OUTPUT_PANEL_ID } from 'vs/workbench/contrib/output/common/output'; import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index 40da268cd5..3904bbc2c8 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -11,7 +11,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorOptions } from 'vs/workbench/common/editor'; -import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, LOG_SCHEME, CONTEXT_ACTIVE_LOG_OUTPUT, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannel, IOutputService, OUTPUT_PANEL_ID, OUTPUT_SCHEME, LOG_SCHEME, CONTEXT_ACTIVE_LOG_OUTPUT, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelDescriptor, Extensions, IOutputChannelRegistry } from 'vs/workbench/services/output/common/output'; import { OutputPanel } from 'vs/workbench/contrib/output/browser/outputPanel'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLinkProvider'; diff --git a/src/vs/workbench/contrib/output/common/output.ts b/src/vs/workbench/contrib/output/common/output.ts index 03be15fcb6..4510c378a6 100644 --- a/src/vs/workbench/contrib/output/common/output.ts +++ b/src/vs/workbench/contrib/output/common/output.ts @@ -3,11 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; -import { Registry } from 'vs/platform/registry/common/platform'; +import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { URI } from 'vs/base/common/uri'; +import { IOutputChannelDescriptor } from 'vs/workbench/services/output/common/output'; /** * Mime type used by the output editor. @@ -44,10 +43,6 @@ export const LOG_MODE_ID = 'log'; */ export const OUTPUT_PANEL_ID = 'workbench.panel.output'; -export const Extensions = { - OutputChannels: 'workbench.contributions.outputChannels' -}; - export const OUTPUT_SERVICE_ID = 'outputService'; export const MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */; @@ -129,74 +124,3 @@ export interface IOutputChannel { */ dispose(): void; } - -export interface IOutputChannelDescriptor { - id: string; - label: string; - log: boolean; - file?: URI; -} - -export interface IFileOutputChannelDescriptor extends IOutputChannelDescriptor { - file: URI; -} - -export interface IOutputChannelRegistry { - - readonly onDidRegisterChannel: Event; - readonly onDidRemoveChannel: Event; - - /** - * Make an output channel known to the output world. - */ - registerChannel(descriptor: IOutputChannelDescriptor): void; - - /** - * Returns the list of channels known to the output world. - */ - getChannels(): IOutputChannelDescriptor[]; - - /** - * Returns the channel with the passed id. - */ - getChannel(id: string): IOutputChannelDescriptor | undefined; - - /** - * Remove the output channel with the passed id. - */ - removeChannel(id: string): void; -} - -class OutputChannelRegistry implements IOutputChannelRegistry { - private channels = new Map(); - - private readonly _onDidRegisterChannel = new Emitter(); - readonly onDidRegisterChannel: Event = this._onDidRegisterChannel.event; - - private readonly _onDidRemoveChannel = new Emitter(); - readonly onDidRemoveChannel: Event = this._onDidRemoveChannel.event; - - public registerChannel(descriptor: IOutputChannelDescriptor): void { - if (!this.channels.has(descriptor.id)) { - this.channels.set(descriptor.id, descriptor); - this._onDidRegisterChannel.fire(descriptor.id); - } - } - - public getChannels(): IOutputChannelDescriptor[] { - const result: IOutputChannelDescriptor[] = []; - this.channels.forEach(value => result.push(value)); - return result; - } - - public getChannel(id: string): IOutputChannelDescriptor | undefined { - return this.channels.get(id); - } - - public removeChannel(id: string): void { - this.channels.delete(id); - this._onDidRemoveChannel.fire(id); - } -} - -Registry.add(Extensions.OutputChannels, new OutputChannelRegistry()); \ No newline at end of file diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index d5caf5f72d..1cb7432078 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -603,14 +603,13 @@ class PreferencesRenderersController extends Disposable { } else { /* __GDPR__ "defaultSettings.searchError" : { - "message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }, - "filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" } } */ const message = getErrorMessage(err).trim(); if (message && message !== 'Error') { // "Error" = any generic network error - this.telemetryService.publicLog('defaultSettings.searchError', { message, filter }); + this.telemetryService.publicLog('defaultSettings.searchError', { message }); this.logService.info('Setting search error: ' + message); } return undefined; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 93337e2b88..d71f1ee0be 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -540,17 +540,6 @@ export class SettingsEditor2 extends BaseEditor { DOM.append(this.noResultsMessage, this.clearFilterLinkContainer); - const clearSearchContainer = $('span.clear-search'); - clearSearchContainer.textContent = ' - '; - - const clearSearch = DOM.append(clearSearchContainer, $('a.pointer.prominent', { tabindex: 0 }, localize('clearSearch', 'Clear Search'))); - this._register(DOM.addDisposableListener(clearSearch, DOM.EventType.CLICK, (e: MouseEvent) => { - DOM.EventHelper.stop(e, false); - this.clearSearchResults(); - })); - - DOM.append(this.noResultsMessage, clearSearchContainer); - this._register(attachStylerCallback(this.themeService, { editorForeground }, colors => { this.noResultsMessage.style.color = colors.editorForeground ? colors.editorForeground.toString() : null; })); @@ -1343,14 +1332,13 @@ export class SettingsEditor2 extends BaseEditor { } else { /* __GDPR__ "settingsEditor.searchError" : { - "message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" }, - "filter": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } + "message": { "classification": "CallstackOrException", "purpose": "FeatureInsight" } } */ const message = getErrorMessage(err).trim(); if (message && message !== 'Error') { // "Error" = any generic network error - this.telemetryService.publicLog('settingsEditor.searchError', { message, filter }); + this.telemetryService.publicLog('settingsEditor.searchError', { message }); this.logService.info('Setting search error: ' + message); } return null; diff --git a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts index a263d50936..1c24bae37c 100644 --- a/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/browser/relauncher.contribution.ts @@ -3,17 +3,23 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; -import { isMacintosh, isNative } from 'vs/base/common/platform'; -import { localize } from 'vs/nls'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; import { Registry } from 'vs/platform/registry/common/platform'; import { IWindowsConfiguration } from 'vs/platform/windows/common/windows'; -import { Extensions as WorkbenchExtensions, IWorkbenchContribution, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { localize } from 'vs/nls'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { isEqual } from 'vs/base/common/resources'; +import { isMacintosh, isNative } from 'vs/base/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IProductService } from 'vs/platform/product/common/productService'; interface IConfiguration extends IWindowsConfiguration { update: { mode: string; }; @@ -126,5 +132,82 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo } } +export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWorkbenchContribution { + + private firstFolderResource?: URI; + private extensionHostRestarter: RunOnceScheduler; + + private onDidChangeWorkspaceFoldersUnbind: IDisposable | undefined; + + constructor( + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IExtensionService extensionService: IExtensionService, + @IHostService hostService: IHostService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + ) { + super(); + + this.extensionHostRestarter = this._register(new RunOnceScheduler(() => { + if (!!environmentService.extensionTestsLocationURI) { + return; // no restart when in tests: see https://github.com/Microsoft/vscode/issues/66936 + } + + if (environmentService.configuration.remoteAuthority) { + hostService.reload(); // TODO@aeschli, workaround + } else if (isNative) { + extensionService.restartExtensionHost(); + } + }, 10)); + + this.contextService.getCompleteWorkspace() + .then(workspace => { + this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + this.handleWorkbenchState(); + this._register(this.contextService.onDidChangeWorkbenchState(() => setTimeout(() => this.handleWorkbenchState()))); + }); + + this._register(toDisposable(() => { + if (this.onDidChangeWorkspaceFoldersUnbind) { + this.onDidChangeWorkspaceFoldersUnbind.dispose(); + } + })); + } + + private handleWorkbenchState(): void { + + // React to folder changes when we are in workspace state + if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE) { + + // Update our known first folder path if we entered workspace + const workspace = this.contextService.getWorkspace(); + this.firstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + + // Install workspace folder listener + if (!this.onDidChangeWorkspaceFoldersUnbind) { + this.onDidChangeWorkspaceFoldersUnbind = this.contextService.onDidChangeWorkspaceFolders(() => this.onDidChangeWorkspaceFolders()); + } + } + + // Ignore the workspace folder changes in EMPTY or FOLDER state + else { + dispose(this.onDidChangeWorkspaceFoldersUnbind); + this.onDidChangeWorkspaceFoldersUnbind = undefined; + } + } + + private onDidChangeWorkspaceFolders(): void { + const workspace = this.contextService.getWorkspace(); + + // Restart extension host if first root folder changed (impact on deprecated workspace.rootPath API) + const newFirstFolderResource = workspace.folders.length > 0 ? workspace.folders[0].uri : undefined; + if (!isEqual(this.firstFolderResource, newFirstFolderResource)) { + this.firstFolderResource = newFirstFolderResource; + + this.extensionHostRestarter.schedule(); // buffer calls to extension host restart + } + } +} + const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(SettingsChangeRelauncher, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(WorkspaceChangeExtHostRelauncher, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 56fec6fd88..0829e132c1 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { FilterViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { FilterViewPaneContainer } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { VIEWLET_ID, VIEW_CONTAINER } from 'vs/workbench/contrib/remote/common/remote.contribution'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IViewDescriptor, IViewsRegistry, Extensions } from 'vs/workbench/common/views'; @@ -24,7 +24,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction } from 'vs/workbench/browser/viewlet'; +import { ViewletRegistry, Extensions as ViewletExtensions, ViewletDescriptor, ShowViewletAction, Viewlet } from 'vs/workbench/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; @@ -46,7 +46,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { startsWith } from 'vs/base/common/strings'; import { TunnelPanelDescriptor, TunnelViewModel } from 'vs/workbench/contrib/remote/browser/tunnelView'; import { IAddedViewDescriptorRef } from 'vs/workbench/browser/parts/views/views'; -import { ViewletPane } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPaneContainer'; class HelpModel { items: IHelpItem[] | undefined; @@ -272,7 +272,23 @@ class HelpAction extends Action { } } -export class RemoteViewlet extends FilterViewContainerViewlet { +export class RemoteViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(RemoteViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class RemoteViewPaneContainer extends FilterViewPaneContainer { private actions: IAction[] | undefined; private tunnelPanelDescriptor: TunnelPanelDescriptor | undefined; @@ -323,9 +339,9 @@ export class RemoteViewlet extends FilterViewContainerViewlet { return title; } - onDidAddViews(added: IAddedViewDescriptorRef[]): ViewletPane[] { + onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { // Call to super MUST be first, since registering the additional view will cause this to be called again. - const panels: ViewletPane[] = super.onDidAddViews(added); + const panels: ViewPane[] = super.onDidAddViews(added); if (this.environmentService.configuration.remoteAuthority && !this.tunnelPanelDescriptor && this.configurationService.getValue('remote.forwardedPortsView.visible')) { this.tunnelPanelDescriptor = new TunnelPanelDescriptor(new TunnelViewModel(this.remoteExplorerService), this.environmentService); const viewsRegistry = Registry.as(Extensions.ViewsRegistry); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index 92c2d7a3af..e532cf65fb 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -21,7 +21,6 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Disposable, IDisposable, toDisposable, MutableDisposable, dispose } from 'vs/base/common/lifecycle'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; import { ActionBar, ActionViewItem, IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { ActionRunner, IAction } from 'vs/base/common/actions'; @@ -36,6 +35,7 @@ import { once } from 'vs/base/common/functional'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { URI } from 'vs/base/common/uri'; class TunnelTreeVirtualDelegate implements IListVirtualDelegate { @@ -51,7 +51,7 @@ class TunnelTreeVirtualDelegate implements IListVirtualDelegate { export interface ITunnelViewModel { onForwardedPortsChanged: Event; readonly forwarded: TunnelItem[]; - readonly published: TunnelItem[]; + readonly detected: TunnelItem[]; readonly candidates: TunnelItem[]; readonly groups: ITunnelGroup[]; } @@ -79,11 +79,11 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { items: this.forwarded }); } - if (this.model.published.size > 0) { + if (this.model.detected.size > 0) { groups.push({ - label: nls.localize('remote.tunnelsView.published', "Published"), - tunnelType: TunnelType.Published, - items: this.published + label: nls.localize('remote.tunnelsView.detected', "Detected"), + tunnelType: TunnelType.Detected, + items: this.detected }); } const candidates = this.candidates; @@ -95,7 +95,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { }); } groups.push({ - label: nls.localize('remote.tunnelsView.add', "Add a Port..."), + label: nls.localize('remote.tunnelsView.add', "Forward Port..."), tunnelType: TunnelType.Add, }); return groups; @@ -107,9 +107,9 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { }); } - get published(): TunnelItem[] { - return Array.from(this.model.published.values()).map(tunnel => { - return new TunnelItem(TunnelType.Published, tunnel.remote, tunnel.localAddress, false, tunnel.name, tunnel.description); + get detected(): TunnelItem[] { + return Array.from(this.model.detected.values()).map(tunnel => { + return new TunnelItem(TunnelType.Detected, tunnel.remote, tunnel.localAddress, false, tunnel.name, tunnel.description); }); } @@ -118,7 +118,7 @@ export class TunnelViewModel extends Disposable implements ITunnelViewModel { const values = this.model.candidates.values(); let iterator = values.next(); while (!iterator.done) { - if (!this.model.forwarded.has(iterator.value.remote) && !this.model.published.has(iterator.value.remote)) { + if (!this.model.forwarded.has(iterator.value.remote) && !this.model.detected.has(iterator.value.remote)) { candidates.push(new TunnelItem(TunnelType.Candidate, iterator.value.remote, iterator.value.localAddress, false, undefined, iterator.value.description)); } iterator = values.next(); @@ -255,6 +255,7 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer { inputBox.element.style.display = 'none'; @@ -323,7 +324,7 @@ class TunnelDataSource implements IAsyncDataSource('tunnelType', TunnelType.Add); export const TunnelCloseableContextKey = new RawContextKey('tunnelCloseable', false); -export class TunnelPanel extends ViewletPane { +export class TunnelPanel extends ViewPane { static readonly ID = '~remote.tunnelPanel'; static readonly TITLE = nls.localize('remote.tunnel', "Tunnels"); private tree!: WorkbenchAsyncDataTree; @@ -388,7 +389,7 @@ export class TunnelPanel extends ViewletPane { constructor( protected viewModel: ITunnelViewModel, - options: IViewletPaneOptions, + options: IViewPaneOptions, @IKeybindingService protected keybindingService: IKeybindingService, @IContextMenuService protected contextMenuService: IContextMenuService, @IContextKeyService protected contextKeyService: IContextKeyService, @@ -450,6 +451,7 @@ export class TunnelPanel extends ViewletPane { return item.label; } }, + multipleSelectionSupport: false } ); const actionRunner: ActionRunner = new ActionRunner(); @@ -497,6 +499,11 @@ export class TunnelPanel extends ViewletPane { return this.titleActions; } + focus(): void { + super.focus(); + this.tree.domFocus(); + } + private onContextMenu(treeEvent: ITreeContextMenuEvent, actionRunner: ActionRunner): void { if (!(treeEvent.element instanceof TunnelItem)) { return; @@ -559,9 +566,9 @@ export class TunnelPanelDescriptor implements IViewDescriptor { } } -namespace NameTunnelAction { - export const ID = 'remote.tunnel.name'; - export const LABEL = nls.localize('remote.tunnel.name', "Rename"); +namespace LabelTunnelAction { + export const ID = 'remote.tunnel.label'; + export const LABEL = nls.localize('remote.tunnel.label', "Set Label"); export function handler(): ICommandHandler { return async (accessor, arg) => { @@ -575,7 +582,7 @@ namespace NameTunnelAction { remoteExplorerService.setEditable(arg.remote, null); }, validationMessage: () => null, - placeholder: nls.localize('remote.tunnelsView.namePlaceholder', "Port name"), + placeholder: nls.localize('remote.tunnelsView.labelPlaceholder', "Port label"), startingValue: arg.name }); } @@ -603,7 +610,7 @@ namespace ForwardPortAction { }, validationMessage: (value) => { const asNumber = Number(value); - if (isNaN(asNumber) || (asNumber < 0) || (asNumber > 65535)) { + if ((value === '') || isNaN(asNumber) || (asNumber < 0) || (asNumber > 65535)) { return nls.localize('remote.tunnelsView.portNumberValid', "Port number is invalid"); } return null; @@ -638,7 +645,7 @@ namespace OpenPortInBrowserAction { if (arg instanceof TunnelItem) { const model = accessor.get(IRemoteExplorerService).tunnelModel; const openerService = accessor.get(IOpenerService); - const tunnel = model.forwarded.has(arg.remote) ? model.forwarded.get(arg.remote) : model.published.get(arg.remote); + const tunnel = model.forwarded.has(arg.remote) ? model.forwarded.get(arg.remote) : model.detected.get(arg.remote); let address: string | undefined; if (tunnel && tunnel.localAddress && (address = model.address(tunnel.remote))) { return openerService.open(URI.parse('http://' + address)); @@ -667,7 +674,7 @@ namespace CopyAddressAction { } } -CommandsRegistry.registerCommand(NameTunnelAction.ID, NameTunnelAction.handler()); +CommandsRegistry.registerCommand(LabelTunnelAction.ID, LabelTunnelAction.handler()); CommandsRegistry.registerCommand(ForwardPortAction.ID, ForwardPortAction.handler()); CommandsRegistry.registerCommand(ClosePortAction.ID, ClosePortAction.handler()); CommandsRegistry.registerCommand(OpenPortInBrowserAction.ID, OpenPortInBrowserAction.handler()); @@ -689,7 +696,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ id: CopyAddressAction.ID, title: CopyAddressAction.LABEL, }, - when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Published)) + when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ group: '0_manage', @@ -698,14 +705,14 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ id: OpenPortInBrowserAction.ID, title: OpenPortInBrowserAction.LABEL, }, - when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Published)) + when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ group: '0_manage', order: 2, command: { - id: NameTunnelAction.ID, - title: NameTunnelAction.LABEL, + id: LabelTunnelAction.ID, + title: LabelTunnelAction.LABEL, }, when: TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded) })); @@ -716,7 +723,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ id: ForwardPortAction.ID, title: ForwardPortAction.LABEL, }, - when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Candidate), TunnelTypeContextKey.isEqualTo(TunnelType.Published)) + when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate) })); MenuRegistry.appendMenuItem(MenuId.TunnelContext, ({ group: '0_manage', @@ -735,7 +742,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ title: OpenPortInBrowserAction.LABEL, icon: { id: 'codicon/globe' } }, - when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Published)) + when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Forwarded), TunnelTypeContextKey.isEqualTo(TunnelType.Detected)) })); MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ order: 0, @@ -744,7 +751,7 @@ MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ title: ForwardPortAction.LABEL, icon: { id: 'codicon/plus' } }, - when: ContextKeyExpr.or(TunnelTypeContextKey.isEqualTo(TunnelType.Candidate), TunnelTypeContextKey.isEqualTo(TunnelType.Published)) + when: TunnelTypeContextKey.isEqualTo(TunnelType.Candidate) })); MenuRegistry.appendMenuItem(MenuId.TunnelInline, ({ order: 2, diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index e69f252480..856d39d7ba 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -12,7 +12,7 @@ import { Schemas } from 'vs/base/common/network'; import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ILogService } from 'vs/platform/log/common/log'; import { LoggerChannelClient } from 'vs/platform/log/common/logIpc'; -import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/services/output/common/output'; import { localize } from 'vs/nls'; import { joinPath } from 'vs/base/common/resources'; import { Disposable } from 'vs/base/common/lifecycle'; diff --git a/src/vs/workbench/contrib/scm/browser/mainPane.ts b/src/vs/workbench/contrib/scm/browser/mainPane.ts index 128c0bd4cd..dc3d1bbe13 100644 --- a/src/vs/workbench/contrib/scm/browser/mainPane.ts +++ b/src/vs/workbench/contrib/scm/browser/mainPane.ts @@ -8,7 +8,7 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; import { basename } from 'vs/base/common/resources'; import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, toggleClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IListRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list'; import { ISCMService, ISCMRepository } from 'vs/workbench/contrib/scm/common/scm'; @@ -25,7 +25,8 @@ import { ActionBar, ActionViewItem } from 'vs/base/browser/ui/actionbar/actionba import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { Command } from 'vs/editor/common/modes'; -import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; +import { renderCodicons } from 'vs/base/common/codicons'; +import { escape } from 'vs/base/common/strings'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewDescriptor } from 'vs/workbench/common/views'; @@ -83,7 +84,7 @@ class StatusBarActionViewItem extends ActionViewItem { updateLabel(): void { if (this.options.label && this.label) { - this.label.innerHTML = renderCodicons(this.getAction().label); + this.label.innerHTML = renderCodicons(escape(this.getAction().label)); } } } @@ -168,7 +169,7 @@ class ProviderRenderer implements IListRenderer .scm-provider > .monaco-action-bar .action-label { @@ -53,6 +54,7 @@ .scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-label .codicon { font-size: 14px; vertical-align: sub; + display: inline-flex; } .scm-viewlet .monaco-list-row > .scm-provider > .monaco-action-bar .action-item:last-of-type { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index c10f83f1c1..8b6c7b5a7a 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { domEvent } from 'vs/base/browser/event'; import { basename, isEqual } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; import { append, $, addClass, toggleClass, trackFocus, removeClass } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ISCMRepository, ISCMResourceGroup, ISCMResource, InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; @@ -586,7 +586,7 @@ function convertValidationType(type: InputValidationType): MessageType { } } -export class RepositoryPane extends ViewletPane { +export class RepositoryPane extends ViewPane { private cachedHeight: number | undefined = undefined; private cachedWidth: number | undefined = undefined; @@ -603,7 +603,7 @@ export class RepositoryPane extends ViewletPane { constructor( readonly repository: ISCMRepository, - options: IViewletPaneOptions, + options: IViewPaneOptions, @IKeybindingService protected keybindingService: IKeybindingService, @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @IContextMenuService protected contextMenuService: IContextMenuService, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts index 38335eeb02..a43a915d88 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewlet.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewlet.ts @@ -23,7 +23,6 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewsRegistry, Extensions } from 'vs/workbench/common/views'; @@ -31,6 +30,8 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { nextTick } from 'vs/base/common/process'; import { RepositoryPane, RepositoryViewDescriptor } from 'vs/workbench/contrib/scm/browser/repositoryPane'; import { MainPaneDescriptor, MainPane } from 'vs/workbench/contrib/scm/browser/mainPane'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { Viewlet } from 'vs/workbench/browser/viewlet'; export interface ISpliceEvent { index: number; @@ -50,7 +51,23 @@ export interface IViewModel { readonly onDidChangeVisibility: Event; } -export class SCMViewlet extends ViewContainerViewlet implements IViewModel { +export class SCMViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(SCMViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class SCMViewPaneContainer extends ViewPaneContainer implements IViewModel { private static readonly STATE_KEY = 'workbench.scm.views.state'; @@ -99,7 +116,7 @@ export class SCMViewlet extends ViewContainerViewlet implements IViewModel { @IWorkspaceContextService protected contextService: IWorkspaceContextService, @IContextKeyService contextKeyService: IContextKeyService, ) { - super(VIEWLET_ID, SCMViewlet.STATE_KEY, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(VIEWLET_ID, SCMViewPaneContainer.STATE_KEY, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); this.menus = instantiationService.createInstance(SCMMenus, undefined); this._register(this.menus.onDidChangeTitle(this.updateTitleArea, this)); diff --git a/src/vs/workbench/contrib/scm/common/scm.ts b/src/vs/workbench/contrib/scm/common/scm.ts index e3e528f9f3..9fd2380ce7 100644 --- a/src/vs/workbench/contrib/scm/common/scm.ts +++ b/src/vs/workbench/contrib/scm/common/scm.ts @@ -3,14 +3,14 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Registry } from 'vs/platform/registry/common/platform'; -import { IViewContainersRegistry, ViewContainer, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Command } from 'vs/editor/common/modes'; import { ISequence } from 'vs/base/common/sequence'; +import { Extensions as ViewContainerExtensions, ViewContainer, IViewContainersRegistry } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; export const VIEWLET_ID = 'workbench.view.scm'; export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index f1d4fc1410..46b3a6b969 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -42,6 +42,7 @@ max-height: 134px; } +/* NOTE: height is also used in searchWidget.ts as a constant*/ .search-view .search-widget .monaco-inputbox > .wrapper > textarea.input { overflow: initial; height: 24px; /* set initial height before measure */ diff --git a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts index 81433e4d9d..01a49653f1 100644 --- a/src/vs/workbench/contrib/search/browser/patternInputWidget.ts +++ b/src/vs/workbench/contrib/search/browser/patternInputWidget.ts @@ -164,6 +164,7 @@ export class PatternInputWidget extends Widget { private onInputKeyUp(keyboardEvent: IKeyboardEvent) { switch (keyboardEvent.keyCode) { case KeyCode.Enter: + this.onSearchSubmit(); this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), 0); return; case KeyCode.Escape: diff --git a/src/vs/workbench/contrib/search/browser/search.contribution.ts b/src/vs/workbench/contrib/search/browser/search.contribution.ts index 11774d33d4..0a3e6c25bf 100644 --- a/src/vs/workbench/contrib/search/browser/search.contribution.ts +++ b/src/vs/workbench/contrib/search/browser/search.contribution.ts @@ -5,13 +5,12 @@ import { Action } from 'vs/base/common/actions'; import { distinct } from 'vs/base/common/arrays'; -import { illegalArgument, onUnexpectedError } from 'vs/base/common/errors'; +import { onUnexpectedError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; import { dirname } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; -import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { getSelectionSearchString } from 'vs/editor/contrib/find/findController'; import { ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel'; @@ -52,11 +51,12 @@ import { ISearchHistoryService, SearchHistoryService } from 'vs/workbench/contri import { FileMatchOrMatch, ISearchWorkbenchService, RenderableMatch, SearchWorkbenchService, FileMatch, Match, FolderMatch } from 'vs/workbench/contrib/search/common/searchModel'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { ISearchConfiguration, ISearchConfigurationProperties, PANEL_ID, VIEWLET_ID, VIEW_CONTAINER, VIEW_ID } from 'vs/workbench/services/search/common/search'; +import { ISearchConfiguration, ISearchConfigurationProperties, PANEL_ID, VIEWLET_ID, VIEW_ID, VIEW_CONTAINER } from 'vs/workbench/services/search/common/search'; 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 { ExplorerViewPaneContainer } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; +import { assertType } from 'vs/base/common/types'; registerSingleton(ISearchWorkbenchService, SearchWorkbenchService, true); registerSingleton(ISearchHistoryService, SearchHistoryService, true); @@ -320,9 +320,11 @@ CommandsRegistry.registerCommand({ const contextService = accessor.get(IWorkspaceContextService); const uri = fileMatch.resource; - viewletService.openViewlet(VIEWLET_ID_FILES, false).then((viewlet: ExplorerViewlet) => { + viewletService.openViewlet(VIEWLET_ID_FILES, false).then((viewlet) => { + const explorerViewContainer = viewlet?.getViewPaneContainer() as ExplorerViewPaneContainer; + if (uri && contextService.isInsideWorkspace(uri)) { - const explorerView = viewlet.getExplorerView(); + const explorerView = explorerViewContainer?.getExplorerView(); if (explorerView) { explorerView.setExpanded(true); explorerService.select(uri, true).then(() => explorerView.focus(), onUnexpectedError); @@ -827,11 +829,9 @@ configurationRegistry.registerConfiguration({ } }); -registerLanguageCommand('_executeWorkspaceSymbolProvider', function (accessor, args: { query: string; }) { - const { query } = args; - if (typeof query !== 'string') { - throw illegalArgument(); - } +CommandsRegistry.registerCommand('_executeWorkspaceSymbolProvider', function (accessor, ...args) { + const [query] = args; + assertType(typeof query === 'string'); return getWorkspaceSymbols(query); }); diff --git a/src/vs/workbench/contrib/search/browser/searchActions.ts b/src/vs/workbench/contrib/search/browser/searchActions.ts index 50d77941cc..a2a80f99f9 100644 --- a/src/vs/workbench/contrib/search/browser/searchActions.ts +++ b/src/vs/workbench/contrib/search/browser/searchActions.ts @@ -26,7 +26,7 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; 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 { SearchViewPaneContainer } 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'; @@ -58,13 +58,13 @@ export function openSearchView(viewletService: IViewletService, panelService: IP return Promise.resolve((panelService.openPanel(PANEL_ID, focus) as SearchPanel).getSearchView()); } - return viewletService.openViewlet(VIEWLET_ID, focus).then(viewlet => (viewlet as SearchViewlet).getSearchView()); + return viewletService.openViewlet(VIEWLET_ID, focus).then(viewlet => (viewlet?.getViewPaneContainer() as SearchViewPaneContainer).getSearchView() as SearchView); } export function getSearchView(viewletService: IViewletService, panelService: IPanelService): SearchView | undefined { const activeViewlet = viewletService.getActiveViewlet(); if (activeViewlet && activeViewlet.getId() === VIEWLET_ID) { - return (activeViewlet as SearchViewlet).getSearchView(); + return (activeViewlet.getViewPaneContainer() as SearchViewPaneContainer).getSearchView(); } const activePanel = panelService.getActivePanel(); diff --git a/src/vs/workbench/contrib/search/browser/searchView.ts b/src/vs/workbench/contrib/search/browser/searchView.ts index eec9b25e75..3d408a1cb5 100644 --- a/src/vs/workbench/contrib/search/browser/searchView.ts +++ b/src/vs/workbench/contrib/search/browser/searchView.ts @@ -56,7 +56,7 @@ import { IPreferencesService, ISettingsEditorOptions } from 'vs/workbench/servic import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { relativePath } from 'vs/base/common/resources'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; -import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet'; +import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer'; 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'; @@ -82,7 +82,7 @@ export enum SearchViewPosition { } const SEARCH_CANCELLED_MESSAGE = nls.localize('searchCanceled', "Search was canceled before any results could be found - "); -export class SearchView extends ViewletPane { +export class SearchView extends ViewPane { private static readonly MAX_TEXT_RESULTS = 10000; @@ -145,7 +145,7 @@ export class SearchView extends ViewletPane { constructor( private position: SearchViewPosition, - options: IViewletPaneOptions, + options: IViewPaneOptions, @IFileService private readonly fileService: IFileService, @IEditorService private readonly editorService: IEditorService, @IProgressService private readonly progressService: IProgressService, @@ -1286,9 +1286,11 @@ export class SearchView extends ViewletPane { } private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): void { - this.addToSearchHistoryDelayer.trigger(() => this.searchWidget.searchInput.onSearchSubmit()); - this.inputPatternExcludes.onSearchSubmit(); - this.inputPatternIncludes.onSearchSubmit(); + this.addToSearchHistoryDelayer.trigger(() => { + this.searchWidget.searchInput.onSearchSubmit(); + this.inputPatternExcludes.onSearchSubmit(); + this.inputPatternIncludes.onSearchSubmit(); + }); this.viewModel.cancelSearch(); diff --git a/src/vs/workbench/contrib/search/browser/searchViewlet.ts b/src/vs/workbench/contrib/search/browser/searchViewlet.ts index 18f516f416..a76dc6b3ad 100644 --- a/src/vs/workbench/contrib/search/browser/searchViewlet.ts +++ b/src/vs/workbench/contrib/search/browser/searchViewlet.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -16,9 +15,27 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { VIEWLET_ID, VIEW_ID } from 'vs/workbench/services/search/common/search'; import { SearchView } from 'vs/workbench/contrib/search/browser/searchView'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ViewletRegistry, Extensions } from 'vs/workbench/browser/viewlet'; +import { ViewletRegistry, Extensions, Viewlet } from 'vs/workbench/browser/viewlet'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; -export class SearchViewlet extends ViewContainerViewlet { + +export class SearchViewlet extends Viewlet { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService protected storageService: IStorageService, + @IInstantiationService protected instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService protected contextMenuService: IContextMenuService, + @IExtensionService protected extensionService: IExtensionService, + @IWorkspaceContextService protected contextService: IWorkspaceContextService, + @IWorkbenchLayoutService protected layoutService: IWorkbenchLayoutService, + @IConfigurationService protected configurationService: IConfigurationService + ) { + super(VIEWLET_ID, instantiationService.createInstance(SearchViewPaneContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } +} + +export class SearchViewPaneContainer extends ViewPaneContainer { constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -29,9 +46,9 @@ export class SearchViewlet extends ViewContainerViewlet { @IInstantiationService protected instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IContextMenuService contextMenuService: IContextMenuService, - @IExtensionService extensionService: IExtensionService + @IExtensionService extensionService: IExtensionService, ) { - super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + super(VIEWLET_ID, `${VIEWLET_ID}.state`, { showHeaderInTitleWhenSingleView: true }, instantiationService, configurationService, layoutService, contextMenuService, telemetryService, extensionService, themeService, storageService, contextService); } getTitle(): string { diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 035c72d41f..802ec06910 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -35,6 +35,9 @@ import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility'; import { isMacintosh } from 'vs/base/common/platform'; +/** Specified in searchview.css */ +export const SingleLineInputHeight = 24; + export interface ISearchWidgetOptions { value?: string; replaceValue?: string; @@ -80,7 +83,7 @@ const ctrlKeyMod = (isMacintosh ? KeyMod.WinCtrl : KeyMod.CtrlCmd); function stopPropagationForMultiLineUpwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) { const isMultiline = !!value.match(/\n/); - if (textarea && isMultiline && textarea.selectionStart > 0) { + if (textarea && (isMultiline || textarea.clientHeight > SingleLineInputHeight) && textarea.selectionStart > 0) { event.stopPropagation(); return; } @@ -88,7 +91,7 @@ function stopPropagationForMultiLineUpwards(event: IKeyboardEvent, value: string function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) { const isMultiline = !!value.match(/\n/); - if (textarea && isMultiline && textarea.selectionEnd < textarea.value.length) { + if (textarea && (isMultiline || textarea.clientHeight > SingleLineInputHeight) && textarea.selectionEnd < textarea.value.length) { event.stopPropagation(); return; } diff --git a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts index 6115924776..28a06cd940 100644 --- a/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts +++ b/src/vs/workbench/contrib/snippets/browser/snippetCompletionProvider.ts @@ -126,8 +126,9 @@ export class SnippetCompletionProvider implements CompletionItemProvider { // add remaing snippets when the current prefix ends in whitespace or when no // interesting positions have been found availableSnippets.forEach(snippet => { - const range = Range.fromPositions(position); - suggestions.push(new SnippetCompletion(snippet, { replace: range, insert: range })); + let insert = Range.fromPositions(position); + let replace = startsWith(lineSuffixLow, snippet.prefixLow) ? insert.setEndPosition(position.lineNumber, position.column + snippet.prefixLow.length) : insert; + suggestions.push(new SnippetCompletion(snippet, { replace, insert })); }); } diff --git a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts index 970561955f..356d3dd083 100644 --- a/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts +++ b/src/vs/workbench/contrib/snippets/test/browser/snippetsService.test.ts @@ -438,5 +438,13 @@ suite('SnippetsService', function () { [first] = result.suggestions; assert.equal((first.range as any).insert.endColumn, 3); assert.equal((first.range as any).replace.endColumn, 3); + + model = TextModel.createFromString('not word', undefined, modeService.getLanguageIdentifier('fooLang')); + result = await provider.provideCompletionItems(model, new Position(1, 1), context)!; + + assert.equal(result.suggestions.length, 1); + [first] = result.suggestions; + assert.equal((first.range as any).insert.endColumn, 1); + assert.equal((first.range as any).replace.endColumn, 9); }); }); diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index f74221c1d0..a35c62e519 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -20,7 +20,7 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; import { IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; -import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/contrib/output/common/output'; +import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/services/output/common/output'; import { Scope, IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbench/browser/actions'; import { TaskEvent, TaskEventKind, TaskGroup, TASK_RUNNING_STATE } from 'vs/workbench/contrib/tasks/common/tasks'; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index e59f9b6c64..d2f54bf5d2 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -1358,7 +1358,13 @@ export class TerminalTaskSystem implements ITaskSystem { private resolveOptions(resolver: VariableResolver, options: CommandOptions | undefined): CommandOptions { if (options === undefined || options === null) { - return { cwd: this.resolveVariable(resolver, '${workspaceFolder}') }; + let cwd: string | undefined; + try { + cwd = this.resolveVariable(resolver, '${workspaceFolder}'); + } catch (e) { + // No workspace + } + return { cwd }; } let result: CommandOptions = Types.isString(options.cwd) ? { cwd: this.resolveVariable(resolver, options.cwd) } diff --git a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts index 8f2f38257c..5646055f3e 100644 --- a/src/vs/workbench/contrib/themes/browser/themes.contribution.ts +++ b/src/vs/workbench/contrib/themes/browser/themes.contribution.ts @@ -11,7 +11,7 @@ import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/ import { Registry } from 'vs/platform/registry/common/platform'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IWorkbenchThemeService, COLOR_THEME_SETTING, ICON_THEME_SETTING, IColorTheme, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService'; -import { VIEWLET_ID, IExtensionsViewlet } from 'vs/workbench/contrib/extensions/common/extensions'; +import { VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions'; // import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IColorRegistry, Extensions as ColorRegistryExtensions } from 'vs/platform/theme/common/colorRegistry'; @@ -213,7 +213,7 @@ class SelectIconThemeAction extends Action { function openExtensionViewlet(viewletService: IViewletService, query: string) { return viewletService.openViewlet(VIEWLET_ID, true).then(viewlet => { if (viewlet) { - (viewlet as IExtensionsViewlet).search(query); + (viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer).search(query); viewlet.focus(); } }); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts index 9fef4396e2..12986acb43 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -161,9 +161,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo let priority: number | undefined = undefined; if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.SignedOut) { - badge = new NumberBadge(1, () => localize('sign in', "Sync: Sign in...")); + badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync")); } else if (this.authTokenService.status === AuthTokenStatus.SigningIn) { - badge = new ProgressBadge(() => localize('signing in', "Signin in...")); + badge = new ProgressBadge(() => localize('signing in', "Signing in...")); clazz = 'progress-badge'; priority = 1; } else if (this.userDataSyncService.status === SyncStatus.HasConflicts) { @@ -315,17 +315,24 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo private registerActions(): void { - const startSyncMenuItem: IMenuItem = { + const turnOnSyncCommandId = 'workbench.userData.actions.syncStart'; + const turnOnSyncWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(AuthTokenStatus.SigningIn)); + CommandsRegistry.registerCommand(turnOnSyncCommandId, () => this.turnOn()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', command: { - id: 'workbench.userData.actions.syncStart', - title: localize('start sync', "Sync: Turn On") + id: turnOnSyncCommandId, + title: localize('global activity turn on sync', "Turn on sync...") }, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(AuthTokenStatus.SigningIn)), - }; - CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.turnOn()); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, startSyncMenuItem); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem); + when: turnOnSyncWhenContext, + }); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: turnOnSyncCommandId, + title: localize('turn on sync', "Sync: Turn on sync...") + }, + when: turnOnSyncWhenContext, + }); const signInCommandId = 'workbench.userData.actions.signin'; const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedOut)); @@ -334,14 +341,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: signInCommandId, - title: localize('global activity sign in', "Sync: Sign in... (1)") + title: localize('global activity sign in', "Sign in to sync... (1)") }, when: signInWhenContext, }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: signInCommandId, - title: localize('sign in', "Sync: Sign in...") + title: localize('sign in', "Sync: Sign in to sync...") }, when: signInWhenContext, }); @@ -352,24 +359,27 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: signingInCommandId, - title: localize('signinig in', "Sync: Signing in..."), + title: localize('signinig in', "Signing in..."), precondition: FalseContext }, when: CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SigningIn) }); - const stopSyncCommand = { - id: 'workbench.userData.actions.stopSync', - title: localize('stop sync', "Sync: Turn Off") - }; - CommandsRegistry.registerCommand(stopSyncCommand.id, () => this.turnOff()); + const stopSyncCommandId = 'workbench.userData.actions.stopSync'; + CommandsRegistry.registerCommand(stopSyncCommandId, () => this.turnOff()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { group: '5_sync', - command: stopSyncCommand, + command: { + id: stopSyncCommandId, + title: localize('global activity stop sync', "Turn off sync") + }, when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts)) }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { - command: stopSyncCommand, + command: { + id: stopSyncCommandId, + title: localize('stop sync', "Sync: Turn off sync") + }, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)), }); @@ -380,14 +390,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: resolveConflictsCommandId, - title: localize('resolveConflicts_global', "Sync: Resolve Conflicts (1)"), + title: localize('resolveConflicts_global', "Resolve sync conflicts (1)"), }, when: resolveConflictsWhenContext, }); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: resolveConflictsCommandId, - title: localize('resolveConflicts', "Sync: Resolve Conflicts"), + title: localize('resolveConflicts', "Sync: Resolve sync conflicts"), }, when: resolveConflictsWhenContext, }); @@ -397,14 +407,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: continueSyncCommandId, - title: localize('continue sync', "Sync: Continue") + title: localize('continue sync', "Sync: Continue sync") }, when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)), }); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: continueSyncCommandId, - title: localize('continue sync', "Sync: Continue"), + title: localize('continue sync', "Sync: Continue sync"), icon: { light: SYNC_PUSH_LIGHT_ICON_URI, dark: SYNC_PUSH_DARK_ICON_URI @@ -417,7 +427,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { id: continueSyncCommandId, - title: localize('continue sync', "Sync: Continue"), + title: localize('continue sync', "Sync: Continue sync"), icon: { light: SYNC_PUSH_LIGHT_ICON_URI, dark: SYNC_PUSH_DARK_ICON_URI @@ -432,7 +442,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo group: '5_sync', command: { id: 'workbench.userData.actions.signout', - title: localize('sign out', "Sign Out") + title: localize('sign out', "Sync: Sign out") }, when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn)), }; diff --git a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts index 7521673750..1d66290754 100644 --- a/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribution.ts @@ -4,21 +4,18 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { ISettingsMergeService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { SettingsMergeChannel } from 'vs/platform/userDataSync/common/settingsSyncIpc'; -import { UserDataSycnUtilServiceChannel } from 'vs/platform/userDataSync/common/keybindingsSyncIpc'; +import { UserDataSycnUtilServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; class UserDataSyncServicesContribution implements IWorkbenchContribution { constructor( - @ISettingsMergeService settingsMergeService: ISettingsMergeService, @IUserDataSyncUtilService userDataSyncUtilService: IUserDataSyncUtilService, @ISharedProcessService sharedProcessService: ISharedProcessService, ) { - sharedProcessService.registerChannel('settingsMerge', new SettingsMergeChannel(settingsMergeService)); sharedProcessService.registerChannel('userDataSyncUtil', new UserDataSycnUtilServiceChannel(userDataSyncUtilService)); } } diff --git a/src/vs/workbench/contrib/webview/common/themeing.ts b/src/vs/workbench/contrib/webview/common/themeing.ts index c067b4295f..cee8218caf 100644 --- a/src/vs/workbench/contrib/webview/common/themeing.ts +++ b/src/vs/workbench/contrib/webview/common/themeing.ts @@ -68,7 +68,7 @@ export class WebviewThemeDataProvider extends Disposable { 'vscode-font-size': '13px', 'vscode-editor-font-family': editorFontFamily, 'vscode-editor-font-weight': editorFontWeight, - 'vscode-editor-font-size': editorFontSize, + 'vscode-editor-font-size': editorFontSize + 'px', ...exportedColors }; diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 760195d8de..acd0ce9ac4 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -525,15 +525,13 @@ class WelcomePage extends Disposable { "WelcomePageInstalled-4" : { "from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "error": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } + "outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ this.telemetryService.publicLog(strings.installedEvent, { from: telemetryFrom, extensionId: extensionSuggestion.id, outcome: isPromiseCanceledError(err) ? 'canceled' : 'error', - error: String(err), }); this.notificationService.error(err); }); @@ -562,15 +560,13 @@ class WelcomePage extends Disposable { "WelcomePageInstalled-6" : { "from" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, "extensionId": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "error": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" } + "outcome": { "classification": "SystemMetaData", "purpose": "FeatureInsight" } } */ this.telemetryService.publicLog(strings.installedEvent, { from: telemetryFrom, extensionId: extensionSuggestion.id, outcome: isPromiseCanceledError(err) ? 'canceled' : 'error', - error: String(err), }); this.notificationService.error(err); }); diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 4b913b299b..2a699005e3 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -266,10 +266,11 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten }, 'window.newWindowDimensions': { 'type': 'string', - 'enum': ['default', 'inherit', 'maximized', 'fullscreen'], + 'enum': ['default', 'inherit', 'offset', 'maximized', 'fullscreen'], 'enumDescriptions': [ nls.localize('window.newWindowDimensions.default', "Open new windows in the center of the screen."), nls.localize('window.newWindowDimensions.inherit', "Open new windows with same dimension as last active one."), + nls.localize('window.newWindowDimensions.offset', "Open new windows with same dimension as last active one with an offset position."), nls.localize('window.newWindowDimensions.maximized', "Open new windows maximized."), nls.localize('window.newWindowDimensions.fullscreen', "Open new windows in full screen mode.") ], diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 0ee0e07b44..439b5e0085 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -609,11 +609,18 @@ export class ElectronWindow extends Disposable { } private trackClosedWaitFiles(waitMarkerFile: URI, resourcesToWaitFor: URI[]): IDisposable { - const listener = this.editorService.onDidCloseEditor(async () => { + const listener = this.editorService.onDidCloseEditor(async event => { + const closedResource = toResource(event.editor, { supportSideBySide: SideBySideEditor.MASTER }); + // In wait mode, listen to changes to the editors and wait until the files // are closed that the user wants to wait for. When this happens we delete // the wait marker file to signal to the outside that editing is done. - if (resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource }))) { + if ( + // for https://github.com/microsoft/vscode/issues/75861 + (resourcesToWaitFor.length === 1 && isEqual(closedResource, resourcesToWaitFor[0])) || + // any other case we simply check for all resources to wait for + resourcesToWaitFor.every(resource => !this.editorService.isOpen({ resource })) + ) { // If auto save is configured with the default delay (1s) it is possible // to close the editor while the save still continues in the background. As such // we have to also check if the files to wait for are dirty and if so wait diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 6cb3d59bc9..054a20bf91 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -22,7 +22,7 @@ import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; +import { equalsIgnoreCase, format, startsWithIgnoreCase, startsWith } from 'vs/base/common/strings'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -205,8 +205,11 @@ export class SimpleFileDialog { } private remoteUriFrom(path: string): URI { - path = path.replace(/\\/g, '/'); - return resources.toLocalResource(URI.from({ scheme: this.scheme, path }), this.scheme === Schemas.file ? undefined : this.remoteAuthority); + if (!startsWith(path, '\\\\')) { + path = path.replace(/\\/g, '/'); + } + const uri: URI = this.scheme === Schemas.file ? URI.file(path) : URI.from({ scheme: this.scheme, path }); + return resources.toLocalResource(uri, uri.scheme === Schemas.file ? undefined : this.remoteAuthority); } private getScheme(available: readonly string[] | undefined, defaultUri: URI | undefined): string { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 13ec07e693..5cdb156984 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,7 +5,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IResourceInput, ITextEditorOptions, IEditorOptions, EditorActivation } from 'vs/platform/editor/common/editor'; -import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions } from 'vs/workbench/common/editor'; +import { IEditorInput, IEditor, GroupIdentifier, IFileEditorInput, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInputFactoryRegistry, Extensions as EditorExtensions, IFileInputFactory, EditorInput, SideBySideEditorInput, IEditorInputWithOptions, isEditorInputWithOptions, EditorOptions, TextEditorOptions, IEditorIdentifier, IEditorCloseEvent, ITextEditor, ITextDiffEditor, ITextSideBySideEditor, toResource, SideBySideEditor, IRevertOptions, SaveReason } from 'vs/workbench/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { Registry } from 'vs/platform/registry/common/platform'; import { ResourceMap } from 'vs/base/common/map'; @@ -687,8 +687,10 @@ export class EditorService extends Disposable implements EditorServiceImpl { // Editors to save in parallel await Promise.all(editorsToSaveParallel.map(({ groupId, editor }) => { - // Use save as a hint to pin the editor - this.editorGroupService.getGroup(groupId)?.pinEditor(editor); + // Use save as a hint to pin the editor if used explicitly + if (options?.reason === SaveReason.EXPLICIT) { + this.editorGroupService.getGroup(groupId)?.pinEditor(editor); + } // Save return editor.save(groupId, options); diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index 5891845799..05ba6152db 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { IEditorModel, EditorActivation } from 'vs/platform/editor/common/editor'; import { URI } from 'vs/base/common/uri'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorOptions, IFileEditorInput, IEditorInput, GroupIdentifier, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IFileEditorInput, GroupIdentifier, ISaveOptions, IRevertOptions } from 'vs/workbench/common/editor'; import { workbenchInstantiationService, TestStorageService } from 'vs/workbench/test/workbenchTestServices'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; @@ -354,7 +354,7 @@ suite.skip('EditorService', () => { // {{SQL CARBON EDIT}} skip suite const inp = instantiationService.createInstance(ResourceEditorInput, 'name', 'description', URI.parse('my://resource-delegate'), undefined); const delegate = instantiationService.createInstance(DelegatingEditorService); - delegate.setEditorOpenHandler((delegate, group: IEditorGroup, input: IEditorInput, options?: EditorOptions) => { + delegate.setEditorOpenHandler((delegate, group, input, options?) => { assert.strictEqual(input, inp); done(); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index 28edfcc91b..e1026fccfa 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -308,36 +308,6 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment break; } } - } else { - // TODO@Ben remove me once environment is adopted - if (document && document.location && document.location.search) { - const map = new Map(); - const query = document.location.search.substring(1); - const vars = query.split('&'); - for (let p of vars) { - const pair = p.split('='); - if (pair.length >= 2) { - map.set(pair[0], decodeURIComponent(pair[1])); - } - } - - const edp = map.get('extensionDevelopmentPath'); - if (edp) { - extensionHostDebugEnvironment.extensionDevelopmentLocationURI = [URI.parse(edp)]; - extensionHostDebugEnvironment.isExtensionDevelopment = true; - } - - const di = map.get('debugId'); - if (di) { - extensionHostDebugEnvironment.params.debugId = di; - } - - const ibe = map.get('inspect-brk-extensions'); - if (ibe) { - extensionHostDebugEnvironment.params.port = parseInt(ibe); - extensionHostDebugEnvironment.params.break = false; - } - } } return extensionHostDebugEnvironment; diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index e5129347f0..636e946a4a 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -17,9 +17,13 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { joinPath } from 'vs/base/common/resources'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; +import { localize } from 'vs/nls'; export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @@ -30,6 +34,8 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { private readonly _onDidExit = new Emitter<[number, string | null]>(); readonly onExit: Event<[number, string | null]> = this._onDidExit.event; + private readonly _extensionHostLogFile: URI; + constructor( private readonly _autoStart: boolean, private readonly _extensions: Promise, @@ -41,7 +47,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @IProductService private readonly _productService: IProductService, ) { - + this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`); } async start(): Promise { @@ -90,6 +96,9 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { protocol.send(VSBuffer.fromString(JSON.stringify(await this._createExtHostInitData()))); await Event.toPromise(Event.filter(protocol.onMessage, msg => isMessageOfType(msg, MessageType.Initialized))); + // Register log channel for web worker exthost log + Registry.as(Extensions.OutputChannels).registerChannel({ id: 'webWorkerExtHostLog', label: localize('name', "Worker Extension Host"), file: this._extensionHostLogFile, log: true }); + this._protocol = protocol; } return this._protocol; @@ -150,6 +159,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, + logFile: this._extensionHostLogFile, autoStart: this._autoStart, remote: { authority: this._environmentService.configuration.remoteAuthority, diff --git a/src/vs/workbench/services/extensions/common/extensionHostMain.ts b/src/vs/workbench/services/extensions/common/extensionHostMain.ts index 77bed26af1..18056116ee 100644 --- a/src/vs/workbench/services/extensions/common/extensionHostMain.ts +++ b/src/vs/workbench/services/extensions/common/extensionHostMain.ts @@ -141,6 +141,7 @@ export class ExtensionHostMain { initData.environment.globalStorageHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.globalStorageHome)); initData.environment.userHome = URI.revive(rpcProtocol.transformIncomingURIs(initData.environment.userHome)); initData.logsLocation = URI.revive(rpcProtocol.transformIncomingURIs(initData.logsLocation)); + initData.logFile = URI.revive(rpcProtocol.transformIncomingURIs(initData.logFile)); initData.workspace = rpcProtocol.transformIncomingURIs(initData.workspace); return initData; } diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index 907f84f0c5..9c14bbc093 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -45,7 +45,7 @@ export function getExtensionKind(manifest: IExtensionManifest, productService: I // check product.json result = getProductExtensionKind(manifest, productService); if (typeof result !== 'undefined') { - return toArray(result); + return result; } // check the manifest itself @@ -88,10 +88,10 @@ function isUIExtensionPoint(extensionPoint: string): boolean { return _uiExtensionPoints.has(extensionPoint); } -let _productExtensionKindsMap: Map | null = null; -function getProductExtensionKind(manifest: IExtensionManifest, productService: IProductService): ExtensionKind | ExtensionKind[] | undefined { +let _productExtensionKindsMap: Map | null = null; +function getProductExtensionKind(manifest: IExtensionManifest, productService: IProductService): ExtensionKind[] | undefined { if (_productExtensionKindsMap === null) { - const productExtensionKindsMap = new Map(); + const productExtensionKindsMap = new Map(); if (productService.extensionKind) { for (const id of Object.keys(productService.extensionKind)) { productExtensionKindsMap.set(ExtensionIdentifier.toKey(id), productService.extensionKind[id]); diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index f7fba51085..4df8e788ef 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -13,7 +13,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -27,6 +27,11 @@ import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IProductService } from 'vs/platform/product/common/productService'; import { ISignService } from 'vs/platform/sign/common/sign'; +import { joinPath } from 'vs/base/common/resources'; +import { URI } from 'vs/base/common/uri'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; +import { localize } from 'vs/nls'; export interface IInitDataProvider { readonly remoteAuthority: string; @@ -131,11 +136,16 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH reject('timeout'); }, 60 * 1000); + let logFile: URI; + const disposable = protocol.onMessage(msg => { if (isMessageOfType(msg, MessageType.Ready)) { // 1) Extension Host is ready to receive messages, initialize it - this._createExtHostInitData(isExtensionDevelopmentDebug).then(data => protocol.send(VSBuffer.fromString(JSON.stringify(data)))); + this._createExtHostInitData(isExtensionDevelopmentDebug).then(data => { + logFile = data.logFile; + protocol.send(VSBuffer.fromString(JSON.stringify(data))); + }); return; } @@ -147,9 +157,13 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH // stop listening for messages here disposable.dispose(); + // Register log channel for remote exthost log + Registry.as(Extensions.OutputChannels).registerChannel({ id: 'remoteExtHostLog', label: localize('remote extension host Log', "Remote Extension Host"), file: logFile, log: true }); + // release this promise this._protocol = protocol; resolve(protocol); + return; } @@ -215,6 +229,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: remoteExtensionHostData.extensionHostLogsPath, + logFile: joinPath(remoteExtensionHostData.extensionHostLogsPath, `${ExtensionHostLogFileName}.log`), autoStart: true, uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop }; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 6b0fbf34b7..e89488a90c 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -36,9 +36,12 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import { parseExtensionDevOptions } from '../common/extensionDevOptions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; +import { IExtensionHostStarter, ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions'; import { isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { joinPath } from 'vs/base/common/resources'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IOutputChannelRegistry, Extensions } from 'vs/workbench/services/output/common/output'; export class ExtensionHostProcessWorker implements IExtensionHostStarter { @@ -65,6 +68,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private _extensionHostConnection: Socket | null; private _messageProtocol: Promise | null; + private readonly _extensionHostLogFile: URI; + constructor( private readonly _autoStart: boolean, private readonly _extensions: Promise, @@ -95,6 +100,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { this._extensionHostConnection = null; this._messageProtocol = null; + this._extensionHostLogFile = joinPath(this._extensionHostLogsLocation, `${ExtensionHostLogFileName}.log`); + this._toDispose.add(this._onExit); this._toDispose.add(this._lifecycleService.onWillShutdown(e => this._onWillShutdown(e))); this._toDispose.add(this._lifecycleService.onShutdown(reason => this.terminate())); @@ -112,7 +119,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const globalExitListener = () => this.terminate(); process.once('exit', globalExitListener); this._toDispose.add(toDisposable(() => { - process.removeListener('exit', globalExitListener); + process.removeListener('exit' as 'loaded', globalExitListener); // https://github.com/electron/electron/issues/21475 })); } @@ -373,6 +380,9 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // stop listening for messages here disposable.dispose(); + // Register log channel for exthost log + Registry.as(Extensions.OutputChannels).registerChannel({ id: 'extHostLog', label: nls.localize('extension host Log', "Extension Host"), file: this._extensionHostLogFile, log: true }); + // release this promise resolve(protocol); return; @@ -425,6 +435,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, + logFile: this._extensionHostLogFile, autoStart: this._autoStart, uiKind: UIKind.Desktop }; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 624d8e1adc..6edfa5bc2e 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -10,6 +10,7 @@ import { IExtensionHostProfile, IExtensionService, ProfileSegmentId, ProfileSess import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; export class ExtensionHostProfiler { @@ -32,7 +33,7 @@ export class ExtensionHostProfiler { let searchTree = TernarySearchTree.forPaths(); for (let extension of extensions) { if (extension.extensionLocation.scheme === Schemas.file) { - searchTree.set(realpathSync(extension.extensionLocation.fsPath), extension); + searchTree.set(URI.file(realpathSync(extension.extensionLocation.fsPath)).toString(), extension); } } @@ -59,7 +60,12 @@ export class ExtensionHostProfiler { break; } } else if (segmentId === 'self' && node.callFrame.url) { - let extension = searchTree.findSubstr(node.callFrame.url); + let extension: IExtensionDescription | undefined; + try { + extension = searchTree.findSubstr(URI.parse(node.callFrame.url).toString()); + } catch { + // ignore + } if (extension) { segmentId = extension.identifier.value; } diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index 2a33b13962..e273ea0a03 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -21,6 +21,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/worker/extHostExtensio import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostLogService } from 'vs/workbench/api/worker/extHostLogService'; +import { IExtHostTunnelService, ExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService'; // register singleton services registerSingleton(ILogService, ExtHostLogService); @@ -33,6 +34,7 @@ registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors); registerSingleton(IExtHostStorage, ExtHostStorage); registerSingleton(IExtHostExtensionService, ExtHostExtensionService); registerSingleton(IExtHostSearch, ExtHostSearch); +registerSingleton(IExtHostTunnelService, ExtHostTunnelService); // register services that only throw errors function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index 35ff71ab03..9e3e9ab286 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -13,6 +13,7 @@ import { IFilesConfiguration, AutoSaveConfiguration, HotExitConfiguration } from import { isUndefinedOrNull } from 'vs/base/common/types'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { equals } from 'vs/base/common/objects'; +import { URI } from 'vs/base/common/uri'; export const AutoSaveAfterShortDelayContext = new RawContextKey('autoSaveAfterShortDelayContext', false); @@ -53,6 +54,8 @@ export interface IFilesConfigurationService { readonly isHotExitEnabled: boolean; readonly hotExitConfiguration: string | undefined; + + preventSaveConflicts(resource: URI): boolean; } export class FilesConfigurationService extends Disposable implements IFilesConfigurationService { @@ -203,6 +206,10 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi get hotExitConfiguration(): string { return this.currentHotExitConfig; } + + preventSaveConflicts(resource: URI): boolean { + return this.configurationService.getValue('files.preventSaveConflicts', { resource }); + } } registerSingleton(IFilesConfigurationService, FilesConfigurationService); diff --git a/src/vs/workbench/services/keybinding/browser/keymapService.ts b/src/vs/workbench/services/keybinding/browser/keymapService.ts index fdc98efcc1..90bd9eddd7 100644 --- a/src/vs/workbench/services/keybinding/browser/keymapService.ts +++ b/src/vs/workbench/services/keybinding/browser/keymapService.ts @@ -349,7 +349,7 @@ export class BrowserKeyboardMapperFactoryBase { // The value is empty when the key is not a printable character, we skip validation. if (keyboardEvent.ctrlKey || keyboardEvent.metaKey) { setTimeout(() => { - this._getBrowserKeyMapping().then((keymap: IKeyboardMapping) => { + this._getBrowserKeyMapping().then((keymap: IRawMixedKeyboardMapping | null) => { if (this.isKeyMappingActive(keymap)) { return; } diff --git a/src/vs/workbench/services/output/common/output.ts b/src/vs/workbench/services/output/common/output.ts new file mode 100644 index 0000000000..bc61645a7e --- /dev/null +++ b/src/vs/workbench/services/output/common/output.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { URI } from 'vs/base/common/uri'; + +export const Extensions = { + OutputChannels: 'workbench.contributions.outputChannels' +}; + +export interface IOutputChannelDescriptor { + id: string; + label: string; + log: boolean; + file?: URI; +} + +export interface IFileOutputChannelDescriptor extends IOutputChannelDescriptor { + file: URI; +} + +export interface IOutputChannelRegistry { + + readonly onDidRegisterChannel: Event; + readonly onDidRemoveChannel: Event; + + /** + * Make an output channel known to the output world. + */ + registerChannel(descriptor: IOutputChannelDescriptor): void; + + /** + * Returns the list of channels known to the output world. + */ + getChannels(): IOutputChannelDescriptor[]; + + /** + * Returns the channel with the passed id. + */ + getChannel(id: string): IOutputChannelDescriptor | undefined; + + /** + * Remove the output channel with the passed id. + */ + removeChannel(id: string): void; +} + +class OutputChannelRegistry implements IOutputChannelRegistry { + private channels = new Map(); + + private readonly _onDidRegisterChannel = new Emitter(); + readonly onDidRegisterChannel: Event = this._onDidRegisterChannel.event; + + private readonly _onDidRemoveChannel = new Emitter(); + readonly onDidRemoveChannel: Event = this._onDidRemoveChannel.event; + + public registerChannel(descriptor: IOutputChannelDescriptor): void { + if (!this.channels.has(descriptor.id)) { + this.channels.set(descriptor.id, descriptor); + this._onDidRegisterChannel.fire(descriptor.id); + } + } + + public getChannels(): IOutputChannelDescriptor[] { + const result: IOutputChannelDescriptor[] = []; + this.channels.forEach(value => result.push(value)); + return result; + } + + public getChannel(id: string): IOutputChannelDescriptor | undefined { + return this.channels.get(id); + } + + public removeChannel(id: string): void { + this.channels.delete(id); + this._onDidRemoveChannel.fire(id); + } +} + +Registry.add(Extensions.OutputChannels, new OutputChannelRegistry()); diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 6d433882fc..06b2695930 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -314,9 +314,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic configureSettingsForLanguage(language: string): void { this.openGlobalSettings(true) .then(editor => this.createPreferencesEditorModel(this.userSettingsResource) - .then((settingsModel: IPreferencesEditorModel) => { + .then((settingsModel: IPreferencesEditorModel | null) => { const codeEditor = editor ? getCodeEditor(editor.getControl()) : null; - if (codeEditor) { + if (codeEditor && settingsModel) { this.addLanguageOverrideEntry(language, settingsModel, codeEditor) .then(position => { if (codeEditor && position) { diff --git a/src/vs/workbench/services/progress/test/progressIndicator.test.ts b/src/vs/workbench/services/progress/test/progressIndicator.test.ts index b85279c4f2..7f7fb62887 100644 --- a/src/vs/workbench/services/progress/test/progressIndicator.test.ts +++ b/src/vs/workbench/services/progress/test/progressIndicator.test.ts @@ -12,6 +12,8 @@ import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewlet } from 'vs/workbench/common/viewlet'; import { TestViewletService, TestPanelService } from 'vs/workbench/test/workbenchTestServices'; import { Event } from 'vs/base/common/event'; +import { IViewPaneContainer } from 'vs/workbench/common/viewPaneContainer'; +import { IView } from 'vs/workbench/common/views'; class TestViewlet implements IViewlet { @@ -29,6 +31,9 @@ class TestViewlet implements IViewlet { getControl(): IEditorControl { return null!; } focus(): void { } getOptimalWidth(): number { return 10; } + openView(id: string, focus?: boolean): IView { return null!; } + getViewPaneContainer(): IViewPaneContainer { return null!; } + saveState(): void { } } class TestCompositeScope extends CompositeScope { diff --git a/src/vs/workbench/services/remote/common/remoteExplorerService.ts b/src/vs/workbench/services/remote/common/remoteExplorerService.ts index 1d1c2ebf37..1d59e6fa17 100644 --- a/src/vs/workbench/services/remote/common/remoteExplorerService.ts +++ b/src/vs/workbench/services/remote/common/remoteExplorerService.ts @@ -10,7 +10,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { ITunnelService } from 'vs/platform/remote/common/tunnel'; +import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import { IEditableData } from 'vs/workbench/common/views'; @@ -28,7 +28,7 @@ export interface Tunnel { export class TunnelModel extends Disposable { readonly forwarded: Map; - readonly published: Map; + readonly detected: Map; readonly candidates: Map; private _onForwardPort: Emitter = new Emitter(); public onForwardPort: Event = this._onForwardPort.event; @@ -53,7 +53,7 @@ export class TunnelModel extends Disposable { }); }); - this.published = new Map(); + this.detected = new Map(); this.candidates = new Map(); this._register(this.tunnelService.onTunnelOpened(tunnel => { if (this.candidates.has(tunnel.tunnelRemotePort)) { @@ -63,7 +63,8 @@ export class TunnelModel extends Disposable { this.forwarded.set(tunnel.tunnelRemotePort, { remote: tunnel.tunnelRemotePort, localAddress: tunnel.localAddress, - local: tunnel.tunnelLocalPort + local: tunnel.tunnelLocalPort, + closeable: true }); } this._onForwardPort.fire(this.forwarded.get(tunnel.tunnelRemotePort)!); @@ -76,7 +77,7 @@ export class TunnelModel extends Disposable { })); } - async forward(remote: number, local?: number, name?: string): Promise { + async forward(remote: number, local?: number, name?: string): Promise { if (!this.forwarded.has(remote)) { const tunnel = await this.tunnelService.openTunnel(remote, local); if (tunnel && tunnel.localAddress) { @@ -89,6 +90,7 @@ export class TunnelModel extends Disposable { }; this.forwarded.set(remote, newForward); this._onForwardPort.fire(newForward); + return tunnel; } } } @@ -97,6 +99,9 @@ export class TunnelModel extends Disposable { if (this.forwarded.has(remote)) { this.forwarded.get(remote)!.name = name; this._onPortName.fire(remote); + } else if (this.detected.has(remote)) { + this.detected.get(remote)!.name = name; + this._onPortName.fire(remote); } } @@ -105,7 +110,17 @@ export class TunnelModel extends Disposable { } address(remote: number): string | undefined { - return (this.forwarded.get(remote) || this.published.get(remote))?.localAddress; + return (this.forwarded.get(remote) || this.detected.get(remote))?.localAddress; + } + + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[]): void { + tunnels.forEach(tunnel => { + this.detected.set(tunnel.remote.port, { + remote: tunnel.remote.port, + localAddress: tunnel.localAddress, + closeable: false + }); + }); } } @@ -118,6 +133,9 @@ export interface IRemoteExplorerService { onDidChangeEditable: Event; setEditable(remote: number | undefined, data: IEditableData | null): void; getEditableData(remote: number | undefined): IEditableData | undefined; + forward(remote: number, local?: number, name?: string): Promise; + close(remote: number): Promise; + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): void; } export interface HelpInformation { @@ -219,6 +237,20 @@ class RemoteExplorerService implements IRemoteExplorerService { return this._tunnelModel; } + forward(remote: number, local?: number, name?: string): Promise { + return this.tunnelModel.forward(remote, local, name); + } + + close(remote: number): Promise { + return this.tunnelModel.close(remote); + } + + addDetected(tunnels: { remote: { port: number, host: string }, localAddress: string }[] | undefined): void { + if (tunnels) { + this.tunnelModel.addDetected(tunnels); + } + } + setEditable(remote: number | undefined, data: IEditableData | null): void { if (!data) { this.editable = undefined; diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 28895626c5..b7da3caf83 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -28,7 +28,8 @@ class NodeRemoteTunnel extends Disposable implements RemoteTunnel { public readonly tunnelRemotePort: number; public tunnelLocalPort!: number; - public localAddress?: string; + public tunnelRemoteHost: string = 'localhost'; + public localAddress!: string; private readonly _options: IConnectionOptions; private readonly _server: net.Server; @@ -145,26 +146,35 @@ export class TunnelService implements ITunnelService { private makeTunnel(tunnel: RemoteTunnel): RemoteTunnel { return { tunnelRemotePort: tunnel.tunnelRemotePort, + tunnelRemoteHost: tunnel.tunnelRemoteHost, tunnelLocalPort: tunnel.tunnelLocalPort, localAddress: tunnel.localAddress, dispose: () => { const existing = this._tunnels.get(tunnel.tunnelRemotePort); if (existing) { - if (--existing.refcount <= 0) { - existing.value.then(tunnel => tunnel.dispose()); - this._tunnels.delete(tunnel.tunnelRemotePort); - this._onTunnelClosed.fire(tunnel.tunnelRemotePort); - } + existing.refcount--; + this.tryDisposeTunnel(tunnel.tunnelRemotePort, existing); } } }; } + private async tryDisposeTunnel(remotePort: number, tunnel: { refcount: number, readonly value: Promise }): Promise { + if (tunnel.refcount <= 0) { + const disposePromise: Promise = tunnel.value.then(tunnel => { + tunnel.dispose(); + this._onTunnelClosed.fire(tunnel.tunnelRemotePort); + }); + this._tunnels.delete(remotePort); + return disposePromise; + } + } + async closeTunnel(remotePort: number): Promise { if (this._tunnels.has(remotePort)) { const value = this._tunnels.get(remotePort)!; - (await value.value).dispose(); value.refcount = 0; + await this.tryDisposeTunnel(remotePort, value); } } @@ -189,8 +199,7 @@ export class TunnelService implements ITunnelService { }; const tunnel = createRemoteTunnel(options, remotePort, localPort); - // Using makeTunnel here for the value does result in dispose getting called twice, but it also ensures that _onTunnelClosed will be fired when closeTunnel is called. - this._tunnels.set(remotePort, { refcount: 1, value: tunnel.then(value => this.makeTunnel(value)) }); + this._tunnels.set(remotePort, { refcount: 1, value: tunnel }); return tunnel; } } diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index b8a3b15d15..23931bc262 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -15,9 +15,9 @@ import { IFilesConfiguration } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { Event } from 'vs/base/common/event'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; -import { Registry } from 'vs/platform/registry/common/platform'; import { relative } from 'vs/base/common/path'; +import { Extensions as ViewContainerExtensions, ViewContainer, IViewContainersRegistry } from 'vs/workbench/common/views'; +import { Registry } from 'vs/platform/registry/common/platform'; export const VIEWLET_ID = 'workbench.view.search'; export const PANEL_ID = 'workbench.view.search'; diff --git a/src/vs/workbench/services/textfile/browser/textFileService.ts b/src/vs/workbench/services/textfile/browser/textFileService.ts index dfd74a862c..2eab9c8ca0 100644 --- a/src/vs/workbench/services/textfile/browser/textFileService.ts +++ b/src/vs/workbench/services/textfile/browser/textFileService.ts @@ -416,7 +416,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // remember each source model to load again after move is done // with optional content to restore if it was dirty - type ModelToRestore = { resource: URI; snapshot?: ITextSnapshot }; + type ModelToRestore = { resource: URI; snapshot?: ITextSnapshot; encoding?: string; mode?: string }; const modelsToRestore: ModelToRestore[] = []; for (const sourceModel of sourceModels) { const sourceModelResource = sourceModel.resource; @@ -433,7 +433,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex modelToRestoreResource = joinPath(target, sourceModelResource.path.substr(source.path.length + 1)); } - const modelToRestore: ModelToRestore = { resource: modelToRestoreResource }; + const modelToRestore: ModelToRestore = { resource: modelToRestoreResource, encoding: sourceModel.getEncoding(), mode: sourceModel.textEditorModel?.getModeId() }; if (sourceModel.isDirty()) { modelToRestore.snapshot = sourceModel.createSnapshot(); } @@ -469,7 +469,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex // we know the file has changed on disk after the move and the // model might have still existed with the previous state. this // ensures we are not tracking a stale state. - const restoredModel = await this.models.loadOrCreate(modelToRestore.resource, { reload: { async: false } }); + const restoredModel = await this.models.loadOrCreate(modelToRestore.resource, { reload: { async: false }, encoding: modelToRestore.encoding, mode: modelToRestore.mode }); // restore previous dirty content if any and ensure to mark // the model as dirty @@ -766,7 +766,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex await this.create(target, ''); } - targetModel = await this.models.loadOrCreate(target); + targetModel = await this.models.loadOrCreate(target, { encoding: sourceModel.getEncoding(), mode: sourceModel.textEditorModel?.getModeId() }); } try { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 22fedc32e0..ebaf0a7c43 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -63,6 +63,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil static DEFAULT_CONTENT_CHANGE_BUFFER_DELAY = CONTENT_CHANGE_EVENT_BUFFER_DELAY; static DEFAULT_ORPHANED_CHANGE_BUFFER_DELAY = 100; + static WHITELIST_JSON = ['package.json', 'package-lock.json', 'tsconfig.json', 'jsconfig.json', 'bower.json', '.eslintrc.json', 'tslint.json', 'composer.json']; static WHITELIST_WORKSPACE_JSON = ['settings.json', 'extensions.json', 'tasks.json', 'launch.json']; @@ -743,7 +744,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil overwriteEncoding: options.overwriteEncoding, mtime: lastResolvedFileStat.mtime, encoding: this.getEncoding(), - etag: lastResolvedFileStat.etag, + etag: (options.ignoreModifiedSince || !this.filesConfigurationService.preventSaveConflicts(lastResolvedFileStat.resource)) ? ETAG_DISABLED : lastResolvedFileStat.etag, writeElevated: options.writeElevated }).then(stat => { this.logService.trace(`doSave(${versionId}) - after write()`, this.resource); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 6a68f44bac..7e158be87b 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -11,35 +11,39 @@ import { ITextFileEditorModel, ITextFileEditorModelManager, TextFileModelChangeE import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ResourceMap } from 'vs/base/common/map'; +import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files'; +import { distinct, coalesce } from 'vs/base/common/arrays'; +import { ResourceQueue } from 'vs/base/common/async'; +import { onUnexpectedError } from 'vs/base/common/errors'; export class TextFileEditorModelManager extends Disposable implements ITextFileEditorModelManager { - private readonly _onModelDisposed: Emitter = this._register(new Emitter()); - readonly onModelDisposed: Event = this._onModelDisposed.event; + private readonly _onModelDisposed = this._register(new Emitter()); + readonly onModelDisposed = this._onModelDisposed.event; - private readonly _onModelContentChanged: Emitter = this._register(new Emitter()); - readonly onModelContentChanged: Event = this._onModelContentChanged.event; + private readonly _onModelContentChanged = this._register(new Emitter()); + readonly onModelContentChanged = this._onModelContentChanged.event; - private readonly _onModelDirty: Emitter = this._register(new Emitter()); - readonly onModelDirty: Event = this._onModelDirty.event; + private readonly _onModelDirty = this._register(new Emitter()); + readonly onModelDirty = this._onModelDirty.event; - private readonly _onModelSaveError: Emitter = this._register(new Emitter()); - readonly onModelSaveError: Event = this._onModelSaveError.event; + private readonly _onModelSaveError = this._register(new Emitter()); + readonly onModelSaveError = this._onModelSaveError.event; - private readonly _onModelSaved: Emitter = this._register(new Emitter()); - readonly onModelSaved: Event = this._onModelSaved.event; + private readonly _onModelSaved = this._register(new Emitter()); + readonly onModelSaved = this._onModelSaved.event; - private readonly _onModelReverted: Emitter = this._register(new Emitter()); - readonly onModelReverted: Event = this._onModelReverted.event; + private readonly _onModelReverted = this._register(new Emitter()); + readonly onModelReverted = this._onModelReverted.event; - private readonly _onModelEncodingChanged: Emitter = this._register(new Emitter()); - readonly onModelEncodingChanged: Event = this._onModelEncodingChanged.event; + private readonly _onModelEncodingChanged = this._register(new Emitter()); + readonly onModelEncodingChanged = this._onModelEncodingChanged.event; - private readonly _onModelOrphanedChanged: Emitter = this._register(new Emitter()); - readonly onModelOrphanedChanged: Event = this._onModelOrphanedChanged.event; + private readonly _onModelOrphanedChanged = this._register(new Emitter()); + readonly onModelOrphanedChanged = this._onModelOrphanedChanged.event; - private _onModelsDirty: Event | undefined; - get onModelsDirty(): Event { + private _onModelsDirty: Event> | undefined; + get onModelsDirty(): Event> { if (!this._onModelsDirty) { this._onModelsDirty = this.debounce(this.onModelDirty); } @@ -47,8 +51,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsDirty; } - private _onModelsSaveError: Event | undefined; - get onModelsSaveError(): Event { + private _onModelsSaveError: Event> | undefined; + get onModelsSaveError(): Event> { if (!this._onModelsSaveError) { this._onModelsSaveError = this.debounce(this.onModelSaveError); } @@ -56,8 +60,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsSaveError; } - private _onModelsSaved: Event | undefined; - get onModelsSaved(): Event { + private _onModelsSaved: Event> | undefined; + get onModelsSaved(): Event> { if (!this._onModelsSaved) { this._onModelsSaved = this.debounce(this.onModelSaved); } @@ -65,8 +69,8 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsSaved; } - private _onModelsReverted: Event | undefined; - get onModelsReverted(): Event { + private _onModelsReverted: Event> | undefined; + get onModelsReverted(): Event> { if (!this._onModelsReverted) { this._onModelsReverted = this.debounce(this.onModelReverted); } @@ -74,15 +78,18 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE return this._onModelsReverted; } - private mapResourceToDisposeListener = new ResourceMap(); - private mapResourceToStateChangeListener = new ResourceMap(); - private mapResourceToModelContentChangeListener = new ResourceMap(); - private mapResourceToModel = new ResourceMap(); - private mapResourceToPendingModelLoaders = new ResourceMap>(); + private readonly mapResourceToDisposeListener = new ResourceMap(); + private readonly mapResourceToStateChangeListener = new ResourceMap(); + private readonly mapResourceToModelContentChangeListener = new ResourceMap(); + private readonly mapResourceToModel = new ResourceMap(); + private readonly mapResourceToPendingModelLoaders = new ResourceMap>(); + + private readonly modelLoadQueue = new ResourceQueue(); constructor( @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IInstantiationService private readonly instantiationService: IInstantiationService + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IFileService private readonly fileService: IFileService ) { super(); @@ -91,12 +98,38 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE private registerListeners(): void { + // Update models from file change events + this._register(this.fileService.onFileChanges(e => this.onFileChanges(e))); + // Lifecycle this.lifecycleService.onShutdown(this.dispose, this); } - private debounce(event: Event): Event { - return Event.debounce(event, (prev: TextFileModelChangeEvent[], cur: TextFileModelChangeEvent) => { + private onFileChanges(e: FileChangesEvent): void { + + // Collect distinct (saved) models to update. + // + // Note: we also consider the added event because it could be that a file was added + // and updated right after. + distinct(coalesce([...e.getUpdated(), ...e.getAdded()] + .map(({ resource }) => this.get(resource))) + .filter(model => model && !model.isDirty()), model => model.resource.toString()) + .forEach(model => this.queueModelLoad(model)); + } + + private queueModelLoad(model: ITextFileEditorModel): void { + + // Load model to update (use a queue to prevent accumulation of loads + // when the load actually takes long. At most we only want the queue + // to have a size of 2 (1 running load and 1 queued load). + const queue = this.modelLoadQueue.queueFor(model.resource); + if (queue.size <= 1) { + queue.queue(() => model.load().then(undefined, onUnexpectedError)); + } + } + + private debounce(event: Event): Event { + return Event.debounce(event, (prev, cur) => { if (!prev) { prev = [cur]; } else { diff --git a/src/vs/workbench/services/textfile/common/textfiles.ts b/src/vs/workbench/services/textfile/common/textfiles.ts index 625657db41..c56406d8ad 100644 --- a/src/vs/workbench/services/textfile/common/textfiles.ts +++ b/src/vs/workbench/services/textfile/common/textfiles.ts @@ -412,6 +412,7 @@ export interface ITextFileSaveOptions extends ISaveOptions { overwriteReadonly?: boolean; overwriteEncoding?: boolean; writeElevated?: boolean; + ignoreModifiedSince?: boolean; } export interface ILoadOptions { diff --git a/src/vs/workbench/services/textfile/test/textFileService.test.ts b/src/vs/workbench/services/textfile/test/textFileService.test.ts index b6728f7a9e..afe68058c4 100644 --- a/src/vs/workbench/services/textfile/test/textFileService.test.ts +++ b/src/vs/workbench/services/textfile/test/textFileService.test.ts @@ -414,7 +414,7 @@ suite('Files - TextFileService', () => { }); }); - async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: true, shouldVeto: boolean): Promise { + async function hotExitTest(this: any, setting: string, shutdownReason: ShutdownReason, multipleWindows: boolean, workspace: boolean, shouldVeto: boolean): Promise { model = instantiationService.createInstance(TextFileEditorModel, toResource.call(this, '/path/file.txt'), 'utf8', undefined); (accessor.textFileService.models).add(model.resource, model); diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts index 11335b84d5..3726c6194c 100644 --- a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts @@ -131,16 +131,19 @@ export class InMemoryFileSystemProvider extends Disposable implements IFileSyste } async delete(resource: URI, opts: FileDeleteOptions): Promise { - let dirname = resources.dirname(resource); - let basename = resources.basename(resource); - let parent = this._lookupAsDirectory(dirname, false); - if (!parent.entries.has(basename)) { - throw new FileSystemProviderError('file not found', FileSystemProviderErrorCode.FileNotFound); + try { + let dirname = resources.dirname(resource); + let basename = resources.basename(resource); + let parent = this._lookupAsDirectory(dirname, false); + if (parent.entries.has(basename)) { + parent.entries.delete(basename); + parent.mtime = Date.now(); + parent.size -= 1; + this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED }); + } + } catch (error) { + // ignore if resource does not exist to keep parity with other file system providers } - parent.entries.delete(basename); - parent.mtime = Date.now(); - parent.size -= 1; - this._fireSoon({ type: FileChangeType.UPDATED, resource: dirname }, { resource, type: FileChangeType.DELETED }); } async mkdir(resource: URI): Promise { diff --git a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts b/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts deleted file mode 100644 index fd90c9088a..0000000000 --- a/src/vs/workbench/services/userDataSync/common/settingsMergeService.ts +++ /dev/null @@ -1,208 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as objects from 'vs/base/common/objects'; -import { parse, findNodeAtLocation, parseTree } from 'vs/base/common/json'; -import { EditOperation } from 'vs/editor/common/core/editOperation'; -import { IModeService } from 'vs/editor/common/services/modeService'; -import { ITextModel } from 'vs/editor/common/model'; -import { setProperty } from 'vs/base/common/jsonEdit'; -import { Range } from 'vs/editor/common/core/range'; -import { Selection } from 'vs/editor/common/core/selection'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { Position } from 'vs/editor/common/core/position'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync'; -import { values } from 'vs/base/common/map'; -import { IStringDictionary } from 'vs/base/common/collections'; - -class SettingsMergeService implements ISettingsMergeService { - - _serviceBrand: undefined; - - constructor( - @IModelService private readonly modelService: IModelService, - @IModeService private readonly modeService: IModeService, - ) { } - - async merge(localContent: string, remoteContent: string, baseContent: string | null, ignoredSettings: string[]): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> { - const local = parse(localContent); - const remote = parse(remoteContent); - const base = baseContent ? parse(baseContent) : null; - const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); - - const localToRemote = this.compare(local, remote, ignored); - if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { - // No changes found between local and remote. - return { mergeContent: localContent, hasChanges: false, hasConflicts: false }; - } - - const conflicts: Set = new Set(); - const baseToLocal = base ? this.compare(base, local, ignored) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - const baseToRemote = base ? this.compare(base, remote, ignored) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; - const settingsPreviewModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); - - // Removed settings in Local - for (const key of values(baseToLocal.removed)) { - // Got updated in remote - if (baseToRemote.updated.has(key)) { - conflicts.add(key); - } - } - - // Removed settings in Remote - for (const key of values(baseToRemote.removed)) { - if (conflicts.has(key)) { - continue; - } - // Got updated in local - if (baseToLocal.updated.has(key)) { - conflicts.add(key); - } else { - this.editSetting(settingsPreviewModel, key, undefined); - } - } - - // Added settings in Local - for (const key of values(baseToLocal.added)) { - if (conflicts.has(key)) { - continue; - } - // Got added in remote - if (baseToRemote.added.has(key)) { - // Has different value - if (localToRemote.updated.has(key)) { - conflicts.add(key); - } - } - } - - // Added settings in remote - for (const key of values(baseToRemote.added)) { - if (conflicts.has(key)) { - continue; - } - // Got added in local - if (baseToLocal.added.has(key)) { - // Has different value - if (localToRemote.updated.has(key)) { - conflicts.add(key); - } - } else { - this.editSetting(settingsPreviewModel, key, remote[key]); - } - } - - // Updated settings in Local - for (const key of values(baseToLocal.updated)) { - if (conflicts.has(key)) { - continue; - } - // Got updated in remote - if (baseToRemote.updated.has(key)) { - // Has different value - if (localToRemote.updated.has(key)) { - conflicts.add(key); - } - } - } - - // Updated settings in Remote - for (const key of values(baseToRemote.updated)) { - if (conflicts.has(key)) { - continue; - } - // Got updated in local - if (baseToLocal.updated.has(key)) { - // Has different value - if (localToRemote.updated.has(key)) { - conflicts.add(key); - } - } else { - this.editSetting(settingsPreviewModel, key, remote[key]); - } - } - - for (const key of values(conflicts)) { - const tree = parseTree(settingsPreviewModel.getValue()); - const valueNode = findNodeAtLocation(tree, [key]); - const eol = settingsPreviewModel.getEOL(); - const remoteEdit = setProperty(`{${eol}\t${eol}}`, [key], remote[key], { tabSize: 4, insertSpaces: false, eol: eol })[0]; - const remoteContent = remoteEdit ? `${remoteEdit.content.substring(remoteEdit.offset + remoteEdit.length + 1)},${eol}` : ''; - if (valueNode) { - // Updated in Local and Remote with different value - const keyPosition = settingsPreviewModel.getPositionAt(valueNode.parent!.offset); - const valuePosition = settingsPreviewModel.getPositionAt(valueNode.offset + valueNode.length); - const editOperations = [ - EditOperation.insert(new Position(keyPosition.lineNumber - 1, settingsPreviewModel.getLineMaxColumn(keyPosition.lineNumber - 1)), `${eol}<<<<<<< local`), - EditOperation.insert(new Position(valuePosition.lineNumber, settingsPreviewModel.getLineMaxColumn(valuePosition.lineNumber)), `${eol}=======${eol}${remoteContent}>>>>>>> remote`) - ]; - settingsPreviewModel.pushEditOperations([new Selection(keyPosition.lineNumber, keyPosition.column, keyPosition.lineNumber, keyPosition.column)], editOperations, () => []); - } else { - // Removed in Local, but updated in Remote - const position = new Position(settingsPreviewModel.getLineCount() - 1, settingsPreviewModel.getLineMaxColumn(settingsPreviewModel.getLineCount() - 1)); - const editOperations = [ - EditOperation.insert(position, `${eol}<<<<<<< local${eol}=======${eol}${remoteContent}>>>>>>> remote`) - ]; - settingsPreviewModel.pushEditOperations([new Selection(position.lineNumber, position.column, position.lineNumber, position.column)], editOperations, () => []); - } - } - return { mergeContent: settingsPreviewModel.getValue(), hasChanges: true, hasConflicts: conflicts.size > 0 }; - } - - async computeRemoteContent(localContent: string, remoteContent: string, ignoredSettings: string[]): Promise { - const remote = parse(remoteContent); - const remoteModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); - const ignored = ignoredSettings.reduce((set, key) => { set.add(key); return set; }, new Set()); - for (const key of ignoredSettings) { - if (ignored.has(key)) { - this.editSetting(remoteModel, key, undefined); - this.editSetting(remoteModel, key, remote[key]); - } - } - return remoteModel.getValue(); - } - - private editSetting(model: ITextModel, key: string, value: any | undefined): void { - const insertSpaces = model.getOptions().insertSpaces; - const tabSize = model.getOptions().tabSize; - const eol = model.getEOL(); - const edit = setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol })[0]; - if (edit) { - const startPosition = model.getPositionAt(edit.offset); - const endPosition = model.getPositionAt(edit.offset + edit.length); - const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); - let currentText = model.getValueInRange(range); - if (edit.content !== currentText) { - const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content); - model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); - } - } - } - - private compare(from: IStringDictionary, to: IStringDictionary, ignored: Set): { added: Set, removed: Set, updated: Set } { - const fromKeys = Object.keys(from).filter(key => !ignored.has(key)); - const toKeys = Object.keys(to).filter(key => !ignored.has(key)); - const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); - const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); - const updated: Set = new Set(); - - for (const key of fromKeys) { - if (removed.has(key)) { - continue; - } - const value1 = from[key]; - const value2 = to[key]; - if (!objects.equals(value1, value2)) { - updated.add(key); - } - } - - return { added, removed, updated }; - } - -} - -registerSingleton(ISettingsMergeService, SettingsMergeService); diff --git a/src/vs/workbench/test/browser/quickopen.test.ts b/src/vs/workbench/test/browser/quickopen.test.ts index 93cba1b1bc..d08e23984d 100644 --- a/src/vs/workbench/test/browser/quickopen.test.ts +++ b/src/vs/workbench/test/browser/quickopen.test.ts @@ -71,8 +71,8 @@ suite('QuickOpen', () => { }); test('QuickOpen Action', () => { - let defaultAction = new QuickOpenAction('id', 'label', (undefined)!, new TestQuickOpenService((prefix: string) => assert(!prefix))); - let prefixAction = new QuickOpenAction('id', 'label', ',', new TestQuickOpenService((prefix: string) => assert(!!prefix))); + let defaultAction = new QuickOpenAction('id', 'label', (undefined)!, new TestQuickOpenService(prefix => assert(!prefix))); + let prefixAction = new QuickOpenAction('id', 'label', ',', new TestQuickOpenService(prefix => assert(!!prefix))); defaultAction.run(); prefixAction.run(); diff --git a/src/vs/workbench/test/browser/viewlet.test.ts b/src/vs/workbench/test/browser/viewlet.test.ts index dacedd8c4d..8f377996b9 100644 --- a/src/vs/workbench/test/browser/viewlet.test.ts +++ b/src/vs/workbench/test/browser/viewlet.test.ts @@ -13,7 +13,7 @@ suite('Viewlets', () => { class TestViewlet extends Viewlet { constructor() { - super('id', null!, null!, null!, null!, null!); + super('id', null!, null!, null!, null!, null!, null!, null!, null!, null!, null!); } public layout(dimension: any): void { 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 bc3dfca0ee..85dd8155d7 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostApiCommands.test.ts @@ -30,6 +30,8 @@ import { NullLogService } from 'vs/platform/log/common/log'; import { ITextModel } from 'vs/editor/common/model'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; import { dispose } from 'vs/base/common/lifecycle'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -90,10 +92,15 @@ suite('ExtHostLanguageFeatureCommands', function () { onModelRemoved: undefined!, getCreationOptions() { throw new Error(); } }); + instantiationService.stub(IEditorWorkerService, new class extends mock() { + async computeMoreMinimalEdits(_uri: any, edits: any) { + return edits || undefined; + } + }); inst = instantiationService; } - const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol); + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: [{ isDirty: false, @@ -195,6 +202,21 @@ suite('ExtHostLanguageFeatureCommands', function () { assert.equal(symbols.length, 1); }); + // --- formatting + test('executeFormatDocumentProvider, back and forth', async function () { + + disposables.push(extHost.registerDocumentFormattingEditProvider(nullExtensionDescription, defaultSelector, new class implements vscode.DocumentFormattingEditProvider { + provideDocumentFormattingEdits() { + return [types.TextEdit.insert(new types.Position(0, 0), '42')]; + } + })); + + await rpcProtocol.sync(); + let edits = await commands.executeCommand('vscode.executeFormatDocumentProvider', model.uri); + assert.equal(edits.length, 1); + }); + + // --- definition test('Definition, invalid arguments', function () { diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts index c155156234..e54beed669 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentSaveParticipant.test.ts @@ -37,7 +37,7 @@ suite('ExtHostDocumentSaveParticipant', () => { }; setup(() => { - const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null)); + const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null), new NullLogService()); documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: [{ isDirty: false, diff --git a/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts b/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts index 8c682c971d..53f7324c2a 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostDocumentsAndEditors.test.ts @@ -7,13 +7,14 @@ import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostDocumentsAndEditors', () => { let editors: ExtHostDocumentsAndEditors; setup(function () { - editors = new ExtHostDocumentsAndEditors(new TestRPCProtocol()); + editors = new ExtHostDocumentsAndEditors(new TestRPCProtocol(), new NullLogService()); }); test('The value of TextDocument.isClosed is incorrect when a text document is closed, #27949', () => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index dfc5d0b0db..68488128c4 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -84,7 +84,7 @@ suite('ExtHostLanguageFeatures', function () { originalErrorHandler = errorHandler.getUnexpectedErrorHandler(); setUnexpectedErrorHandler(() => { }); - const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol); + const extHostDocumentsAndEditors = new ExtHostDocumentsAndEditors(rpcProtocol, new NullLogService()); extHostDocumentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: [{ isDirty: false, diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts index 1512f53e89..d6841a5bdd 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditor.test.ts @@ -10,6 +10,7 @@ import { ExtHostTextEditorOptions, ExtHostTextEditor } from 'vs/workbench/api/co import { ExtHostDocumentData } from 'vs/workbench/api/common/extHostDocumentData'; import { URI } from 'vs/base/common/uri'; import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostTextEditor', () => { @@ -19,7 +20,7 @@ suite('ExtHostTextEditor', () => { ], '\n', 'text', 1, false); setup(() => { - editor = new ExtHostTextEditor(null!, 'fake', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + editor = new ExtHostTextEditor('fake', null!, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); }); test('disposed editor', () => { @@ -40,12 +41,13 @@ suite('ExtHostTextEditor', () => { test('API [bug]: registerTextEditorCommand clears redo stack even if no edits are made #55163', async function () { let applyCount = 0; - let editor = new ExtHostTextEditor(new class extends mock() { - $tryApplyEdits(): Promise { - applyCount += 1; - return Promise.resolve(true); - } - }, 'edt1', doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); + let editor = new ExtHostTextEditor('edt1', + new class extends mock() { + $tryApplyEdits(): Promise { + applyCount += 1; + return Promise.resolve(true); + } + }, new NullLogService(), doc, [], { cursorStyle: 0, insertSpaces: true, lineNumbers: 1, tabSize: 4, indentSize: 4 }, [], 1); await editor.edit(edit => { }); assert.equal(applyCount, 0); @@ -92,7 +94,7 @@ suite('ExtHostTextEditorOptions', () => { insertSpaces: false, cursorStyle: TextEditorCursorStyle.Line, lineNumbers: RenderLineNumbersType.On - }); + }, new NullLogService()); }); teardown(() => { diff --git a/src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts b/src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts index 1f3e0ae797..4b49ab6e39 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostTextEditors.test.ts @@ -11,6 +11,7 @@ import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocum import { SingleProxyRPCProtocol, TestRPCProtocol } from 'vs/workbench/test/electron-browser/api/testRPCProtocol'; import { ExtHostEditors } from 'vs/workbench/api/common/extHostTextEditors'; import { ResourceTextEdit } from 'vs/editor/common/modes'; +import { NullLogService } from 'vs/platform/log/common/log'; suite('ExtHostTextEditors.applyWorkspaceEdit', () => { @@ -28,7 +29,7 @@ suite('ExtHostTextEditors.applyWorkspaceEdit', () => { return Promise.resolve(true); } }); - const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null)); + const documentsAndEditors = new ExtHostDocumentsAndEditors(SingleProxyRPCProtocol(null), new NullLogService()); documentsAndEditors.$acceptDocumentsAndEditorsDelta({ addedDocuments: [{ isDirty: false, diff --git a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts index c40255e2df..58706c9fd5 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostWorkspace.test.ts @@ -120,7 +120,7 @@ suite('ExtHostWorkspace', function () { assert.equal(ws.getPath(), undefined); ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('Folder'), 0), aWorkspaceFolderData(URI.file('Another/Folder'), 1)] }, new NullLogService()); - assert.equal(ws.getPath(), undefined); + assert.equal(ws.getPath()!.replace(/\\/g, '/'), '/Folder'); ws = createExtHostWorkspace(new TestRPCProtocol(), { id: 'foo', name: 'Test', folders: [aWorkspaceFolderData(URI.file('/Folder'), 0)] }, new NullLogService()); assert.equal(ws.getPath()!.replace(/\\/g, '/'), '/Folder'); diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 9049477fd9..f96bd1937e 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -80,7 +80,6 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/extensions/common/staticExtensions'; -import 'vs/workbench/services/userDataSync/common/settingsMergeService'; import 'vs/workbench/services/userDataSync/common/userDataSyncUtil'; import 'vs/workbench/services/path/common/remotePathService'; import 'vs/workbench/services/remote/common/remoteExplorerService'; @@ -268,6 +267,7 @@ import 'vs/workbench/contrib/debug/browser/debug.contribution'; import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; import 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; +import 'vs/workbench/contrib/debug/browser/callStackEditorContribution'; import 'vs/workbench/contrib/debug/browser/repl'; import 'vs/workbench/contrib/debug/browser/debugViewlet'; */ diff --git a/tslint.json b/tslint.json index 50787f9417..f7bb25f3df 100644 --- a/tslint.json +++ b/tslint.json @@ -81,6 +81,7 @@ "target": "**/{vs,sql}/base/test/common/**", "restrictions": [ "assert", + "sinon", "vs/nls", "**/{vs,sql}/base/common/**", "**/{vs,sql}/base/test/common/**" diff --git a/yarn.lock b/yarn.lock index 5b3f26dae6..e08a619eb4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -219,6 +219,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== +"@types/node@^10.12.18": + version "10.17.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.9.tgz#4f251a1ed77ac7ef09d456247d67fc8173f6b9da" + integrity sha512-+6VygF9LbG7Gaqeog2G7u1+RUcmo0q1rI+2ZxdIg2fAUngk5Vz9fOCHXdloNUOHEPd1EuuOpL5O0CdgN9Fx5UQ== + "@types/plotly.js@^1.44.9": version "1.44.9" resolved "https://registry.yarnpkg.com/@types/plotly.js/-/plotly.js-1.44.9.tgz#d20bd229b409f83b5e9bc06df0c948a27b8fbc0d" @@ -786,6 +791,11 @@ array-each@^1.0.0, array-each@^1.0.1: resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1405,6 +1415,19 @@ callsites@^3.0.0: resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw== +camelcase-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" + integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= + dependencies: + camelcase "^2.0.0" + map-obj "^1.0.0" + +camelcase@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" + integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= + camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -1898,7 +1921,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0, concat-stream@^1.6.0: +concat-stream@1.6.2, concat-stream@^1.5.0, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -2203,6 +2226,13 @@ cuint@^0.2.1: resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= + dependencies: + array-find-index "^1.0.1" + cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -2239,7 +2269,7 @@ debug@2.2.0: dependencies: ms "0.7.1" -debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3: +debug@2.6.9, debug@^2.1.2, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -2253,7 +2283,7 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@^3.1.0: +debug@^3.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== @@ -2595,11 +2625,35 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +electron-download@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/electron-download/-/electron-download-4.1.1.tgz#02e69556705cc456e520f9e035556ed5a015ebe8" + integrity sha512-FjEWG9Jb/ppK/2zToP+U5dds114fM1ZOJqMAR4aXXL5CvyPE9fiqBK/9YcwC9poIFQTEJk/EM/zyRwziziRZrg== + dependencies: + debug "^3.0.0" + env-paths "^1.0.0" + fs-extra "^4.0.1" + minimist "^1.2.0" + nugget "^2.0.1" + path-exists "^3.0.0" + rc "^1.2.1" + semver "^5.4.1" + sumchecker "^2.0.2" + electron-to-chromium@^1.2.7: version "1.3.27" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.27.tgz#78ecb8a399066187bb374eede35d9c70565a803d" integrity sha1-eOy4o5kGYYe7N07t412ccFZagD0= +electron@6.1.5: + version "6.1.5" + resolved "https://registry.yarnpkg.com/electron/-/electron-6.1.5.tgz#1f1bc54042587d8368edd43ffecb0ce7c84cab87" + integrity sha512-PrdJKkAS0IaSJwu4him03VYqvAKK1qyWTE/ieb4LgcbR4F4u90b91/7xna6P1GpD/FXiHqzZQcs0SvK/o08ckQ== + dependencies: + "@types/node" "^10.12.18" + electron-download "^4.1.0" + extract-zip "^1.0.3" + elliptic@^6.0.0: version "6.4.0" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" @@ -2656,6 +2710,11 @@ entities@^1.1.1, entities@~1.1.1: resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA= +env-paths@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-1.0.0.tgz#4168133b42bb05c38a35b1ae4397c8298ab369e0" + integrity sha1-QWgTO0K7BcOKNbGuQ5fIKYqzaeA= + errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" @@ -3030,6 +3089,16 @@ extglob@^2.0.4: snapdragon "^0.8.1" to-regex "^3.0.1" +extract-zip@^1.0.3: + version "1.6.7" + resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.6.7.tgz#a840b4b8af6403264c8db57f4f1a74333ef81fe9" + integrity sha1-qEC0uK9kAyZMjbV/Txp0Mz74H+k= + dependencies: + concat-stream "1.6.2" + debug "2.6.9" + mkdirp "0.5.1" + yauzl "2.4.1" + extsprintf@1.3.0, extsprintf@^1.2.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -3368,6 +3437,15 @@ fs-extra@0.26.7: path-is-absolute "^1.0.0" rimraf "^2.2.8" +fs-extra@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" + integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-extra@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -3486,6 +3564,11 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-stdin@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" + integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -4352,6 +4435,13 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +indent-string@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" + integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= + dependencies: + repeating "^2.0.0" + indexes-of@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" @@ -4588,6 +4678,13 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= + dependencies: + number-is-nan "^1.0.0" + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -5371,6 +5468,14 @@ long@^3.2.0: resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" integrity sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s= +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + lru-cache@2: version "2.7.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" @@ -5436,6 +5541,11 @@ map-cache@^0.2.0, map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= +map-obj@^1.0.0, map-obj@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= + map-stream@0.0.7, map-stream@~0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.0.7.tgz#8a1f07896d82b10926bd3744a2420009f88974a8" @@ -5523,6 +5633,22 @@ memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: errno "^0.1.3" readable-stream "^2.0.1" +meow@^3.1.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" + integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= + dependencies: + camelcase-keys "^2.0.0" + decamelize "^1.1.2" + loud-rejection "^1.0.0" + map-obj "^1.0.1" + minimist "^1.1.3" + normalize-package-data "^2.3.4" + object-assign "^4.0.1" + read-pkg-up "^1.0.1" + redent "^1.0.0" + trim-newlines "^1.0.0" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -5684,7 +5810,7 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@1.2.0, minimist@^1.2.0: +minimist@1.2.0, minimist@^1.1.0, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= @@ -6035,6 +6161,16 @@ normalize-package-data@^2.3.2: semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" +normalize-package-data@^2.3.4: + version "2.5.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + normalize-path@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" @@ -6116,6 +6252,19 @@ nth-check@~1.0.1: dependencies: boolbase "~1.0.0" +nugget@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/nugget/-/nugget-2.0.1.tgz#201095a487e1ad36081b3432fa3cada4f8d071b0" + integrity sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA= + dependencies: + debug "^2.1.3" + minimist "^1.1.0" + pretty-bytes "^1.0.2" + progress-stream "^1.1.0" + request "^2.45.0" + single-line-log "^1.1.2" + throttleit "0.0.2" + num2fraction@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" @@ -7001,6 +7150,14 @@ preserve@^0.2.0: resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= +pretty-bytes@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-1.0.4.tgz#0a22e8210609ad35542f8c8d5d2159aff0751c84" + integrity sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ= + dependencies: + get-stdin "^4.0.1" + meow "^3.1.0" + pretty-hrtime@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -7029,6 +7186,14 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= +progress-stream@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/progress-stream/-/progress-stream-1.2.0.tgz#2cd3cfea33ba3a89c9c121ec3347abe9ab125f77" + integrity sha1-LNPP6jO6OonJwSHsM0er6asSX3c= + dependencies: + speedometer "~0.1.2" + through2 "~0.2.3" + progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" @@ -7236,7 +7401,7 @@ raw-body@2.3.2: iconv-lite "0.4.19" unpipe "1.0.0" -rc@^1.2.7: +rc@^1.2.1, rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== @@ -7312,7 +7477,7 @@ read@^1.0.7: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@^1.1.8: +readable-stream@^1.1.8, readable-stream@~1.1.9: version "1.1.14" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= @@ -7389,6 +7554,14 @@ rechoir@^0.6.2: dependencies: resolve "^1.1.6" +redent@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" + integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= + dependencies: + indent-string "^2.1.0" + strip-indent "^1.0.1" + reduce-css-calc@^1.2.6: version "1.3.0" resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" @@ -7468,6 +7641,13 @@ repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= + dependencies: + is-finite "^1.0.0" + replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" @@ -7550,7 +7730,7 @@ request@2.79.0: tunnel-agent "^0.6.0" uuid "^3.1.0" -request@^2.86.0, request@^2.88.0: +request@^2.45.0, request@^2.86.0, 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== @@ -7644,6 +7824,13 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.3.2: dependencies: path-parse "^1.0.5" +resolve@^1.10.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16" + integrity sha512-CxqObCX8K8YtAhOBRg+lrcdn+LK+WYOS8tSjqSFbjtrI5PnS63QPhZl4+yKfrU9tdsbMu9Anr/amegT87M9Z6w== + dependencies: + path-parse "^1.0.6" + resolve@^1.4.0: version "1.10.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba" @@ -7799,10 +7986,10 @@ semver-greatest-satisfied-range@^1.1.0: dependencies: sver-compat "^1.5.0" -semver-umd@^5.5.3: - version "5.5.3" - resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.3.tgz#b64d7a2d4f5a717b369d56e31940a38e47e34d1e" - integrity sha512-HOnQrn2iKnVe/xlqCTzMXQdvSz3rPbD0DmQXYuQ+oK1dpptGFfPghonQrx5JHl2O7EJwDqtQnjhE7ME23q6ngw== +semver-umd@^5.5.5: + version "5.5.5" + resolved "https://registry.yarnpkg.com/semver-umd/-/semver-umd-5.5.5.tgz#a2e4280d0e92a2b27695c18811f0e939e144d86f" + integrity sha512-8rUq0nnTzlexpAdYmm8UDYsLkBn0MnBkfrGWPmyDBDDzv71dPOH07szOOaLj/5hO3BYmumYwS+wp3C60zLzh5g== "semver@2 || 3 || 4 || 5", semver@^5.1.0, semver@^5.3.0: version "5.4.1" @@ -7957,6 +8144,13 @@ simple-get@^2.7.0: once "^1.3.1" simple-concat "^1.0.0" +single-line-log@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/single-line-log/-/single-line-log-1.1.2.tgz#c2f83f273a3e1a16edb0995661da0ed5ef033364" + integrity sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q= + dependencies: + string-width "^1.0.1" + sinon@^1.17.2: version "1.17.7" resolved "https://registry.yarnpkg.com/sinon/-/sinon-1.17.7.tgz#4542a4f49ba0c45c05eb2e9dd9d203e2b8efe0bf" @@ -8126,6 +8320,11 @@ spdx-license-ids@^1.0.2: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz#c9df7a3424594ade6bd11900d596696dc06bac57" integrity sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc= +speedometer@~0.1.2: + version "0.1.4" + resolved "https://registry.yarnpkg.com/speedometer/-/speedometer-0.1.4.tgz#9876dbd2a169d3115402d48e6ea6329c8816a50d" + integrity sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0= + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -8389,6 +8588,13 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-indent@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" + integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= + dependencies: + get-stdin "^4.0.1" + strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -8399,6 +8605,13 @@ sudo-prompt@9.1.1: resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.1.tgz#73853d729770392caec029e2470db9c221754db0" integrity sha512-es33J1g2HjMpyAhz8lOR+ICmXXAqTuKbuXuUWLhOLew20oN9oUCgCJx615U/v7aioZg7IX5lIh9x34vwneu4pA== +sumchecker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-2.0.2.tgz#0f42c10e5d05da5d42eea3e56c3399a37d6c5b3e" + integrity sha1-D0LBDl0F2l1C7qPlbDOZo31sWz4= + dependencies: + debug "^2.2.0" + supports-color@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-1.2.0.tgz#ff1ed1e61169d06b3cf2d588e188b18d8847e17e" @@ -8565,6 +8778,11 @@ textextensions@~1.0.0: resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-1.0.2.tgz#65486393ee1f2bb039a60cbba05b0b68bd9501d2" integrity sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI= +throttleit@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-0.0.2.tgz#cfedf88e60c00dd9697b61fdd2a8343a9b680eaf" + integrity sha1-z+34jmDADdlpe2H90qg0OptoDq8= + through2-filter@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-2.0.0.tgz#60bc55a0dacb76085db1f9dae99ab43f83d622ec" @@ -8597,6 +8815,14 @@ through2@^3.0.0: readable-stream "2 || 3" xtend "~4.0.1" +through2@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-0.2.3.tgz#eb3284da4ea311b6cc8ace3653748a52abf25a3f" + integrity sha1-6zKE2k6jEbbMis42U3SKUqvyWj8= + dependencies: + readable-stream "~1.1.9" + xtend "~2.1.1" + through2@~0.4.0: version "0.4.2" resolved "https://registry.yarnpkg.com/through2/-/through2-0.4.2.tgz#dbf5866031151ec8352bb6c4db64a2292a840b9b" @@ -8751,6 +8977,11 @@ tough-cookie@~2.4.3: resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk= +trim-newlines@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" + integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= + trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" @@ -8887,10 +9118,10 @@ typescript-formatter@7.1.0: commandpost "^1.0.0" editorconfig "^0.15.0" -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@3.7.3: + version "3.7.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.3.tgz#b36840668a16458a7025b9eabfad11b66ab85c69" + integrity sha512-Mcr/Qk7hXqFBXMN7p7Lusj1ktCBydylfQM/FZCk5glCNQJrCUKPkMHdo9R0MTFWsC/4kPFvDS0fDPvukfCkFsw== typescript@^2.6.2: version "2.6.2" @@ -9749,6 +9980,13 @@ yargs@^7.1.0: y18n "^3.2.1" yargs-parser "^5.0.0" +yauzl@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.4.1.tgz#9528f442dab1b2284e58b4379bb194e22e0c4005" + integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU= + dependencies: + fd-slicer "~1.0.1" + yauzl@^2.2.1, yauzl@^2.3.1: version "2.9.1" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.9.1.tgz#a81981ea70a57946133883f029c5821a89359a7f"