diff --git a/.git-blame-ignore b/.git-blame-ignore-revs similarity index 86% rename from .git-blame-ignore rename to .git-blame-ignore-revs index 457d2604f3c..c0c9a544148 100644 --- a/.git-blame-ignore +++ b/.git-blame-ignore-revs @@ -1,4 +1,5 @@ # https://git-scm.com/docs/git-blame#Documentation/git-blame.txt---ignore-revs-fileltfilegt +# https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view # mjbvz: Fix spacing 13f4f052582bcec3d6c6c6a70d995c9dee2cac13 @@ -23,6 +24,5 @@ ae1452eea678f5266ef513f22dacebb90955d6c9 a3cb14be7f2cceadb17adf843675b1a59537dbbd ee1655a82ebdfd38bf8792088a6602c69f7bbd94 - # jrieken: new eslint-rule 4a130c40ed876644ed8af2943809d08221375408 diff --git a/.github/commands.json b/.github/commands.json index 9d2ac7d7090..ea5f8bc38e1 100644 --- a/.github/commands.json +++ b/.github/commands.json @@ -35,7 +35,7 @@ "name": "*dev-question", "action": "close", "reason": "not_planned", - "comment": "We have a great extension developer community over on [GitHub discussions](https://github.com/microsoft/vscode-discussions/discussions) and [Slack](https://aka.ms/vscode-dev-community) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!" + "comment": "We have a great extension developer community over on [GitHub discussions](https://github.com/microsoft/vscode-discussions/discussions) and [Slack](https://vscode-dev-community.slack.com/) where extension authors help each other. This is a great place for you to ask questions and find support.\n\nHappy Coding!" }, { "type": "label", diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 4ae98a95577..857114fea15 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -64,7 +64,7 @@ const vscodeResources = [ 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', - 'out-build/vs/base/parts/sandbox/electron-sandbox/preload-slim.js', + 'out-build/vs/base/parts/sandbox/electron-sandbox/preload-aux.js', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index 09df602a3bf..07b4412c458 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -112,4 +112,4 @@ for (let dir of dirs) { } cp.execSync('git config pull.rebase merges'); -cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore'); +cp.execSync('git config blame.ignoreRevsFile .git-blame-ignore-revs'); diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 176ee56da48..ad27f27b271 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "772323937fedd65c6dc1c8ce6ea41d97415ed7d1" + "commitHash": "6666eb1d5e6fb565a4110d6db645cc534fb3c6d2" } }, "license": "MIT", @@ -15,4 +15,4 @@ } ], "version": 1 -} +} \ No newline at end of file diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index c4266da462d..9b6cf2e9664 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.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/dotnet/csharp-tmLanguage/commit/772323937fedd65c6dc1c8ce6ea41d97415ed7d1", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/6666eb1d5e6fb565a4110d6db645cc534fb3c6d2", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -210,9 +210,6 @@ { "include": "#else-part" }, - { - "include": "#switch-statement" - }, { "include": "#goto-statement" }, @@ -238,10 +235,10 @@ "include": "#checked-unchecked-statement" }, { - "include": "#lock-statement" + "include": "#context-control-statement" }, { - "include": "#using-statement" + "include": "#context-control-paren-statement" }, { "include": "#labeled-statement" @@ -278,13 +275,10 @@ "include": "#comment" }, { - "include": "#checked-unchecked-expression" + "include": "#expression-operator-expression" }, { - "include": "#typeof-or-default-expression" - }, - { - "include": "#nameof-expression" + "include": "#type-operator-expression" }, { "include": "#default-literal-expression" @@ -305,14 +299,20 @@ "include": "#type-builtin" }, { - "include": "#this-or-base-expression" + "include": "#language-variable" }, { - "include": "#switch-expression" + "include": "#switch-statement-or-expression" + }, + { + "include": "#with-expression" }, { "include": "#conditional-operator" }, + { + "include": "#assignment-expression" + }, { "include": "#expression-operators" }, @@ -370,36 +370,39 @@ ] }, "extern-alias-directive": { - "begin": "\\s*(extern)\\b\\s*(alias)\\b\\s*(@?[_[:alpha:]][_[:alnum:]]*)", + "begin": "\\b(extern)\\s+(alias)\\b", "beginCaptures": { "1": { - "name": "keyword.other.extern.cs" + "name": "keyword.other.directive.extern.cs" }, "2": { - "name": "keyword.other.alias.cs" - }, - "3": { - "name": "variable.other.alias.cs" + "name": "keyword.other.directive.alias.cs" } }, - "end": "(?=;)" + "end": "(?=;)", + "patterns": [ + { + "match": "\\@?[_[:alpha:]][_[:alnum:]]*", + "name": "variable.other.alias.cs" + } + ] }, "using-directive": { "patterns": [ { - "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(static)\\b\\s+(\\b(unsafe)\\b\\s+)?", + "begin": "\\b(?:(global)\\s+)?(using)\\s+(static)\\b\\s*(?:(unsafe)\\b\\s*)?", "beginCaptures": { + "1": { + "name": "keyword.other.directive.global.cs" + }, "2": { - "name": "keyword.other.global.cs" + "name": "keyword.other.directive.using.cs" }, "3": { - "name": "keyword.other.using.cs" + "name": "keyword.other.directive.static.cs" }, "4": { - "name": "keyword.other.static.cs" - }, - "6": { - "name": "storage.modifier.cs" + "name": "storage.modifier.unsafe.cs" } }, "end": "(?=;)", @@ -410,19 +413,22 @@ ] }, { - "begin": "(\\b(global)\\b\\s+)?\\b(using)\\b\\s+(\\b(unsafe)\\b\\s+)?(?=(@?[_[:alpha:]][_[:alnum:]]*)\\s*=)", + "begin": "\\b(?:(global)\\s+)?(using)\\b\\s*(?:(unsafe)\\b\\s*)?(@?[_[:alpha:]][_[:alnum:]]*)\\s*(=)", "beginCaptures": { + "1": { + "name": "keyword.other.directive.global.cs" + }, "2": { - "name": "keyword.other.global.cs" + "name": "keyword.other.directive.using.cs" }, "3": { - "name": "keyword.other.using.cs" + "name": "storage.modifier.unsafe.cs" + }, + "4": { + "name": "entity.name.type.alias.cs" }, "5": { - "name": "storage.modifier.cs" - }, - "6": { - "name": "entity.name.type.alias.cs" + "name": "keyword.operator.assignment.cs" } }, "end": "(?=;)", @@ -432,20 +438,17 @@ }, { "include": "#type" - }, - { - "include": "#operator-assignment" } ] }, { - "begin": "(\\b(global)\\b\\s+)?\\b(using)\\s*(?!\\(|\\s|var)", + "begin": "\\b(?:(global)\\s+)?(using)\\b\\s*+(?!\\(|var\\b)", "beginCaptures": { - "2": { - "name": "keyword.other.global.cs" + "1": { + "name": "keyword.other.directive.global.cs" }, - "3": { - "name": "keyword.other.using.cs" + "2": { + "name": "keyword.other.directive.using.cs" } }, "end": "(?=;)", @@ -455,7 +458,10 @@ }, { "name": "entity.name.type.namespace.cs", - "match": "@?[_[:alpha:]][_[:alnum:]]*" + "match": "\\@?[_[:alpha:]][_[:alnum:]]*" + }, + { + "include": "#punctuation-accessor" }, { "include": "#operator-assignment" @@ -551,7 +557,7 @@ "begin": "\\b(namespace)\\s+", "beginCaptures": { "1": { - "name": "keyword.other.namespace.cs" + "name": "storage.type.namespace.cs" } }, "end": "(?<=\\})|(?=;)", @@ -594,7 +600,7 @@ ] }, "storage-modifier": { - "name": "storage.modifier.cs", + "name": "storage.modifier.$1.cs", "match": "(?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+\n(\\g)\\s*\n(<([^<>]+)>)?\\s*\n(?=\\()", "beginCaptures": { "1": { - "name": "keyword.other.delegate.cs" + "name": "storage.type.delegate.cs" }, "2": { "patterns": [ @@ -712,7 +718,7 @@ "match": "(enum)\\s+(@?[_[:alpha:]][_[:alnum:]]*)", "captures": { "1": { - "name": "keyword.other.enum.cs" + "name": "storage.type.enum.cs" }, "2": { "name": "entity.name.type.enum.cs" @@ -796,7 +802,7 @@ "begin": "(?x)\n(interface)\\b\\s+\n(@?[_[:alpha:]][_[:alnum:]]*)", "beginCaptures": { "1": { - "name": "keyword.other.interface.cs" + "name": "storage.type.interface.cs" }, "2": { "name": "entity.name.type.interface.cs" @@ -853,7 +859,7 @@ "begin": "(?x)\n(record)\\b\\s+\n(@?[_[:alpha:]][_[:alnum:]]*)", "beginCaptures": { "1": { - "name": "keyword.other.record.cs" + "name": "storage.type.record.cs" }, "2": { "name": "entity.name.type.class.cs" @@ -913,10 +919,10 @@ "begin": "(?x)\n(\\b(record)\\b\\s+)?\n(struct)\\b\\s+\n(@?[_[:alpha:]][_[:alnum:]]*)", "beginCaptures": { "2": { - "name": "keyword.other.record.cs" + "name": "storage.type.record.cs" }, "3": { - "name": "keyword.other.struct.cs" + "name": "storage.type.struct.cs" }, "4": { "name": "entity.name.type.struct.cs" @@ -984,19 +990,11 @@ "patterns": [ { "match": "\\b(in|out)\\b", - "captures": { - "1": { - "name": "storage.modifier.cs" - } - } + "name": "storage.modifier.$1.cs" }, { "match": "(@?[_[:alpha:]][_[:alnum:]]*)\\b", - "captures": { - "1": { - "name": "entity.name.type.type-parameter.cs" - } - } + "name": "entity.name.type.type-parameter.cs" }, { "include": "#comment" @@ -1033,7 +1031,7 @@ "begin": "(where)\\s+(@?[_[:alpha:]][_[:alnum:]]*)\\s*(:)", "beginCaptures": { "1": { - "name": "keyword.other.where.cs" + "name": "storage.modifier.where.cs" }, "2": { "name": "entity.name.type.type-parameter.cs" @@ -1045,18 +1043,18 @@ "end": "(?=\\{|where|;|=>)", "patterns": [ { - "name": "keyword.other.class.cs", + "name": "storage.type.class.cs", "match": "\\bclass\\b" }, { - "name": "keyword.other.struct.cs", + "name": "storage.type.struct.cs", "match": "\\bstruct\\b" }, { "match": "(new)\\s*(\\()\\s*(\\))", "captures": { "1": { - "name": "keyword.other.new.cs" + "name": "keyword.operator.expression.new.cs" }, "2": { "name": "punctuation.parenthesis.open.cs" @@ -1112,7 +1110,7 @@ ] }, "property-declaration": { - "begin": "(?x)\n\n# The negative lookahead below ensures that we don't match nested types\n# or other declarations as properties.\n(?![[:word:][:space:]]*\\b(?:class|interface|struct|enum|event)\\b)\n\n(?\n (?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?\\g)\\s*\n(?=\\{|=>|$)", + "begin": "(?x)\n\n# The negative lookahead below ensures that we don't match nested types\n# or other declarations as properties.\n(?![[:word:][:space:]]*\\b(?:class|interface|struct|enum|event)\\b)\n\n(?\n (?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?\\g)\\s*\n(?=\\{|=>|//|/\\*|$)", "beginCaptures": { "1": { "patterns": [ @@ -1144,7 +1142,7 @@ "include": "#property-accessors" }, { - "include": "#expression-body" + "include": "#accessor-getter-expression" }, { "include": "#variable-initializer" @@ -1175,7 +1173,7 @@ ] }, "8": { - "name": "keyword.other.this.cs" + "name": "variable.language.this.cs" } }, "end": "(?<=\\})|(?=;)", @@ -1190,7 +1188,7 @@ "include": "#property-accessors" }, { - "include": "#expression-body" + "include": "#accessor-getter-expression" }, { "include": "#variable-initializer" @@ -1198,10 +1196,10 @@ ] }, "event-declaration": { - "begin": "(?x)\n\\b(event)\\b\\s*\n(?\n (?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(?\\g(?:\\s*,\\s*\\g)*)\\s*\n(?=\\{|;|$)", + "begin": "(?x)\n\\b(event)\\b\\s*\n(?\n (?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\\s+\n)\n(?\\g\\s*\\.\\s*)?\n(\\g)\\s* # first event name\n(?=\\{|;|,|=|//|/\\*|$)", "beginCaptures": { "1": { - "name": "keyword.other.event.cs" + "name": "storage.type.event.cs" }, "2": { "patterns": [ @@ -1221,15 +1219,7 @@ ] }, "9": { - "patterns": [ - { - "name": "entity.name.variable.event.cs", - "match": "@?[_[:alpha:]][_[:alnum:]]*" - }, - { - "include": "#punctuation-comma" - } - ] + "name": "entity.name.variable.event.cs" } }, "end": "(?<=\\})|(?=;)", @@ -1240,8 +1230,29 @@ { "include": "#event-accessors" }, + { + "name": "entity.name.variable.event.cs", + "match": "@?[_[:alpha:]][_[:alnum:]]*" + }, { "include": "#punctuation-comma" + }, + { + "begin": "=", + "beginCaptures": { + "0": { + "name": "keyword.operator.assignment.cs" + } + }, + "end": "(?<=,)|(?=;)", + "patterns": [ + { + "include": "#expression" + }, + { + "include": "#punctuation-comma" + } + ] } ] }, @@ -1259,22 +1270,6 @@ } }, "patterns": [ - { - "name": "storage.modifier.cs", - "match": "\\b(private|protected|internal)\\b" - }, - { - "name": "keyword.other.get.cs", - "match": "\\b(get)\\b" - }, - { - "name": "keyword.other.set.cs", - "match": "\\b(set)\\b" - }, - { - "name": "keyword.other.init.cs", - "match": "\\b(init)\\b" - }, { "include": "#comment" }, @@ -1282,13 +1277,36 @@ "include": "#attribute-section" }, { - "include": "#expression-body" + "name": "storage.modifier.$1.cs", + "match": "\\b(private|protected|internal)\\b" }, { - "include": "#block" + "begin": "\\b(get)\\b\\s*(?=\\{|;|=>|//|/\\*|$)", + "beginCaptures": { + "1": { + "name": "storage.type.accessor.$1.cs" + } + }, + "end": "(?<=\\}|;)|(?=\\})", + "patterns": [ + { + "include": "#accessor-getter" + } + ] }, { - "include": "#punctuation-semicolon" + "begin": "\\b(set|init)\\b\\s*(?=\\{|;|=>|//|/\\*|$)", + "beginCaptures": { + "1": { + "name": "storage.type.accessor.$1.cs" + } + }, + "end": "(?<=\\}|;)|(?=\\})", + "patterns": [ + { + "include": "#accessor-setter" + } + ] } ] }, @@ -1306,14 +1324,6 @@ } }, "patterns": [ - { - "name": "keyword.other.add.cs", - "match": "\\b(add)\\b" - }, - { - "name": "keyword.other.remove.cs", - "match": "\\b(remove)\\b" - }, { "include": "#comment" }, @@ -1321,10 +1331,108 @@ "include": "#attribute-section" }, { - "include": "#expression-body" + "begin": "\\b(add|remove)\\b\\s*(?=\\{|;|=>|//|/\\*|$)", + "beginCaptures": { + "1": { + "name": "storage.type.accessor.$1.cs" + } + }, + "end": "(?<=\\}|;)|(?=\\})", + "patterns": [ + { + "include": "#accessor-setter" + } + ] + } + ] + }, + "accessor-getter": { + "patterns": [ + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "contentName": "meta.accessor.getter.cs", + "patterns": [ + { + "include": "#statement" + } + ] }, { - "include": "#block" + "include": "#accessor-getter-expression" + }, + { + "include": "#punctuation-semicolon" + } + ] + }, + "accessor-getter-expression": { + "begin": "=>", + "beginCaptures": { + "0": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=;|\\})", + "contentName": "meta.accessor.getter.cs", + "patterns": [ + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + "accessor-setter": { + "patterns": [ + { + "begin": "\\{", + "beginCaptures": { + "0": { + "name": "punctuation.curlybrace.open.cs" + } + }, + "end": "\\}", + "endCaptures": { + "0": { + "name": "punctuation.curlybrace.close.cs" + } + }, + "contentName": "meta.accessor.setter.cs", + "patterns": [ + { + "include": "#statement" + } + ] + }, + { + "begin": "=>", + "beginCaptures": { + "0": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=;|\\})", + "contentName": "meta.accessor.setter.cs", + "patterns": [ + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] }, { "include": "#punctuation-semicolon" @@ -1425,13 +1533,10 @@ ] }, "constructor-initializer": { - "begin": "\\b(?:(base)|(this))\\b\\s*(?=\\()", + "begin": "\\b(base|this)\\b\\s*(?=\\()", "beginCaptures": { "1": { - "name": "keyword.other.base.cs" - }, - "2": { - "name": "keyword.other.this.cs" + "name": "variable.language.$1.cs" } }, "end": "(?<=\\))", @@ -1468,7 +1573,7 @@ ] }, "operator-declaration": { - "begin": "(?x)\n(?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n(?(?:\\b(?:operator)))\\s*\n(?(?:\\+|-|\\*|/|%|&|\\||\\^|\\<\\<|\\>\\>|==|!=|\\>|\\<|\\>=|\\<=|!|~|\\+\\+|--|true|false))\\s*\n(?=\\()", + "begin": "(?x)\n(?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n\\b(?operator)\\b\\s*\n(?[+\\-*/%&|\\^!=~<>]+|true|false)\\s*\n(?=\\()", "beginCaptures": { "1": { "patterns": [ @@ -1478,7 +1583,7 @@ ] }, "6": { - "name": "keyword.other.operator-decl.cs" + "name": "storage.type.operator.cs" }, "7": { "name": "entity.name.function.cs" @@ -1509,7 +1614,7 @@ "match": "\\b(explicit)\\b", "captures": { "1": { - "name": "keyword.other.explicit.cs" + "name": "storage.modifier.explicit.cs" } } }, @@ -1517,14 +1622,14 @@ "match": "\\b(implicit)\\b", "captures": { "1": { - "name": "keyword.other.implicit.cs" + "name": "storage.modifier.implicit.cs" } } } ] }, "2": { - "name": "keyword.other.operator-decl.cs" + "name": "storage.type.operator.cs" }, "3": { "patterns": [ @@ -1607,19 +1712,19 @@ "begin": "(?", + "beginCaptures": { + "0": { + "name": "keyword.operator.arrow.cs" + } + }, + "end": "(?=,|})", + "patterns": [ + { + "include": "#expression" + } + ] + }, + { + "begin": "\\b(when)\\b", + "beginCaptures": { + "1": { + "name": "keyword.control.conditional.when.cs" + } + }, + "end": "(?==>|,|})", + "patterns": [ + { + "include": "#case-guard" + } + ] + }, + { + "begin": "(?!\\s)", + "end": "(?=\\bwhen\\b|=>|,|})", + "patterns": [ + { + "include": "#pattern" + } + ] + } + ] + }, + "case-guard": { + "patterns": [ + { + "include": "#parenthesized-expression" + }, + { + "include": "#expression" + } + ] + }, + "is-expression": { + "begin": "(?=?", + "beginCaptures": { + "0": { + "name": "keyword.operator.relational.cs" + } + }, + "end": "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#expression" + } + ] + }, + "var-pattern": { + "begin": "\\b(var)\\b", + "beginCaptures": { + "1": { + "name": "storage.type.var.cs" + } + }, + "end": "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#designation-pattern" + } + ] + }, + "designation-pattern": { + "patterns": [ + { + "include": "#intrusive" + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#punctuation-comma" + }, + { + "include": "#designation-pattern" + } + ] + }, + { + "include": "#simple-designation-pattern" + } + ] + }, + "simple-designation-pattern": { + "patterns": [ + { + "include": "#discard-pattern" + }, + { + "match": "@?[_[:alpha:]][_[:alnum:]]*", + "name": "entity.name.variable.local.cs" + } + ] + }, + "type-pattern": { + "begin": "(?=@?[_[:alpha:]][_[:alnum:]]*)", + "end": "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "begin": "\\G", + "end": "(?!\\G[@_[:alpha:]])(?=[\\({@_[:alpha:])}\\],;:=&|^]|(?:\\s|^)\\?|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#intrusive" + }, + { + "include": "#type-subpattern" + } + ] + }, + { + "begin": "(?=[\\({@_[:alpha:]])", + "end": "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#intrusive" + }, + { + "include": "#positional-pattern" + }, + { + "include": "#property-pattern" + }, + { + "include": "#simple-designation-pattern" + } + ] + } + ] + }, + "type-subpattern": { + "patterns": [ + { + "include": "#type-builtin" + }, + { + "begin": "(@?[_[:alpha:]][_[:alnum:]]*)\\s*(::)", + "beginCaptures": { + "1": { + "name": "entity.name.type.alias.cs" + }, + "2": { + "name": "punctuation.separator.coloncolon.cs" + } + }, + "end": "(?<=[_[:alnum:]])|(?=[.<\\[\\({)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#intrusive" + }, + { + "match": "\\@?[_[:alpha:]][_[:alnum:]]*", + "name": "entity.name.type.cs" + } + ] + }, + { + "match": "\\@?[_[:alpha:]][_[:alnum:]]*", + "name": "entity.name.type.cs" + }, + { + "begin": "\\.", + "beginCaptures": { + "0": { + "name": "punctuation.accessor.cs" + } + }, + "end": "(?<=[_[:alnum:]])|(?=[<\\[\\({)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", + "patterns": [ + { + "include": "#intrusive" + }, + { + "match": "\\@?[_[:alpha:]][_[:alnum:]]*", + "name": "entity.name.type.cs" + } + ] + }, + { + "include": "#type-arguments" + }, + { + "include": "#type-array-suffix" + }, + { + "match": "(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+\n(\\g)\\b\\s*", - "beginCaptures": { - "1": { + }, + { + "begin": "(?<=\\})", + "end": "(?=[)}\\],;:?=&|^]|!=|\\b(and|or|when)\\b)", "patterns": [ { - "include": "#type" - } - ] - }, - "2": { - "name": "entity.name.variable.local.cs" - } - }, - "end": "(?==>)", - "patterns": [ - { - "include": "#comment" - }, - { - "include": "#switch-when-clause" - } - ] - }, - "switch-property-expression": { - "begin": "(?x) # e.g. int x OR var x\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?\\s*\n(\\{)", - "beginCaptures": { - "1": { - "patterns": [ + "include": "#intrusive" + }, { - "include": "#type" - } - ] - }, - "6": { - "name": "punctuation.curlybrace.open.cs" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.curlybrace.close.cs" - } - }, - "patterns": [ - { - "include": "#expression" - }, - { - "include": "#punctuation-comma" - } - ] - }, - "switch-var-pattern": { - "begin": "(?x) # match foreach (var (x, y) in ...)\n(?:\\b(var)\\b\\s*)\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s*", - "beginCaptures": { - "1": { - "name": "keyword.other.var.cs" - }, - "2": { - "patterns": [ - { - "include": "#tuple-declaration-deconstruction-element-list" + "include": "#simple-designation-pattern" } ] } - }, - "end": "(?==>)", - "patterns": [ - { - "include": "#comment" - }, - { - "include": "#switch-when-clause" - } ] }, - "switch-when-clause": { - "begin": "(?)", + "subpattern": { "patterns": [ { - "include": "#comment" - }, - { - "include": "#expression" - }, - { - "include": "#punctuation-comma" - }, - { - "match": "\\(", - "captures": { - "0": { - "name": "punctuation.parenthesis.open.cs" - } - } - }, - { - "match": "\\)", - "captures": { - "0": { - "name": "punctuation.parenthesis.close.cs" - } - } - } - ] - }, - "switch-label": { - "patterns": [ - { - "begin": "(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\s+\n\\b(in)\\b", "captures": { "1": { - "name": "keyword.other.var.cs" + "name": "storage.type.var.cs" }, "2": { "patterns": [ @@ -2176,7 +2719,7 @@ "match": "(?x) # match foreach (var (x, y) in ...)\n(?:\\b(var)\\b\\s*)?\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s+\n\\b(in)\\b", "captures": { "1": { - "name": "keyword.other.var.cs" + "name": "storage.type.var.cs" }, "2": { "patterns": [ @@ -2194,9 +2737,6 @@ "include": "#expression" } ] - }, - { - "include": "#statement" } ] }, @@ -2217,7 +2757,7 @@ "begin": "(?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref local\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\s*\n(?!=>)\n(?=,|;|=|\\))", + "begin": "(?x)\n(?:\n (?:(\\bref)\\s+(?:(\\breadonly)\\s+)?)?(\\bvar\\b)| # ref local\n (?\n (?:\n (?:ref\\s+(?:readonly\\s+)?)? # ref local\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*[?*]\\s*)? # nullable or pointer suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\s*\n(?!=>)\n(?=,|;|=|\\))", "beginCaptures": { "1": { - "name": "keyword.other.using.cs" + "name": "storage.modifier.ref.cs" }, "2": { - "name": "storage.modifier.cs" + "name": "storage.modifier.readonly.cs" }, "3": { - "name": "storage.modifier.cs" + "name": "storage.type.var.cs" }, "4": { - "name": "keyword.other.var.cs" - }, - "5": { "patterns": [ { "include": "#type" } ] }, - "10": { + "9": { "name": "entity.name.variable.local.cs" } }, - "end": "(?=;|\\))", + "end": "(?=[;)}])", "patterns": [ { "name": "entity.name.variable.local.cs", @@ -2486,7 +3068,7 @@ "begin": "(?x)\n(?\\b(?:const)\\b)\\s*\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+\n(\\g)\\s*\n(?=,|;|=)", "beginCaptures": { "1": { - "name": "storage.modifier.cs" + "name": "storage.modifier.const.cs" }, "2": { "patterns": [ @@ -2517,9 +3099,49 @@ ] }, "local-function-declaration": { + "begin": "(?x)\n\\b((?:(?:async|unsafe|static|extern)\\s+)*)\n(?\n (?:ref\\s+(?:readonly\\s+)?)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n \\s*(?:,\\s*)* # commata for multi-dimensional arrays\n \\]\n (?:\\s*\\?)? # arrays can be nullable reference types\n )*\n)\\s+\n(\\g)\\s*\n(<[^<>]+>)?\\s*\n(?=\\()", + "beginCaptures": { + "1": { + "patterns": [ + { + "include": "#storage-modifier" + } + ] + }, + "2": { + "patterns": [ + { + "include": "#type" + } + ] + }, + "7": { + "name": "entity.name.function.cs" + }, + "8": { + "patterns": [ + { + "include": "#type-parameter-list" + } + ] + } + }, + "end": "(?<=\\})|(?=;)", "patterns": [ { - "include": "#method-declaration" + "include": "#comment" + }, + { + "include": "#parenthesized-parameter-list" + }, + { + "include": "#generic-constraints" + }, + { + "include": "#expression-body" + }, + { + "include": "#block" } ] }, @@ -2527,7 +3149,7 @@ "begin": "(?x) # e.g. var (x, y) = GetPoint();\n(?:\\b(var)\\b\\s*)\n(?\\((?:[^\\(\\)]|\\g)+\\))\\s*\n(?=;|=|\\))", "beginCaptures": { "1": { - "name": "keyword.other.var.cs" + "name": "storage.type.var.cs" }, "2": { "patterns": [ @@ -2635,7 +3257,7 @@ "match": "(?x) # e.g. int x OR var x\n(?:\n \\b(var)\\b|\n (?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\b\\s*\n(?=[,)\\]])", "captures": { "1": { - "name": "keyword.other.var.cs" + "name": "storage.type.var.cs" }, "2": { "patterns": [ @@ -2653,7 +3275,7 @@ "match": "(?x) # e.g. int x OR var x\n(?:\n \\b(var)\\b|\n (?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n )\n)\\s+\n(\\g)\\b\\s*\n(?=[,)])", "captures": { "1": { - "name": "keyword.other.var.cs" + "name": "storage.type.var.cs" }, "2": { "patterns": [ @@ -2667,16 +3289,13 @@ } } }, - "checked-unchecked-expression": { - "begin": "(?>>?|\\|)?=(?!=|>)", + "beginCaptures": { + "0": { + "patterns": [ + { + "include": "#assignment-operators" + } + ] + } + }, + "end": "(?=[,\\)\\];}])", + "patterns": [ + { + "include": "#ref-modifier" + }, + { + "include": "#expression" + } + ] + }, + "assignment-operators": { "patterns": [ { "name": "keyword.operator.assignment.compound.cs", @@ -3391,11 +4006,19 @@ }, { "name": "keyword.operator.assignment.compound.bitwise.cs", - "match": "\\&=|\\^=|<<=|>>=|\\|=" + "match": "\\&=|\\^=|<<=|>>>?=|\\|=" }, + { + "name": "keyword.operator.assignment.cs", + "match": "\\=" + } + ] + }, + "expression-operators": { + "patterns": [ { "name": "keyword.operator.bitwise.shift.cs", - "match": "<<|>>" + "match": "<<|>>>?" }, { "name": "keyword.operator.comparison.cs", @@ -3413,10 +4036,6 @@ "name": "keyword.operator.bitwise.cs", "match": "\\&|~|\\^|\\|" }, - { - "name": "keyword.operator.assignment.cs", - "match": "\\=" - }, { "name": "keyword.operator.decrement.cs", "match": "--" @@ -3427,56 +4046,50 @@ }, { "name": "keyword.operator.arithmetic.cs", - "match": "%|\\*|/|-|\\+" + "match": "\\+|-(?!>)|\\*|/|%" }, { "name": "keyword.operator.null-coalescing.cs", "match": "\\?\\?" + }, + { + "name": "keyword.operator.range.cs", + "match": "\\.\\." } ] }, - "switch-literal": { - "name": "constant.language.null.cs", - "match": "(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?", + "match": "(?x)\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?(?!\\?))? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n \\s*(?:,\\s*)* # commata for multi-dimensional arrays\n \\]\n (?:\\s*\\?(?!\\?))? # arrays can be nullable reference types\n )*\n )\n)?", "captures": { "1": { - "name": "keyword.other.as.cs" + "name": "keyword.operator.expression.as.cs" }, "2": { "patterns": [ @@ -3556,34 +4169,20 @@ } } }, - "is-expression": { - "match": "(?x)\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?", - "captures": { - "1": { - "name": "keyword.other.is.cs" + "language-variable": { + "patterns": [ + { + "name": "variable.language.$1.cs", + "match": "\\b(base|this)\\b" }, - "2": { - "patterns": [ - { - "include": "#type" - } - ] + { + "name": "variable.other.$1.cs", + "match": "\\b(value)\\b" } - } - }, - "this-or-base-expression": { - "match": "\\b(?:(base)|(this))\\b", - "captures": { - "1": { - "name": "keyword.other.base.cs" - }, - "2": { - "name": "keyword.other.this.cs" - } - } + ] }, "invocation-expression": { - "begin": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(?:(\\.)\\s*)? # preceding dot?\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # method name\n(?\\s*<([^<>]|\\g)+>\\s*)?\\s* # type arguments\n(?=\\() # open paren of argument list", + "begin": "(?x)\n(?:\n (?:(\\?)\\s*)? # preceding null-conditional operator?\n (\\.)\\s*| # preceding dot?\n (->)\\s* # preceding pointer arrow?\n)?\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # method name\n(\n <\n (?\n [^<>()]++|\n <\\g*+>|\n \\(\\g*+\\)\n )*+\n >\\s*\n)? # type arguments\n(?=\\() # open paren of argument list", "beginCaptures": { "1": { "name": "keyword.operator.null-conditional.cs" @@ -3592,9 +4191,12 @@ "name": "punctuation.accessor.cs" }, "3": { - "name": "entity.name.function.cs" + "name": "punctuation.accessor.pointer.cs" }, "4": { + "name": "entity.name.function.cs" + }, + "5": { "patterns": [ { "include": "#type-arguments" @@ -3610,7 +4212,7 @@ ] }, "element-access-expression": { - "begin": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(?:(\\.)\\s*)? # preceding dot?\n(?:(@?[_[:alpha:]][_[:alnum:]]*)\\s*)? # property name\n(?:(\\?)\\s*)? # null-conditional operator?\n(?=\\[) # open bracket of argument list", + "begin": "(?x)\n(?:\n (?:(\\?)\\s*)? # preceding null-conditional operator?\n (\\.)\\s*| # preceding dot?\n (->)\\s* # preceding pointer arrow?\n)?\n(?:(@?[_[:alpha:]][_[:alnum:]]*)\\s*)? # property name\n(?:(\\?)\\s*)? # null-conditional operator?\n(?=\\[) # open bracket of argument list", "beginCaptures": { "1": { "name": "keyword.operator.null-conditional.cs" @@ -3619,9 +4221,12 @@ "name": "punctuation.accessor.cs" }, "3": { - "name": "variable.other.object.property.cs" + "name": "punctuation.accessor.pointer.cs" }, "4": { + "name": "variable.other.object.property.cs" + }, + "5": { "name": "keyword.operator.null-conditional.cs" } }, @@ -3635,7 +4240,7 @@ "member-access-expression": { "patterns": [ { - "match": "(?x)\n(?:(\\?)\\s*)? # preceding null-conditional operator?\n(\\.)\\s* # preceding dot\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # property name\n(?![_[:alnum:]]|\\(|(\\?)?\\[|<) # next character is not alpha-numeric, nor a (, [, or <. Also, test for ?[", + "match": "(?x)\n(?:\n (?:(\\?)\\s*)? # preceding null-conditional operator?\n (\\.)\\s*| # preceding dot?\n (->)\\s* # preceding pointer arrow?\n)\n(@?[_[:alpha:]][_[:alnum:]]*)\\s* # property name\n(?![_[:alnum:]]|\\(|(\\?)?\\[|<) # next character is not alpha-numeric, nor a (, [, or <. Also, test for ?[", "captures": { "1": { "name": "keyword.operator.null-conditional.cs" @@ -3644,6 +4249,9 @@ "name": "punctuation.accessor.cs" }, "3": { + "name": "punctuation.accessor.pointer.cs" + }, + "4": { "name": "variable.other.object.property.cs" } } @@ -3667,7 +4275,7 @@ } }, { - "match": "(?x)\n(@?[_[:alpha:]][_[:alnum:]]*)\n(?=\n (\\s*\\?)?\n \\s*\\.\\s*@?[_[:alpha:]][_[:alnum:]]*\n)", + "match": "(?x)\n(@?[_[:alpha:]][_[:alnum:]]*)\n(?=\n \\s*(?:(?:\\?\\s*)?\\.|->)\n \\s*@?[_[:alpha:]][_[:alnum:]]*\n)", "captures": { "1": { "name": "variable.other.object.cs" @@ -3690,7 +4298,7 @@ "begin": "(?x)\n(new)(?:\\s+\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n))?\\s*\n(?=\\()", "beginCaptures": { "1": { - "name": "keyword.other.new.cs" + "name": "keyword.operator.expression.new.cs" }, "2": { "patterns": [ @@ -3708,10 +4316,10 @@ ] }, "object-creation-expression-with-no-parameters": { - "match": "(?x)\n(new)\\s+\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n(?=\\{|$)", + "match": "(?x)\n(new)\\s+\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n(?=\\{|//|/\\*|$)", "captures": { "1": { - "name": "keyword.other.new.cs" + "name": "keyword.operator.expression.new.cs" }, "2": { "patterns": [ @@ -3726,7 +4334,7 @@ "begin": "(?x)\n\\b(new|stackalloc)\\b\\s*\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?\\s*\n(?=\\[)", "beginCaptures": { "1": { - "name": "keyword.other.new.cs" + "name": "keyword.operator.expression.$1.cs" }, "2": { "patterns": [ @@ -3744,14 +4352,17 @@ ] }, "anonymous-object-creation-expression": { - "begin": "\\b(new)\\b\\s*(?=\\{|$)", + "begin": "\\b(new)\\b\\s*(?=\\{|//|/\\*|$)", "beginCaptures": { "1": { - "name": "keyword.other.new.cs" + "name": "keyword.operator.expression.new.cs" } }, "end": "(?<=\\})", "patterns": [ + { + "include": "#comment" + }, { "include": "#initializer-expression" } @@ -3826,10 +4437,10 @@ ] }, "parameter": { - "match": "(?x)\n(?:(?:\\b(ref|params|out|in|this)\\b)\\s+)?\n(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+\n(\\g)", + "match": "(?x)\n(?:(?:\\b(ref|params|out|in|this)\\b)\\s+)?\n(?\n (?:\n (?:ref\\s+)? # ref return\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^()]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+\n(\\g)", "captures": { "1": { - "name": "storage.modifier.cs" + "name": "storage.modifier.$1.cs" }, "2": { "patterns": [ @@ -3913,11 +4524,25 @@ "argument": { "patterns": [ { - "name": "storage.modifier.cs", - "match": "\\b(ref|out|in)\\b" + "name": "storage.modifier.$1.cs", + "match": "\\b(ref|in)\\b" }, { - "include": "#declaration-expression-local" + "begin": "\\b(out)\\b", + "beginCaptures": { + "1": { + "name": "storage.modifier.out.cs" + } + }, + "end": "(?=,|\\)|\\])", + "patterns": [ + { + "include": "#declaration-expression-local" + }, + { + "include": "#expression" + } + ] }, { "include": "#expression" @@ -3928,7 +4553,7 @@ "begin": "(?x)\n\\b(from)\\b\\s*\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?\n\\s+(\\g)\\b\\s*\n\\b(in)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.from.cs" + "name": "keyword.operator.expression.query.from.cs" }, "2": { "patterns": [ @@ -3941,7 +4566,7 @@ "name": "entity.name.variable.range-variable.cs" }, "8": { - "name": "keyword.query.in.cs" + "name": "keyword.operator.expression.query.in.cs" } }, "end": "(?=;|\\))", @@ -3980,7 +4605,7 @@ "begin": "(?x)\n\\b(let)\\b\\s*\n(@?[_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(=)\\s*", "beginCaptures": { "1": { - "name": "keyword.query.let.cs" + "name": "keyword.operator.expression.query.let.cs" }, "2": { "name": "entity.name.variable.range-variable.cs" @@ -4003,7 +4628,7 @@ "begin": "(?x)\n\\b(where)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.where.cs" + "name": "keyword.operator.expression.query.where.cs" } }, "end": "(?=;|\\))", @@ -4020,7 +4645,7 @@ "begin": "(?x)\n\\b(join)\\b\\s*\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)?\n\\s+(\\g)\\b\\s*\n\\b(in)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.join.cs" + "name": "keyword.operator.expression.query.join.cs" }, "2": { "patterns": [ @@ -4033,7 +4658,7 @@ "name": "entity.name.variable.range-variable.cs" }, "8": { - "name": "keyword.query.in.cs" + "name": "keyword.operator.expression.query.in.cs" } }, "end": "(?=;|\\))", @@ -4059,7 +4684,7 @@ "match": "\\b(on)\\b\\s*", "captures": { "1": { - "name": "keyword.query.on.cs" + "name": "keyword.operator.expression.query.on.cs" } } }, @@ -4067,7 +4692,7 @@ "match": "\\b(equals)\\b\\s*", "captures": { "1": { - "name": "keyword.query.equals.cs" + "name": "keyword.operator.expression.query.equals.cs" } } }, @@ -4075,7 +4700,7 @@ "match": "(?x)\n\\b(into)\\b\\s*\n(@?[_[:alpha:]][_[:alnum:]]*)\\b\\s*", "captures": { "1": { - "name": "keyword.query.into.cs" + "name": "keyword.operator.expression.query.into.cs" }, "2": { "name": "entity.name.variable.range-variable.cs" @@ -4086,7 +4711,7 @@ "begin": "\\b(orderby)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.orderby.cs" + "name": "keyword.operator.expression.query.orderby.cs" } }, "end": "(?=;|\\))", @@ -4106,13 +4731,10 @@ ] }, "ordering-direction": { - "match": "\\b(?:(ascending)|(descending))\\b", + "match": "\\b(ascending|descending)\\b", "captures": { "1": { - "name": "keyword.query.ascending.cs" - }, - "2": { - "name": "keyword.query.descending.cs" + "name": "keyword.operator.expression.query.$1.cs" } } }, @@ -4120,7 +4742,7 @@ "begin": "\\b(select)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.select.cs" + "name": "keyword.operator.expression.query.select.cs" } }, "end": "(?=;|\\))", @@ -4137,7 +4759,7 @@ "begin": "\\b(group)\\b\\s*", "beginCaptures": { "1": { - "name": "keyword.query.group.cs" + "name": "keyword.operator.expression.query.group.cs" } }, "end": "(?=;|\\))", @@ -4160,7 +4782,7 @@ "match": "\\b(by)\\b\\s*", "captures": { "1": { - "name": "keyword.query.by.cs" + "name": "keyword.operator.expression.query.by.cs" } } }, @@ -4168,7 +4790,7 @@ "match": "(?x)\n\\b(into)\\b\\s*\n(@?[_[:alpha:]][_[:alnum:]]*)\\b\\s*", "captures": { "1": { - "name": "keyword.query.into.cs" + "name": "keyword.operator.expression.query.into.cs" }, "2": { "name": "entity.name.variable.range-variable.cs" @@ -4178,119 +4800,138 @@ "anonymous-method-expression": { "patterns": [ { - "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n(@?[_[:alpha:]][_[:alnum:]]*)\\b\\s*\n(=>)", + "begin": "(?x)\n((?:\\b(?:async|static)\\b\\s*)*)\n(?:\n (@?[_[:alpha:]][_[:alnum:]]*)\\b|\n (\\()\n (?(?:[^()]|\\(\\g\\))*)\n (\\))\n)\\s*\n(=>)", "beginCaptures": { "1": { - "name": "storage.modifier.cs" + "patterns": [ + { + "match": "async|static", + "name": "storage.modifier.$0.cs" + } + ] }, "2": { "name": "entity.name.variable.parameter.cs" }, "3": { - "name": "keyword.operator.arrow.cs" - } - }, - "end": "(?=\\)|;|}|,)", - "patterns": [ - { - "include": "#block" + "name": "punctuation.parenthesis.open.cs" }, - { - "include": "#ref-modifier" - }, - { - "include": "#expression" - } - ] - }, - { - "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n(\\(.*?\\))\\s*\n(=>)", - "beginCaptures": { - "1": { - "name": "storage.modifier.cs" - }, - "2": { + "4": { "patterns": [ { - "include": "#lambda-parameter-list" + "include": "#comment" + }, + { + "include": "#explicit-anonymous-function-parameter" + }, + { + "include": "#implicit-anonymous-function-parameter" + }, + { + "include": "#default-argument" + }, + { + "include": "#punctuation-comma" } ] }, - "3": { + "5": { + "name": "punctuation.parenthesis.close.cs" + }, + "6": { "name": "keyword.operator.arrow.cs" } }, - "end": "(?=\\)|;|}|,)", + "end": "(?=[,;)}])", "patterns": [ { - "include": "#block" + "include": "#intrusive" }, { - "include": "#ref-modifier" + "begin": "(?={)", + "end": "(?=[,;)}])", + "patterns": [ + { + "include": "#block" + }, + { + "include": "#intrusive" + } + ] }, { - "include": "#expression" + "begin": "\\b(ref)\\b|(?=\\S)", + "beginCaptures": { + "1": { + "name": "storage.modifier.ref.cs" + } + }, + "end": "(?=[,;)}])", + "patterns": [ + { + "include": "#expression" + } + ] } ] }, { - "begin": "(?x)\n(?:\\b(async)\\b\\s*)?\n(?:\\b(delegate)\\b\\s*)", + "begin": "(?x)\n((?:\\b(?:async|static)\\b\\s*)*)\n\\b(delegate)\\b\\s*", "beginCaptures": { "1": { - "name": "storage.modifier.cs" + "patterns": [ + { + "match": "async|static", + "name": "storage.modifier.$0.cs" + } + ] }, "2": { - "name": "keyword.other.delegate.cs" + "name": "storage.type.delegate.cs" } }, - "end": "(?=\\)|;|}|,)", + "end": "(?<=})|(?=[,;)}])", "patterns": [ { - "include": "#parenthesized-parameter-list" + "include": "#intrusive" + }, + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.parenthesis.open.cs" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.parenthesis.close.cs" + } + }, + "patterns": [ + { + "include": "#intrusive" + }, + { + "include": "#explicit-anonymous-function-parameter" + }, + { + "include": "#punctuation-comma" + } + ] }, { "include": "#block" - }, - { - "include": "#expression" } ] } ] }, - "lambda-parameter-list": { - "begin": "\\(", - "beginCaptures": { - "0": { - "name": "punctuation.parenthesis.open.cs" - } - }, - "end": "\\)", - "endCaptures": { - "0": { - "name": "punctuation.parenthesis.close.cs" - } - }, - "patterns": [ - { - "include": "#comment" - }, - { - "include": "#attribute-section" - }, - { - "include": "#lambda-parameter" - }, - { - "include": "#punctuation-comma" - } - ] - }, - "lambda-parameter": { - "match": "(?x)\n(?:\\b(ref|out|in)\\b)?\\s*\n(?:(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?\\s*<(?:[^<>]|\\g)+>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^\\(\\)]|\\g)+\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s+)?\n(\\g)\\b\\s*\n(?=[,)])", + "explicit-anonymous-function-parameter": { + "match": "(?x)\n(?:\\b(ref|params|out|in)\\b\\s*)?\n(?\n (?:\n (?:\n (?:(?@?[_[:alpha:]][_[:alnum:]]*)\\s*\\:\\:\\s*)? # alias-qualification\n (? # identifier + type arguments (if any)\n \\g\\s*\n (?<(?:[^<>]|\\g)*>\\s*)?\n )\n (?:\\s*\\.\\s*\\g)* | # Are there any more names being dotted into?\n (?\\s*\\((?:[^()]|\\g)*\\))\n )\n (?:\\s*\\?\\s*)? # nullable suffix?\n (?:\\s* # array suffix?\n \\[\n (?:\\s*,\\s*)* # commata for multi-dimensional arrays\n \\]\n \\s*\n (?:\\?)? # arrays can be nullable reference types\n \\s*\n )*\n )\n)\\s*\n\\b(\\g)\\b", "captures": { "1": { - "name": "storage.modifier.cs" + "name": "storage.modifier.$1.cs" }, "2": { "patterns": [ @@ -4304,8 +4945,25 @@ } } }, + "implicit-anonymous-function-parameter": { + "match": "\\@?[_[:alpha:]][_[:alnum:]]*\\b", + "name": "entity.name.variable.parameter.cs" + }, + "default-argument": { + "begin": "=", + "beginCaptures": { + "0": { + "name": "keyword.operator.assignment.cs" + } + }, + "end": "(?=,|\\))", + "patterns": [ + { + "include": "#expression" + } + ] + }, "type": { - "name": "meta.type.cs", "patterns": [ { "include": "#comment" @@ -4333,16 +4991,19 @@ }, { "include": "#type-nullable-suffix" + }, + { + "include": "#type-pointer-suffix" } ] }, "ref-modifier": { - "name": "storage.modifier.cs", - "match": "\\b(ref)\\b" + "name": "storage.modifier.ref.cs", + "match": "\\bref\\b" }, "readonly-modifier": { - "name": "storage.modifier.cs", - "match": "\\b(readonly)\\b" + "name": "storage.modifier.readonly.cs", + "match": "\\breadonly\\b" }, "tuple-type": { "begin": "\\(", @@ -4382,10 +5043,10 @@ } }, "type-builtin": { - "match": "\\b(bool|byte|char|decimal|double|float|int|long|object|sbyte|short|string|uint|ulong|ushort|void|dynamic)\\b", + "match": "\\b(bool|s?byte|u?short|n?u?int|u?long|float|double|decimal|char|string|object|void|dynamic)\\b", "captures": { "1": { - "name": "keyword.type.cs" + "name": "keyword.type.$1.cs" } } }, @@ -4444,9 +5105,6 @@ } }, "patterns": [ - { - "include": "#comment" - }, { "include": "#type" }, @@ -4469,6 +5127,9 @@ } }, "patterns": [ + { + "include": "#intrusive" + }, { "include": "#punctuation-comma" } @@ -4476,11 +5137,11 @@ }, "type-nullable-suffix": { "match": "\\?", - "captures": { - "0": { - "name": "punctuation.separator.question-mark.cs" - } - } + "name": "punctuation.separator.question-mark.cs" + }, + "type-pointer-suffix": { + "match": "\\*", + "name": "punctuation.separator.asterisk.cs" }, "operator-assignment": { "name": "keyword.operator.assignment.cs", @@ -4498,6 +5159,16 @@ "name": "punctuation.accessor.cs", "match": "\\." }, + "intrusive": { + "patterns": [ + { + "include": "#preprocessor" + }, + { + "include": "#comment" + } + ] + }, "preprocessor": { "name": "meta.preprocessor.cs", "begin": "^\\s*(\\#)\\s*", @@ -4803,38 +5474,47 @@ "comment": { "patterns": [ { - "name": "comment.block.cs", - "begin": "/\\*", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.cs" - } - }, - "end": "\\*/", - "endCaptures": { - "0": { - "name": "punctuation.definition.comment.cs" - } - } - }, - { - "begin": "(^\\s+)?(?=//)", - "beginCaptures": { + "name": "comment.block.documentation.cs", + "begin": "(^\\s+)?(///)(?!/)", + "while": "^(\\s*)(///)(?!/)", + "captures": { "1": { "name": "punctuation.whitespace.comment.leading.cs" + }, + "2": { + "name": "punctuation.definition.comment.cs" } }, - "end": "(?=$)", "patterns": [ { - "name": "comment.block.documentation.cs", - "begin": "(??@^|/])///(?!/)", + "while": "(??@^|/])///(?!/)", "patterns": [ { "include": "text.html.markdown" @@ -600,7 +600,7 @@ }, { "name": "comment.line.double-slash.fsharp", - "match": "//.*$" + "match": "(??@^|/])//(?![!%&+-.<=>?@^|]).*$" } ] }, @@ -1125,7 +1125,7 @@ }, { "name": "keyword.symbol.fsharp", - "match": "(&&&|\\|\\|\\||\\^\\^\\^|~~~|<<<|>>>|\\|>|:>|:\\?>|:|\\[|\\]|\\;|<>|=|@|\\|\\||&&|{|}|\\||_|\\.\\.|\\,|\\+|\\-|\\*|\\/|\\^|\\!|\\>|\\>\\=|\\>\\>|\\<|\\<\\=|\\(|\\)|\\<\\<)" + "match": "(&&&|\\|\\|\\||\\^\\^\\^|~~~|~\\+|~\\-|<<<|>>>|\\|>|:>|:\\?>|:|\\[|\\]|\\;|<>|=|@|\\|\\||&&|&|%|{|}|\\||_|\\.\\.|\\,|\\+|\\-|\\*|\\/|\\^|\\!|\\>|\\>\\=|\\>\\>|\\<|\\<\\=|\\(|\\)|\\<\\<)" } ] }, @@ -1133,7 +1133,7 @@ "patterns": [ { "name": "entity.name.section.fsharp", - "begin": "\\b(namespace global)|\\b(namespace|module)\\s*(public|internal|private|rec)?\\s+([[:alpha:]][[:alpha:]0-9'_. ]*)", + "begin": "\\b(namespace global)|\\b(namespace|module)\\s*(public|internal|private|rec)?\\s+([[:alpha:]|``][[:alpha:]0-9'_. ]*)", "end": "(\\s?=|\\s|$)", "beginCaptures": { "1": { @@ -1171,7 +1171,7 @@ }, { "name": "namespace.open.fsharp", - "begin": "\\b(open type|open)\\s+([[:alpha:]][[:alpha:]0-9'_]*)(?=(\\.[A-Z][[:alpha:]0-9_]*)*)", + "begin": "\\b(open type|open)\\s+([[:alpha:]|``][[:alpha:]0-9'_]*)(?=(\\.[A-Z][[:alpha:]0-9_]*)*)", "end": "(\\s|$)", "beginCaptures": { "1": { @@ -1331,7 +1331,7 @@ "match": "\\(\\)" }, { - "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)", + "match": "(\\?{0,1})(``[[:alpha:]0-9'`^:,._ ]+``|(?!private|struct\\b)\\b[\\w[:alpha:]0-9'`<>^._ ]+)", "captures": { "1": { "name": "keyword.symbol.fsharp" diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 250008ee311..4632cd83bb3 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -602,8 +602,8 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } async openParentRepository(repoPath: string): Promise { - await this.openRepository(repoPath); this._parentRepositoriesManager.openRepository(repoPath); + await this.openRepository(repoPath); } private async getRepositoryRoot(repoPath: string): Promise<{ repositoryRoot: string; unsafeRepositoryMatch: RegExpMatchArray | null }> { diff --git a/extensions/r/syntaxes/r.tmLanguage.json b/extensions/r/syntaxes/r.tmLanguage.json index 88de4fd07d9..d50ab5b6d78 100644 --- a/extensions/r/syntaxes/r.tmLanguage.json +++ b/extensions/r/syntaxes/r.tmLanguage.json @@ -639,4 +639,4 @@ ] } } -} +} \ No newline at end of file diff --git a/extensions/razor/build/update-grammar.mjs b/extensions/razor/build/update-grammar.mjs index db0ac9dbd6f..cacb9e79abc 100644 --- a/extensions/razor/build/update-grammar.mjs +++ b/extensions/razor/build/update-grammar.mjs @@ -12,7 +12,7 @@ function patchGrammar(grammar) { } const razorGrammarRepo = 'dotnet/razor'; -const grammarPath = 'src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension/syntaxes/aspnetcorerazor.tmLanguage.json'; +const grammarPath = 'src/Razor/src/Microsoft.VisualStudio.RazorExtension/EmbeddedGrammars/aspnetcorerazor.tmLanguage.json'; vscodeGrammarUpdater.update(razorGrammarRepo, grammarPath, './syntaxes/cshtml.tmLanguage.json', grammar => patchGrammar(grammar), 'main'); diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index 799e12cf325..e90c7d75d8c 100644 --- a/extensions/razor/cgmanifest.json +++ b/extensions/razor/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/razor", "repositoryUrl": "https://github.com/dotnet/razor", - "commitHash": "69f60231df08319b544d3d32a588575acbb58ff0" + "commitHash": "b44d0a906d054d2d343adc3f58cbea11d97d7488" } }, "license": "MIT", diff --git a/extensions/razor/syntaxes/cshtml.tmLanguage.json b/extensions/razor/syntaxes/cshtml.tmLanguage.json index 0b5463ee3ca..4594037960a 100644 --- a/extensions/razor/syntaxes/cshtml.tmLanguage.json +++ b/extensions/razor/syntaxes/cshtml.tmLanguage.json @@ -1,10 +1,10 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/dotnet/razor/blob/master/src/Razor/src/Microsoft.AspNetCore.Razor.VSCode.Extension/syntaxes/aspnetcorerazor.tmLanguage.json", + "This file has been converted from https://github.com/dotnet/razor/blob/master/src/Razor/src/Microsoft.VisualStudio.RazorExtension/EmbeddedGrammars/aspnetcorerazor.tmLanguage.json", "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/dotnet/razor/commit/69f60231df08319b544d3d32a588575acbb58ff0", + "version": "https://github.com/dotnet/razor/commit/b44d0a906d054d2d343adc3f58cbea11d97d7488", "name": "ASP.NET Razor", "scopeName": "text.html.cshtml", "patterns": [ diff --git a/extensions/rust/cgmanifest.json b/extensions/rust/cgmanifest.json index 512dc80a65b..46bf27cd454 100644 --- a/extensions/rust/cgmanifest.json +++ b/extensions/rust/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "rust-syntax", "repositoryUrl": "https://github.com/dustypomerleau/rust-syntax", - "commitHash": "328a68299533bc2b8c71028be741cce78a9e0d53" + "commitHash": "20730dff3c367cb40a7edd278fdaf0239ea50833" } }, "license": "MIT", diff --git a/extensions/rust/syntaxes/rust.tmLanguage.json b/extensions/rust/syntaxes/rust.tmLanguage.json index 8fcbdc5082f..542e8ec1fd1 100644 --- a/extensions/rust/syntaxes/rust.tmLanguage.json +++ b/extensions/rust/syntaxes/rust.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/dustypomerleau/rust-syntax/commit/328a68299533bc2b8c71028be741cce78a9e0d53", + "version": "https://github.com/dustypomerleau/rust-syntax/commit/20730dff3c367cb40a7edd278fdaf0239ea50833", "name": "Rust", "scopeName": "source.rust", "patterns": [ @@ -119,54 +119,6 @@ } } }, - { - "comment": "attributes", - "name": "meta.attribute.rust", - "begin": "(#)(\\!?)(\\[)", - "beginCaptures": { - "1": { - "name": "punctuation.definition.attribute.rust" - }, - "2": { - "name": "keyword.operator.attribute.inner.rust" - }, - "3": { - "name": "punctuation.brackets.attribute.rust" - } - }, - "end": "\\]", - "endCaptures": { - "0": { - "name": "punctuation.brackets.attribute.rust" - } - }, - "patterns": [ - { - "include": "#block-comments" - }, - { - "include": "#comments" - }, - { - "include": "#keywords" - }, - { - "include": "#lifetimes" - }, - { - "include": "#punctuation" - }, - { - "include": "#strings" - }, - { - "include": "#gtypes" - }, - { - "include": "#types" - } - ] - }, { "comment": "modules", "match": "(mod)\\s+((?:r#(?!crate|[Ss]elf|super))?[a-z][A-Za-z0-9_]*)", @@ -257,6 +209,9 @@ { "include": "#comments" }, + { + "include": "#attributes" + }, { "include": "#lvariables" }, @@ -421,7 +376,7 @@ "escapes": { "comment": "escapes: ASCII, byte, Unicode, quote, regex", "name": "constant.character.escape.rust", - "match": "(\\\\)(?:(?:(x[0-7][0-7a-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", + "match": "(\\\\)(?:(?:(x[0-7][\\da-fA-F])|(u(\\{)[\\da-fA-F]{4,6}(\\}))|.))", "captures": { "1": { "name": "constant.character.escape.backslash.rust" @@ -440,6 +395,51 @@ } } }, + "attributes": { + "comment": "attributes", + "name": "meta.attribute.rust", + "begin": "(#)(\\!?)(\\[)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.attribute.rust" + }, + "3": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "end": "\\]", + "endCaptures": { + "0": { + "name": "punctuation.brackets.attribute.rust" + } + }, + "patterns": [ + { + "include": "#block-comments" + }, + { + "include": "#comments" + }, + { + "include": "#keywords" + }, + { + "include": "#lifetimes" + }, + { + "include": "#punctuation" + }, + { + "include": "#strings" + }, + { + "include": "#gtypes" + }, + { + "include": "#types" + } + ] + }, "functions": { "patterns": [ { @@ -548,6 +548,9 @@ { "include": "#comments" }, + { + "include": "#attributes" + }, { "include": "#keywords" }, @@ -608,6 +611,9 @@ { "include": "#comments" }, + { + "include": "#attributes" + }, { "include": "#keywords" }, @@ -913,7 +919,7 @@ }, { "comment": "parameterized types", - "begin": "\\b([A-Z][A-Za-z0-9]*)(<)", + "begin": "\\b(_?[A-Z][A-Za-z0-9_]*)(<)", "beginCaptures": { "1": { "name": "entity.name.type.rust" @@ -962,7 +968,7 @@ }, { "comment": "trait declarations", - "match": "\\b(trait)\\s+([A-Z][A-Za-z0-9]*)\\b", + "match": "\\b(trait)\\s+(_?[A-Z][A-Za-z0-9_]*)\\b", "captures": { "1": { "name": "keyword.declaration.trait.rust storage.type.rust" @@ -974,7 +980,7 @@ }, { "comment": "struct declarations", - "match": "\\b(struct)\\s+([A-Z][A-Za-z0-9]*)\\b", + "match": "\\b(struct)\\s+(_?[A-Z][A-Za-z0-9_]*)\\b", "captures": { "1": { "name": "keyword.declaration.struct.rust storage.type.rust" @@ -986,7 +992,7 @@ }, { "comment": "enum declarations", - "match": "\\b(enum)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "match": "\\b(enum)\\s+(_?[A-Z][A-Za-z0-9_]*)\\b", "captures": { "1": { "name": "keyword.declaration.enum.rust storage.type.rust" @@ -998,7 +1004,7 @@ }, { "comment": "type declarations", - "match": "\\b(type)\\s+([A-Z][A-Za-z0-9_]*)\\b", + "match": "\\b(type)\\s+(_?[A-Z][A-Za-z0-9_]*)\\b", "captures": { "1": { "name": "keyword.declaration.type.rust storage.type.rust" @@ -1011,7 +1017,7 @@ { "comment": "types", "name": "entity.name.type.rust", - "match": "\\b[A-Z][A-Za-z0-9]*\\b(?!!)" + "match": "\\b_?[A-Z][A-Za-z0-9_]*\\b(?!!)" } ] }, diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 850a0855a8d..33e66196742 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -1067,6 +1067,12 @@ "markdownDescription": "%typescript.preferences.autoImportFileExcludePatterns%", "scope": "resource" }, + "typescript.preferences.preferTypeOnlyAutoImports": { + "type": "boolean", + "default": false, + "markdownDescription": "%typescript.preferences.preferTypeOnlyAutoImports%", + "scope": "resource" + }, "javascript.preferences.renameShorthandProperties": { "type": "boolean", "default": true, diff --git a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts index e3ef52002a5..3162410275e 100644 --- a/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts +++ b/extensions/typescript-language-features/src/languageFeatures/fileConfigurationManager.ts @@ -191,6 +191,8 @@ export default class FileConfigurationManager extends Disposable { includeCompletionsWithClassMemberSnippets: config.get('suggest.classMemberSnippets.enabled', true), includeCompletionsWithObjectLiteralMethodSnippets: config.get('suggest.objectLiteralMethodSnippets.enabled', true), autoImportFileExcludePatterns: this.getAutoImportFileExcludePatternsPreference(preferencesConfig, vscode.workspace.getWorkspaceFolder(document.uri)?.uri), + // @ts-expect-error until 5.3 #56090 + preferTypeOnlyAutoImports: config.get('preferTypeOnlyAutoImports', false), useLabelDetailsInCompletionEntries: true, allowIncompleteCompletions: true, displayPartsForJSDoc: true, diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test-166781.rs b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-166781.rs new file mode 100644 index 00000000000..3347f5e6ac7 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-166781.rs @@ -0,0 +1,5 @@ +let _ = b"\ +\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\ +\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\ +\n +"; diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-166781_rs.json b/extensions/vscode-colorize-tests/test/colorize-results/test-166781_rs.json new file mode 100644 index 00000000000..c5b1c10ad35 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-166781_rs.json @@ -0,0 +1,1318 @@ +[ + { + "c": "let", + "t": "source.rust keyword.other.rust storage.type.rust", + "r": { + "dark_modern": "storage.type: #569CD6", + "light_modern": "storage.type: #0000FF", + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_modern": "default: #CCCCCC", + "light_modern": "default: #3B3B3B", + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "_", + "t": "source.rust variable.other.rust", + "r": { + "dark_modern": "variable: #9CDCFE", + "light_modern": "variable: #001080", + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "hc_light": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_modern": "default: #CCCCCC", + "light_modern": "default: #3B3B3B", + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "=", + "t": "source.rust keyword.operator.assignment.equal.rust", + "r": { + "dark_modern": "keyword.operator: #D4D4D4", + "light_modern": "keyword.operator: #000000", + "dark_plus": "keyword.operator: #D4D4D4", + "light_plus": "keyword.operator: #000000", + "dark_vs": "keyword.operator: #D4D4D4", + "light_vs": "keyword.operator: #000000", + "hc_black": "keyword.operator: #D4D4D4", + "hc_light": "keyword.operator: #000000" + } + }, + { + "c": " ", + "t": "source.rust", + "r": { + "dark_modern": "default: #CCCCCC", + "light_modern": "default: #3B3B3B", + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + }, + { + "c": "b", + "t": "source.rust string.quoted.double.rust string.quoted.byte.raw.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x00", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x01", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x02", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x03", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x04", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x05", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x06", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x07", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x08", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x09", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0a", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0b", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0c", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0d", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0e", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x0f", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a0", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a1", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a2", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a3", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a4", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a5", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a6", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a7", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a8", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "a9", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "aa", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "ab", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "ac", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "ad", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "ae", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "x", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "af\\", + "t": "source.rust string.quoted.double.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": "\\", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.backslash.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "n", + "t": "source.rust string.quoted.double.rust constant.character.escape.rust constant.character.escape.bit.rust", + "r": { + "dark_modern": "constant.character.escape: #D7BA7D", + "light_modern": "constant.character.escape: #EE0000", + "dark_plus": "constant.character.escape: #D7BA7D", + "light_plus": "constant.character.escape: #EE0000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "constant.character: #569CD6", + "hc_light": "constant.character.escape: #EE0000" + } + }, + { + "c": "\"", + "t": "source.rust string.quoted.double.rust punctuation.definition.string.rust", + "r": { + "dark_modern": "string: #CE9178", + "light_modern": "string: #A31515", + "dark_plus": "string: #CE9178", + "light_plus": "string: #A31515", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", + "hc_black": "string: #CE9178", + "hc_light": "string: #0F4A85" + } + }, + { + "c": ";", + "t": "source.rust punctuation.semi.rust", + "r": { + "dark_modern": "default: #CCCCCC", + "light_modern": "default: #3B3B3B", + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "hc_light": "default: #292929" + } + } +] diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_cs.json b/extensions/vscode-colorize-tests/test/colorize-results/test_cs.json index bf42bc29b6d..7f4bbb7758b 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_cs.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_cs.json @@ -1,16 +1,16 @@ [ { "c": "using", - "t": "source.cs keyword.other.using.cs", + "t": "source.cs keyword.other.directive.using.cs", "r": { - "dark_plus": "keyword.other.using: #C586C0", - "light_plus": "keyword.other.using: #AF00DB", + "dark_plus": "keyword.other.directive.using: #C586C0", + "light_plus": "keyword.other.directive.using: #AF00DB", "dark_vs": "keyword: #569CD6", "light_vs": "keyword: #0000FF", - "hc_black": "keyword.other.using: #C586C0", - "dark_modern": "keyword.other.using: #C586C0", - "hc_light": "keyword.other.using: #B5200D", - "light_modern": "keyword.other.using: #AF00DB" + "hc_black": "keyword.other.directive.using: #C586C0", + "dark_modern": "keyword.other.directive.using: #C586C0", + "hc_light": "keyword.other.directive.using: #B5200D", + "light_modern": "keyword.other.directive.using: #AF00DB" } }, { @@ -57,16 +57,16 @@ }, { "c": "namespace", - "t": "source.cs keyword.other.namespace.cs", + "t": "source.cs storage.type.namespace.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -127,16 +127,16 @@ }, { "c": "class", - "t": "source.cs keyword.other.class.cs", + "t": "source.cs storage.type.class.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -211,7 +211,7 @@ }, { "c": "static", - "t": "source.cs storage.modifier.cs", + "t": "source.cs storage.modifier.static.cs", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -239,7 +239,7 @@ }, { "c": "void", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.void.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -295,7 +295,7 @@ }, { "c": "string", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.string.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -925,7 +925,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1303,7 +1303,7 @@ }, { "c": "const", - "t": "source.cs storage.modifier.cs", + "t": "source.cs storage.modifier.const.cs", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1331,7 +1331,7 @@ }, { "c": "double", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.double.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1527,7 +1527,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1681,7 +1681,7 @@ }, { "c": "double", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.double.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -1933,16 +1933,16 @@ }, { "c": " ", - "t": "source.cs punctuation.whitespace.comment.leading.cs", + "t": "source.cs comment.line.double-slash.cs punctuation.whitespace.comment.leading.cs", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { @@ -2255,7 +2255,7 @@ }, { "c": "public", - "t": "source.cs storage.modifier.cs", + "t": "source.cs storage.modifier.public.cs", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -2283,7 +2283,7 @@ }, { "c": "void", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.void.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -2451,16 +2451,16 @@ }, { "c": "new", - "t": "source.cs keyword.other.new.cs", + "t": "source.cs keyword.operator.expression.new.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" } }, { @@ -2507,7 +2507,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -2619,7 +2619,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -2759,16 +2759,16 @@ }, { "c": "new", - "t": "source.cs keyword.other.new.cs", + "t": "source.cs keyword.operator.expression.new.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" } }, { @@ -2815,7 +2815,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -2927,7 +2927,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", @@ -3025,16 +3025,16 @@ }, { "c": "new", - "t": "source.cs keyword.other.new.cs", + "t": "source.cs keyword.operator.expression.new.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" } }, { @@ -3081,7 +3081,7 @@ }, { "c": "int", - "t": "source.cs keyword.type.cs", + "t": "source.cs keyword.type.int.cs", "r": { "dark_plus": "keyword: #569CD6", "light_plus": "keyword: #0000FF", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json b/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json index 5923b8411fe..4307f03ed1e 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_cshtml.json @@ -43,16 +43,16 @@ }, { "c": "var", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs storage.type.var.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -169,16 +169,16 @@ }, { "c": "var", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs storage.type.var.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -505,16 +505,16 @@ }, { "c": " ", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.whitespace.comment.leading.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs punctuation.whitespace.comment.leading.cs", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { @@ -561,16 +561,16 @@ }, { "c": "var", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock storage.type.var.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -757,16 +757,16 @@ }, { "c": "var", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock keyword.other.var.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock storage.type.var.cs", "r": { - "dark_plus": "keyword: #569CD6", - "light_plus": "keyword: #0000FF", - "dark_vs": "keyword: #569CD6", - "light_vs": "keyword: #0000FF", - "hc_black": "keyword: #569CD6", - "dark_modern": "keyword: #569CD6", - "hc_light": "keyword: #0F4A85", - "light_modern": "keyword: #0000FF" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { @@ -939,16 +939,16 @@ }, { "c": " ", - "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock punctuation.whitespace.comment.leading.cs", + "t": "text.html.cshtml meta.structure.razor.codeblock source.cs meta.statement.if.razor meta.structure.razor.csharp.codeblock comment.line.double-slash.cs punctuation.whitespace.comment.leading.cs", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "comment: #6A9955", + "light_plus": "comment: #008000", + "dark_vs": "comment: #6A9955", + "light_vs": "comment: #008000", + "hc_black": "comment: #7CA668", + "dark_modern": "comment: #6A9955", + "hc_light": "comment: #515151", + "light_modern": "comment: #008000" } }, { @@ -4535,4 +4535,4 @@ "light_modern": "punctuation.definition.tag: #800000" } } -] +] \ No newline at end of file diff --git a/package.json b/package.json index 42feba8d1d1..d850ee9300e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.84.0", + "version": "1.85.0", "distro": "ff0198cd90b25ba7ca853279cea9b8bb3cf5164d", "author": { "name": "Microsoft Corporation" @@ -208,7 +208,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.3.0-dev.20231002", + "typescript": "^5.3.0-dev.20231030", "typescript-formatter": "7.1.0", "vscode-nls-dev": "^3.3.1", "webpack": "^5.77.0", @@ -227,4 +227,4 @@ "optionalDependencies": { "windows-foreground-love": "0.5.0" } -} \ No newline at end of file +} diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 30a25285bc5..13ff65cfb4e 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -12,10 +12,11 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import * as event from 'vs/base/common/event'; import * as dompurify from 'vs/base/browser/dompurify/dompurify'; import { KeyCode } from 'vs/base/common/keyCodes'; -import { Disposable, DisposableStore, IDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { FileAccess, RemoteAuthorities, Schemas } from 'vs/base/common/network'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; +import { hash } from 'vs/base/common/hash'; export const { registerWindow, getWindows, getWindowsCount, onDidRegisterWindow, onWillUnregisterWindow, onDidUnregisterWindow } = (function () { const windows = new Set([window]); @@ -740,11 +741,23 @@ export function isAncestorOfActiveElement(ancestor: Element): boolean { return isAncestor(ancestor.ownerDocument.activeElement, ancestor); } +/** + * Returns whether the element is in the active `document`. + */ +export function isActiveDocument(element: Element): boolean { + return element.ownerDocument === getActiveDocument(); +} + /** * Returns the active document across all child windows. - * Use this instead of `document` when reacting to dom events to handle multiple windows. + * Use this instead of `document` when reacting to dom + * events to handle multiple windows. */ export function getActiveDocument(): Document { + if (getWindowsCount() <= 1) { + return document; + } + const documents = Array.from(getWindows()).map(window => window.document); return documents.find(doc => doc.hasFocus()) ?? document; } @@ -778,44 +791,47 @@ export function focusWindow(element: Node): void { } } -const globalStylesheets = new Map>(); - -export function createStyleSheet(container: HTMLElement = document.head, beforeAppend?: (style: HTMLStyleElement) => void): HTMLStyleElement { +export function createStyleSheet(container: HTMLElement = document.head, beforeAppend?: (style: HTMLStyleElement) => void, disposableStore?: DisposableStore): HTMLStyleElement { const style = document.createElement('style'); style.type = 'text/css'; style.media = 'screen'; beforeAppend?.(style); container.appendChild(style); + if (disposableStore) { + disposableStore.add(toDisposable(() => container.removeChild(style))); + } + // With as container, the stylesheet becomes global and is tracked // to support auxiliary windows to clone the stylesheet. if (container === document.head) { - const clonedGlobalStylesheets = new Set(); - globalStylesheets.set(style, clonedGlobalStylesheets); - for (const targetWindow of getWindows()) { if (targetWindow === window) { continue; // main window is already tracked } - const disposable = cloneGlobalStyleSheet(style, targetWindow); + const cloneDisposable = cloneGlobalStyleSheet(style, targetWindow); + disposableStore?.add(cloneDisposable); - event.Event.once(onDidUnregisterWindow)(unregisteredWindow => { + disposableStore?.add(event.Event.once(onDidUnregisterWindow)(unregisteredWindow => { if (unregisteredWindow === targetWindow) { - disposable.dispose(); + cloneDisposable.dispose(); } - }); + })); } + } return style; } +const globalStylesheets = new Map>(); + export function isGlobalStylesheet(node: Node): boolean { return globalStylesheets.has(node as HTMLStyleElement); } -export function cloneGlobalStylesheets(targetWindow: Window & typeof globalThis): IDisposable { +export function cloneGlobalStylesheets(targetWindow: Window): IDisposable { const disposables = new DisposableStore(); for (const [globalStylesheet] of globalStylesheets) { @@ -825,29 +841,85 @@ export function cloneGlobalStylesheets(targetWindow: Window & typeof globalThis) return disposables; } -function cloneGlobalStyleSheet(globalStylesheet: HTMLStyleElement, targetWindow: Window & typeof globalThis): IDisposable { +function cloneGlobalStyleSheet(globalStylesheet: HTMLStyleElement, targetWindow: Window): IDisposable { + const disposables = new DisposableStore(); + const clone = globalStylesheet.cloneNode(true) as HTMLStyleElement; targetWindow.document.head.appendChild(clone); + disposables.add(toDisposable(() => targetWindow.document.head.removeChild(clone))); for (const rule of getDynamicStyleSheetRules(globalStylesheet)) { clone.sheet?.insertRule(rule.cssText, clone.sheet?.cssRules.length); } - const observer = new MutationObserver(() => { + disposables.add(sharedMutationObserver.observe(globalStylesheet, disposables, { childList: true })(() => { clone.textContent = globalStylesheet.textContent; - }); - observer.observe(globalStylesheet, { childList: true }); + })); - globalStylesheets.get(globalStylesheet)?.add(clone); + let clonedGlobalStylesheets = globalStylesheets.get(globalStylesheet); + if (!clonedGlobalStylesheets) { + clonedGlobalStylesheets = new Set(); + globalStylesheets.set(globalStylesheet, clonedGlobalStylesheets); + } + clonedGlobalStylesheets.add(clone); + disposables.add(toDisposable(() => clonedGlobalStylesheets?.delete(clone))); - return toDisposable(() => { - observer.disconnect(); - targetWindow.document.head.removeChild(clone); - - globalStylesheets.get(globalStylesheet)?.delete(clone); - }); + return disposables; } +interface IMutationObserver { + users: number; + readonly observer: MutationObserver; + readonly onDidMutate: event.Event; +} + +export const sharedMutationObserver = new class { + + readonly mutationObservers = new Map>(); + + observe(target: Node, disposables: DisposableStore, options?: MutationObserverInit): event.Event { + let mutationObserversPerTarget = this.mutationObservers.get(target); + if (!mutationObserversPerTarget) { + mutationObserversPerTarget = new Map(); + this.mutationObservers.set(target, mutationObserversPerTarget); + } + + const optionsHash = hash(options); + let mutationObserverPerOptions = mutationObserversPerTarget.get(optionsHash); + if (!mutationObserverPerOptions) { + const onDidMutate = new event.Emitter(); + const observer = new MutationObserver(mutations => onDidMutate.fire(mutations)); + observer.observe(target, options); + + const resolvedMutationObserverPerOptions = mutationObserverPerOptions = { + users: 1, + observer, + onDidMutate: onDidMutate.event + }; + + disposables.add(toDisposable(() => { + resolvedMutationObserverPerOptions.users -= 1; + + if (resolvedMutationObserverPerOptions.users === 0) { + onDidMutate.dispose(); + observer.disconnect(); + + mutationObserversPerTarget?.delete(optionsHash); + if (mutationObserversPerTarget?.size === 0) { + this.mutationObservers.delete(target); + } + } + })); + + mutationObserversPerTarget.set(optionsHash, mutationObserverPerOptions); + } else { + mutationObserverPerOptions.users += 1; + } + + return mutationObserverPerOptions.onDidMutate; + } +}; + export function createMetaElement(container: HTMLElement = document.head): HTMLMetaElement { const meta = document.createElement('meta'); container.appendChild(meta); @@ -2085,35 +2157,6 @@ function camelCaseToHyphenCase(str: string) { return str.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase(); } -interface IObserver extends IDisposable { - readonly onDidChangeAttribute: event.Event; -} - -function observeAttributes(element: Element, filter?: string[]): IObserver { - const onDidChangeAttribute = new event.Emitter(); - - const observer = new MutationObserver(mutations => { - for (const mutation of mutations) { - if (mutation.type === 'attributes' && mutation.attributeName) { - onDidChangeAttribute.fire(mutation.attributeName); - } - } - }); - - observer.observe(element, { - attributes: true, - attributeFilter: filter - }); - - return { - onDidChangeAttribute: onDidChangeAttribute.event, - dispose: () => { - observer.disconnect(); - onDidChangeAttribute.dispose(); - } - }; -} - export function copyAttributes(from: Element, to: Element): void { for (const { name, value } of from.attributes) { to.setAttribute(name, value); @@ -2132,10 +2175,15 @@ function copyAttribute(from: Element, to: Element, name: string): void { export function trackAttributes(from: Element, to: Element, filter?: string[]): IDisposable { copyAttributes(from, to); - const observer = observeAttributes(from, filter); + const disposables = new DisposableStore(); - return combinedDisposable( - observer, - observer.onDidChangeAttribute(name => copyAttribute(from, to, name)) - ); + disposables.add(sharedMutationObserver.observe(from, disposables, { attributes: true, attributeFilter: filter })(mutations => { + for (const mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName) { + copyAttribute(from, to, mutation.attributeName); + } + } + })); + + return disposables; } diff --git a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts index d0ae5a5ff0e..2fd7a08808a 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/globals.ts +++ b/src/vs/base/parts/sandbox/electron-sandbox/globals.ts @@ -121,31 +121,11 @@ export const webFrame: WebFrame = globals.vscode.webFrame; export const process: ISandboxNodeProcess = globals.vscode.process; export const context: ISandboxContext = globals.vscode.context; -export interface IGlobalsSlim { +/** + * A set of globals that are available in all windows that either + * depend on `preload.js` or `preload-aux.js`. + */ +export interface ISandboxGlobals { readonly ipcRenderer: Pick; readonly webFrame: import('vs/base/parts/sandbox/electron-sandbox/electronTypes').WebFrame; } - -/** - * Get the globals that are available in the given window. Since - * this method supports auxiliary windows, only a subset of globals - * is returned. - */ -export function getGlobals(win: Window): IGlobalsSlim | undefined { - if (win === window) { - return { ipcRenderer, webFrame }; - } - - const auxiliaryWindowCandidate = win as unknown as { - vscode: { - ipcRenderer: Pick; - webFrame: import('vs/base/parts/sandbox/electron-sandbox/electronTypes').WebFrame; - }; - }; - - if (auxiliaryWindowCandidate?.vscode?.ipcRenderer && auxiliaryWindowCandidate?.vscode?.webFrame) { - return auxiliaryWindowCandidate.vscode; - } - - return undefined; -} diff --git a/src/vs/base/parts/sandbox/electron-sandbox/preload-slim.js b/src/vs/base/parts/sandbox/electron-sandbox/preload-aux.js similarity index 93% rename from src/vs/base/parts/sandbox/electron-sandbox/preload-slim.js rename to src/vs/base/parts/sandbox/electron-sandbox/preload-aux.js index 7b6256756ac..c73cabcdfa5 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/preload-slim.js +++ b/src/vs/base/parts/sandbox/electron-sandbox/preload-aux.js @@ -46,12 +46,12 @@ /** * @param {string} channel * @param {any[]} args - * @returns {Promise | never} + * @returns {Promise} */ invoke(channel, ...args) { - if (validateIPC(channel)) { - return ipcRenderer.invoke(channel, ...args); - } + validateIPC(channel); + + return ipcRenderer.invoke(channel, ...args); } }, diff --git a/src/vs/base/parts/sandbox/electron-sandbox/preload.js b/src/vs/base/parts/sandbox/electron-sandbox/preload.js index 0494b7ddda7..90ac940861f 100644 --- a/src/vs/base/parts/sandbox/electron-sandbox/preload.js +++ b/src/vs/base/parts/sandbox/electron-sandbox/preload.js @@ -56,24 +56,23 @@ } try { - if (validateIPC(windowConfigIpcChannel)) { + validateIPC(windowConfigIpcChannel); - // Resolve configuration from electron-main - configuration = await ipcRenderer.invoke(windowConfigIpcChannel); + // Resolve configuration from electron-main + const resolvedConfiguration = configuration = await ipcRenderer.invoke(windowConfigIpcChannel); - // Apply `userEnv` directly - Object.assign(process.env, configuration.userEnv); + // Apply `userEnv` directly + Object.assign(process.env, resolvedConfiguration.userEnv); - // Apply zoom level early before even building the - // window DOM elements to avoid UI flicker. We always - // have to set the zoom level from within the window - // because Chrome has it's own way of remembering zoom - // settings per origin (if vscode-file:// is used) and - // we want to ensure that the user configuration wins. - webFrame.setZoomLevel(configuration.zoomLevel ?? 0); + // Apply zoom level early before even building the + // window DOM elements to avoid UI flicker. We always + // have to set the zoom level from within the window + // because Chrome has it's own way of remembering zoom + // settings per origin (if vscode-file:// is used) and + // we want to ensure that the user configuration wins. + webFrame.setZoomLevel(resolvedConfiguration.zoomLevel ?? 0); - return configuration; - } + return resolvedConfiguration; } catch (error) { throw new Error(`Preload: unable to fetch vscode-window-config: ${error}`); } @@ -145,51 +144,51 @@ /** * @param {string} channel * @param {any[]} args - * @returns {Promise | never} + * @returns {Promise} */ invoke(channel, ...args) { - if (validateIPC(channel)) { - return ipcRenderer.invoke(channel, ...args); - } + validateIPC(channel); + + return ipcRenderer.invoke(channel, ...args); }, /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer | never} + * @returns {IpcRenderer} */ on(channel, listener) { - if (validateIPC(channel)) { - ipcRenderer.on(channel, listener); + validateIPC(channel); - return this; - } + ipcRenderer.on(channel, listener); + + return this; }, /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer | never} + * @returns {IpcRenderer} */ once(channel, listener) { - if (validateIPC(channel)) { - ipcRenderer.once(channel, listener); + validateIPC(channel); - return this; - } + ipcRenderer.once(channel, listener); + + return this; }, /** * @param {string} channel * @param {(event: IpcRendererEvent, ...args: any[]) => void} listener - * @returns {IpcRenderer | never} + * @returns {IpcRenderer} */ removeListener(channel, listener) { - if (validateIPC(channel)) { - ipcRenderer.removeListener(channel, listener); + validateIPC(channel); - return this; - } + ipcRenderer.removeListener(channel, listener); + + return this; } }, diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 5eef587493f..6cb6877f8b3 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -449,8 +449,6 @@ export class CodeApplication extends Disposable { //#region Bootstrap IPC Handlers - validatedIpcMain.handle('vscode:getWindowId', event => Promise.resolve(event.sender.id)); - validatedIpcMain.handle('vscode:fetchShellEnv', event => { // Prefer to use the args and env from the target window diff --git a/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts index da67be66109..23af4d227b9 100644 --- a/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/storageDataCleaner.ts @@ -52,7 +52,7 @@ export class UnusedWorkspaceStorageDataCleaner extends Disposable { return; // keep workspace storage for empty extension development workspaces } - const windows = await this.nativeHostService.getWindows(); + const windows = await this.nativeHostService.getWindows({ includeAuxiliaryWindows: false }); if (windows.some(window => window.workspace?.id === workspaceStorageFolder)) { return; // keep workspace storage for empty workspaces opened as window } diff --git a/src/vs/editor/browser/editorBrowser.ts b/src/vs/editor/browser/editorBrowser.ts index cebc4712635..4e840e837ff 100644 --- a/src/vs/editor/browser/editorBrowser.ts +++ b/src/vs/editor/browser/editorBrowser.ts @@ -1276,6 +1276,8 @@ export interface IDiffEditor extends editorCommon.IEditor { accessibleDiffViewerNext(): void; accessibleDiffViewerPrev(): void; + + handleInitialized(): void; } /** diff --git a/src/vs/editor/browser/editorDom.ts b/src/vs/editor/browser/editorDom.ts index 60379467426..e533d4d13ca 100644 --- a/src/vs/editor/browser/editorDom.ts +++ b/src/vs/editor/browser/editorDom.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { GlobalPointerMoveMonitor } from 'vs/base/browser/globalPointerMoveMonitor'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { asCssVariable } from 'vs/platform/theme/common/colorRegistry'; import { ThemeColor } from 'vs/base/common/themables'; @@ -358,7 +358,8 @@ export interface CssProperties { class RefCountedCssRule { private _referenceCount: number = 0; - private _styleElement: HTMLStyleElement; + private _styleElement: HTMLStyleElement | undefined; + private _styleElementDisposables: DisposableStore; constructor( public readonly key: string, @@ -366,10 +367,8 @@ class RefCountedCssRule { _containerElement: HTMLElement | undefined, public readonly properties: CssProperties, ) { - this._styleElement = dom.createStyleSheet( - _containerElement - ); - + this._styleElementDisposables = new DisposableStore(); + this._styleElement = dom.createStyleSheet(_containerElement, undefined, this._styleElementDisposables); this._styleElement.textContent = this.getCssText(this.className, this.properties); } @@ -392,7 +391,8 @@ class RefCountedCssRule { } public dispose(): void { - this._styleElement.remove(); + this._styleElementDisposables.dispose(); + this._styleElement = undefined; } public increaseRefCount(): void { diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index ee4e12ee881..b125fb00c01 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -373,6 +373,11 @@ export class DiffEditorWidget extends DelegatingEditor implements IDiffEditor { } } + public handleInitialized(): void { + this._editors.original.handleInitialized(); + this._editors.modified.handleInitialized(); + } + public createViewModel(model: IDiffEditorModel): IDiffEditorViewModel { return this._instantiationService.createInstance(DiffEditorViewModel, model, this._options, this); } diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index eb18dced3c1..00e3ffb0de0 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -491,7 +491,12 @@ export class ViewModel extends Disposable implements IViewModel { this.viewLayout.onFlushed(this.getLineCount()); this.viewLayout.onHeightMaybeChanged(); } - stableViewport.recoverViewportStart(this.coordinatesConverter, this.viewLayout); + + const firstModelLineInViewPort = stableViewport.viewportStartModelPosition?.lineNumber; + const firstModelLineIsHidden = firstModelLineInViewPort && mergedRanges.some(range => range.startLineNumber <= firstModelLineInViewPort && firstModelLineInViewPort <= range.endLineNumber); + if (!firstModelLineIsHidden) { + stableViewport.recoverViewportStart(this.coordinatesConverter, this.viewLayout); + } } finally { this._eventDispatcher.endEmitViewEvents(); } diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index a003de17958..a1e82f322d5 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -321,6 +321,19 @@ export abstract class ZoneWidget implements IHorizontalSashLayoutProvider { this._positionMarkerId.set([{ range, options: ModelDecorationOptions.EMPTY }]); } + updatePositionAndHeight(rangeOrPos: IRange | IPosition, heightInLines?: number): void { + if (this._viewZone) { + rangeOrPos = Range.isIRange(rangeOrPos) ? Range.getStartPosition(rangeOrPos) : rangeOrPos; + this._viewZone.afterLineNumber = rangeOrPos.lineNumber; + this._viewZone.afterColumn = rangeOrPos.column; + this._viewZone.heightInLines = heightInLines ?? this._viewZone.heightInLines; + + this.editor.changeViewZones(accessor => { + accessor.layoutZone(this._viewZone!.id); + }); + } + } + hide(): void { if (this._viewZone) { this.editor.changeViewZones(accessor => { diff --git a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts index 549a1d85c77..2c45263ca24 100644 --- a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts +++ b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./standaloneQuickInput'; +import { Event } from 'vs/base/common/event'; import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition, OverlayWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; @@ -42,10 +43,14 @@ class EditorScopedQuickInputService extends QuickInputService { get container() { return widget.getDomNode(); }, get containers() { return [widget.getDomNode()]; }, get activeContainer() { return widget.getDomNode(); }, - get dimension() { return editor.getLayoutInfo(); }, - get onDidLayout() { return editor.onDidLayoutChange; }, - focus: () => editor.focus(), - offset: { top: 0, quickPickTop: 0 } + get mainContainerDimension() { return editor.getLayoutInfo(); }, + get activeContainerDimension() { return editor.getLayoutInfo(); }, + get onDidLayoutMainContainer() { return editor.onDidLayoutChange; }, + get onDidLayoutActiveContainer() { return editor.onDidLayoutChange; }, + get onDidChangeActiveContainer() { return Event.None; }, + get mainContainerOffset() { return { top: 0, quickPickTop: 0 }; }, + get activeContainerOffset() { return { top: 0, quickPickTop: 0 }; }, + focus: () => editor.focus() }; } else { this.host = undefined; diff --git a/src/vs/editor/standalone/browser/standaloneLayoutService.ts b/src/vs/editor/standalone/browser/standaloneLayoutService.ts index 361d84f2357..86aeb14e2ab 100644 --- a/src/vs/editor/standalone/browser/standaloneLayoutService.ts +++ b/src/vs/editor/standalone/browser/standaloneLayoutService.ts @@ -13,10 +13,12 @@ import { coalesce } from 'vs/base/common/arrays'; class StandaloneLayoutService implements ILayoutService { declare readonly _serviceBrand: undefined; - public onDidLayout = Event.None; + readonly onDidLayoutMainContainer = Event.None; + readonly onDidLayoutActiveContainer = Event.None; + readonly onDidChangeActiveContainer = Event.None; private _dimension?: dom.IDimension; - get dimension(): dom.IDimension { + get mainContainerDimension(): dom.IDimension { if (!this._dimension) { this._dimension = dom.getClientArea(window.document.body); } @@ -24,6 +26,11 @@ class StandaloneLayoutService implements ILayoutService { return this._dimension; } + get activeContainerDimension() { return this.mainContainerDimension; } + + readonly mainContainerOffset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; + readonly activeContainerOffset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; + get hasContainer(): boolean { return false; } @@ -51,8 +58,6 @@ class StandaloneLayoutService implements ILayoutService { this._codeEditorService.getFocusedCodeEditor()?.focus(); } - readonly offset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; - constructor( @ICodeEditorService private _codeEditorService: ICodeEditorService ) { } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 0b300b52fea..5aafcb30cb7 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -5991,6 +5991,7 @@ declare namespace monaco.editor { updateOptions(newOptions: IDiffEditorOptions): void; accessibleDiffViewerNext(): void; accessibleDiffViewerPrev(): void; + handleInitialized(): void; } export class FontInfo extends BareFontInfo { diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts index 1b1271068a4..a73b50a6683 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts @@ -15,15 +15,25 @@ export interface IAuxiliaryWindow { readonly id: number; readonly win: BrowserWindow | null; + readonly parentId: number; + readonly lastFocusTime: number; focus(options?: { force: boolean }): void; + + setRepresentedFilename(name: string): void; + getRepresentedFilename(): string | undefined; + + setDocumentEdited(edited: boolean): void; + isDocumentEdited(): boolean; } export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { readonly id = this.contents.id; + parentId = -1; + private readonly _onDidClose = this._register(new Emitter()); readonly onDidClose = this._onDidClose.event; diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts index 01718d334cc..aae6d0f62b4 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.ts @@ -6,6 +6,7 @@ import { BrowserWindow, BrowserWindowConstructorOptions, WebContents, app } from 'electron'; import { Event } from 'vs/base/common/event'; import { FileAccess } from 'vs/base/common/network'; +import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { AuxiliaryWindow, IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -26,7 +27,7 @@ export class AuxiliaryWindowsMainService implements IAuxiliaryWindowsMainService private registerListeners(): void { // We have to ensure that an auxiliary window gets to know its - // parent `BrowserWindow` so that it can apply listeners to it + // containing `BrowserWindow` so that it can apply listeners to it // Unfortunately we cannot rely on static `BrowserWindow` methods // because we might call the methods too early before the window // is created. @@ -37,12 +38,21 @@ export class AuxiliaryWindowsMainService implements IAuxiliaryWindowsMainService auxiliaryWindow.tryClaimWindow(); } }); + + validatedIpcMain.handle('vscode:registerAuxiliaryWindow', async (event, mainWindowId: number) => { + const auxiliaryWindow = this.getWindowById(event.sender.id); + if (auxiliaryWindow) { + auxiliaryWindow.parentId = mainWindowId; + } + + return event.sender.id; + }); } createWindow(): BrowserWindowConstructorOptions { return this.instantiationService.invokeFunction(defaultBrowserWindowOptions, undefined, { webPreferences: { - preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-sandbox/preload-slim.js').fsPath + preload: FileAccess.asFileUri('vs/base/parts/sandbox/electron-sandbox/preload-aux.js').fsPath } }); } diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts index ed305ef3881..beb879021a0 100644 --- a/src/vs/platform/contextview/browser/contextViewService.ts +++ b/src/vs/platform/contextview/browser/contextViewService.ts @@ -25,7 +25,7 @@ export class ContextViewService extends Disposable implements IContextViewServic this.contextView = this._register(new ContextView(this.container, ContextViewDOMPosition.ABSOLUTE)); this.layout(); - this._register(layoutService.onDidLayout(() => this.layout())); + this._register(layoutService.onDidLayoutMainContainer(() => this.layout())); } // ContextView diff --git a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts index 2d5a39fa1da..19fcb4dd70a 100644 --- a/src/vs/platform/extensions/electron-main/extensionHostStarter.ts +++ b/src/vs/platform/extensions/electron-main/extensionHostStarter.ts @@ -78,6 +78,21 @@ export class ExtensionHostStarter implements IDisposable, IExtensionHostStarter extHost.dispose(); this._extHosts.delete(id); }); + + // See https://github.com/microsoft/vscode/issues/194477 + // We have observed that sometimes the process sends an exit + // event, but does not really exit and is stuck in an endless + // loop. In these cases we kill the process forcefully after + // a certain timeout. + setTimeout(() => { + try { + process.kill(pid, 0); // will throw if the process doesn't exist anymore. + this._logService.error(`Extension host with pid ${pid} still exists, forcefully killing it...`); + process.kill(pid); + } catch (er) { + // ignore, as the process is already gone + } + }, 1000); }); return { id }; } diff --git a/src/vs/platform/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts index e84f9f3107f..8ba84c5d9e5 100644 --- a/src/vs/platform/layout/browser/layoutService.ts +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -10,10 +10,12 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const ILayoutService = createDecorator('layoutService'); export interface ILayoutOffsetInfo { + /** * Generic top offset */ readonly top: number; + /** * Quick pick specific top offset. */ @@ -25,15 +27,29 @@ export interface ILayoutService { readonly _serviceBrand: undefined; /** - * An event that is emitted when the container is layed out. The - * event carries the dimensions of the container as part of it. + * An event that is emitted when the main container is layed out. */ - readonly onDidLayout: Event; + readonly onDidLayoutMainContainer: Event; /** - * The dimensions of the container. + * An event that is emitted when the active container is layed out. */ - readonly dimension: IDimension; + readonly onDidLayoutActiveContainer: Event; + + /** + * An event that is emitted when the active container changes. + */ + readonly onDidChangeActiveContainer: Event; + + /** + * The dimensions of the main container. + */ + readonly mainContainerDimension: IDimension; + + /** + * The dimensions of the active container. + */ + readonly activeContainerDimension: IDimension; /** * Does the application have a single container? @@ -41,7 +57,7 @@ export interface ILayoutService { readonly hasContainer: boolean; /** - * Container of the application. + * Main container of the application. * * **NOTE**: In the standalone editor case, multiple editors can be created on a page. * Therefore, in the standalone editor case, there are multiple containers, not just @@ -66,12 +82,17 @@ export interface ILayoutService { readonly containers: Iterable; /** - * An offset to use for positioning elements inside the container. + * An offset to use for positioning elements inside the main container. */ - readonly offset: ILayoutOffsetInfo; + readonly mainContainerOffset: ILayoutOffsetInfo; /** - * Focus the primary component of the container. + * An offset to use for positioning elements inside the container. + */ + readonly activeContainerOffset: ILayoutOffsetInfo; + + /** + * Focus the primary component of the active container. */ focus(): void; } diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 0ef12205389..ed5141b1d0a 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -49,7 +49,7 @@ export class Menubar { private willShutdown: boolean | undefined; private appMenuInstalled: boolean | undefined; private closedLastWindow: boolean; - private noActiveWindow: boolean; + private noActiveMainWindow: boolean; private menuUpdater: RunOnceScheduler; private menuGC: RunOnceScheduler; @@ -92,7 +92,7 @@ export class Menubar { this.addFallbackHandlers(); this.closedLastWindow = false; - this.noActiveWindow = false; + this.noActiveMainWindow = false; this.oldMenus = []; @@ -117,7 +117,11 @@ export class Menubar { private addFallbackHandlers(): void { // File Menu Items - this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win?.id }); + this.fallbackMenuHandlers['workbench.action.files.newUntitledFile'] = (menuItem, win, event) => { + if (!this.runActionInRenderer({ type: 'commandId', commandId: 'workbench.action.files.newUntitledFile' })) { // this is one of the few supported actions when aux window has focus + this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win?.id }); + } + }; this.fallbackMenuHandlers['workbench.action.newWindow'] = (menuItem, win, event) => this.windowsMainService.openEmptyWindow({ context: OpenContext.MENU, contextWindowId: win?.id }); this.fallbackMenuHandlers['workbench.action.files.openFileFolder'] = (menuItem, win, event) => this.nativeHostMainService.pickFileFolderAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); this.fallbackMenuHandlers['workbench.action.files.openFolder'] = (menuItem, win, event) => this.nativeHostMainService.pickFolderAndOpen(undefined, { forceNewWindow: this.isOptionClick(event), telemetryExtraData: { from: telemetryFrom } }); @@ -242,7 +246,8 @@ export class Menubar { return; } - this.noActiveWindow = !BrowserWindow.getFocusedWindow(); + const focusedWindow = BrowserWindow.getFocusedWindow(); + this.noActiveMainWindow = !focusedWindow || !!this.auxiliaryWindowsMainService.getWindowById(focusedWindow.id); this.scheduleUpdateMenu(); } @@ -377,8 +382,10 @@ export class Menubar { Menu.setApplicationMenu(menu); - for (const window of this.auxiliaryWindowsMainService.getWindows()) { - window.win?.setMenu(null); + if (menu) { + for (const window of this.auxiliaryWindowsMainService.getWindows()) { + window.win?.setMenu(null); + } } } @@ -470,12 +477,12 @@ export class Menubar { case 'File': case 'Help': if (isMacintosh) { - return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow) || (!!this.menubarMenus && !!this.menubarMenus[menuId]); + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveMainWindow) || (!!this.menubarMenus && !!this.menubarMenus[menuId]); } case 'Window': if (isMacintosh) { - return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow) || !!this.menubarMenus; + return (this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || (this.windowsMainService.getWindowCount() > 0 && this.noActiveMainWindow) || !!this.menubarMenus; } default: @@ -502,7 +509,7 @@ export class Menubar { if (isMacintosh) { if ((this.windowsMainService.getWindowCount() === 0 && this.closedLastWindow) || - (this.windowsMainService.getWindowCount() > 0 && this.noActiveWindow)) { + (this.windowsMainService.getWindowCount() > 0 && this.noActiveMainWindow)) { // In the fallback scenario, we are either disabled or using a fallback handler if (this.fallbackMenuHandlers[item.id]) { menu.append(new MenuItem(this.likeAction(item.id, { label: this.mnemonicLabel(item.label), click: this.fallbackMenuHandlers[item.id] }))); @@ -747,13 +754,24 @@ export class Menubar { }; } - private runActionInRenderer(invocation: IMenuItemInvocation): void { + private runActionInRenderer(invocation: IMenuItemInvocation): boolean { + + // We want to support auxililary windows that may have focus by + // returning their parent windows as target to support running + // actions via the main window. + let activeBrowserWindow = BrowserWindow.getFocusedWindow(); + if (activeBrowserWindow) { + const auxiliaryWindowCandidate = this.auxiliaryWindowsMainService.getWindowById(activeBrowserWindow.id); + if (auxiliaryWindowCandidate) { + activeBrowserWindow = this.windowsMainService.getWindowById(auxiliaryWindowCandidate.parentId)?.win ?? null; + } + } + // We make sure to not run actions when the window has no focus, this helps // for https://github.com/microsoft/vscode/issues/25907 and specifically for // https://github.com/microsoft/vscode/issues/11928 // Still allow to run when the last active window is minimized though for // https://github.com/microsoft/vscode/issues/63000 - let activeBrowserWindow = BrowserWindow.getFocusedWindow(); if (!activeBrowserWindow) { const lastActiveWindow = this.windowsMainService.getLastActiveWindow(); if (lastActiveWindow?.isMinimized()) { @@ -770,7 +788,7 @@ export class Menubar { // prevent this action from running twice on macOS (https://github.com/microsoft/vscode/issues/62719) // we already register a keybinding in bootstrap-window.js for opening developer tools in case something // goes wrong and that keybinding is only removed when the application has loaded (= window ready). - return; + return false; } } @@ -781,8 +799,12 @@ export class Menubar { const runKeybindingPayload: INativeRunKeybindingInWindowRequest = { userSettingsLabel: invocation.userSettingsLabel }; activeWindow.sendWhenReady('vscode:runKeybinding', CancellationToken.None, runKeybindingPayload); } + + return true; } else { this.logService.trace('menubar#runActionInRenderer: no active window found', invocation); + + return false; } } diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 6b2d0f538b4..59218bc0fe8 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -12,7 +12,7 @@ import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IV8Profile } from 'vs/platform/profiling/common/profiling'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IColorScheme, IOpenedWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window'; +import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window'; export interface ICPUProperties { model: string; @@ -60,7 +60,8 @@ export interface ICommonNativeHostService { readonly onDidTriggerSystemContextMenu: Event<{ windowId: number; x: number; y: number }>; // Window - getWindows(): Promise; + getWindows(options: { includeAuxiliaryWindows: true }): Promise>; + getWindows(options: { includeAuxiliaryWindows: false }): Promise>; getWindowCount(): Promise; getActiveWindowId(): Promise; @@ -111,8 +112,8 @@ export interface ICommonNativeHostService { // OS showItemInFolder(path: string): Promise; - setRepresentedFilename(path: string): Promise; - setDocumentEdited(edited: boolean): Promise; + setRepresentedFilename(path: string, options?: { targetWindowId?: number }): Promise; + setDocumentEdited(edited: boolean, options?: { targetWindowId?: number }): Promise; openExternal(url: string): Promise; moveItemToTrash(fullPath: string): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 92855b5ae24..43b3fd95917 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -33,7 +33,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { ICodeWindow } from 'vs/platform/window/electron-main/window'; -import { IColorScheme, IOpenedWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window'; +import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window'; import { getFocusedOrLastActiveWindow, IWindowsMainService, OpenContext } from 'vs/platform/windows/electron-main/windows'; import { isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; @@ -112,16 +112,28 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Window - async getWindows(): Promise { - const windows = this.windowsMainService.getWindows(); - - return windows.map(window => ({ + getWindows(windowId: number | undefined, options: { includeAuxiliaryWindows: true }): Promise>; + getWindows(windowId: number | undefined, options: { includeAuxiliaryWindows: false }): Promise>; + async getWindows(windowId: number | undefined, options: { includeAuxiliaryWindows: boolean }): Promise> { + const mainWindows = this.windowsMainService.getWindows().map(window => ({ id: window.id, workspace: window.openedWorkspace ?? toWorkspaceIdentifier(window.backupPath, window.isExtensionDevelopmentHost), title: window.win?.getTitle() ?? '', filename: window.getRepresentedFilename(), dirty: window.isDocumentEdited() })); + + const auxiliaryWindows = []; + if (options.includeAuxiliaryWindows) { + auxiliaryWindows.push(...this.auxiliaryWindowsMainService.getWindows().map(window => ({ + id: window.id, + parentId: window.parentId, + title: window.win?.getTitle() ?? '', + filename: window.getRepresentedFilename() + }))); + } + + return [...mainWindows, ...auxiliaryWindows]; } async getWindowCount(windowId: number | undefined): Promise { @@ -218,7 +230,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async moveWindowTop(windowId: number | undefined, options?: { targetWindowId?: number }): Promise { - const window = this.windowById(options?.targetWindowId) ?? this.codeWindowById(windowId); + const window = this.windowById(options?.targetWindowId, windowId); if (window?.win) { window.win.moveTop(); } @@ -245,7 +257,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } async focusWindow(windowId: number | undefined, options?: { targetWindowId?: number; force?: boolean }): Promise { - const window = this.windowById(options?.targetWindowId) ?? this.codeWindowById(windowId); + const window = this.windowById(options?.targetWindowId, windowId); window?.focus({ force: options?.force ?? false }); } @@ -439,13 +451,13 @@ export class NativeHostMainService extends Disposable implements INativeHostMain shell.showItemInFolder(path); } - async setRepresentedFilename(windowId: number | undefined, path: string): Promise { - const window = this.codeWindowById(windowId); + async setRepresentedFilename(windowId: number | undefined, path: string, options?: { targetWindowId?: number }): Promise { + const window = this.windowById(options?.targetWindowId, windowId); window?.setRepresentedFilename(path); } - async setDocumentEdited(windowId: number | undefined, edited: boolean): Promise { - const window = this.codeWindowById(windowId); + async setDocumentEdited(windowId: number | undefined, edited: boolean, options?: { targetWindowId?: number }): Promise { + const window = this.windowById(options?.targetWindowId, windowId); window?.setDocumentEdited(edited); } @@ -797,8 +809,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#endregion - private windowById(windowId: number | undefined): ICodeWindow | IAuxiliaryWindow | undefined { - return this.codeWindowById(windowId) ?? this.auxiliaryWindowById(windowId); + private windowById(windowId: number | undefined, fallbackCodeWindowId?: number): ICodeWindow | IAuxiliaryWindow | undefined { + return this.codeWindowById(windowId) ?? this.auxiliaryWindowById(windowId) ?? this.codeWindowById(fallbackCodeWindowId); } private codeWindowById(windowId: number | undefined): ICodeWindow | undefined { diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 86976df649f..a2f2e4cb1b9 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -81,12 +81,14 @@ export class QuickInputController extends Disposable { } } - private getUI() { + private getUI(showInActiveContainer?: boolean) { if (this.ui) { // In order to support aux windows, re-parent the controller // if the original event is from a different document - if (this.parentElement.ownerDocument !== this.layoutService.activeContainer.ownerDocument) { - this.reparentUI(this.layoutService.activeContainer); + if (showInActiveContainer) { + if (this.parentElement.ownerDocument !== this.layoutService.activeContainer.ownerDocument) { + this.reparentUI(this.layoutService.activeContainer); + } } return this.ui; @@ -506,22 +508,22 @@ export class QuickInputController extends Disposable { backButton = backButton; createQuickPick(): IQuickPick { - const ui = this.getUI(); + const ui = this.getUI(true); return new QuickPick(ui); } createInputBox(): IInputBox { - const ui = this.getUI(); + const ui = this.getUI(true); return new InputBox(ui); } createQuickWidget(): IQuickWidget { - const ui = this.getUI(); + const ui = this.getUI(true); return new QuickWidget(ui); } private show(controller: IQuickInput) { - const ui = this.getUI(); + const ui = this.getUI(true); this.onShowEmitter.fire(); const oldController = this.controller; this.controller = controller; @@ -559,6 +561,10 @@ export class QuickInputController extends Disposable { ui.inputBox.setFocus(); } + isVisible(): boolean { + return !!this.ui && this.ui.container.style.display !== 'none'; + } + private setVisibilities(visibilities: Visibilities) { const ui = this.getUI(); ui.title.style.display = visibilities.title ? '' : 'none'; @@ -605,7 +611,9 @@ export class QuickInputController extends Disposable { const focusChanged = container && !dom.isAncestorOfActiveElement(container); this.controller = null; this.onHideEmitter.fire(); - this.getUI().container.style.display = 'none'; + if (container) { + container.style.display = 'none'; + } if (!focusChanged) { let currentElement = this.previousFocusElement; while (currentElement && !currentElement.offsetParent) { @@ -622,7 +630,7 @@ export class QuickInputController extends Disposable { } focus() { - if (this.isDisplayed()) { + if (this.isVisible()) { const ui = this.getUI(); if (ui.inputBox.enabled) { ui.inputBox.setFocus(); @@ -633,13 +641,13 @@ export class QuickInputController extends Disposable { } toggle() { - if (this.isDisplayed() && this.controller instanceof QuickPick && this.controller.canSelectMany) { + if (this.isVisible() && this.controller instanceof QuickPick && this.controller.canSelectMany) { this.getUI().list.toggleCheckbox(); } } navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { - if (this.isDisplayed() && this.getUI().list.isDisplayed()) { + if (this.isVisible() && this.getUI().list.isDisplayed()) { this.getUI().list.focus(next ? QuickInputListFocus.Next : QuickInputListFocus.Previous); if (quickNavigate && this.controller instanceof QuickPick) { this.controller.quickNavigate = quickNavigate; @@ -673,7 +681,7 @@ export class QuickInputController extends Disposable { } private updateLayout() { - if (this.ui && this.isDisplayed()) { + if (this.ui && this.isVisible()) { this.ui.container.style.top = `${this.titleBarOffset}px`; const style = this.ui.container.style; @@ -745,10 +753,6 @@ export class QuickInputController extends Disposable { } } } - - private isDisplayed() { - return this.ui && this.ui.container.style.display !== 'none'; - } } export interface IQuickInputControllerHost extends ILayoutService { } diff --git a/src/vs/platform/quickinput/browser/quickInputService.ts b/src/vs/platform/quickinput/browser/quickInputService.ts index a5892ba0281..0d6884730e1 100644 --- a/src/vs/platform/quickinput/browser/quickInputService.ts +++ b/src/vs/platform/quickinput/browser/quickInputService.ts @@ -67,7 +67,7 @@ export class QuickInputService extends Themable implements IQuickInputService { protected createController(host: IQuickInputControllerHost = this.layoutService, options?: Partial): QuickInputController { const defaultOptions: IQuickInputOptions = { idPrefix: 'quickInput_', - container: host.container, + container: host.activeContainer, ignoreFocusOut: () => false, backKeybindingLabel: () => undefined, setContextKey: (id?: string) => this.setContextKey(id), @@ -94,12 +94,20 @@ export class QuickInputService extends Themable implements IQuickInputService { ...options }, this.themeService, - this.layoutService)); + this.layoutService + )); - controller.layout(host.dimension, host.offset.quickPickTop); + controller.layout(host.activeContainerDimension, host.activeContainerOffset.quickPickTop); // Layout changes - this._register(host.onDidLayout(dimension => controller.layout(dimension, host.offset.quickPickTop))); + this._register(host.onDidLayoutActiveContainer(dimension => controller.layout(dimension, host.activeContainerOffset.quickPickTop))); + this._register(host.onDidChangeActiveContainer(() => { + if (controller.isVisible()) { + return; + } + + controller.layout(host.activeContainerDimension, host.activeContainerOffset.quickPickTop); + })); // Context keys this._register(controller.onShow(() => { diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index bf5375b17a1..e6bb70145b4 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -18,6 +18,10 @@ import { findExecutable, getWindowsBuildNumber } from 'vs/platform/terminal/node import { ThemeIcon } from 'vs/base/common/themables'; import { dirname, resolve } from 'path'; +const enum Constants { + UnixShellsPath = '/etc/shells' +} + let profileSources: Map | undefined; let logIfWslNotInstalled: boolean = true; @@ -405,8 +409,8 @@ async function detectAvailableUnixProfiles( const detectedProfiles: Map = new Map(); // Add non-quick launch profiles - if (includeDetectedProfiles) { - const contents = (await fsProvider.readFile('/etc/shells')).toString(); + if (includeDetectedProfiles && await fsProvider.existsFile(Constants.UnixShellsPath)) { + const contents = (await fsProvider.readFile(Constants.UnixShellsPath)).toString(); const profiles = ( (testPaths || contents.split('\n')) .map(e => { diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 4e7a1872b0e..442c421ef5e 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -70,14 +70,25 @@ export interface IAddFoldersRequest { readonly foldersToAdd: UriComponents[]; } -export interface IOpenedWindow { +interface IOpenedWindow { readonly id: number; - readonly workspace?: IAnyWorkspaceIdentifier; readonly title: string; readonly filename?: string; +} + +export interface IOpenedMainWindow extends IOpenedWindow { + readonly workspace?: IAnyWorkspaceIdentifier; readonly dirty: boolean; } +export interface IOpenedAuxiliaryWindow extends IOpenedWindow { + readonly parentId: number; +} + +export function isOpenedAuxiliaryWindow(candidate: IOpenedMainWindow | IOpenedAuxiliaryWindow): candidate is IOpenedAuxiliaryWindow { + return typeof (candidate as IOpenedAuxiliaryWindow).parentId === 'number'; +} + export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions { } export type IWindowOpenable = IWorkspaceToOpen | IFolderToOpen | IFileToOpen; diff --git a/src/vs/platform/window/electron-main/window.ts b/src/vs/platform/window/electron-main/window.ts index 379bdd161f6..ac0eda4c73e 100644 --- a/src/vs/platform/window/electron-main/window.ts +++ b/src/vs/platform/window/electron-main/window.ts @@ -15,6 +15,12 @@ import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platf export interface IBaseWindow extends IDisposable { focus(options?: { force: boolean }): void; + + setRepresentedFilename(name: string): void; + getRepresentedFilename(): string | undefined; + + setDocumentEdited(edited: boolean): void; + isDocumentEdited(): boolean; } export interface ICodeWindow extends IBaseWindow { @@ -65,12 +71,6 @@ export interface ICodeWindow extends IBaseWindow { isMinimized(): boolean; - setRepresentedFilename(name: string): void; - getRepresentedFilename(): string | undefined; - - setDocumentEdited(edited: boolean): void; - isDocumentEdited(): boolean; - handleTitleDoubleClick(): void; updateTouchBar(items: ISerializableCommandAction[][]): void; diff --git a/src/vs/platform/window/electron-sandbox/window.ts b/src/vs/platform/window/electron-sandbox/window.ts index b4c27b676f0..28968b4f9cc 100644 --- a/src/vs/platform/window/electron-sandbox/window.ts +++ b/src/vs/platform/window/electron-sandbox/window.ts @@ -5,7 +5,7 @@ import { getZoomLevel, setZoomFactor, setZoomLevel } from 'vs/base/browser/browser'; import { getWindows } from 'vs/base/browser/dom'; -import { getGlobals } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { ISandboxGlobals, ipcRenderer, webFrame } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { zoomLevelToZoomFactor } from 'vs/platform/window/common/window'; /** @@ -20,6 +20,21 @@ export function applyZoom(zoomLevel: number): void { setZoomLevel(zoomLevel); } +function getGlobals(win: Window): ISandboxGlobals | undefined { + if (win === window) { + // main window + return { ipcRenderer, webFrame }; + } else { + // auxiliary window + const auxiliaryWindow = win as unknown as { vscode: ISandboxGlobals }; + if (auxiliaryWindow?.vscode?.ipcRenderer && auxiliaryWindow?.vscode?.webFrame) { + return auxiliaryWindow.vscode; + } + } + + return undefined; +} + export function zoomIn(): void { applyZoom(getZoomLevel() + 1); } diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index ea74e8a2811..2bdd624c092 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -86,6 +86,41 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { protected abstract getWin(): BrowserWindow | null; + private representedFilename: string | undefined; + private documentEdited: boolean | undefined; + + setRepresentedFilename(filename: string): void { + if (isMacintosh) { + this.getWin()?.setRepresentedFilename(filename); + } else { + this.representedFilename = filename; + } + } + + getRepresentedFilename(): string | undefined { + if (isMacintosh) { + return this.getWin()?.getRepresentedFilename(); + } + + return this.representedFilename; + } + + setDocumentEdited(edited: boolean): void { + if (isMacintosh) { + this.getWin()?.setDocumentEdited(edited); + } + + this.documentEdited = edited; + } + + isDocumentEdited(): boolean { + if (isMacintosh) { + return Boolean(this.getWin()?.isDocumentEdited()); + } + + return !!this.documentEdited; + } + focus(options?: { force: boolean }): void { if (isMacintosh && options?.force) { app.focus({ steal: true }); @@ -182,9 +217,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private transientIsNativeFullScreen: boolean | undefined = undefined; private joinNativeFullScreenTransition: DeferredPromise | undefined = undefined; - private representedFilename: string | undefined; - private documentEdited: boolean | undefined; - private readonly hasWindowControlOverlay: boolean = false; private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[] = []; @@ -374,10 +406,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { this._win.maximize(); if (this.windowState.mode === WindowMode.Fullscreen) { - this.logConditionally('WindowMode.Fullscreen'); this.setFullScreen(true); - } else { - this.logConditionally('WindowMode.Maximized'); } // to reduce flicker from the default window size @@ -405,45 +434,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { this.registerListeners(); } - private logConditionally(msg: string): void { - // TODO@bpasero remove native fullscreen logging eventually - if (this.configurationService.getValue('window.logFullScreenTransitions')) { - this.logService.info(`window-fullscreen-bug: ${msg})`); - } - } - - setRepresentedFilename(filename: string): void { - if (isMacintosh) { - this._win.setRepresentedFilename(filename); - } else { - this.representedFilename = filename; - } - } - - getRepresentedFilename(): string | undefined { - if (isMacintosh) { - return this._win.getRepresentedFilename(); - } - - return this.representedFilename; - } - - setDocumentEdited(edited: boolean): void { - if (isMacintosh) { - this._win.setDocumentEdited(edited); - } - - this.documentEdited = edited; - } - - isDocumentEdited(): boolean { - if (isMacintosh) { - return this._win.isDocumentEdited(); - } - - return !!this.documentEdited; - } - private readyState = ReadyState.NONE; setReady(): void { @@ -550,7 +540,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { this._win.on('enter-full-screen', () => { this.sendWhenReady('vscode:enterFullScreen', CancellationToken.None); - this.logConditionally(`enter-full-screen: ${Date.now()}`); this.joinNativeFullScreenTransition?.complete(); this.joinNativeFullScreenTransition = undefined; }); @@ -558,7 +547,6 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { this._win.on('leave-full-screen', () => { this.sendWhenReady('vscode:leaveFullScreen', CancellationToken.None); - this.logConditionally(`leave-full-screen: ${Date.now()}`); this.joinNativeFullScreenTransition?.complete(); this.joinNativeFullScreenTransition = undefined; }); @@ -1288,19 +1276,13 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { } get isFullScreen(): boolean { - this.logConditionally(`isFullScreen(): begin at ${Date.now()}`); - if (isMacintosh && typeof this.transientIsNativeFullScreen === 'boolean') { - this.logConditionally(`isFullScreen(): returning transientIsNativeFullScreen = ${this.transientIsNativeFullScreen}`); - return this.transientIsNativeFullScreen; } const isFullScreen = this._win.isFullScreen(); const isSimpleFullScreen = this._win.isSimpleFullScreen(); - this.logConditionally(`isFullScreen(): returning natively, isFullScreen = ${isFullScreen}, isSimpleFullScreen = ${isSimpleFullScreen}`); - return isFullScreen || isSimpleFullScreen; } @@ -1314,16 +1296,18 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private doSetNativeFullScreen(fullscreen: boolean): void { if (isMacintosh) { - this.logConditionally(`doSetNativeFullScreen(${fullscreen}): begin at ${Date.now()}`); - this.transientIsNativeFullScreen = fullscreen; this.joinNativeFullScreenTransition = new DeferredPromise(); Promise.race([ this.joinNativeFullScreenTransition.p, - timeout(10000) // still timeout after some time in case the transition is unusually slow + // still timeout after some time in case the transition is unusually slow + // this can easily happen for an OS update where macOS tries to reopen + // previous applications and that can take multiple seconds, probably due + // to security checks. its worth noting that if this takes more than + // 10 seconds, users would see a window that is not-fullscreen but without + // custom titlebar... + timeout(10000) ]).finally(() => { - this.logConditionally(`doSetNativeFullScreen(${fullscreen}): finish at ${Date.now()}`); - this.transientIsNativeFullScreen = undefined; }); } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 89b2e26c645..a2f3527ee50 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -54,6 +54,8 @@ import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataPro import { IPolicyService } from 'vs/platform/policy/common/policy'; import { IUserDataProfilesMainService } from 'vs/platform/userDataProfile/electron-main/userDataProfile'; import { ILoggerMainService } from 'vs/platform/log/electron-main/loggerService'; +import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; +import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; //#region Helper Interfaces @@ -216,7 +218,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic @IDialogMainService private readonly dialogMainService: IDialogMainService, @IFileService private readonly fileService: IFileService, @IProtocolMainService private readonly protocolMainService: IProtocolMainService, - @IThemeMainService private readonly themeMainService: IThemeMainService + @IThemeMainService private readonly themeMainService: IThemeMainService, + @IAuxiliaryWindowsMainService private readonly auxiliaryWindowsMainService: IAuxiliaryWindowsMainService ) { super(); @@ -636,7 +639,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private doOpenFilesInExistingWindow(configuration: IOpenConfiguration, window: ICodeWindow, filesToOpen?: IFilesToOpen): ICodeWindow { this.logService.trace('windowsManager#doOpenFilesInExistingWindow', { filesToOpen }); - window.focus(); // make sure window has focus + this.focusMainOrChildWindow(window); // make sure window or any of the children has focus const params: INativeOpenFileRequest = { filesToOpenOrCreate: filesToOpen?.filesToOpenOrCreate, @@ -650,6 +653,20 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic return window; } + private focusMainOrChildWindow(mainWindow: ICodeWindow): void { + let windowToFocus: ICodeWindow | IAuxiliaryWindow = mainWindow; + + const focusedWindow = BrowserWindow.getFocusedWindow(); + if (focusedWindow && focusedWindow.id !== mainWindow.id) { + const auxiliaryWindowCandidate = this.auxiliaryWindowsMainService.getWindowById(focusedWindow.id); + if (auxiliaryWindowCandidate && auxiliaryWindowCandidate.parentId === mainWindow.id) { + windowToFocus = auxiliaryWindowCandidate; + } + } + + windowToFocus.focus(); + } + private doAddFoldersToExistingWindow(window: ICodeWindow, foldersToAdd: URI[]): ICodeWindow { this.logService.trace('windowsManager#doAddFoldersToExistingWindow', { foldersToAdd }); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index 49144b6102d..18d160704a6 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -56,10 +56,7 @@ class InspectContextKeysAction extends Action2 { const disposables = new DisposableStore(); - const stylesheet = createStyleSheet(); - disposables.add(toDisposable(() => { - stylesheet.parentNode?.removeChild(stylesheet); - })); + const stylesheet = createStyleSheet(undefined, undefined, disposables); createCSSRule('*', 'cursor: crosshair !important;', stylesheet); const hoverFeedback = document.createElement('div'); diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 5a9796b18db..3a7ba7f708d 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -96,14 +96,14 @@ export class ResourcesDropHandler { ) { } - async handleDrop(event: DragEvent, resolveTargetGroup?: () => IEditorGroup | undefined, afterDrop?: (targetGroup: IEditorGroup | undefined) => void, options?: IEditorOptions): Promise { + async handleDrop(event: DragEvent, targetWindow: Window, resolveTargetGroup?: () => IEditorGroup | undefined, afterDrop?: (targetGroup: IEditorGroup | undefined) => void, options?: IEditorOptions): Promise { const editors = await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, event)); if (!editors.length) { return; } // Make the window active to handle the drop properly within - await this.hostService.focus(); + await this.hostService.focus(targetWindow); // Check for workspace file / folder being dropped if we are allowed to do so if (this.options.allowWorkspaceOpen) { @@ -168,9 +168,6 @@ export class ResourcesDropHandler { return false; } - // Pass focus to window - this.hostService.focus(); - // Open in separate windows if we drop workspaces or just one folder if (toOpen.length > folderURIs.length || folderURIs.length === 1) { await this.hostService.openWindow(toOpen); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index a3adccf5491..19553fffaa9 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -5,7 +5,7 @@ import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; -import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow } from 'vs/base/browser/dom'; +import { EventType, addDisposableListener, getClientArea, Dimension, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow, isActiveDocument, getWindow } from 'vs/base/browser/dom'; import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser'; import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup'; import { isWindows, isLinux, isMacintosh, isWeb, isNative, isIOS } from 'vs/base/common/platform'; @@ -47,11 +47,12 @@ import { IBannerService } from 'vs/workbench/services/banner/browser/bannerServi import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { AuxiliaryBarPart } from 'vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; +import { IAuxiliaryWindowService, isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; //#region Layout Implementation interface ILayoutRuntimeState { + activeContainer: 'main' | number /* window ID */; fullscreen: boolean; maximized: boolean; hasFocus: boolean; @@ -145,8 +146,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private readonly _onDidChangeNotificationsVisibility = this._register(new Emitter()); readonly onDidChangeNotificationsVisibility = this._onDidChangeNotificationsVisibility.event; - private readonly _onDidLayout = this._register(new Emitter()); - readonly onDidLayout = this._onDidLayout.event; + private readonly _onDidLayoutMainContainer = this._register(new Emitter()); + readonly onDidLayoutMainContainer = this._onDidLayoutMainContainer.event; + + private readonly _onDidLayoutActiveContainer = this._register(new Emitter()); + readonly onDidLayoutActiveContainer = this._onDidLayoutActiveContainer.event; private readonly _onDidAddContainer = this._register(new Emitter()); readonly onDidAddContainer = this._onDidAddContainer.event; @@ -154,6 +158,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private readonly _onDidRemoveContainer = this._register(new Emitter()); readonly onDidRemoveContainer = this._onDidRemoveContainer.event; + private readonly _onDidChangeActiveContainer = this._register(new Emitter()); + readonly onDidChangeActiveContainer = this._onDidChangeActiveContainer.event; + //#endregion //#region Properties @@ -180,27 +187,54 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } - private _dimension!: IDimension; - get dimension(): IDimension { return this._dimension; } + private _mainContainerDimension!: IDimension; + get mainContainerDimension(): IDimension { return this._mainContainerDimension; } - get offset() { + get activeContainerDimension(): IDimension { + const activeContainer = this.activeContainer; + if (activeContainer === this.container) { + // main window + return this.mainContainerDimension; + } else { + // auxiliary window + return getClientArea(activeContainer); + } + } + + get mainContainerOffset() { let top = 0; let quickPickTop = 0; + if (this.isVisible(Parts.BANNER_PART)) { top = this.getPart(Parts.BANNER_PART).maximumHeight; quickPickTop = top; } + if (this.isVisible(Parts.TITLEBAR_PART)) { top += this.getPart(Parts.TITLEBAR_PART).maximumHeight; quickPickTop = top; } - // If the command center is visible then the quickinput should go over the title bar and the banner + if (this.titleService.isCommandCenterVisible) { + // If the command center is visible then the quickinput + // should go over the title bar and the banner quickPickTop = 6; } + return { top, quickPickTop }; } + get activeContainerOffset() { + const activeContainer = this.activeContainer; + if (activeContainer === this.container) { + // main window + return this.mainContainerOffset; + } else { + // TODO@bpasero auxiliary window: no support for custom title bar or banner yet + return { top: 0, quickPickTop: 0 }; + } + } + //#endregion private readonly parts = new Map(); @@ -346,6 +380,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(this.auxiliaryWindowService.onDidOpenAuxiliaryWindow(({ window, disposables }) => { this._onDidAddContainer.fire(window.container); + disposables.add(window.onDidLayout(dimension => this.handleContainerDidLayout(window.container, dimension))); disposables.add(toDisposable(() => this._onDidRemoveContainer.fire(window.container))); })); } @@ -369,7 +404,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Move layout call to any time the menubar // is toggled to update consumers of offset // see issue #115267 - this._onDidLayout.fire(this._dimension); + this.handleContainerDidLayout(this.container, this._mainContainerDimension); + } + } + + private handleContainerDidLayout(container: HTMLElement, dimension: IDimension): void { + if (container === this.container) { + this._onDidLayoutMainContainer.fire(dimension); + } + + if (isActiveDocument(container)) { + this._onDidLayoutActiveContainer.fire(dimension); } } @@ -406,12 +451,30 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } private onWindowFocusChanged(hasFocus: boolean): void { - if (this.state.runtime.hasFocus === hasFocus) { - return; + if (hasFocus) { + const activeContainerId = this.getActiveContainerId(); + if (this.state.runtime.activeContainer !== activeContainerId) { + this.state.runtime.activeContainer = activeContainerId; + this._onDidChangeActiveContainer.fire(); + } } - this.state.runtime.hasFocus = hasFocus; - this.updateWindowBorder(); + if (this.state.runtime.hasFocus !== hasFocus) { + this.state.runtime.hasFocus = hasFocus; + this.updateWindowBorder(); + } + } + + private getActiveContainerId(): 'main' | number { + const activeContainer = this.activeContainer; + if (activeContainer !== this.container) { + const containerWindow = getWindow(activeContainer); + if (isAuxiliaryWindow(containerWindow)) { + return containerWindow.vscodeWindowId; + } + } + + return 'main'; } private doUpdateLayoutConfiguration(skipLayout?: boolean): void { @@ -552,6 +615,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Layout Runtime State const layoutRuntimeState: ILayoutRuntimeState = { + activeContainer: this.getActiveContainerId(), fullscreen: isFullscreen(), hasFocus: this.hostService.hasFocus, maximized: false, @@ -1166,7 +1230,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } focus(): void { - this.focusPart(Parts.EDITOR_PART); + const activeContainer = this.activeContainer; + if (activeContainer === this.container) { + // main window + this.focusPart(Parts.EDITOR_PART); + } else { + // auxiliary window + this.editorGroupService.getPart(activeContainer)?.activeGroup.focus(); + } } getDimension(part: Parts): Dimension | undefined { @@ -1187,8 +1258,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi (this.isVisible(Parts.STATUSBAR_PART) ? this.statusBarPartView.minimumHeight : 0) + (this.isVisible(Parts.PANEL_PART) && !isColumn ? this.panelPartView.minimumHeight : 0); - const availableWidth = this.dimension.width - takenWidth; - const availableHeight = this.dimension.height - takenHeight; + const availableWidth = this._mainContainerDimension.width - takenWidth; + const availableHeight = this._mainContainerDimension.height - takenHeight; return new Dimension(availableWidth, availableHeight); } @@ -1401,7 +1472,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setEditorHidden(!visible, true); } this._onDidChangePartVisibility.fire(); - this._onDidLayout.fire(this._dimension); + this.handleContainerDidLayout(this.container, this._mainContainerDimension); })); } @@ -1430,24 +1501,20 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi })); } - private getClientArea(): Dimension { - return getClientArea(this.parent); - } - layout(): void { if (!this.disposed) { - this._dimension = this.getClientArea(); - this.logService.trace(`Layout#layout, height: ${this._dimension.height}, width: ${this._dimension.width}`); + this._mainContainerDimension = getClientArea(this.parent); + this.logService.trace(`Layout#layout, height: ${this._mainContainerDimension.height}, width: ${this._mainContainerDimension.width}`); position(this.container, 0, 0, 0, 0, 'relative'); - size(this.container, this._dimension.width, this._dimension.height); + size(this.container, this._mainContainerDimension.width, this._mainContainerDimension.height); // Layout the grid widget - this.workbenchGrid.layout(this._dimension.width, this._dimension.height); + this.workbenchGrid.layout(this._mainContainerDimension.width, this._mainContainerDimension.height); this.initialized = true; // Emit as event - this._onDidLayout.fire(this._dimension); + this.handleContainerDidLayout(this.container, this._mainContainerDimension); } } diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 684c704ffc3..5ad1c648d6f 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -46,7 +46,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { get preferredHeight(): number | undefined { // Don't worry about titlebar or statusbar visibility // The difference is minimal and keeps this function clean - return this.layoutService.dimension.height * 0.4; + return this.layoutService.mainContainerDimension.height * 0.4; } get preferredWidth(): number | undefined { diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 3a706cb6371..1fefdd61b31 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -213,6 +213,8 @@ export interface IEditorPartsView { */ export interface IEditorGroupsView { + readonly isAuxiliary: boolean; + readonly groups: IEditorGroupView[]; readonly activeGroup: IEditorGroupView; @@ -224,7 +226,7 @@ export interface IEditorGroupsView { getGroup(identifier: GroupIdentifier): IEditorGroupView | undefined; getGroups(order: GroupsOrder): IEditorGroupView[]; - activateGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; + activateGroup(identifier: IEditorGroupView | GroupIdentifier, preserveWindowOrder?: boolean): IEditorGroupView; restoreGroup(identifier: IEditorGroupView | GroupIdentifier): IEditorGroupView; addGroup(location: IEditorGroupView | GroupIdentifier, direction: GroupDirection, groupToCopy?: IEditorGroupView): IEditorGroupView; @@ -233,7 +235,7 @@ export interface IEditorGroupsView { moveGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView; copyGroup(group: IEditorGroupView | GroupIdentifier, location: IEditorGroupView | GroupIdentifier, direction: GroupDirection): IEditorGroupView; - removeGroup(group: IEditorGroupView | GroupIdentifier): void; + removeGroup(group: IEditorGroupView | GroupIdentifier, preserveFocus?: boolean): void; arrangeGroups(arrangement: GroupsArrangement, target?: IEditorGroupView | GroupIdentifier): void; toggleMaximizeGroup(group?: IEditorGroupView | GroupIdentifier): void; diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 64221b9382d..646f5869387 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -373,7 +373,7 @@ class DropOverlay extends Themable { // Check for URI transfer else { const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: !isWeb || isTemporaryWorkspace(this.contextService.getWorkspace()) }); - dropHandler.handleDrop(event, () => ensureTargetGroup(), targetGroup => targetGroup?.focus()); + dropHandler.handleDrop(event, getWindow(this.groupView.element), () => ensureTargetGroup(), targetGroup => targetGroup?.focus()); } } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index d35cf305995..988614634cd 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -709,12 +709,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Close all inactive editors first to prevent UI flicker for (const inactiveEditor of inactiveEditors) { - this.doCloseEditor(inactiveEditor, false); + this.doCloseEditor(inactiveEditor, true); } // Close active one last if (activeEditor) { - this.doCloseEditor(activeEditor, false); + this.doCloseEditor(activeEditor, true); } } @@ -1088,9 +1088,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Conditionally lock the group if ( - isNew && // only if this editor was new for the group - this.count === 1 && // only when this editor was the first editor in the group - this.groupsView.groups.length > 1 // only when there are more than one groups open + isNew && // only if this editor was new for the group + this.count === 1 && // only when this editor was the first editor in the group + this.canLock() // only when we met the requirements to lock ) { // only when the editor identifier is configured as such if (openedEditor.editorId && this.groupsView.partOptions.autoLockGroups?.has(openedEditor.editorId)) { @@ -1137,8 +1137,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Without an editor pane, recover by closing the active editor // (if the input is still the active one) if (!pane && this.activeEditor === editor) { - const focusNext = !options || !options.preserveFocus; - this.doCloseEditor(editor, focusNext, { fromError: true }); + this.doCloseEditor(editor, options?.preserveFocus, { fromError: true }); } return pane; @@ -1312,7 +1311,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // ...and a close afterwards (unless we copy) if (!keepCopy) { - this.doCloseEditor(editor, false /* do not focus next one behind if any */, { ...internalOptions, context: EditorCloseContext.MOVE }); + this.doCloseEditor(editor, true /* do not focus next one behind if any */, { ...internalOptions, context: EditorCloseContext.MOVE }); } } @@ -1377,12 +1376,12 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Do close - this.doCloseEditor(editor, options?.preserveFocus ? false : undefined, internalOptions); + this.doCloseEditor(editor, options?.preserveFocus, internalOptions); return true; } - private doCloseEditor(editor: EditorInput, focusNext = (this.groupsView.activeGroup === this), internalOptions?: IInternalEditorCloseOptions): void { + private doCloseEditor(editor: EditorInput, preserveFocus = (this.groupsView.activeGroup !== this), internalOptions?: IInternalEditorCloseOptions): void { // Forward to title control unless skipped via internal options if (!internalOptions?.skipTitleUpdate) { @@ -1391,7 +1390,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Closing the active editor of the group is a bit more work if (this.model.isActive(editor)) { - this.doCloseActiveEditor(focusNext, internalOptions); + this.doCloseActiveEditor(preserveFocus, internalOptions); } // Closing inactive editor is just a model update @@ -1405,9 +1404,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } - private doCloseActiveEditor(focusNext = (this.groupsView.activeGroup === this), internalOptions?: IInternalEditorCloseOptions): void { + private doCloseActiveEditor(preserveFocus = (this.groupsView.activeGroup !== this), internalOptions?: IInternalEditorCloseOptions): void { const editorToClose = this.activeEditor; - const restoreFocus = this.shouldRestoreFocus(this.element); + const restoreFocus = !preserveFocus && this.shouldRestoreFocus(this.element); // Optimization: if we are about to close the last editor in this group and settings // are configured to close the group since it will be empty, we first set the last @@ -1424,7 +1423,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { if (restoreFocus) { nextActiveGroup.focus(); } else { - this.groupsView.activateGroup(nextActiveGroup); + this.groupsView.activateGroup(nextActiveGroup, true); } } } @@ -1437,8 +1436,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Open next active if there are more to show const nextActiveEditor = this.model.activeEditor; if (nextActiveEditor) { - const preserveFocus = !focusNext; - let activation: EditorActivation | undefined = undefined; if (preserveFocus && this.groupsView.activeGroup !== this) { // If we are opening the next editor in an inactive group @@ -1487,7 +1484,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Remove empty group if we should if (closeEmptyGroup) { - this.groupsView.removeGroup(this); + this.groupsView.removeGroup(this, preserveFocus); } } } @@ -1752,7 +1749,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Close active editor last if contained in editors list to close if (closeActiveEditor) { - this.doCloseActiveEditor(options?.preserveFocus ? false : undefined); + this.doCloseActiveEditor(options?.preserveFocus); } // Forward to title control @@ -1856,7 +1853,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { if (!editor.matches(replacement)) { let closed = false; if (forceReplaceDirty) { - this.doCloseEditor(editor, false, { context: EditorCloseContext.REPLACE }); + this.doCloseEditor(editor, true, { context: EditorCloseContext.REPLACE }); closed = true; } else { closed = await this.doCloseEditorWithConfirmationHandling(editor, { preserveFocus: true }, { context: EditorCloseContext.REPLACE }); @@ -1877,7 +1874,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Close replaced active editor unless they match if (!activeReplacement.editor.matches(activeReplacement.replacement)) { if (activeReplacement.forceReplaceDirty) { - this.doCloseEditor(activeReplacement.editor, false, { context: EditorCloseContext.REPLACE }); + this.doCloseEditor(activeReplacement.editor, true, { context: EditorCloseContext.REPLACE }); } else { await this.doCloseEditorWithConfirmationHandling(activeReplacement.editor, { preserveFocus: true }, { context: EditorCloseContext.REPLACE }); } @@ -1892,7 +1889,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region Locking get isLocked(): boolean { - if (this.groupsView.groups.length === 1) { + if (!this.canLock()) { // Special case: if only 1 group is opened, never report it as locked // to ensure editors can always open in the "default" editor group return false; @@ -1902,7 +1899,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } lock(locked: boolean): void { - if (this.groupsView.groups.length === 1) { + if (!this.canLock()) { // Special case: if only 1 group is opened, never allow to lock // to ensure editors can always open in the "default" editor group locked = false; @@ -1911,6 +1908,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.model.lock(locked); } + private canLock(): boolean { + if (this.groupsView.isAuxiliary) { + return true; // always allow locking in auxiliary parts, even single groups + } + + return this.groupsView.groups.length > 1; // only allow locking if more than 1 group is opened + } + //#endregion //#region Editor Actions diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 5c4832ccd65..6cffd8296b4 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -14,7 +14,7 @@ import { IView, orthogonal, LayoutPriority, IViewSize, Direction, SerializableGr import { GroupIdentifier, EditorInputWithOptions, IEditorPartOptions, IEditorPartOptionsChangeEvent, GroupModelChangeKind } from 'vs/workbench/common/editor'; import { EDITOR_GROUP_BORDER, EDITOR_PANE_BACKGROUND } from 'vs/workbench/common/theme'; import { distinct, coalesce, firstOrDefault } from 'vs/base/common/arrays'; -import { IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartCreationOptions, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor'; +import { IEditorGroupView, getEditorPartOptions, impactsEditorPartOptions, IEditorPartCreationOptions, IEditorPartsView, IEditorGroupsView } from 'vs/workbench/browser/parts/editor/editor'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -79,7 +79,7 @@ class GridWidgetView implements IView { } } -export class EditorPart extends Part implements IEditorPart { +export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { private static readonly EDITOR_PART_UI_STATE_STORAGE_KEY = 'editorpart.state'; private static readonly EDITOR_PART_CENTERED_VIEW_STORAGE_KEY = 'editorpart.centeredview'; @@ -150,6 +150,7 @@ export class EditorPart extends Part implements IEditorPart { private readonly editorPartsView: IEditorPartsView, id: string, private readonly groupsLabel: string, + public readonly isAuxiliary: boolean, @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -337,10 +338,15 @@ export class EditorPart extends Part implements IEditorPart { } } - activateGroup(group: IEditorGroupView | GroupIdentifier): IEditorGroupView { + activateGroup(group: IEditorGroupView | GroupIdentifier, preserveWindowOrder?: boolean): IEditorGroupView { const groupView = this.assertGroupView(group); this.doSetGroupActive(groupView); + // Ensure window on top unless disabled + if (!preserveWindowOrder) { + this.hostService.moveTop(getWindow(this.element)); + } + return groupView; } @@ -667,10 +673,6 @@ export class EditorPart extends Part implements IEditorPart { } private doSetGroupActive(group: IEditorGroupView): void { - - // Ensure window on top - this.hostService.moveTop(getWindow(this.element)); - if (this._activeGroup !== group) { const previousActiveGroup = this._activeGroup; this._activeGroup = group; @@ -743,7 +745,7 @@ export class EditorPart extends Part implements IEditorPart { return fallback; } - removeGroup(group: IEditorGroupView | GroupIdentifier): void { + removeGroup(group: IEditorGroupView | GroupIdentifier, preserveFocus?: boolean): void { const groupView = this.assertGroupView(group); if (this.count === 1) { return; // Cannot remove the last root group @@ -751,7 +753,7 @@ export class EditorPart extends Part implements IEditorPart { // Remove empty group if (groupView.isEmpty) { - return this.doRemoveEmptyGroup(groupView); + return this.doRemoveEmptyGroup(groupView, preserveFocus); } // Remove group with editors @@ -773,14 +775,14 @@ export class EditorPart extends Part implements IEditorPart { this.mergeGroup(groupView, lastActiveGroup); } - private doRemoveEmptyGroup(groupView: IEditorGroupView): void { - const restoreFocus = this.shouldRestoreFocus(this.container); + private doRemoveEmptyGroup(groupView: IEditorGroupView, preserveFocus?: boolean): void { + const restoreFocus = !preserveFocus && this.shouldRestoreFocus(this.container); // Activate next group if the removed one was active if (this._activeGroup === groupView) { const mostRecentlyActiveGroups = this.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE); const nextActiveGroup = mostRecentlyActiveGroups[1]; // [0] will be the current group we are about to dispose - this.activateGroup(nextActiveGroup); + this.doSetGroupActive(nextActiveGroup); } // Remove from grid widget & dispose @@ -830,7 +832,7 @@ export class EditorPart extends Part implements IEditorPart { else { movedView = targetView.groupsView.addGroup(targetView, direction, sourceView); sourceView.closeAllEditors(); - this.removeGroup(sourceView); + this.removeGroup(sourceView, restoreFocus); } // Restore focus if we had it previously after completing the grid @@ -892,7 +894,7 @@ export class EditorPart extends Part implements IEditorPart { // Remove source if the view is now empty and not already removed if (sourceView.isEmpty && !sourceView.disposed /* could have been disposed already via workbench.editor.closeEmptyGroups setting */) { - this.removeGroup(sourceView); + this.removeGroup(sourceView, true); } return targetView; @@ -1335,7 +1337,7 @@ export class MainEditorPart extends EditorPart { @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IHostService hostService: IHostService ) { - super(editorPartsView, Parts.EDITOR_PART, '', instantiationService, themeService, configurationService, storageService, layoutService, hostService); + super(editorPartsView, Parts.EDITOR_PART, '', false, instantiationService, themeService, configurationService, storageService, layoutService, hostService); } } @@ -1357,7 +1359,7 @@ export class AuxiliaryEditorPart extends EditorPart implements IAuxiliaryEditorP @IHostService hostService: IHostService ) { const id = AuxiliaryEditorPart.COUNTER++; - super(editorPartsView, `workbench.parts.auxiliaryEditor.${id}`, groupsLabel, instantiationService, themeService, configurationService, storageService, layoutService, hostService); + super(editorPartsView, `workbench.parts.auxiliaryEditor.${id}`, groupsLabel, true, instantiationService, themeService, configurationService, storageService, layoutService, hostService); } protected override saveState(): void { diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index 42bffcb5bb2..7b52a57a303 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -61,7 +61,7 @@ export class EditorParts extends Disposable implements IEditorGroupsService, IEd disposables.add(this.instantiationService.createInstance(WindowTitle, auxiliaryWindow.window, editorPart)); - disposables.add(auxiliaryWindow.onWillLayout(dimension => editorPart.layout(dimension.width, dimension.height, 0, 0))); + disposables.add(auxiliaryWindow.onDidLayout(dimension => editorPart.layout(dimension.width, dimension.height, 0, 0))); auxiliaryWindow.layout(); this._onDidAddGroup.fire(editorPart.activeGroup); diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 8872ad93a95..250063acea5 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -32,7 +32,7 @@ import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdenti import { Color } from 'vs/base/common/color'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { MergeGroupMode, IMergeGroupOptions, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent } from 'vs/base/browser/dom'; +import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent, getWindow } from 'vs/base/browser/dom'; import { localize } from 'vs/nls'; import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView } from 'vs/workbench/browser/parts/editor/editor'; import { CloseOneEditorAction, UnpinEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; @@ -2069,7 +2069,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { // Check for URI transfer else { const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: false }); - dropHandler.handleDrop(e, () => this.groupView, () => this.groupView.focus(), options); + dropHandler.handleDrop(e, getWindow(this.titleContainer), () => this.groupView, () => this.groupView.focus(), options); } } diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index dfa29995ecc..9b3a2f90ba0 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -147,6 +147,8 @@ export class TextDiffEditor extends AbstractTextEditor imp originalEditable: !resolvedDiffEditorModel.originalModel?.isReadonly() }); + control.handleInitialized(); + // Start to measure input lifecycle this.inputLifecycleStopWatch = new StopWatch(false); } catch (error) { diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index 0a3a8b7931f..b6e16473762 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -110,11 +110,11 @@ export class GlobalCompositeBar extends Disposable { } private registerListeners(): void { - // Extension registration - const disposables = this._register(new DisposableStore()); - this._register(this.extensionService.onDidRegisterExtensions(() => { - this.storageService.onDidChangeValue(StorageScope.PROFILE, AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, disposables)(() => this.toggleAccountsActivity(), this, disposables); - })); + this.extensionService.whenInstalledExtensionsRegistered().then(() => { + if (!this._store.isDisposed) { + this._register(this.storageService.onDidChangeValue(StorageScope.PROFILE, AccountsActivityActionViewItem.ACCOUNTS_VISIBILITY_PREFERENCE_KEY, this._store)(() => this.toggleAccountsActivity())); + } + }); } create(parent: HTMLElement): void { diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 4f55e904f65..c4c160fcfe6 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -185,9 +185,10 @@ } .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact.progress-badge .badge-content::before { - mask-size: 12px; - -webkit-mask-size: 12px; - top: 2px; + mask-size: 11px; + -webkit-mask-size: 11px; + top: 3px; + left: 1px; } /* active item indicator */ diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 51115350f94..8ff0cdd81d1 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -65,7 +65,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente private registerListeners(): void { this._register(this.model.onDidChangeNotification(e => this.onDidChangeNotification(e))); - this._register(this.layoutService.onDidLayout(dimension => this.layout(Dimension.lift(dimension)))); + this._register(this.layoutService.onDidLayoutMainContainer(dimension => this.layout(Dimension.lift(dimension)))); this._register(this.notificationService.onDidChangeDoNotDisturbMode(() => this.onDidChangeDoNotDisturbMode())); } diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index f6fa63e49f2..c3f0490bedd 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -93,7 +93,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast private registerListeners(): void { // Layout - this._register(this.layoutService.onDidLayout(dimension => this.layout(Dimension.lift(dimension)))); + this._register(this.layoutService.onDidLayoutMainContainer(dimension => this.layout(Dimension.lift(dimension)))); // Delay some tasks until after we have restored // to reduce UI pressure from the startup phase diff --git a/src/vs/workbench/browser/parts/paneCompositeBar.ts b/src/vs/workbench/browser/parts/paneCompositeBar.ts index d444c8d565e..7777a0ace1c 100644 --- a/src/vs/workbench/browser/parts/paneCompositeBar.ts +++ b/src/vs/workbench/browser/parts/paneCompositeBar.ts @@ -407,7 +407,7 @@ export class PaneCompositeBar extends Disposable { } // Check cache only if extensions are not yet registered and current window is not native (desktop) remote connection window - if (this.options.icon && !this.hasExtensionsRegistered && !(this.environmentService.remoteAuthority && isNative)) { + if (!this.hasExtensionsRegistered && !(this.part === Parts.SIDEBAR_PART && this.environmentService.remoteAuthority && isNative)) { cachedViewContainer = cachedViewContainer || this.cachedViewContainers.find(({ id }) => id === viewContainerId); // Show builtin ViewContainer if not registered yet diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 05e15a7698b..00b10081e8a 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -42,7 +42,7 @@ export class PanelPart extends AbstractPaneCompositePart { get preferredHeight(): number | undefined { // Don't worry about titlebar or statusbar visibility // The difference is minimal and keeps this function clean - return this.layoutService.dimension.height * 0.4; + return this.layoutService.mainContainerDimension.height * 0.4; } get preferredWidth(): number | undefined { diff --git a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts index e9b53aef62e..8b2176b6466 100644 --- a/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/commandCenterControl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { reset } from 'vs/base/browser/dom'; +import { isActiveDocument, reset } from 'vs/base/browser/dom'; import { BaseActionViewItem, IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IHoverDelegate } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; @@ -57,8 +57,8 @@ export class CommandCenterControl { } }); - this._disposables.add(quickInputService.onShow(this._setVisibility.bind(this, false))); - this._disposables.add(quickInputService.onHide(this._setVisibility.bind(this, true))); + this._disposables.add(Event.filter(quickInputService.onShow, () => isActiveDocument(this.element), this._disposables)(this._setVisibility.bind(this, false))); + this._disposables.add(Event.filter(quickInputService.onHide, () => isActiveDocument(this.element), this._disposables)(this._setVisibility.bind(this, true))); this._disposables.add(titleToolbar); } diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 5e97cbebd85..48b791514f2 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -54,7 +54,7 @@ export class WindowTitle extends Disposable { private readonly editorService: IEditorService; constructor( - private readonly targetWindow: Window & typeof globalThis, + private readonly targetWindow: Window, editorGroupsContainer: IEditorGroupsContainer | 'main', @IConfigurationService protected readonly configurationService: IConfigurationService, @IEditorService editorService: IEditorService, diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 70bbf980dab..3e90066ce50 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -484,7 +484,7 @@ const registry = Registry.as(ConfigurationExtensions.Con 'type': 'string', 'enum': ['side', 'top', 'hidden'], 'default': 'side', - 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the activity bar. It can either show to the `side` or `top` (requires `{0}`) of the primary side bar or `hidden`.", '#window.commandCenter#'), + 'markdownDescription': localize({ comment: ['This is the description for a setting'], key: 'activityBarLocation' }, "Controls the location of the activity bar. It can either show to the `side` or `top` (requires {0} set to {1}) of the primary side bar or `hidden`.", '`#window.titleBarStyle#`', '`custom`'), 'enumDescriptions': [ localize('workbench.activityBar.location.side', "Show the activity bar to the side of the primary side bar."), localize('workbench.activityBar.location.top', "Show the activity bar on top of the primary side bar."), diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index a844abc4607..3757bda1c58 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -265,7 +265,7 @@ export class AccessibleView extends Disposable { return; } const delegate: IContextViewDelegate = { - getAnchor: () => { return { x: (getActiveWindow().innerWidth / 2) - ((Math.min(this._layoutService.dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.offset.quickPickTop }; }, + getAnchor: () => { return { x: (getActiveWindow().innerWidth / 2) - ((Math.min(this._layoutService.activeContainerDimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH)) / 2), y: this._layoutService.activeContainerOffset.quickPickTop }; }, render: (container) => { container.classList.add('accessible-view-container'); return this._render(provider!, container, showAccessibleViewHelp); @@ -531,7 +531,7 @@ export class AccessibleView extends Disposable { } })); disposableStore.add(this._editorWidget.onDidContentSizeChange(() => this._layout())); - disposableStore.add(this._layoutService.onDidLayout(() => this._layout())); + disposableStore.add(this._layoutService.onDidLayoutActiveContainer(() => this._layout())); return disposableStore; } @@ -552,7 +552,7 @@ export class AccessibleView extends Disposable { } private _layout(): void { - const dimension = this._layoutService.dimension; + const dimension = this._layoutService.activeContainerDimension; const maxHeight = dimension.height && dimension.height * .4; const height = Math.min(maxHeight, this._editorWidget.getContentHeight()); const width = Math.min(dimension.width * 0.62 /* golden cut */, DIMENSIONS.MAX_WIDTH); diff --git a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts index 9319c21af7f..013341c5f18 100644 --- a/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/unfocusedViewDimmingContribution.ts @@ -5,7 +5,7 @@ import { createStyleSheet } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { clamp } from 'vs/base/common/numbers'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -13,6 +13,7 @@ import { AccessibilityWorkbenchSettingId, ViewDimUnfocusedOpacityProperties } fr export class UnfocusedViewDimmingContribution extends Disposable implements IWorkbenchContribution { private _styleElement?: HTMLStyleElement; + private _styleElementDisposables: DisposableStore | undefined = undefined; constructor( @IConfigurationService configurationService: IConfigurationService, @@ -74,14 +75,16 @@ export class UnfocusedViewDimmingContribution extends Disposable implements IWor private _getStyleElement(): HTMLStyleElement { if (!this._styleElement) { - this._styleElement = createStyleSheet(); + this._styleElementDisposables = new DisposableStore(); + this._styleElement = createStyleSheet(undefined, undefined, this._styleElementDisposables); this._styleElement.className = 'accessibilityUnfocusedViewOpacity'; } return this._styleElement; } private _removeStyleElement(): void { - this._styleElement?.remove(); + this._styleElementDisposables?.dispose(); + this._styleElementDisposables = undefined; this._styleElement = undefined; } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 95d19ee7643..4a5e08e11f5 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -406,8 +406,8 @@ export function registerChatCodeBlockActions() { super({ id: 'workbench.action.chat.runInTerminal', title: { - value: localize('interactive.runInTerminal.label', "Run in Terminal"), - original: 'Run in Terminal' + value: localize('interactive.runInTerminal.label', "Insert into Terminal"), + original: 'Insert into Terminal' }, precondition: CONTEXT_PROVIDER_EXISTS, f1: true, diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 257e3f418ca..cbe23536b18 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -176,6 +176,9 @@ class ChatAccessibleViewContribution extends Disposable { } responseContent = welcomeReplyContents.join('\n'); } + if (!responseContent && 'errorDetails' in focusedItem && focusedItem.errorDetails) { + responseContent = focusedItem.errorDetails.message; + } if (!responseContent) { return false; } diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 0f7cf6d28b3..ee80bfeacb9 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -63,8 +63,8 @@ export interface IQuickChatOpenOptions { export interface IChatAccessibilityService { readonly _serviceBrand: undefined; - acceptRequest(): void; - acceptResponse(response?: IChatResponseViewModel | string): void; + acceptRequest(): number; + acceptResponse(response: IChatResponseViewModel | string | undefined, requestId: number): void; } export interface IChatCodeBlockInfo { diff --git a/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts b/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts index 3eca2e294d3..b7173483cd1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts @@ -16,34 +16,34 @@ export class ChatAccessibilityService extends Disposable implements IChatAccessi declare readonly _serviceBrand: undefined; - private _responsePendingAudioCue: IDisposable | undefined; - private _hasReceivedRequest: boolean = false; + private _pendingCueMap: Map = new Map(); + private _runOnceScheduler: RunOnceScheduler; private _lastResponse: string | undefined; + private _requestId: number = 0; constructor(@IAudioCueService private readonly _audioCueService: IAudioCueService) { super(); this._register(this._runOnceScheduler = new RunOnceScheduler(() => { - if (!this._hasReceivedRequest) { - this._responsePendingAudioCue = this._audioCueService.playAudioCueLoop(AudioCue.chatResponsePending, CHAT_RESPONSE_PENDING_AUDIO_CUE_LOOP_MS); - } + this._pendingCueMap.set(this._requestId, this._audioCueService.playAudioCueLoop(AudioCue.chatResponsePending, CHAT_RESPONSE_PENDING_AUDIO_CUE_LOOP_MS)); }, CHAT_RESPONSE_PENDING_ALLOWANCE_MS)); } - acceptRequest(): void { + acceptRequest(): number { + this._requestId++; this._audioCueService.playAudioCue(AudioCue.chatRequestSent, { allowManyInParallel: true }); this._runOnceScheduler.schedule(); + return this._requestId; } - acceptResponse(response?: IChatResponseViewModel | string): void { - this._hasReceivedRequest = true; + acceptResponse(response: IChatResponseViewModel | string | undefined, requestId: number): void { + this._pendingCueMap.get(requestId)?.dispose(); + this._pendingCueMap.delete(requestId); const isPanelChat = typeof response !== 'string'; - this._responsePendingAudioCue?.dispose(); this._runOnceScheduler?.cancel(); const responseContent = typeof response === 'string' ? response : response?.response.asString(); if (this._lastResponse === responseContent) { return; } this._audioCueService.playAudioCue(AudioCue.chatResponseReceived, { allowManyInParallel: true }); - this._hasReceivedRequest = false; if (!response) { return; } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index c1811430075..82c067d6b1a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -372,6 +372,9 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { + this._register(this.layoutService.onDidLayoutMainContainer(() => { if (this.widget.visible) { this.widget.updateDynamicChatTreeItemLayout(2, this.maxHeight); } else { diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index b5710ac7c2b..d7526a49c87 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -531,7 +531,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this._onDidAcceptInput.fire(); const editorValue = this.getInput(); - this._chatAccessibilityService.acceptRequest(); + const requestId = this._chatAccessibilityService.acceptRequest(); const input = !opts ? editorValue : 'query' in opts ? opts.query : `${opts.prefix} ${editorValue}`; @@ -544,10 +544,10 @@ export class ChatWidget extends Disposable implements IChatWidget { result.responseCompletePromise.then(async () => { const responses = this.viewModel?.getItems().filter(isResponseVM); const lastResponse = responses?.[responses.length - 1]; - this._chatAccessibilityService.acceptResponse(lastResponse); + this._chatAccessibilityService.acceptResponse(lastResponse, requestId); }); } else { - this._chatAccessibilityService.acceptResponse(); + this._chatAccessibilityService.acceptResponse(undefined, requestId); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 8dbd2c26bfc..48edae1a29a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -1012,7 +1012,7 @@ export class DebugSession implements IDebugSession, IDisposable { } if (this.configurationService.getValue('debug').focusWindowOnBreak && !this.workbenchEnvironmentService.extensionTestsLocationURI) { - await this.hostService.focus({ force: true /* Application may not be active */ }); + await this.hostService.focus(window, { force: true /* Application may not be active */ }); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index fec7dc3a786..83372ce9637 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -72,7 +72,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { super(themeService); this.$el = dom.$('div.debug-toolbar'); - this.$el.style.top = `${layoutService.offset.top}px`; + this.$el.style.top = `${layoutService.mainContainerOffset.top}px`; this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); @@ -163,7 +163,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { // Prevent default to stop editor selecting text #8524 mouseMoveEvent.preventDefault(); // Reduce x by width of drag handle to reduce jarring #16604 - this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - (this.layoutService.offset.top)); + this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - (this.layoutService.mainContainerOffset.top)); }); const mouseUpListener = dom.addDisposableGenericMouseUpListener(window, (e: MouseEvent) => { @@ -209,7 +209,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } private setYCoordinate(y = this.yCoordinate): void { - const titlebarOffset = this.layoutService.offset.top; + const titlebarOffset = this.layoutService.mainContainerOffset.top; this.$el.style.top = `${titlebarOffset + y}px`; this.yCoordinate = y; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index fc8215dff5b..9ff8b5fd618 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -513,7 +513,7 @@ export class ExtensionsListView extends ViewPane { value = value.replace(/@installed/g, '').replace(/@sort:(\w+)(-\w*)?/g, '').trim().toLowerCase(); - const matchingText = (e: IExtension) => (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1) + const matchingText = (e: IExtension) => (e.name.toLowerCase().indexOf(value) > -1 || e.displayName.toLowerCase().indexOf(value) > -1 || e.description.toLowerCase().indexOf(value) > -1) && (!categories.length || categories.some(category => (e.local && e.local.manifest.categories || []).some(c => c.toLowerCase() === category))); let result; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 3bb04e433a2..55c422a9310 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1928,7 +1928,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension [extension] = await this.getExtensions([{ id: extensionId }], { source: 'uri' }, CancellationToken.None); } if (extension) { - await this.hostService.focus(); + await this.hostService.focus(window); await this.open(extension); } }).then(undefined, error => this.onError(error)); diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index 0ad3bfa636a..10a1453fd05 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -402,7 +402,7 @@ export class ExternalFileImport { ) { } - async import(target: ExplorerItem, source: DragEvent): Promise { + async import(target: ExplorerItem, source: DragEvent, targetWindow: Window): Promise { const cts = new CancellationTokenSource(); // Indicate progress globally @@ -413,7 +413,7 @@ export class ExternalFileImport { cancellable: true, title: localize('copyingFiles', "Copying...") }, - async () => await this.doImport(target, source, cts.token), + async () => await this.doImport(target, source, targetWindow, cts.token), () => cts.dispose(true) ); @@ -423,7 +423,7 @@ export class ExternalFileImport { return importPromise; } - private async doImport(target: ExplorerItem, source: DragEvent, token: CancellationToken): Promise { + private async doImport(target: ExplorerItem, source: DragEvent, targetWindow: Window, token: CancellationToken): Promise { // Activate all providers for the resources dropped const candidateFiles = coalesce((await this.instantiationService.invokeFunction(accessor => extractEditorsAndFilesDropData(accessor, source))).map(editor => editor.resource)); @@ -438,7 +438,7 @@ export class ExternalFileImport { } // Pass focus to window - this.hostService.focus(); + this.hostService.focus(targetWindow); // Handle folders by adding to workspace if we are in workspace context and if dropped on top const folders = resolvedFiles.filter(resolvedFile => resolvedFile.success && resolvedFile.stat?.isDirectory).map(resolvedFile => ({ uri: resolvedFile.stat!.resource })); diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index 4f450ff50e0..aba7df4c942 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -20,7 +20,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { isWeb } from 'vs/base/common/platform'; -import { DragAndDropObserver } from 'vs/base/browser/dom'; +import { DragAndDropObserver, getWindow } from 'vs/base/browser/dom'; import { ILocalizedString } from 'vs/platform/action/common/action'; export class EmptyView extends ViewPane { @@ -60,7 +60,7 @@ export class EmptyView extends ViewPane { onDrop: e => { container.style.backgroundColor = ''; const dropHandler = this.instantiationService.createInstance(ResourcesDropHandler, { allowWorkspaceOpen: !isWeb || isTemporaryWorkspace(this.contextService.getWorkspace()) }); - dropHandler.handleDrop(e); + dropHandler.handleDrop(e, getWindow(container)); }, onDragEnter: () => { const color = this.themeService.getColorTheme().getColor(listDropBackground); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index d567ec80916..ab9459d78a8 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -1260,7 +1260,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { // Use local file import when supported if (!isWeb || (isTemporaryWorkspace(this.contextService.getWorkspace()) && WebFileSystemAccess.supported(window))) { const fileImport = this.instantiationService.createInstance(ExternalFileImport); - await fileImport.import(resolvedTarget, originalEvent); + await fileImport.import(resolvedTarget, originalEvent, window); } // Otherwise fallback to browser based file upload else { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 5a881944d4f..96edb1c4abe 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -718,7 +718,7 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop group, () => group.focus(), { index }); + this.dropHandler.handleDrop(originalEvent, window, () => group, () => group.focus(), { index }); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index ac8682ce0ef..71f0d80893f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -24,7 +24,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { EditResponse, EmptyResponse, ErrorResponse, ExpansionState, IInlineChatSessionService, MarkdownResponse, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { ReplyResponse, EmptyResponse, ErrorResponse, ExpansionState, IInlineChatSessionService, Session, SessionExchange, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { EditModeStrategy, LivePreviewStrategy, LiveStrategy, PreviewStrategy, ProgressingEditsOptions } from 'vs/workbench/contrib/inlineChat/browser/inlineChatStrategies'; import { InlineChatZoneWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_HAS_ACTIVE_REQUEST, CTX_INLINE_CHAT_LAST_FEEDBACK, IInlineChatRequest, IInlineChatResponse, INLINE_CHAT_ID, EditMode, InlineChatResponseFeedbackKind, CTX_INLINE_CHAT_LAST_RESPONSE_TYPE, InlineChatResponseType, CTX_INLINE_CHAT_DID_EDIT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, InlineChateResponseTypes, CTX_INLINE_CHAT_RESPONSE_TYPES, CTX_INLINE_CHAT_USER_DID_EDIT, IInlineChatProgressItem } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; @@ -273,8 +273,12 @@ export class InlineChatController implements IEditorContribution { if (this._strategy) { needsMargin = this._strategy.needsMargin(); } - this._zone.value.setWidgetMargins(widgetPosition, !needsMargin ? 0 : undefined); - this._zone.value.show(widgetPosition); + if (!this._zone.value.position) { + this._zone.value.setWidgetMargins(widgetPosition, !needsMargin ? 0 : undefined); + this._zone.value.show(widgetPosition); + } else { + this._zone.value.updatePositionAndHeight(widgetPosition); + } } protected async _nextState(state: State, options: InlineChatRunOptions): Promise { @@ -510,7 +514,7 @@ export class InlineChatController implements IEditorContribution { if (message & Message.RERUN_INPUT && this._activeSession.lastExchange) { const { lastExchange } = this._activeSession; this._activeSession.addInput(lastExchange.prompt.retry()); - if (lastExchange.response instanceof EditResponse) { + if (lastExchange.response instanceof ReplyResponse) { try { this._ignoreModelContentChanged = true; await this._strategy.undoChanges(lastExchange.response.modelAltVersionId); @@ -650,12 +654,12 @@ export class InlineChatController implements IEditorContribution { let a11yResponse: string | undefined; const a11yVerboseInlineChat = this._configurationService.getValue('accessibility.verbosity.inlineChat') === true; - this._chatAccessibilityService.acceptRequest(); + const requestId = this._chatAccessibilityService.acceptRequest(); const task = this._activeSession.provider.provideResponse(this._activeSession.session, request, progress, requestCts.token); this._log('request started', this._activeSession.provider.debugName, this._activeSession.session, request); - let response: EditResponse | MarkdownResponse | ErrorResponse | EmptyResponse; + let response: ReplyResponse | ErrorResponse | EmptyResponse; let reply: IInlineChatResponse | null | undefined; try { this._zone.value.widget.updateProgress(true); @@ -669,22 +673,25 @@ export class InlineChatController implements IEditorContribution { } await progress.drain(); - if (reply?.type === InlineChatResponseType.Message) { - markdownContents.appendMarkdown(reply.message.value); - response = new MarkdownResponse(this._activeSession.textModelN.uri, reply, markdownContents); - a11yResponse = renderMarkdownAsPlaintext(markdownContents); - } else if (reply) { - const editResponse = new EditResponse(this._activeSession.textModelN.uri, modelAltVersionIdNow, reply, progressEdits); - for (let i = progressEdits.length; i < editResponse.allLocalEdits.length; i++) { - await this._makeChanges(editResponse.allLocalEdits[i], undefined); - } - response = editResponse; - a11yResponse = this._strategy.checkChanges(editResponse) && a11yVerboseInlineChat - ? localize('editResponseMessage', "Review proposed changes in the diff editor.") - : ''; - } else { + if (!reply) { response = new EmptyResponse(); a11yResponse = localize('empty', "No results, please refine your input and try again"); + } else { + + const replyResponse = response = new ReplyResponse(reply, markdownContents, this._activeSession.textModelN.uri, modelAltVersionIdNow, progressEdits); + if (reply.type === InlineChatResponseType.Message) { + markdownContents.appendMarkdown(reply.message.value); + } + + for (let i = progressEdits.length; i < replyResponse.allLocalEdits.length; i++) { + await this._makeChanges(replyResponse.allLocalEdits[i], undefined); + } + + const a11yMessageResponse = renderMarkdownAsPlaintext(replyResponse.mdContent); + + a11yResponse = this._strategy.checkChanges(replyResponse) && a11yVerboseInlineChat + ? a11yMessageResponse ? localize('editResponseMessage2', "{0}, also review proposed changes in the diff editor.", a11yMessageResponse) : localize('editResponseMessage', "Review proposed changes in the diff editor.") + : a11yMessageResponse; } } catch (e) { @@ -697,7 +704,7 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.updateInfo(''); this._zone.value.widget.updateToolbar(true); this._log('request took', requestClock.elapsed(), this._activeSession.provider.debugName); - this._chatAccessibilityService.acceptResponse(a11yResponse); + this._chatAccessibilityService.acceptResponse(a11yResponse, requestId); } progressiveEditsCts.dispose(true); @@ -705,7 +712,7 @@ export class InlineChatController implements IEditorContribution { msgListener.dispose(); typeListener.dispose(); - if (request.live && !(response instanceof EditResponse)) { + if (request.live && !(response instanceof ReplyResponse)) { this._strategy?.undoChanges(modelAltVersionIdNow); } @@ -727,7 +734,7 @@ export class InlineChatController implements IEditorContribution { assertType(this._strategy); const { response } = this._activeSession.lastExchange!; - if (response instanceof EditResponse) { + if (response instanceof ReplyResponse) { // edit response -> complex... this._zone.value.widget.updateMarkdownMessage(undefined); @@ -774,16 +781,14 @@ export class InlineChatController implements IEditorContribution { const { response } = this._activeSession.lastExchange!; - this._ctxLastResponseType.set(response instanceof EditResponse || response instanceof MarkdownResponse - ? response.raw.type - : undefined); + this._ctxLastResponseType.set(response instanceof ReplyResponse ? response.raw.type : undefined); let responseTypes: InlineChateResponseTypes | undefined; for (const { response } of this._activeSession.exchanges) { - const thisType = response instanceof MarkdownResponse - ? InlineChateResponseTypes.OnlyMessages : response instanceof EditResponse - ? InlineChateResponseTypes.OnlyEdits : undefined; + const thisType = response instanceof ReplyResponse + ? response.responseType + : undefined; if (responseTypes === undefined) { responseTypes = thisType; @@ -807,17 +812,11 @@ export class InlineChatController implements IEditorContribution { this._zone.value.widget.updateStatus(response.message, { classes: ['error'] }); } - } else if (response instanceof MarkdownResponse) { - // clear status, show MD message - + } else if (response instanceof ReplyResponse) { + // real response -> complex... this._zone.value.widget.updateStatus(''); this._zone.value.widget.updateMarkdownMessage(response.mdContent); - this._zone.value.widget.updateToolbar(true); this._activeSession.lastExpansionState = this._zone.value.widget.expansionState; - - } else if (response instanceof EditResponse) { - // edit response -> complex... - this._zone.value.widget.updateMarkdownMessage(undefined); this._zone.value.widget.updateToolbar(true); const canContinue = this._strategy.checkChanges(response); @@ -895,10 +894,6 @@ export class InlineChatController implements IEditorContribution { } } - private static isEditOrMarkdownResponse(response: EditResponse | MarkdownResponse | EmptyResponse | ErrorResponse | undefined): response is EditResponse | MarkdownResponse { - return response instanceof EditResponse || response instanceof MarkdownResponse; - } - // ---- controller API acceptInput(): void { @@ -956,7 +951,7 @@ export class InlineChatController implements IEditorContribution { } viewInChat() { - if (this._activeSession?.lastExchange?.response instanceof MarkdownResponse) { + if (this._activeSession?.lastExchange?.response instanceof ReplyResponse) { this._instaService.invokeFunction(showMessageResponse, this._activeSession.lastExchange.prompt.value, this._activeSession.lastExchange.response.mdContent.value); } } @@ -970,7 +965,7 @@ export class InlineChatController implements IEditorContribution { } feedbackLast(helpful: boolean) { - if (this._activeSession?.lastExchange && InlineChatController.isEditOrMarkdownResponse(this._activeSession.lastExchange.response)) { + if (this._activeSession?.lastExchange && this._activeSession.lastExchange.response instanceof ReplyResponse) { const kind = helpful ? InlineChatResponseFeedbackKind.Helpful : InlineChatResponseFeedbackKind.Unhelpful; this._activeSession.provider.handleInlineChatResponseFeedback?.(this._activeSession.session, this._activeSession.lastExchange.response.raw, kind); this._ctxLastFeedbackKind.set(helpful ? 'helpful' : 'unhelpful'); @@ -985,7 +980,7 @@ export class InlineChatController implements IEditorContribution { } acceptSession(): void { - if (this._activeSession?.lastExchange && InlineChatController.isEditOrMarkdownResponse(this._activeSession.lastExchange.response)) { + if (this._activeSession?.lastExchange && this._activeSession.lastExchange.response instanceof ReplyResponse) { this._activeSession.provider.handleInlineChatResponseFeedback?.(this._activeSession.session, this._activeSession.lastExchange.response.raw, InlineChatResponseFeedbackKind.Accepted); } this._messages.fire(Message.ACCEPT_SESSION); @@ -999,7 +994,7 @@ export class InlineChatController implements IEditorContribution { const diff = await this._editorWorkerService.computeDiff(this._activeSession.textModel0.uri, this._activeSession.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); result = this._activeSession.asChangedText(diff?.changes ?? []); - if (this._activeSession.lastExchange && InlineChatController.isEditOrMarkdownResponse(this._activeSession.lastExchange.response)) { + if (this._activeSession.lastExchange && this._activeSession.lastExchange.response instanceof ReplyResponse) { this._activeSession.provider.handleInlineChatResponseFeedback?.(this._activeSession.session, this._activeSession.lastExchange.response.raw, InlineChatResponseFeedbackKind.Undone); } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts index 53ecd4bd9ae..b518fdf294b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatDecorations.ts @@ -7,7 +7,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { IActiveCodeEditor, ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { GlyphMarginLane, IModelDecorationsChangeAccessor, TrackedRangeStickiness } from 'vs/editor/common/model'; +import { GlyphMarginLane, IModelDecorationOptions, IModelDecorationsChangeAccessor, TrackedRangeStickiness } from 'vs/editor/common/model'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { localize } from 'vs/nls'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; @@ -30,8 +30,9 @@ const GUTTER_INLINE_CHAT_ICON = registerIcon('inline-chat', Codicon.sparkle, loc export class InlineChatDecorationsContribution extends Disposable implements IEditorContribution { private _gutterDecorationID: string | undefined; + private _inlineChatKeybinding: string | undefined; private readonly _localToDispose = new DisposableStore(); - private readonly _gutterDecoration: ModelDecorationOptions; + private readonly _gutterDecoration: IModelDecorationOptions; public static readonly GUTTER_SETTING_ID = 'inlineChat.showGutterIcon'; private static readonly GUTTER_ICON_CLASSNAME = 'codicon-inline-chat'; @@ -41,14 +42,12 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd @IInlineChatService private readonly _inlineChatService: IInlineChatService, @IInlineChatSessionService private readonly _inlineChatSessionService: IInlineChatSessionService, @IConfigurationService private readonly _configurationService: IConfigurationService, - @IKeybindingService readonly _keybindingService: IKeybindingService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, ) { super(); - const keybindings = _keybindingService.lookupKeybinding('inlineChat.start')?.getLabel(); this._gutterDecoration = ModelDecorationOptions.register({ description: 'inline-chat-decoration', glyphMarginClassName: ThemeIcon.asClassName(GUTTER_INLINE_CHAT_ICON), - glyphMarginHoverMessage: new MarkdownString(LOCALIZED_START_INLINE_CHAT_STRING + (keybindings ? ` [${keybindings}]` : '')), glyphMargin: { position: GlyphMarginLane.Left }, stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, }); @@ -60,9 +59,23 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd })); this._register(this._inlineChatService.onDidChangeProviders(() => this._onEnablementOrModelChanged())); this._register(this._editor.onDidChangeModel(() => this._onEnablementOrModelChanged())); + this._register(this._keybindingService.onDidUpdateKeybindings(() => { + this._updateDecorationHover(); + this._onEnablementOrModelChanged(); + })); + this._updateDecorationHover(); this._onEnablementOrModelChanged(); } + private _updateDecorationHover(): void { + const keybinding = this._keybindingService.lookupKeybinding('inlineChat.start')?.getLabel() ?? undefined; + if (this._inlineChatKeybinding === keybinding) { + return; + } + this._inlineChatKeybinding = keybinding; + this._gutterDecoration.glyphMarginHoverMessage = new MarkdownString(keybinding ? localize('runWithKeybinding', 'Start Inline Chat [{0}]', keybinding) : LOCALIZED_START_INLINE_CHAT_STRING); + } + private _onEnablementOrModelChanged(): void { // cancels the scheduler, removes editor listeners / removes decoration this._localToDispose.clear(); @@ -145,6 +158,11 @@ export class InlineChatDecorationsContribution extends Disposable implements IEd } GutterActionsRegistry.registerGutterActionsGenerator(({ lineNumber, editor, accessor }, result) => { + const inlineChatService = accessor.get(IInlineChatService); + const noProviders = Iterable.isEmpty(inlineChatService.getAllProvider()); + if (noProviders) { + return; + } const configurationService = accessor.get(IConfigurationService); result.push(new Action( 'inlineChat.toggleShowGutterIcon', diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts index c1081af9cb1..31bf2d50717 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget.ts @@ -21,7 +21,7 @@ import { LineRange } from 'vs/editor/common/core/lineRange'; import { LineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { Position } from 'vs/editor/common/core/position'; import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; -import { IEditorDecorationsCollection, ScrollType } from 'vs/editor/common/editorCommon'; +import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { ILogService } from 'vs/platform/log/common/log'; import { lineRangeAsRange, invertLineRange } from 'vs/workbench/contrib/inlineChat/browser/utils'; import { ResourceLabel } from 'vs/workbench/browser/labels'; @@ -57,7 +57,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { @ILogService private readonly _logService: ILogService, @IAccessibilityService private readonly accessibilityService: IAccessibilityService, ) { - super(editor, { showArrow: false, showFrame: false, isResizeable: false, isAccessible: true, allowUnlimitedHeight: true, showInHiddenAreas: true, ordinal: 10000 + 1 }); + super(editor, { showArrow: false, showFrame: false, isResizeable: false, isAccessible: true, allowUnlimitedHeight: true, showInHiddenAreas: true, keepEditorSelection: true, ordinal: 10000 + 1 }); super.create(); assertType(editor.hasModel()); @@ -203,7 +203,7 @@ export class InlineChatLivePreviewWidget extends ZoneWidget { this._hideEditorRanges(this._diffEditor.getOriginalEditor(), ranges.originalDiffHidden); this._hideEditorRanges(this._diffEditor.getModifiedEditor(), ranges.modifiedDiffHidden); - this._diffEditor.revealLine(ranges.modifiedHidden.startLineNumber, ScrollType.Immediate); + // this._diffEditor.revealLine(ranges.modifiedHidden.startLineNumber, ScrollType.Immediate); const lineCountModified = ranges.modifiedHidden.length; const lineCountOriginal = ranges.originalHidden.length; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 891b3c02da2..98a1f36d345 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -9,7 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import { ResourceEdit, ResourceFileEdit, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { TextEdit } from 'vs/editor/common/languages'; import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; -import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatResponse, IInlineChatService } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; +import { EditMode, IInlineChatSessionProvider, IInlineChatSession, IInlineChatBulkEditResponse, IInlineChatEditResponse, IInlineChatMessageResponse, IInlineChatResponse, IInlineChatService, InlineChatResponseType, InlineChateResponseTypes } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IRange, Range } from 'vs/editor/common/core/range'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -226,7 +226,7 @@ export class Session { }; for (const exchange of this._exchange) { const response = exchange.response; - if (response instanceof MarkdownResponse || response instanceof EditResponse) { + if (response instanceof ReplyResponse) { result.exchanges.push({ prompt: exchange.prompt.value, res: response.raw }); } } @@ -258,7 +258,7 @@ export class SessionExchange { constructor( readonly prompt: SessionPrompt, - readonly response: MarkdownResponse | EditResponse | EmptyResponse | ErrorResponse + readonly response: ReplyResponse | EmptyResponse | ErrorResponse ) { } } @@ -279,37 +279,32 @@ export class ErrorResponse { } } -export class MarkdownResponse { - constructor( - readonly localUri: URI, - readonly raw: IInlineChatMessageResponse, - readonly mdContent: IMarkdownString, - ) { } -} - -export class EditResponse { +export class ReplyResponse { readonly allLocalEdits: TextEdit[][] = []; readonly singleCreateFileEdit: { uri: URI; edits: Promise[] } | undefined; readonly workspaceEdits: ResourceEdit[] | undefined; readonly workspaceEditsIncludeLocalEdits: boolean = false; + readonly responseType: InlineChateResponseTypes; + constructor( + readonly raw: IInlineChatBulkEditResponse | IInlineChatEditResponse | IInlineChatMessageResponse, + readonly mdContent: IMarkdownString, localUri: URI, readonly modelAltVersionId: number, - readonly raw: IInlineChatBulkEditResponse | IInlineChatEditResponse, progressEdits: TextEdit[][], ) { this.allLocalEdits.push(...progressEdits); - if (raw.type === 'editorEdit') { + if (raw.type === InlineChatResponseType.EditorEdit) { // this.allLocalEdits.push(raw.edits); this.singleCreateFileEdit = undefined; this.workspaceEdits = undefined; - } else { + } else if (raw.type === InlineChatResponseType.BulkEdit) { // const edits = ResourceEdit.convert(raw.edits); this.workspaceEdits = edits; @@ -351,6 +346,10 @@ export class EditResponse { this.singleCreateFileEdit = undefined; } } + + this.responseType = (this.allLocalEdits.length || this.workspaceEdits) + ? mdContent.value ? InlineChateResponseTypes.Mixed : InlineChateResponseTypes.OnlyEdits + : InlineChateResponseTypes.OnlyMessages; } } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 31359f738a2..41271d9c977 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -27,7 +27,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { IStorageService } from 'vs/platform/storage/common/storage'; import { countWords, getNWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { InlineChatFileCreatePreviewWidget, InlineChatLivePreviewWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatLivePreviewWidget'; -import { EditResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { CTX_INLINE_CHAT_DOCUMENT_CHANGED } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; @@ -36,7 +36,7 @@ export abstract class EditModeStrategy { abstract dispose(): void; - abstract checkChanges(response: EditResponse): boolean; + abstract checkChanges(response: ReplyResponse): boolean; abstract apply(): Promise; @@ -48,7 +48,7 @@ export abstract class EditModeStrategy { abstract undoChanges(altVersionId: number): Promise; - abstract renderChanges(response: EditResponse): Promise; + abstract renderChanges(response: ReplyResponse): Promise; abstract hasFocus(): boolean; @@ -82,7 +82,7 @@ export class PreviewStrategy extends EditModeStrategy { this._ctxDocumentChanged.reset(); } - checkChanges(response: EditResponse): boolean { + checkChanges(response: ReplyResponse): boolean { if (!response.workspaceEdits || response.singleCreateFileEdit) { // preview stategy can handle simple workspace edit (single file create) return true; @@ -93,7 +93,7 @@ export class PreviewStrategy extends EditModeStrategy { async apply() { - if (!(this._session.lastExchange?.response instanceof EditResponse)) { + if (!(this._session.lastExchange?.response instanceof ReplyResponse)) { return; } const editResponse = this._session.lastExchange?.response; @@ -132,7 +132,7 @@ export class PreviewStrategy extends EditModeStrategy { // nothing to do } - override async renderChanges(response: EditResponse): Promise { + override async renderChanges(response: ReplyResponse): Promise { if (response.allLocalEdits.length > 0) { const allEditOperation = response.allLocalEdits.map(edits => edits.map(TextEdit.asEditOperation)); await this._widget.showEditsPreview(this._session.textModel0, this._session.textModelN, allEditOperation); @@ -235,7 +235,7 @@ export class LiveStrategy extends EditModeStrategy { private readonly _inlineDiffDecorations: InlineDiffDecorations; private readonly _store: DisposableStore = new DisposableStore(); - private _lastResponse?: EditResponse; + private _lastResponse?: ReplyResponse; private _editCount: number = 0; constructor( @@ -271,7 +271,7 @@ export class LiveStrategy extends EditModeStrategy { this._inlineDiffDecorations.visible = this._diffEnabled; } - checkChanges(response: EditResponse): boolean { + checkChanges(response: ReplyResponse): boolean { this._lastResponse = response; if (response.singleCreateFileEdit) { // preview stategy can handle simple workspace edit (single file create) @@ -341,7 +341,7 @@ export class LiveStrategy extends EditModeStrategy { } } - override async renderChanges(response: EditResponse) { + override async renderChanges(response: ReplyResponse) { const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: 5000, computeMoves: false }, 'advanced'); this._updateSummaryMessage(diff?.changes ?? []); this._inlineDiffDecorations.update(); @@ -509,7 +509,7 @@ export class LivePreviewStrategy extends LiveStrategy { await this._updateDiffZones(); } - override async renderChanges(response: EditResponse) { + override async renderChanges(response: ReplyResponse) { await this._updateDiffZones(); @@ -525,7 +525,7 @@ export class LivePreviewStrategy extends LiveStrategy { } } -function showSingleCreateFile(accessor: ServicesAccessor, edit: EditResponse) { +function showSingleCreateFile(accessor: ServicesAccessor, edit: ReplyResponse) { const editorService = accessor.get(IEditorService); if (edit.singleCreateFileEdit) { editorService.openEditor({ resource: edit.singleCreateFileEdit.uri }, SIDE_GROUP); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index cc825b900db..e33a5ea2193 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -542,10 +542,11 @@ export class InlineChatWidget { updateMarkdownMessage(message: IMarkdownString | undefined) { this._codeBlockDisposables.clear(); - this._elements.markdownMessage.classList.toggle('hidden', !message); + const hasMessage = message?.value; + this._elements.markdownMessage.classList.toggle('hidden', !hasMessage); let expansionState: ExpansionState; let textContent: string | undefined = undefined; - if (!message) { + if (!hasMessage) { reset(this._elements.message); this._ctxMessageCropState.reset(); expansionState = ExpansionState.NOT_CROPPED; diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index bc8f99f4408..abd8a72e7bb 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -113,8 +113,8 @@ suite('InteractiveChatController', function () { } }], [IChatAccessibilityService, new class extends mock() { - override acceptResponse(response?: IChatResponseViewModel): void { } - override acceptRequest(): void { } + override acceptResponse(response: IChatResponseViewModel | undefined, requestId: number): void { } + override acceptRequest(): number { return -1; } }], [IAccessibleViewService, new class extends mock() { override getOpenAriaHint(verbositySettingKey: AccessibilityVerbositySettingId): string | null { diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts index 69b4fac15a9..8d88a891a5b 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/conflictActions.ts @@ -6,7 +6,7 @@ import { $, createStyleSheet, h, isInShadowDOM, reset } from 'vs/base/browser/dom'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { hash } from 'vs/base/common/hash'; -import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { autorun, derived, IObservable, transaction } from 'vs/base/common/observable'; import { ICodeEditor, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; import { EditorOption, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; @@ -32,13 +32,9 @@ export class ConflictActionsFactory extends Disposable { this._styleElement = createStyleSheet( isInShadowDOM(this._editor.getContainerDomNode()) ? this._editor.getContainerDomNode() - : undefined + : undefined, undefined, this._store ); - this._register(toDisposable(() => { - this._styleElement.remove(); - })); - this._updateLensStyle(); } diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 6fc13ccd0ef..f330ff686e1 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -723,8 +723,7 @@ export class DirtyDiffController extends Disposable implements DirtyDiffContribu ) { super(); this.enabled = !contextKeyService.getContextKeyValue('isInDiffEditor'); - this.stylesheet = dom.createStyleSheet(); - this._register(toDisposable(() => this.stylesheet.remove())); + this.stylesheet = dom.createStyleSheet(undefined, undefined, this._store); if (this.enabled) { this.isDirtyDiffVisible = isDirtyDiffVisible.bindTo(contextKeyService); @@ -1557,8 +1556,7 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor @ITextFileService private readonly textFileService: ITextFileService ) { super(); - this.stylesheet = dom.createStyleSheet(); - this._register(toDisposable(() => this.stylesheet.parentElement!.removeChild(this.stylesheet))); + this.stylesheet = dom.createStyleSheet(undefined, undefined, this._store); const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.diffDecorations')); this._register(onDidChangeConfiguration(this.onDidChangeConfiguration, this)); diff --git a/src/vs/workbench/contrib/splash/browser/partsSplash.ts b/src/vs/workbench/contrib/splash/browser/partsSplash.ts index d8a0657f0f0..08fd7e5a8d5 100644 --- a/src/vs/workbench/contrib/splash/browser/partsSplash.ts +++ b/src/vs/workbench/contrib/splash/browser/partsSplash.ts @@ -37,7 +37,7 @@ export class PartsSplash { @IConfigurationService private readonly _configService: IConfigurationService, @ISplashStorageService private readonly _partSplashService: ISplashStorageService ) { - Event.once(_layoutService.onDidLayout)(() => { + Event.once(_layoutService.onDidLayoutMainContainer)(() => { this._removePartsSplash(); perf.mark('code/didRemovePartsSplash'); }, undefined, this._disposables); diff --git a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index f3cea74430c..170b8e82ff0 100644 --- a/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -11,7 +11,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; +import { Severity, INotificationService, NotificationPriority } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { platform } from 'vs/base/common/process'; @@ -85,7 +85,7 @@ class NPSContribution implements IWorkbenchContribution { storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.APPLICATION, StorageTarget.USER); } }], - { sticky: true } + { sticky: true, priority: NotificationPriority.URGENT } ); } } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index e58fdc8ba41..037abbeff2c 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -2387,10 +2387,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const taskSystemInfo: ITaskSystemInfo | undefined = this._getTaskSystemInfo(workspaceFolder.uri.scheme); const problemReporter = new ProblemReporter(this._outputChannel); - if (!taskSystemInfo) { - problemReporter.fatal(nls.localize('TaskSystem.workspaceFolderError', 'Workspace folder was undefined')); - return true; - } const parseResult = TaskConfig.parse(workspaceFolder, this._workspace, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, config, problemReporter, source, this._contextKeyService, isRecentTask); let hasErrors = false; if (!parseResult.validationStatus.isOK() && (parseResult.validationStatus.state !== ValidationState.Info)) { diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index c222df0deb8..5c30094e8b9 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -53,14 +53,26 @@ outline: 0 !important; } -.xterm.dev-mode .xterm-helper-textarea { +.monaco-workbench .xterm.dev-mode .xterm-helper-textarea { z-index: 36 !important; - opacity: 1 !important; - left: 0 !important; - width: fit-content !important; + opacity: 0.8 !important; + right: 0 !important; + left: initial !important; + width: 50% !important; + background-color: var(--vscode-terminal-background, var(--vscode-panel-background)); + color: var(--vscode-terminal-foreground); + transform: translateY(-100%); } .monaco-workbench .xterm.dev-mode .xterm-helper-textarea:focus { - opacity: 1 !important; + opacity: 0.8 !important; +} +.monaco-workbench .xterm.dev-mode .xterm-helpers { + /* This could maybe be done outside of .dev-mode, but I'm scared to break something */ + left: 0; + right: 0; +} +.monaco-workbench .xterm.dev-mode .xterm-helper-textarea:hover { + opacity: 0.25 !important; } .monaco-workbench .xterm .xterm-helper-textarea:focus { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 3b98991958d..9d5896ce8f6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -64,6 +64,7 @@ import { Iterable } from 'vs/base/common/iterator'; import { AccessibleViewProviderId, accessibleViewCurrentProviderId, accessibleViewIsShown, accessibleViewOnLastLine } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { isKeyboardEvent, isMouseEvent, isPointerEvent } from 'vs/base/browser/dom'; import { editorGroupToColumn } from 'vs/workbench/services/editor/common/editorGroupColumn'; +import { InstanceContext } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; export const switchTerminalActionViewItemSeparator = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500'; export const switchTerminalShowTabsTitle = localize('showTerminalTabs', "Show Tabs"); @@ -143,7 +144,7 @@ export class TerminalLaunchHelpAction extends Action { * - `precondition`: TerminalContextKeys.processSupported */ export function registerTerminalAction( - options: IAction2Options & { run: (c: ITerminalServicesCollection, accessor: ServicesAccessor, args?: unknown) => void | Promise } + options: IAction2Options & { run: (c: ITerminalServicesCollection, accessor: ServicesAccessor, args?: unknown, args2?: unknown) => void | Promise } ): IDisposable { // Set defaults options.f1 = options.f1 ?? true; @@ -158,8 +159,68 @@ export function registerTerminalAction( constructor() { super(strictOptions as IAction2Options); } - run(accessor: ServicesAccessor, args?: unknown) { - return runFunc(getTerminalServices(accessor), accessor, args); + run(accessor: ServicesAccessor, args?: unknown, args2?: unknown) { + return runFunc(getTerminalServices(accessor), accessor, args, args2); + } + }); +} + +function parseActionArgs(args?: unknown): InstanceContext[] | undefined { + if (Array.isArray(args)) { + if (args.every(e => e instanceof InstanceContext)) { + return args as InstanceContext[]; + } + } else if (args instanceof InstanceContext) { + return [args]; + } + return undefined; +} +/** + * A wrapper around {@link registerTerminalAction} that runs a callback for all currently selected + * instances provided in the action context. This falls back to the active instance if there are no + * contextual instances provided. + */ +export function registerContextualInstanceAction( + options: IAction2Options & { + /** + * When specified, only this type of active instance will be used when there are no + * contextual instances. + */ + activeInstanceType?: 'view' | 'editor'; + run: (instance: ITerminalInstance, c: ITerminalServicesCollection, accessor: ServicesAccessor, args?: unknown) => void | Promise; + /** + * A callback to run after the the `run` callbacks have completed. + * @param instances The selected instance(s) that the command was run on. + */ + runAfter?: (instances: ITerminalInstance[], c: ITerminalServicesCollection, accessor: ServicesAccessor, args?: unknown) => void | Promise; + } +): IDisposable { + const originalRun = options.run; + return registerTerminalAction({ + ...options, + run: async (c, accessor, focusedInstanceArgs, allInstanceArgs) => { + let instances = getSelectedInstances2(accessor, allInstanceArgs); + if (!instances) { + const activeInstance = ( + options.activeInstanceType === 'view' + ? c.groupService + : options.activeInstanceType === 'editor' ? + c.editorService + : c.service + ).activeInstance; + if (!activeInstance) { + return; + } + instances = [activeInstance]; + } + const results: (Promise | void)[] = []; + for (const instance of instances) { + results.push(originalRun(instance, c, accessor, focusedInstanceArgs)); + } + await Promise.all(results); + if (options.runAfter) { + options.runAfter(instances, c, accessor, focusedInstanceArgs); + } } }); } @@ -284,28 +345,13 @@ export function registerTerminalActions() { } }); - registerActiveInstanceAction({ + registerContextualInstanceAction({ id: TerminalCommandId.MoveToEditor, title: terminalStrings.moveToEditor, - precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.terminalEditorActive.toNegated(), TerminalContextKeys.viewShowing), - run: (activeInstance, c) => c.service.moveToEditor(activeInstance) - }); - - registerTerminalAction({ - id: TerminalCommandId.MoveToEditorActiveTab, - title: terminalStrings.moveToEditor, - f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.isOpen), - run: async (c, accessor) => { - const selectedInstances = getSelectedInstances(accessor); - if (!selectedInstances || selectedInstances.length === 0) { - return; - } - for (const instance of selectedInstances) { - c.service.moveToEditor(instance); - } - selectedInstances[selectedInstances.length - 1].focus(); - } + activeInstanceType: 'view', + run: (instance, c) => c.service.moveToEditor(instance), + runAfter: (instances) => instances.at(-1)?.focus() }); registerTerminalAction({ @@ -698,14 +744,6 @@ export function registerTerminalActions() { run: (c, _, args: unknown) => getResourceOrActiveInstance(c, args)?.changeIcon() }); - registerTerminalAction({ - id: TerminalCommandId.ChangeIconPanel, - title: terminalStrings.changeIcon, - f1: false, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: (c) => c.groupService.activeInstance?.changeIcon() - }); - registerTerminalAction({ id: TerminalCommandId.ChangeIconActiveTab, title: terminalStrings.changeIcon, @@ -721,14 +759,6 @@ export function registerTerminalActions() { run: (c, _, args) => getResourceOrActiveInstance(c, args)?.changeColor() }); - registerTerminalAction({ - id: TerminalCommandId.ChangeColorPanel, - title: terminalStrings.changeColor, - f1: false, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: (c) => c.groupService.activeInstance?.changeColor() - }); - registerTerminalAction({ id: TerminalCommandId.ChangeColorActiveTab, title: terminalStrings.changeColor, @@ -744,14 +774,6 @@ export function registerTerminalActions() { run: (c, accessor, args) => renameWithQuickPick(c, accessor, args) }); - registerTerminalAction({ - id: TerminalCommandId.RenamePanel, - title: terminalStrings.rename, - f1: false, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: (c, accessor) => renameWithQuickPick(c, accessor) - }); - registerTerminalAction({ id: TerminalCommandId.RenameActiveTab, title: terminalStrings.rename, @@ -767,20 +789,27 @@ export function registerTerminalActions() { precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.tabsSingularSelection), run: async (c, accessor) => { const notificationService = accessor.get(INotificationService); - const instance = getSelectedInstances(accessor)?.[0]; - if (!instance) { + const instances = getSelectedInstances(accessor); + const firstInstance = instances?.[0]; + if (!firstInstance) { return; } - c.service.setEditingTerminal(instance); - c.service.setEditable(instance, { + c.service.setEditingTerminal(firstInstance); + c.service.setEditable(firstInstance, { validationMessage: value => validateTerminalName(value), onFinish: async (value, success) => { // Cancel editing first as instance.rename will trigger a rerender automatically - c.service.setEditable(instance, null); + c.service.setEditable(firstInstance, null); c.service.setEditingTerminal(undefined); if (success) { + const promises: Promise[] = []; + for (const instance of instances) { + promises.push((async () => { + await instance.rename(value); + })()); + } try { - await instance.rename(value); + await Promise.all(promises); } catch (e) { notificationService.error(e); } @@ -1078,27 +1107,14 @@ export function registerTerminalActions() { } }); - registerActiveInstanceAction({ + registerContextualInstanceAction({ id: TerminalCommandId.Unsplit, title: terminalStrings.unsplit, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: (activeInstance, c) => c.groupService.unsplitInstance(activeInstance) - }); - - registerTerminalAction({ - id: TerminalCommandId.UnsplitActiveTab, - title: terminalStrings.unsplit, - f1: false, - precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), - run: async (c, accessor) => { - const instances = getSelectedInstances(accessor); - // should not even need this check given the context key - // but TS complains - if (instances?.length === 1) { - const group = c.groupService.getGroupForInstance(instances[0]); - if (group && group?.terminalInstances.length > 1) { - c.groupService.unsplitInstance(instances[0]); - } + run: async (instance, c) => { + const group = c.groupService.getGroupForInstance(instance); + if (group && group?.terminalInstances.length > 1) { + c.groupService.unsplitInstance(instance); } } }); @@ -1291,7 +1307,7 @@ export function registerTerminalActions() { run: (c, accessor) => accessor.get(ICommandService).executeCommand(CLOSE_EDITOR_COMMAND_ID) }); - registerTerminalAction({ + registerContextualInstanceAction({ id: TerminalCommandId.KillActiveTab, title: terminalStrings.kill, f1: false, @@ -1305,22 +1321,8 @@ export function registerTerminalActions() { weight: KeybindingWeight.WorkbenchContrib, when: TerminalContextKeys.tabsFocus }, - run: async (c, accessor) => { - const selectedInstances = getSelectedInstances(accessor); - if (!selectedInstances) { - return; - } - const listService = accessor.get(IListService); - const disposePromises: Promise[] = []; - for (const instance of selectedInstances) { - disposePromises.push(c.service.safeDisposeTerminal(instance)); - } - await Promise.all(disposePromises); - if (c.service.instances.length > 0) { - c.groupService.focusTabs(); - listService.lastFocusedList?.focusNext(); - } - } + run: (instance, c) => c.service.safeDisposeTerminal(instance), + runAfter: (instances, c) => c.groupService.focusTabs() }); registerTerminalAction({ @@ -1372,24 +1374,16 @@ export function registerTerminalActions() { run: (activeInstance) => activeInstance.setFixedDimensions() }); - registerActiveInstanceAction({ + registerContextualInstanceAction({ id: TerminalCommandId.SizeToContentWidth, - title: { value: localize('workbench.action.terminal.sizeToContentWidth', "Toggle Size to Content Width"), original: 'Toggle Size to Content Width' }, + title: terminalStrings.toggleSizeToContentWidth, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.isOpen), keybinding: { primary: KeyMod.Alt | KeyCode.KeyZ, weight: KeybindingWeight.WorkbenchContrib, when: TerminalContextKeys.focus }, - run: (instancactiveInstance) => instancactiveInstance.toggleSizeToContentWidth() - }); - - registerTerminalAction({ - id: TerminalCommandId.SizeToContentWidthActiveTab, - title: terminalStrings.toggleSizeToContentWidth, - f1: false, - precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus), - run: (c, accessor) => getSelectedInstances(accessor)?.[0].toggleSizeToContentWidth() + run: (instance) => instance.toggleSizeToContentWidth() }); registerTerminalAction({ @@ -1608,10 +1602,29 @@ interface IRemoteTerminalPick extends IQuickPickItem { term: IRemoteTerminalAttachTarget; } -function getSelectedInstances(accessor: ServicesAccessor): ITerminalInstance[] | undefined { +function getSelectedInstances2(accessor: ServicesAccessor, args?: unknown): ITerminalInstance[] | undefined { + const terminalService = accessor.get(ITerminalService); + const result: ITerminalInstance[] = []; + const context = parseActionArgs(args); + if (context && context.length > 0) { + for (const instanceContext of context) { + const instance = terminalService.getInstanceFromId(instanceContext.instanceId); + if (instance) { + result.push(instance); + } + } + if (result.length > 0) { + return result; + } + } + return undefined; +} + +function getSelectedInstances(accessor: ServicesAccessor, args?: unknown, args2?: unknown): ITerminalInstance[] | undefined { const listService = accessor.get(IListService); const terminalService = accessor.get(ITerminalService); const terminalGroupService = accessor.get(ITerminalGroupService); + const result: ITerminalInstance[] = []; // Get inline tab instance if (terminalGroupService.lastAccessedMenu === 'inline-tab') { @@ -1625,20 +1638,19 @@ function getSelectedInstances(accessor: ServicesAccessor): ITerminalInstance[] | } const selections = listService.lastFocusedList.getSelection(); const focused = listService.lastFocusedList.getFocus(); - const instances: ITerminalInstance[] = []; if (focused.length === 1 && !selections.includes(focused[0])) { // focused length is always a max of 1 // if the focused one is not in the selected list, return that item - instances.push(terminalService.getInstanceFromIndex(focused[0]) as ITerminalInstance); - return instances; + result.push(terminalService.getInstanceFromIndex(focused[0]) as ITerminalInstance); + return result; } // multi-select for (const selection of selections) { - instances.push(terminalService.getInstanceFromIndex(selection) as ITerminalInstance); + result.push(terminalService.getInstanceFromIndex(selection) as ITerminalInstance); } - return instances; + return result; } export function validateTerminalName(name: string): { content: string; severity: Severity } | null { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalContextMenu.ts b/src/vs/workbench/contrib/terminal/browser/terminalContextMenu.ts index 4c36da7dd25..d8fcacd6ce2 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalContextMenu.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalContextMenu.ts @@ -14,25 +14,30 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ISerializedTerminalInstanceContext } from 'vs/workbench/contrib/terminal/common/terminal'; -class InstanceContext { - private _instanceId: number; +/** + * A context that is passed to actions as arguments to represent the terminal instance(s) being + * acted upon. + */ +export class InstanceContext { + readonly instanceId: number; constructor(instance: ITerminalInstance) { - this._instanceId = instance.instanceId; + // Only store the instance to avoid contexts holding on to disposed instances. + this.instanceId = instance.instanceId; } toJSON(): ISerializedTerminalInstanceContext { return { $mid: MarshalledId.TerminalContext, - instanceId: this._instanceId + instanceId: this.instanceId }; } } -class TerminalContextActionRunner extends ActionRunner { +export class TerminalContextActionRunner extends ActionRunner { // eslint-disable-next-line @typescript-eslint/naming-convention - protected override async runAction(action: IAction, context?: InstanceContext): Promise { + protected override async runAction(action: IAction, context?: InstanceContext | InstanceContext[]): Promise { if (Array.isArray(context) && context.every(e => e instanceof InstanceContext)) { // arg1: The (first) focused instance // arg2: All selected instances diff --git a/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts b/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts index 07b474a831f..1ea310c533f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalIcon.ts @@ -15,7 +15,7 @@ import { ITerminalInstance } from 'vs/workbench/contrib/terminal/browser/termina import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal'; import { ansiColorMap } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { createStyleSheet } from 'vs/base/browser/dom'; -import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; export function getColorClass(colorKey: string): string; @@ -50,8 +50,9 @@ export function getStandardColors(colorTheme: IColorTheme): string[] { } export function createColorStyleElement(colorTheme: IColorTheme): IDisposable { + const disposable = new DisposableStore(); const standardColors = getStandardColors(colorTheme); - const styleElement = createStyleSheet(); + const styleElement = createStyleSheet(undefined, undefined, disposable); let css = ''; for (const colorKey of standardColors) { const colorClass = getColorClass(colorKey); @@ -64,7 +65,7 @@ export function createColorStyleElement(colorTheme: IColorTheme): IDisposable { } } styleElement.textContent = css; - return toDisposable(() => styleElement.remove()); + return disposable; } export function getColorStyleContent(colorTheme: IColorTheme, editor?: boolean): string { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts index e0beb9ec636..dfdaa699110 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalMenus.ts @@ -569,7 +569,7 @@ export function setupTerminalMenus(): void { id: MenuId.TerminalTabContext, item: { command: { - id: TerminalCommandId.MoveToEditorActiveTab, + id: TerminalCommandId.MoveToEditor, title: terminalStrings.moveToEditor.value }, group: ContextMenuGroup.Create, @@ -610,8 +610,8 @@ export function setupTerminalMenus(): void { id: MenuId.TerminalTabContext, item: { command: { - id: TerminalCommandId.SizeToContentWidthActiveTab, - title: localize('workbench.action.terminal.sizeToContentWidthInstance', "Toggle Size to Content Width") + id: TerminalCommandId.SizeToContentWidth, + title: terminalStrings.toggleSizeToContentWidth }, group: ContextMenuGroup.Edit } @@ -631,7 +631,7 @@ export function setupTerminalMenus(): void { id: MenuId.TerminalTabContext, item: { command: { - id: TerminalCommandId.UnsplitActiveTab, + id: TerminalCommandId.Unsplit, title: terminalStrings.unsplit.value }, when: ContextKeyExpr.and(TerminalContextKeys.tabsSingularSelection, TerminalContextKeys.splitTerminal), diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 4c1950fb74a..3ef8dcf7dd8 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -49,6 +49,7 @@ import { defaultInputBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Emitter } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; import { getColorForSeverity } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; +import { TerminalContextActionRunner } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; const $ = DOM.$; @@ -275,6 +276,7 @@ class TerminalTabsRenderer implements IListRenderer action instanceof MenuItemAction ? this._instantiationService.createInstance(MenuEntryActionViewItem, action, undefined) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index 2c775e39639..de489c3a4dd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -47,6 +47,7 @@ import { Event } from 'vs/base/common/event'; import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { InstanceContext, TerminalContextActionRunner } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; export class TerminalViewPane extends ViewPane { private _parentDomElement: HTMLElement | undefined; @@ -260,7 +261,7 @@ export class TerminalViewPane extends ViewPane { case TerminalCommandId.Focus: { if (action instanceof MenuItemAction) { const actions: IAction[] = []; - createAndFillInContextMenuActions(this._singleTabMenu, undefined, actions); + createAndFillInContextMenuActions(this._singleTabMenu, { shouldForwardArgs: true }, actions); return this._instantiationService.createInstance(SingleTerminalTabActionViewItem, action, actions); } } @@ -515,9 +516,14 @@ class SingleTerminalTabActionViewItem extends MenuEntryActionViewItem { private _openContextMenu() { this._contextMenuService.showContextMenu({ + actionRunner: new TerminalContextActionRunner(), getAnchor: () => this.element!, getActions: () => this._actions, - getActionsContext: () => this.label + // The context is always the active instance in the terminal view + getActionsContext: () => { + const instance = this._terminalGroupService.activeInstance; + return instance ? [new InstanceContext(instance)] : []; + } }); } } diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 03979a19aa9..69930c200e8 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -431,7 +431,6 @@ export const enum TerminalCommandId { SplitInActiveWorkspace = 'workbench.action.terminal.splitInActiveWorkspace', ShowQuickFixes = 'workbench.action.terminal.showQuickFixes', Unsplit = 'workbench.action.terminal.unsplit', - UnsplitActiveTab = 'workbench.action.terminal.unsplitActiveTab', JoinActiveTab = 'workbench.action.terminal.joinActiveTab', Join = 'workbench.action.terminal.join', Relaunch = 'workbench.action.terminal.relaunch', @@ -465,13 +464,10 @@ export const enum TerminalCommandId { Clear = 'workbench.action.terminal.clear', ClearSelection = 'workbench.action.terminal.clearSelection', ChangeIcon = 'workbench.action.terminal.changeIcon', - ChangeIconPanel = 'workbench.action.terminal.changeIconPanel', ChangeIconActiveTab = 'workbench.action.terminal.changeIconActiveTab', ChangeColor = 'workbench.action.terminal.changeColor', - ChangeColorPanel = 'workbench.action.terminal.changeColorPanel', ChangeColorActiveTab = 'workbench.action.terminal.changeColorActiveTab', Rename = 'workbench.action.terminal.rename', - RenamePanel = 'workbench.action.terminal.renamePanel', RenameActiveTab = 'workbench.action.terminal.renameActiveTab', RenameWithArgs = 'workbench.action.terminal.renameWithArg', FindFocus = 'workbench.action.terminal.focusFind', @@ -491,7 +487,6 @@ export const enum TerminalCommandId { AttachToSession = 'workbench.action.terminal.attachToSession', DetachSession = 'workbench.action.terminal.detachSession', MoveToEditor = 'workbench.action.terminal.moveToEditor', - MoveToEditorActiveTab = 'workbench.action.terminal.moveToEditorActiveTab', MoveToTerminalPanel = 'workbench.action.terminal.moveToTerminalPanel', SetDimensions = 'workbench.action.terminal.setDimensions', ClearPreviousSessionHistory = 'workbench.action.terminal.clearPreviousSessionHistory', diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts index 649ef681da3..7847e7b1087 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminal.accessibility.contribution.ts @@ -217,7 +217,7 @@ class FocusAccessibleBufferAction extends Action2 { constructor() { super({ id: TerminalCommandId.FocusAccessibleBuffer, - title: { value: localize('workbench.action.terminal.focusAccessibleBuffer', 'Focus Accessible Buffer'), original: 'Focus Accessible Buffer' }, + title: { value: localize('workbench.action.terminal.focusAccessibleBuffer', 'Focus Accessible Terminal View'), original: 'Focus Accessible Terminal View' }, precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), keybinding: [ { diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 7815d8776e8..61f0d1355ca 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -75,7 +75,7 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc provideContent(): string { const content = []; - content.push(this._descriptionForCommand(TerminalCommandId.FocusAccessibleBuffer, localize('focusAccessibleBuffer', 'The Focus Accessible Buffer ({0}) command enables screen readers to read terminal contents.'), localize('focusAccessibleBufferNoKb', 'The Focus Accessible Buffer command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.'))); + content.push(this._descriptionForCommand(TerminalCommandId.FocusAccessibleBuffer, localize('focusAccessibleTerminalView', 'The Focus Accessible Terminal View ({0}) command enables screen readers to read terminal contents.'), localize('focusAccessibleTerminalViewNoKb', 'The Focus Terminal Accessible View command enables screen readers to read terminal contents and is currently not triggerable by a keybinding.'))); content.push(localize('preserveCursor', 'Customize the behavior of the cursor when toggling between the terminal and accessible view with `terminal.integrated.accessibleViewPreserveCursorPosition.`')); if (this._instance.shellType === WindowsShellType.CommandPrompt) { content.push(localize('commandPromptMigration', "Consider using powershell instead of command prompt for an improved experience")); diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts index 61b17085bb6..35851c4137e 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService.ts @@ -6,6 +6,7 @@ import { Emitter } from 'vs/base/common/event'; import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { localize } from 'vs/nls'; +import { ILogService } from 'vs/platform/log/common/log'; import { ITerminalCommandSelector } from 'vs/platform/terminal/common/terminal'; import { ITerminalQuickFixService, ITerminalQuickFixProvider, ITerminalQuickFixProviderSelector } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; @@ -28,7 +29,9 @@ export class TerminalQuickFixService implements ITerminalQuickFixService { readonly extensionQuickFixes: Promise>; - constructor() { + constructor( + @ILogService private readonly _logService: ILogService, + ) { this.extensionQuickFixes = new Promise((r) => quickFixExtensionPoint.setHandler(fixes => { r(fixes.filter(c => isProposedApiEnabled(c.description, 'terminalQuickFixProvider')).map(c => { if (!c.value) { @@ -61,7 +64,8 @@ export class TerminalQuickFixService implements ITerminalQuickFixService { this._providers.set(id, provider); const selector = this._selectors.get(id); if (!selector) { - throw new Error(`No registered selector for ID: ${id}`); + this._logService.error(`No registered selector for ID: ${id}`); + return; } this._onDidRegisterProvider.fire({ selector, provider }); }); diff --git a/src/vs/workbench/contrib/webviewPanel/browser/webviewIconManager.ts b/src/vs/workbench/contrib/webviewPanel/browser/webviewIconManager.ts index 7ffb3adf213..f24731711d3 100644 --- a/src/vs/workbench/contrib/webviewPanel/browser/webviewIconManager.ts +++ b/src/vs/workbench/contrib/webviewPanel/browser/webviewIconManager.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; @@ -19,6 +19,7 @@ export class WebviewIconManager implements IDisposable { private readonly _icons = new Map(); private _styleElement: HTMLStyleElement | undefined; + private _styleElementDisposable: DisposableStore | undefined; constructor( @ILifecycleService private readonly _lifecycleService: ILifecycleService, @@ -32,13 +33,15 @@ export class WebviewIconManager implements IDisposable { } dispose() { - this._styleElement?.remove(); + this._styleElementDisposable?.dispose(); + this._styleElementDisposable = undefined; this._styleElement = undefined; } private get styleElement(): HTMLStyleElement { if (!this._styleElement) { - this._styleElement = dom.createStyleSheet(); + this._styleElementDisposable = new DisposableStore(); + this._styleElement = dom.createStyleSheet(undefined, undefined, this._styleElementDisposable); this._styleElement.className = 'webview-icons'; } return this._styleElement; diff --git a/src/vs/workbench/electron-sandbox/actions/windowActions.ts b/src/vs/workbench/electron-sandbox/actions/windowActions.ts index d7471d3cd8f..2e1902cd787 100644 --- a/src/vs/workbench/electron-sandbox/actions/windowActions.ts +++ b/src/vs/workbench/electron-sandbox/actions/windowActions.ts @@ -12,7 +12,7 @@ import { getZoomLevel } from 'vs/base/browser/browser'; import { FileKind } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { IQuickInputService, IQuickInputButton } from 'vs/platform/quickinput/common/quickInput'; +import { IQuickInputService, IQuickInputButton, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -29,8 +29,9 @@ import { isMacintosh } from 'vs/base/common/platform'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { getActiveWindow } from 'vs/base/browser/dom'; -import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService'; +import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; +import { IOpenedAuxiliaryWindow, IOpenedMainWindow, isOpenedAuxiliaryWindow } from 'vs/platform/window/common/window'; export class CloseWindowAction extends Action2 { @@ -64,7 +65,7 @@ export class CloseWindowAction extends Action2 { const window = getActiveWindow(); if (isAuxiliaryWindow(window)) { - return nativeHostService.closeWindowById(await window.vscodeWindowId); + return nativeHostService.closeWindowById(window.vscodeWindowId); } return nativeHostService.closeWindow(); @@ -216,23 +217,70 @@ abstract class BaseSwitchWindow extends Action2 { const languageService = accessor.get(ILanguageService); const nativeHostService = accessor.get(INativeHostService); - const currentWindowId = nativeHostService.windowId; + let currentWindowId: number; + const activeWindow = getActiveWindow(); + if (isAuxiliaryWindow(activeWindow)) { + currentWindowId = activeWindow.vscodeWindowId; + } else { + currentWindowId = nativeHostService.windowId; + } + + const windows = await nativeHostService.getWindows({ includeAuxiliaryWindows: true }); + + const mainWindows = new Set(); + const mapMainWindowToAuxiliaryWindows = new Map>(); + for (const window of windows) { + if (isOpenedAuxiliaryWindow(window)) { + let auxiliaryWindows = mapMainWindowToAuxiliaryWindows.get(window.parentId); + if (!auxiliaryWindows) { + auxiliaryWindows = new Set(); + mapMainWindowToAuxiliaryWindows.set(window.parentId, auxiliaryWindows); + } + auxiliaryWindows.add(window); + } else { + mainWindows.add(window); + } + } + + interface IWindowPickItem extends IQuickPickItem { + readonly windowId: number; + } + + const picks: Array = []; + for (const window of mainWindows) { + const auxiliaryWindows = mapMainWindowToAuxiliaryWindows.get(window.id); + if (mapMainWindowToAuxiliaryWindows.size > 0) { + picks.push({ type: 'separator', payload: -1, label: auxiliaryWindows ? localize('windowGroup', "Window Group") : undefined } as unknown as IWindowPickItem); + } - const windows = await nativeHostService.getWindows(); - const placeHolder = localize('switchWindowPlaceHolder', "Select a window to switch to"); - const picks = windows.map(window => { const resource = window.filename ? URI.file(window.filename) : isSingleFolderWorkspaceIdentifier(window.workspace) ? window.workspace.uri : isWorkspaceIdentifier(window.workspace) ? window.workspace.configPath : undefined; const fileKind = window.filename ? FileKind.FILE : isSingleFolderWorkspaceIdentifier(window.workspace) ? FileKind.FOLDER : isWorkspaceIdentifier(window.workspace) ? FileKind.ROOT_FOLDER : FileKind.FILE; - return { - payload: window.id, + const pick: IWindowPickItem = { + windowId: window.id, label: window.title, ariaLabel: window.dirty ? localize('windowDirtyAriaLabel', "{0}, window with unsaved changes", window.title) : window.title, iconClasses: getIconClasses(modelService, languageService, resource, fileKind), description: (currentWindowId === window.id) ? localize('current', "Current Window") : undefined, buttons: currentWindowId !== window.id ? window.dirty ? [this.closeDirtyWindowAction] : [this.closeWindowAction] : undefined }; - }); - const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.payload === currentWindowId)[0]) + 1) % picks.length; + picks.push(pick); + + if (auxiliaryWindows) { + for (const auxiliaryWindow of auxiliaryWindows) { + const pick: IWindowPickItem = { + windowId: auxiliaryWindow.id, + label: auxiliaryWindow.title, + iconClasses: getIconClasses(modelService, languageService, auxiliaryWindow.filename ? URI.file(auxiliaryWindow.filename) : undefined, FileKind.FILE), + description: (currentWindowId === auxiliaryWindow.id) ? localize('current', "Current Window") : undefined, + buttons: [this.closeWindowAction] + }; + picks.push(pick); + } + } + } + + const placeHolder = localize('switchWindowPlaceHolder', "Select a window to switch to"); + const autoFocusIndex = (picks.indexOf(picks.filter(pick => pick.windowId === currentWindowId)[0]) + 1) % picks.length; const pick = await quickInputService.pick(picks, { contextKey: 'inWindowsPicker', @@ -241,13 +289,13 @@ abstract class BaseSwitchWindow extends Action2 { quickNavigate: this.isQuickNavigate() ? { keybindings: keybindingService.lookupKeybindings(this.desc.id) } : undefined, hideInput: this.isQuickNavigate(), onDidTriggerItemButton: async context => { - await nativeHostService.closeWindowById(context.item.payload); + await nativeHostService.closeWindowById(context.item.windowId); context.removeItem(); } }); if (pick) { - nativeHostService.focusWindow({ targetWindowId: pick.payload }); + nativeHostService.focusWindow({ targetWindowId: pick.windowId }); } } } @@ -368,7 +416,7 @@ export class ExperimentalSplitWindowAction extends Action2 { let activeWindowId: number; const activeWindow = getActiveWindow(); if (isAuxiliaryWindow(activeWindow)) { - activeWindowId = await activeWindow.vscodeWindowId; + activeWindowId = activeWindow.vscodeWindowId; } else { activeWindowId = environmentService.window.id; } diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 0c606c68a2c..bddfc483eb4 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -70,6 +70,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { IUtilityProcessWorkerWorkbenchService } from 'vs/workbench/services/utilityProcess/electron-sandbox/utilityProcessWorkerWorkbenchService'; import { registerWindowDriver } from 'vs/workbench/services/driver/electron-sandbox/driver'; +import { IAuxiliaryWindowService } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; export class NativeWindow extends Disposable { @@ -86,6 +87,8 @@ export class NativeWindow extends Disposable { private isDocumentedEdited = false; + private readonly mainPartEditorService: IEditorService; + constructor( @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -122,10 +125,13 @@ export class NativeWindow extends Disposable { @IBannerService private readonly bannerService: IBannerService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IPreferencesService private readonly preferencesService: IPreferencesService, - @IUtilityProcessWorkerWorkbenchService private readonly utilityProcessWorkerWorkbenchService: IUtilityProcessWorkerWorkbenchService + @IUtilityProcessWorkerWorkbenchService private readonly utilityProcessWorkerWorkbenchService: IUtilityProcessWorkerWorkbenchService, + @IAuxiliaryWindowService private readonly auxiliaryWindowService: IAuxiliaryWindowService ) { super(); + this.mainPartEditorService = editorService.createScoped('main', this._store); + this.registerListeners(); this.create(); } @@ -334,7 +340,7 @@ export class NativeWindow extends Disposable { })); // Listen to visible editor changes - this._register(this.editorService.onDidVisibleEditorsChange(() => this.onDidChangeVisibleEditors())); + this._register(this.mainPartEditorService.onDidVisibleEditorsChange(() => this.onDidChangeVisibleEditors())); // Listen to editor closing (if we run with --wait) const filesToWait = this.environmentService.filesToWait; @@ -344,14 +350,26 @@ export class NativeWindow extends Disposable { // macOS OS integration if (isMacintosh) { - this._register(this.editorService.onDidActiveEditorChange(() => { - const file = EditorResourceAccessor.getOriginalUri(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: Schemas.file }); + const updateRepresentedFilename = (editorService: IEditorService, targetWindowId: number | undefined) => { + const file = EditorResourceAccessor.getOriginalUri(editorService.activeEditor, { supportSideBySide: SideBySideEditor.PRIMARY, filterByScheme: Schemas.file }); // Represented Filename - this.nativeHostService.setRepresentedFilename(file?.fsPath ?? ''); + this.nativeHostService.setRepresentedFilename(file?.fsPath ?? '', { targetWindowId }); - // Custom title menu - this.provideCustomTitleContextMenu(file?.fsPath); + // Custom title menu (main window only currently) + if (typeof targetWindowId !== 'number') { + this.provideCustomTitleContextMenu(file?.fsPath); + } + }; + + this._register(this.mainPartEditorService.onDidActiveEditorChange(() => updateRepresentedFilename(this.mainPartEditorService, undefined))); + + this._register(this.auxiliaryWindowService.onDidOpenAuxiliaryWindow(({ window, disposables }) => { + const auxiliaryWindowEditorPart = this.editorGroupService.getPart(window.container); + if (auxiliaryWindowEditorPart) { + const auxiliaryEditorService = this.editorService.createScoped(auxiliaryWindowEditorPart, disposables); + disposables.add(auxiliaryEditorService.onDidActiveEditorChange(() => updateRepresentedFilename(auxiliaryEditorService, window.window.vscodeWindowId))); + } })); } @@ -585,7 +603,7 @@ export class NativeWindow extends Disposable { // Close when empty: check if we should close the window based on the setting // Overruled by: window has a workspace opened or this window is for extension development // or setting is disabled. Also enabled when running with --wait from the command line. - const visibleEditorPanes = this.editorService.visibleEditorPanes; + const visibleEditorPanes = this.mainPartEditorService.visibleEditorPanes; if (visibleEditorPanes.length === 0 && this.contextService.getWorkbenchState() === WorkbenchState.EMPTY && !this.environmentService.isExtensionDevelopment) { const closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty'); if (closeWhenEmpty || this.environmentService.args.wait) { @@ -595,7 +613,7 @@ export class NativeWindow extends Disposable { } private onDidAllEditorsClose(): void { - const visibleEditorPanes = this.editorService.visibleEditorPanes.length; + const visibleEditorPanes = this.mainPartEditorService.visibleEditorPanes.length; if (visibleEditorPanes === 0) { this.nativeHostService.closeWindow(); } @@ -683,7 +701,7 @@ export class NativeWindow extends Disposable { originalWindowFocus(); if (getActiveWindow() !== window) { - that.nativeHostService.focusWindow(); + that.nativeHostService.focusWindow({ targetWindowId: that.nativeHostService.windowId }); } }; } diff --git a/src/vs/workbench/services/activity/browser/activityService.ts b/src/vs/workbench/services/activity/browser/activityService.ts index 8295fe57470..962af523c69 100644 --- a/src/vs/workbench/services/activity/browser/activityService.ts +++ b/src/vs/workbench/services/activity/browser/activityService.ts @@ -10,6 +10,7 @@ import { IViewDescriptorService, ViewContainer } from 'vs/workbench/common/views import { GLOBAL_ACTIVITY_ID, ACCOUNTS_ACTIVITY_ID } from 'vs/workbench/common/activity'; import { Emitter, Event } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { isUndefined } from 'vs/base/common/types'; class ViewContainerActivityByView extends Disposable { @@ -82,10 +83,10 @@ export class ActivityService extends Disposable implements IActivityService { this.viewContainerActivities.set(viewContainerId, activities); } for (let i = 0; i <= activities.length; i++) { - if (i === activities.length) { + if (i === activities.length || isUndefined(activity.priority)) { activities.push(activity); break; - } else if (activities[i].priority ?? 0 <= (activity.priority ?? 0)) { + } else if (isUndefined(activities[i].priority) || activities[i].priority! <= activity.priority) { activities.splice(i, 0, activity); break; } diff --git a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts index 6cf0b30d30c..909a16a2afd 100644 --- a/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; +import { mark } from 'vs/base/common/performance'; import { Emitter, Event } from 'vs/base/common/event'; -import { Dimension, EventHelper, EventType, addDisposableListener, cloneGlobalStylesheets, copyAttributes, createMetaElement, getActiveWindow, getClientArea, isGlobalStylesheet, position, registerWindow, size, trackAttributes } from 'vs/base/browser/dom'; +import { Dimension, EventHelper, EventType, addDisposableListener, cloneGlobalStylesheets, copyAttributes, createMetaElement, getActiveWindow, getClientArea, isGlobalStylesheet, position, registerWindow, sharedMutationObserver, size, trackAttributes } from 'vs/base/browser/dom'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -34,16 +35,24 @@ export interface IAuxiliaryWindowService { export interface IAuxiliaryWindow extends IDisposable { - readonly onWillLayout: Event; + readonly onDidLayout: Event; readonly onDidClose: Event; - readonly window: Window & typeof globalThis; + readonly window: AuxiliaryWindow; readonly container: HTMLElement; layout(): void; } -export type AuxiliaryWindow = Window & typeof globalThis; +export type AuxiliaryWindow = Window & typeof globalThis & { + readonly vscodeWindowId: number; +}; + +export function isAuxiliaryWindow(obj: unknown): obj is AuxiliaryWindow { + const candidate = obj as AuxiliaryWindow | undefined; + + return !!candidate && Object.hasOwn(candidate, 'vscodeWindowId'); +} export class BrowserAuxiliaryWindowService extends Disposable implements IAuxiliaryWindowService { @@ -51,6 +60,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili private static readonly DEFAULT_SIZE = { width: 800, height: 600 }; + private static WINDOW_IDS = 0; + private readonly _onDidOpenAuxiliaryWindow = this._register(new Emitter()); readonly onDidOpenAuxiliaryWindow = this._onDidOpenAuxiliaryWindow.event; @@ -62,6 +73,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili } async open(options?: { position?: IRectangle }): Promise { + mark('code/auxiliaryWindow/willOpen'); + const disposables = new DisposableStore(); const auxiliaryWindow = await this.doOpen(options); @@ -72,14 +85,14 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili disposables.add(registerWindow(auxiliaryWindow)); disposables.add(toDisposable(() => auxiliaryWindow.close())); - const { container, onWillLayout, onDidClose } = this.create(auxiliaryWindow, disposables); + const { container, onDidLayout, onDidClose } = await this.create(auxiliaryWindow, disposables); - const result = { + const result: IAuxiliaryWindow = { window: auxiliaryWindow, container, - onWillLayout: onWillLayout.event, + onDidLayout: onDidLayout.event, onDidClose: onDidClose.event, - layout: () => onWillLayout.fire(getClientArea(container)), + layout: () => onDidLayout.fire(getClientArea(container)), dispose: () => disposables.dispose() }; @@ -87,6 +100,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili disposables.add(eventDisposables); this._onDidOpenAuxiliaryWindow.fire({ window: result, disposables: eventDisposables }); + mark('code/auxiliaryWindow/didOpen'); + return result; } @@ -118,20 +133,20 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili })).result; } - return auxiliaryWindow?.window; + return auxiliaryWindow?.window as AuxiliaryWindow | undefined; } - protected create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) { - this.patchMethods(auxiliaryWindow); + protected async create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) { + await this.patchMethods(auxiliaryWindow); this.applyMeta(auxiliaryWindow); this.applyCSS(auxiliaryWindow, disposables); const container = this.applyHTML(auxiliaryWindow, disposables); - const { onWillLayout, onDidClose } = this.registerListeners(auxiliaryWindow, container, disposables); + const { onDidLayout, onDidClose } = this.registerListeners(auxiliaryWindow, container, disposables); - return { container, onWillLayout, onDidClose }; + return { container, onDidLayout, onDidClose }; } private applyMeta(auxiliaryWindow: AuxiliaryWindow): void { @@ -151,6 +166,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili } protected applyCSS(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore): void { + mark('code/auxiliaryWindow/willApplyCSS'); + const mapOriginalToClone = new Map(); function cloneNode(originalNode: Node): void { @@ -174,7 +191,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili // Listen to new stylesheets as they are being added or removed in the main window // and apply to child window (including changes to existing stylesheets elements) - const observer = new MutationObserver(mutations => { + disposables.add(sharedMutationObserver.observe(document.head, disposables, { childList: true, subtree: true })(mutations => { for (const mutation of mutations) { if ( mutation.type !== 'childList' || // only interested in added/removed nodes @@ -209,13 +226,13 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili } } } - }); + })); - observer.observe(document.head, { childList: true, subtree: true }); - disposables.add(toDisposable(() => observer.disconnect())); + mark('code/auxiliaryWindow/didApplyCSS'); } private applyHTML(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore): HTMLElement { + mark('code/auxiliaryWindow/willApplyHTML'); // Create workbench container and apply classes const container = document.createElement('div'); @@ -226,6 +243,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili disposables.add(trackAttributes(document.body, auxiliaryWindow.document.body)); disposables.add(trackAttributes(this.layoutService.container, container, ['class'])); // only class attribute + mark('code/auxiliaryWindow/didApplyHTML'); + return container; } @@ -240,16 +259,16 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili e.preventDefault(); })); - const onWillLayout = disposables.add(new Emitter()); + const onDidLayout = disposables.add(new Emitter()); disposables.add(addDisposableListener(auxiliaryWindow, EventType.RESIZE, () => { const dimension = getClientArea(auxiliaryWindow.document.body); position(container, 0, 0, 0, 0, 'relative'); size(container, dimension.width, dimension.height); - onWillLayout.fire(dimension); + onDidLayout.fire(dimension); })); - this._register(addDisposableListener(container, EventType.SCROLL, () => container.scrollTop = 0)); // // Prevent container from scrolling (#55456) + this._register(addDisposableListener(container, EventType.SCROLL, () => container.scrollTop = 0)); // Prevent container from scrolling (#55456) if (isWeb) { disposables.add(addDisposableListener(container, EventType.DROP, e => EventHelper.stop(e, true))); // Prevent default navigation on drop @@ -260,10 +279,21 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili disposables.add(addDisposableListener(auxiliaryWindow.document.body, EventType.DROP, (e: DragEvent) => EventHelper.stop(e))); // Prevent default navigation on drop } - return { onWillLayout, onDidClose }; + return { onDidLayout, onDidClose }; } - protected patchMethods(auxiliaryWindow: AuxiliaryWindow): void { + protected async resolveWindowId(auxiliaryWindow: AuxiliaryWindow): Promise { + return BrowserAuxiliaryWindowService.WINDOW_IDS++; + } + + protected async patchMethods(auxiliaryWindow: AuxiliaryWindow): Promise { + mark('code/auxiliaryWindow/willPatchMethods'); + + // Add a `vscodeWindowId` property to identify auxiliary windows + const resolvedWindowId = await this.resolveWindowId(auxiliaryWindow); + Object.defineProperty(auxiliaryWindow, 'vscodeWindowId', { + get: () => resolvedWindowId + }); // Disallow `createElement` because it would create // HTML Elements in the "wrong" context and break @@ -271,6 +301,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili auxiliaryWindow.document.createElement = function () { throw new Error('Not allowed to create elements in child window JavaScript context. Always use the main window so that "xyz instanceof HTMLElement" continues to work.'); }; + + mark('code/auxiliaryWindow/didPatchMethods'); } } diff --git a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts index 4787b33d38a..eb51b355295 100644 --- a/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts +++ b/src/vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService.ts @@ -5,26 +5,19 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { BrowserAuxiliaryWindowService, IAuxiliaryWindowService, AuxiliaryWindow as BaseAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; -import { getGlobals } from 'vs/base/parts/sandbox/electron-sandbox/globals'; +import { BrowserAuxiliaryWindowService, IAuxiliaryWindowService, AuxiliaryWindow as BrowserAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; +import { ISandboxGlobals } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IWindowsConfiguration } from 'vs/platform/window/common/window'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { INativeHostService } from 'vs/platform/native/common/native'; -import { DeferredPromise } from 'vs/base/common/async'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { getActiveWindow } from 'vs/base/browser/dom'; -type AuxiliaryWindow = BaseAuxiliaryWindow & { - readonly vscodeWindowId: Promise; +type NativeAuxiliaryWindow = BrowserAuxiliaryWindow & { + readonly vscode: ISandboxGlobals; }; -export function isAuxiliaryWindow(obj: unknown): obj is AuxiliaryWindow { - const candidate = obj as AuxiliaryWindow | undefined; - - return candidate?.vscodeWindowId instanceof Promise; -} - export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService { constructor( @@ -36,43 +29,33 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService super(layoutService, dialogService); } - protected override create(auxiliaryWindow: AuxiliaryWindow, disposables: DisposableStore) { + protected override create(auxiliaryWindow: NativeAuxiliaryWindow, disposables: DisposableStore) { // Zoom level const windowConfig = this.configurationService.getValue(); const windowZoomLevel = typeof windowConfig.window?.zoomLevel === 'number' ? windowConfig.window.zoomLevel : 0; - getGlobals(auxiliaryWindow)?.webFrame?.setZoomLevel(windowZoomLevel); + auxiliaryWindow.vscode.webFrame.setZoomLevel(windowZoomLevel); return super.create(auxiliaryWindow, disposables); } - protected override patchMethods(auxiliaryWindow: AuxiliaryWindow): void { - super.patchMethods(auxiliaryWindow); + protected override resolveWindowId(auxiliaryWindow: NativeAuxiliaryWindow): Promise { + return auxiliaryWindow.vscode.ipcRenderer.invoke('vscode:registerAuxiliaryWindow', this.nativeHostService.windowId); + } - // Obtain window identifier - const windowId = new DeferredPromise(); - (async () => { - windowId.complete(await getGlobals(auxiliaryWindow)?.ipcRenderer.invoke('vscode:getWindowId')); - })(); - - // Add a `windowId` property - Object.defineProperty(auxiliaryWindow, 'vscodeWindowId', { - value: windowId.p, - writable: false, - enumerable: false, - configurable: false - }); + protected override async patchMethods(auxiliaryWindow: NativeAuxiliaryWindow): Promise { + await super.patchMethods(auxiliaryWindow); // Enable `window.focus()` to work in Electron by // asking the main process to focus the window. // https://github.com/electron/electron/issues/25578 const that = this; const originalWindowFocus = auxiliaryWindow.focus.bind(auxiliaryWindow); - auxiliaryWindow.focus = async function () { + auxiliaryWindow.focus = function () { originalWindowFocus(); if (getActiveWindow() !== auxiliaryWindow) { - that.nativeHostService.focusWindow({ targetWindowId: await windowId.p }); + that.nativeHostService.focusWindow({ targetWindowId: auxiliaryWindow.vscodeWindowId }); } }; } diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index e725eccdd1b..31f1057999d 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -158,16 +158,15 @@ class DecorationRule { class DecorationStyles { - private readonly _styleElement = createStyleSheet(); - private readonly _decorationRules = new Map(); private readonly _dispoables = new DisposableStore(); + private readonly _styleElement = createStyleSheet(undefined, undefined, this._dispoables); + private readonly _decorationRules = new Map(); constructor(private readonly _themeService: IThemeService) { } dispose(): void { this._dispoables.dispose(); - this._styleElement.remove(); } asDecoration(data: IDecorationData[], onlyChildren: boolean): IDecoration { diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index e3c409ce671..a699a6eef8d 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -493,6 +493,11 @@ export interface IEditorGroupsService extends IEditorGroupsContainer { */ getPart(group: IEditorGroup | GroupIdentifier): IEditorPart; + /** + * Get the editor part that is rooted in the provided container. + */ + getPart(container: unknown /* HTMLElement */): IEditorPart | undefined; + /** * Opens a new window with a full editor part instantiated * in there at the optional position on screen. diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 4f56301f2f9..40f79b6a74c 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -14,7 +14,7 @@ import { isResourceEditorInput, pathsToEditors } from 'vs/workbench/common/edito import { whenEditorClosed } from 'vs/workbench/browser/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService, Verbosity } from 'vs/platform/label/common/label'; -import { ModifierKeyEmitter, getActiveDocument, getActiveWindow, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; +import { ModifierKeyEmitter, getActiveDocument, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { memoize } from 'vs/base/common/decorators'; @@ -201,8 +201,8 @@ export class BrowserHostService extends Disposable implements IHostService { return true; } - async focus(): Promise { - getActiveWindow().focus(); + async focus(window: Window): Promise { + window.focus(); } //#endregion @@ -505,7 +505,7 @@ export class BrowserHostService extends Disposable implements IHostService { } } - async moveTop(window: Window & typeof globalThis): Promise { + async moveTop(window: Window): Promise { // There seems to be no API to bring a window to front in browsers } diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index 569f40e6c60..fb2eb102556 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -46,7 +46,7 @@ export interface IHostService { * focused application which may not be VSCode. It may not be supported * in all environments. */ - focus(options?: { force: boolean }): Promise; + focus(window: Window, options?: { force: boolean }): Promise; //#endregion @@ -72,7 +72,7 @@ export interface IHostService { /** * Bring a window to the front and restore it if needed. */ - moveTop(window: Window & typeof globalThis): Promise; + moveTop(window: Window): Promise; //#endregion diff --git a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts index 9c1cd113aaf..d33a776105a 100644 --- a/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-sandbox/nativeHostService.ts @@ -14,7 +14,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { NativeHostService } from 'vs/platform/native/electron-sandbox/nativeHostService'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { IMainProcessService } from 'vs/platform/ipc/common/mainProcessService'; -import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/electron-sandbox/auxiliaryWindowService'; +import { isAuxiliaryWindow } from 'vs/workbench/services/auxiliaryWindow/browser/auxiliaryWindowService'; import { getActiveDocument, getWindowsCount, onDidRegisterWindow, trackFocus } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { memoize } from 'vs/base/common/decorators'; @@ -133,12 +133,12 @@ class WorkbenchHostService extends Disposable implements IHostService { return this.nativeHostService.toggleFullScreen(); } - async moveTop(window: Window & typeof globalThis): Promise { + async moveTop(window: Window): Promise { if (getWindowsCount() <= 1) { return; // does not apply when only one window is opened } - return this.nativeHostService.moveWindowTop(isAuxiliaryWindow(window) ? { targetWindowId: await window.vscodeWindowId } : undefined); + return this.nativeHostService.moveWindowTop(isAuxiliaryWindow(window) ? { targetWindowId: window.vscodeWindowId } : undefined); } //#endregion @@ -146,8 +146,11 @@ class WorkbenchHostService extends Disposable implements IHostService { //#region Lifecycle - focus(options?: { force: boolean }): Promise { - return this.nativeHostService.focusWindow(options); + focus(window: Window, options?: { force: boolean }): Promise { + return this.nativeHostService.focusWindow({ + force: options?.force, + targetWindowId: isAuxiliaryWindow(window) ? window.vscodeWindowId : this.nativeHostService.windowId + }); } restart(): Promise { diff --git a/src/vs/workbench/services/url/electron-sandbox/urlService.ts b/src/vs/workbench/services/url/electron-sandbox/urlService.ts index 3b3000abce6..6e3662033e6 100644 --- a/src/vs/workbench/services/url/electron-sandbox/urlService.ts +++ b/src/vs/workbench/services/url/electron-sandbox/urlService.ts @@ -70,7 +70,7 @@ export class RelayURLService extends NativeURLService implements IURLHandler, IO if (result) { this.logService.trace('URLService#handleURL(): handled', uri.toString(true)); - await this.nativeHostService.focusWindow({ force: true /* Application may not be active */ }); + await this.nativeHostService.focusWindow({ force: true /* Application may not be active */, targetWindowId: this.nativeHostService.windowId }); } else { this.logService.trace('URLService#handleURL(): not handled', uri.toString(true)); } diff --git a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts index a3548351314..ad00669d146 100644 --- a/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-sandbox/workspaceEditingService.ts @@ -138,7 +138,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi } override async isValidTargetWorkspacePath(workspaceUri: URI): Promise { - const windows = await this.nativeHostService.getWindows(); + const windows = await this.nativeHostService.getWindows({ includeAuxiliaryWindows: false }); // Prevent overwriting a workspace that is currently opened in another window if (windows.some(window => isWorkspaceIdentifier(window.workspace) && this.uriIdentityService.extUri.isEqual(window.workspace.configPath, workspaceUri))) { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 12516bf4bb4..f9111951ff1 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -589,8 +589,10 @@ export class TestLayoutService implements IWorkbenchLayoutService { openedDefaultEditors = false; - dimension: IDimension = { width: 800, height: 600 }; - offset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; + mainContainerDimension: IDimension = { width: 800, height: 600 }; + activeContainerDimension: IDimension = { width: 800, height: 600 }; + mainContainerOffset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; + activeContainerOffset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; hasContainer = true; container: HTMLElement = window.document.body; @@ -604,10 +606,12 @@ export class TestLayoutService implements IWorkbenchLayoutService { onDidChangePanelPosition: Event = Event.None; onDidChangePanelAlignment: Event = Event.None; onDidChangePartVisibility: Event = Event.None; - onDidLayout = Event.None; + onDidLayoutMainContainer = Event.None; + onDidLayoutActiveContainer = Event.None; onDidChangeNotificationsVisibility = Event.None; onDidAddContainer = Event.None; onDidRemoveContainer = Event.None; + onDidChangeActiveContainer = Event.None; layout(): void { } isRestored(): boolean { return true; } @@ -956,6 +960,7 @@ export class TestEditorGroupView implements IEditorGroupView { export class TestEditorGroupAccessor implements IEditorGroupsView { label: string = ''; + isAuxiliary: boolean = false; groups: IEditorGroupView[] = []; activeGroup!: IEditorGroupView; @@ -1483,7 +1488,7 @@ export class TestHostService implements IHostService { return await expectedShutdownTask(); } - async focus(options?: { force: boolean }): Promise { } + async focus(): Promise { } async moveTop(): Promise { } async openWindow(arg1?: IOpenEmptyWindowOptions | IWindowOpenable[], arg2?: IOpenWindowOptions): Promise { } diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index a6e43ee5000..c3783382057 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -12,7 +12,7 @@ import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IFileDialogService, INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; -import { IOpenedWindow, IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IColorScheme, IRectangle } from 'vs/platform/window/common/window'; +import { IOpenedMainWindow, IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions, IColorScheme, IRectangle } from 'vs/platform/window/common/window'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -78,7 +78,7 @@ export class TestNativeHostService implements INativeHostService { windowCount = Promise.resolve(1); getWindowCount(): Promise { return this.windowCount; } - async getWindows(): Promise { return []; } + async getWindows(): Promise { return []; } async getActiveWindowId(): Promise { return undefined; } openWindow(options?: IOpenEmptyWindowOptions): Promise; diff --git a/yarn.lock b/yarn.lock index ca045170ba6..ec86215b388 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9724,10 +9724,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.3.0-dev.20231002: - version "5.3.0-dev.20231002" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20231002.tgz#75a727afbb3a1a1b56306fb106d3799b6638759e" - integrity sha512-TQXM13rrDgID8W4Ok4KDQAqwLUjLcAXL+NKv0zJiIEQfa0bOM0yoRiSgvcjaCJ4lZT4bScrq3vHspVM2x8rkOQ== +typescript@^5.3.0-dev.20231030: + version "5.3.0-dev.20231030" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.3.0-dev.20231030.tgz#679edb7a1fc0411bb741b8b02fe03e48204e1749" + integrity sha512-azWrF0lAiu8cBHNr6yq3RDkU/DtQScvT48ea3Ngq6oXJ/GPO0n96wCFSRQA5UbZlVVf3fAAqsUfvOdohRbrPVg== typical@^4.0.0: version "4.0.0"