diff --git a/.eslintplugin/code-no-dangerous-type-assertions.ts b/.eslintplugin/code-no-dangerous-type-assertions.ts new file mode 100644 index 0000000000000..5cf12b858f610 --- /dev/null +++ b/.eslintplugin/code-no-dangerous-type-assertions.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; + +export = new class NoDangerousTypeAssertions implements eslint.Rule.RuleModule { + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + // Disable in tests for now + if (context.getFilename().includes('.test')) { + return {}; + } + + return { + // Disallow type assertions on object literals: { ... } or {} as T + ['TSTypeAssertion > ObjectExpression, TSAsExpression > ObjectExpression']: (node: any) => { + const objectNode = node as TSESTree.Node; + + const parent = objectNode.parent as TSESTree.TSTypeAssertion | TSESTree.TSAsExpression; + if ( + // Allow `as const` assertions + (parent.typeAnnotation.type === 'TSTypeReference' && parent.typeAnnotation.typeName.type === 'Identifier' && parent.typeAnnotation.typeName.name === 'cost') + + // For also now still allow `any` casts + || (parent.typeAnnotation.type === 'TSAnyKeyword') + ) { + return; + } + + context.report({ + node, + message: "Don't use type assertions for creating objects as this can hide type errors." + }); + }, + }; + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index 19d2c1e06a589..8382822ac14af 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -73,6 +73,7 @@ "local/code-parameter-properties-must-have-explicit-accessibility": "warn", "local/code-no-nls-in-standalone-editor": "warn", "local/code-no-potentially-unsafe-disposables": "warn", + "local/code-no-dangerous-type-assertions": "off", "local/code-no-standalone-editor": "warn", "local/code-no-unexternalized-strings": "warn", "local/code-must-use-super-dispose": "warn", @@ -671,7 +672,6 @@ "vscode-regexpp", "vscode-textmate", "worker_threads", - "@xterm/addon-canvas", "@xterm/addon-image", "@xterm/addon-search", "@xterm/addon-serialize", diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index 7b179b322bf46..4fb94c29edda6 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -114,6 +114,16 @@ "language": "github-issues", "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:verification-steps-needed -label:z-author-verified -label:unreleased -label:*not-reproducible" }, + { + "kind": 1, + "language": "markdown", + "value": "## Verifiable Fixes Missing Steps" + }, + { + "kind": 2, + "language": "github-issues", + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed sort:updated-asc label:bug label:verification-steps-needed -label:verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:error-telemetry -label:z-author-verified -label:unreleased -label:*not-reproducible" + }, { "kind": 1, "language": "markdown", diff --git a/build/.webignore b/build/.webignore index 0145e3b7da4d9..88fe96f5cc16d 100644 --- a/build/.webignore +++ b/build/.webignore @@ -20,9 +20,6 @@ vscode-textmate/webpack.config.js @xterm/xterm/src/** -@xterm/addon-canvas/src/** -@xterm/addon-canvas/out/** - @xterm/addon-image/src/** @xterm/addon-image/out/** diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index dbc9ccb946bb3..0027a774b3570 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -181,6 +181,10 @@ extends: notificationAliases: ['monacotools@microsoft.com'] validateToolOutput: None allTools: true + codeql: + compiled: + enabled: true + runSourceLanguagesInSourceAnalysis: true credscan: suppressionsFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/CredScanSuppressions.json eslint: diff --git a/build/filters.js b/build/filters.js index 0e0c5fcabb8ca..c7be2d818d9bf 100644 --- a/build/filters.js +++ b/build/filters.js @@ -81,6 +81,7 @@ module.exports.indentationFilter = [ '!resources/linux/snap/electron-launch', '!build/ext.js', '!build/npm/gyp/patches/gyp_spectre_mitigation_support.patch', + '!product.overrides.json', // except specific folders '!test/automation/out/**', diff --git a/cli/src/commands/tunnels.rs b/cli/src/commands/tunnels.rs index 98a2a2e3a3a65..f06cd9a1e2a2e 100644 --- a/cli/src/commands/tunnels.rs +++ b/cli/src/commands/tunnels.rs @@ -556,6 +556,16 @@ async fn serve_with_csa( match acquire_singleton(&paths.tunnel_lockfile()).await { Ok(SingletonConnection::Client(stream)) => { debug!(log, "starting as client to singleton"); + if gateway_args.name.is_none() + || !gateway_args.install_extension.is_empty() + || gateway_args.tunnel.tunnel_id.is_some() + { + warning!( + log, + "Command-line options will not be applied until the existing tunnel exits." + ); + } + let should_exit = start_singleton_client(SingletonClientArgs { log: log.clone(), shutdown: shutdown.clone(), diff --git a/cli/src/tunnels/code_server.rs b/cli/src/tunnels/code_server.rs index 16cc3325a4a8e..0579f8ef0dcfe 100644 --- a/cli/src/tunnels/code_server.rs +++ b/cli/src/tunnels/code_server.rs @@ -394,6 +394,16 @@ impl<'a> ServerBuilder<'a> { } } + /// Removes a cached server. + pub async fn evict(&self) -> Result<(), WrappedError> { + let name = get_server_folder_name( + self.server_params.release.quality, + &self.server_params.release.commit, + ); + + self.launcher_paths.server_cache.delete(&name) + } + /// Ensures the server is set up in the configured directory. pub async fn setup(&self) -> Result<(), AnyError> { debug!( @@ -479,7 +489,7 @@ impl<'a> ServerBuilder<'a> { .arg("--enable-remote-auto-shutdown") .arg(format!("--port={}", port)); - let child = self.spawn_server_process(cmd)?; + let child = self.spawn_server_process(cmd).await?; let log_file = self.get_logfile()?; let plog = self.logger.prefixed(&log::new_code_server_prefix()); @@ -487,16 +497,16 @@ impl<'a> ServerBuilder<'a> { monitor_server::(child, Some(log_file), plog, false); let port = match timeout(Duration::from_secs(8), listen_rx).await { - Err(e) => { + Err(_) => { origin.kill().await; - Err(wrap(e, "timed out looking for port")) + return Err(CodeError::ServerOriginTimeout.into()); } - Ok(Err(e)) => { + Ok(Err(s)) => { origin.kill().await; - Err(wrap(e, "server exited without writing port")) + return Err(CodeError::ServerUnexpectedExit(format!("{}", s)).into()); } - Ok(Ok(p)) => Ok(p), - }?; + Ok(Ok(p)) => p, + }; info!(self.logger, "Server started"); @@ -553,7 +563,7 @@ impl<'a> ServerBuilder<'a> { .arg("--enable-remote-auto-shutdown") .arg(format!("--socket-path={}", socket.display())); - let child = self.spawn_server_process(cmd)?; + let child = self.spawn_server_process(cmd).await?; let log_file = self.get_logfile()?; let plog = self.logger.prefixed(&log::new_code_server_prefix()); @@ -561,16 +571,16 @@ impl<'a> ServerBuilder<'a> { monitor_server::(child, Some(log_file), plog, false); let socket = match timeout(Duration::from_secs(30), listen_rx).await { - Err(e) => { + Err(_) => { origin.kill().await; - Err(wrap(e, "timed out looking for socket")) + return Err(CodeError::ServerOriginTimeout.into()); } - Ok(Err(e)) => { + Ok(Err(s)) => { origin.kill().await; - Err(wrap(e, "server exited without writing socket")) + return Err(CodeError::ServerUnexpectedExit(format!("{}", s)).into()); } - Ok(Ok(socket)) => Ok(socket), - }?; + Ok(Ok(socket)) => socket, + }; info!(self.logger, "Server started"); @@ -581,26 +591,7 @@ impl<'a> ServerBuilder<'a> { }) } - /// Starts with a given opaque set of args. Does not set up any port or - /// socket, but does return one if present, in the form of a channel. - pub async fn start_opaque_with_args( - &self, - args: &[String], - ) -> Result<(CodeServerOrigin, Receiver), AnyError> - where - M: ServerOutputMatcher, - R: 'static + Send + std::fmt::Debug, - { - let mut cmd = self.get_base_command(); - cmd.args(args); - - let child = self.spawn_server_process(cmd)?; - let plog = self.logger.prefixed(&log::new_code_server_prefix()); - - Ok(monitor_server::(child, None, plog, true)) - } - - fn spawn_server_process(&self, mut cmd: Command) -> Result { + async fn spawn_server_process(&self, mut cmd: Command) -> Result { info!(self.logger, "Starting server..."); debug!(self.logger, "Starting server with command... {:?}", cmd); @@ -615,14 +606,17 @@ impl<'a> ServerBuilder<'a> { let cmd = cmd.creation_flags( winapi::um::winbase::CREATE_NO_WINDOW | winapi::um::winbase::CREATE_NEW_PROCESS_GROUP - | winapi::um::winbase::CREATE_BREAKAWAY_FROM_JOB, + | get_should_use_breakaway_from_job() + .await + .then_some(winapi::um::winbase::CREATE_BREAKAWAY_FROM_JOB) + .unwrap_or_default(), ); let child = cmd .stderr(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) .spawn() - .map_err(|e| wrap(e, "error spawning server"))?; + .map_err(|e| CodeError::ServerUnexpectedExit(format!("{}", e)))?; self.server_paths .write_pid(child.id().expect("expected server to have pid"))?; @@ -874,3 +868,13 @@ pub async fn download_cli_into_cache( } } } + +#[cfg(target_os = "windows")] +async fn get_should_use_breakaway_from_job() -> bool { + let mut cmd = Command::new("cmd"); + cmd.creation_flags( + winapi::um::winbase::CREATE_NO_WINDOW | winapi::um::winbase::CREATE_BREAKAWAY_FROM_JOB, + ); + + cmd.args(["/C", "echo ok"]).output().await.is_ok() +} diff --git a/cli/src/tunnels/control_server.rs b/cli/src/tunnels/control_server.rs index ec09e4512a06e..f42984cfac150 100644 --- a/cli/src/tunnels/control_server.rs +++ b/cli/src/tunnels/control_server.rs @@ -760,17 +760,18 @@ async fn handle_serve( macro_rules! do_setup { ($sb:expr) => { match $sb.get_running().await? { - Some(AnyCodeServer::Socket(s)) => s, + Some(AnyCodeServer::Socket(s)) => ($sb, Ok(s)), Some(_) => return Err(AnyError::from(MismatchedLaunchModeError())), None => { $sb.setup().await?; - $sb.listen_on_default_socket().await? + let r = $sb.listen_on_default_socket().await; + ($sb, r) } } }; } - let server = if params.use_local_download { + let (sb, server) = if params.use_local_download { let sb = ServerBuilder::new( &install_log, &resolved, @@ -784,6 +785,24 @@ async fn handle_serve( do_setup!(sb) }; + let server = match server { + Ok(s) => s, + Err(e) => { + // we don't loop to avoid doing so infinitely: allow the client to reconnect in this case. + if let AnyError::CodeError(CodeError::ServerUnexpectedExit(ref e)) = e { + warning!( + c.log, + "({}), removing server due to possible corruptions", + e + ); + if let Err(e) = sb.evict().await { + warning!(c.log, "Failed to evict server: {}", e); + } + } + return Err(e); + } + }; + server_ref.replace(server.clone()); server } diff --git a/cli/src/util/errors.rs b/cli/src/util/errors.rs index fc706199aabe8..7d28ce9f741ba 100644 --- a/cli/src/util/errors.rs +++ b/cli/src/util/errors.rs @@ -516,6 +516,10 @@ pub enum CodeError { CouldNotCreateConnectionTokenFile(std::io::Error), #[error("A tunnel with the name {0} exists and is in-use. Please pick a different name or stop the existing tunnel.")] TunnelActiveAndInUse(String), + #[error("Timed out looking for port/socket")] + ServerOriginTimeout, + #[error("Server exited without writing port/socket: {0}")] + ServerUnexpectedExit(String), } makeAnyError!( diff --git a/extensions/dart/cgmanifest.json b/extensions/dart/cgmanifest.json index 9c90588adf182..df4e4f0aae967 100644 --- a/extensions/dart/cgmanifest.json +++ b/extensions/dart/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dart-lang/dart-syntax-highlight", "repositoryUrl": "/~https://github.com/dart-lang/dart-syntax-highlight", - "commitHash": "272e2f89f85073c04b7e15b582257f76d2489970" + "commitHash": "bb8f7eebf5a1028e70dbebcf35cfef738dddc7fe" } }, "licenseDetail": [ diff --git a/extensions/dart/syntaxes/dart.tmLanguage.json b/extensions/dart/syntaxes/dart.tmLanguage.json index cc9dee8d2754e..5a4a9393bc749 100644 --- a/extensions/dart/syntaxes/dart.tmLanguage.json +++ b/extensions/dart/syntaxes/dart.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/dart-lang/dart-syntax-highlight/commit/272e2f89f85073c04b7e15b582257f76d2489970", + "version": "/~https://github.com/dart-lang/dart-syntax-highlight/commit/bb8f7eebf5a1028e70dbebcf35cfef738dddc7fe", "name": "Dart", "scopeName": "source.dart", "patterns": [ @@ -14,7 +14,7 @@ }, { "name": "meta.declaration.dart", - "begin": "^\\w*\\b(library|import|part of|part|export)\\b", + "begin": "^\\w*\\b(augment\\s+library|library|import\\s+augment|import|part\\s+of|part|export)\\b", "beginCaptures": { "0": { "name": "keyword.other.import.dart" @@ -208,7 +208,7 @@ }, { "name": "variable.language.dart", - "match": "(?|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -2150,14 +2133,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -2420,308 +2403,53 @@ }, "name": "meta.preprocessor.import.cpp" }, - "d9bc4796b0b_preprocessor_number_literal": { - "match": "(?|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4261,14 +3972,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "24": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -4404,7 +4115,7 @@ "51": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -4527,24 +4238,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4605,14 +4299,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "17": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -4686,7 +4380,7 @@ }, "function_pointer": { "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=[{=,);>]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -4718,24 +4412,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -4796,14 +4473,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -4955,6 +4632,41 @@ }, "5": { "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "name": "storage.modifier.specifier.functional.post-parameters.$10.cpp" + }, + "11": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" } }, "patterns": [ @@ -4965,7 +4677,7 @@ }, "function_pointer_parameter": { "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=[{=,);>]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -4997,24 +4709,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -5075,14 +4770,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -5234,6 +4929,41 @@ }, "5": { "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "name": "storage.modifier.specifier.functional.post-parameters.$10.cpp" + }, + "11": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" } }, "patterns": [ @@ -5613,24 +5343,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -5709,14 +5422,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "8": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -5787,7 +5500,7 @@ "name": "invalid.illegal.unexpected.punctuation.definition.comment.end.cpp" }, "label": { - "match": "((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -6872,14 +6568,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "24": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -7107,24 +6803,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -7185,14 +6864,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "24": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -7655,24 +7334,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -7733,14 +7395,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "16": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -7890,14 +7552,14 @@ "name": "entity.name.scope-resolution.operator.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "46": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -7951,14 +7613,14 @@ "name": "entity.name.scope-resolution.operator-overload.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "59": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -8111,7 +7773,7 @@ "include": "#ever_present_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" }, { "begin": "\\(", @@ -9611,7 +9273,7 @@ "include": "#scope_resolution_parameter_inner_generated" }, { - "match": "(?:(?:struct)|(?:class)|(?:union)|(?:enum))", + "match": "(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.])", + "preprocessor_number_literal": { + "match": "(?|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", + "begin": "(?=.)", + "end": "$|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": {}, + "endCaptures": {}, "patterns": [ { - "include": "#template_call_context" - } - ] - }, - { - "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", - "name": "entity.name.type.cpp" - } - ] - }, - "1": { - "patterns": [ - { - "include": "#attributes_context" - }, - { - "include": "#number_literal" - } - ] - }, - "2": { - "patterns": [ - { - "include": "#inline_comment" - } - ] - }, - "3": { - "patterns": [ - { - "match": "\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+", - "captures": { - "1": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "2": { - "name": "comment.block.cpp" + "match": "(\\G0[xX])([0-9a-fA-F](?:[0-9a-fA-F]|((?<=[0-9a-fA-F])'(?=[0-9a-fA-F])))*)?((?:(?<=[0-9a-fA-F])\\.|\\.(?=[0-9a-fA-F])))([0-9a-fA-F](?:[0-9a-fA-F]|((?<=[0-9a-fA-F])'(?=[0-9a-fA-F])))*)?(?:(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:((?:\\s*+\\/\\*(?:[^\\*]++|\\*+(?!\\/))*+\\*\\/\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.])", + "captures": { + "0": { + "patterns": [ + { + "match": "::", + "name": "punctuation.separator.namespace.access.cpp punctuation.separator.scope-resolution.cpp" + }, + { + "match": "(?|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -11737,14 +11620,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "21": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -11940,24 +11823,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -12018,14 +11884,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "21": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12249,7 +12115,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12271,7 +12137,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12293,7 +12159,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12305,7 +12171,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12340,7 +12206,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12362,7 +12228,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12374,7 +12240,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12409,7 +12275,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12431,7 +12297,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12443,7 +12309,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12478,7 +12344,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12490,7 +12356,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12525,7 +12391,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12547,7 +12413,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12559,7 +12425,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12594,7 +12460,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12616,7 +12482,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12628,7 +12494,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12663,7 +12529,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12685,7 +12551,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12697,7 +12563,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12732,7 +12598,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12754,7 +12620,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12766,7 +12632,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12801,7 +12667,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12823,7 +12689,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12835,7 +12701,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12870,7 +12736,7 @@ "2": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] } @@ -12892,7 +12758,7 @@ "3": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12904,7 +12770,7 @@ "name": "meta.template.call.cpp", "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -12960,24 +12826,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -13056,14 +12905,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "8": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -14310,8 +14159,8 @@ ] }, { - "begin": "((?:[uUL]8?)?R)\\\"(?:[pP]?(?:sql|SQL)|d[dm]l)\\(", - "end": "\\)(?:[pP]?(?:sql|SQL)|d[dm]l)\\\"|(?=\\\\end\\{(?:minted|cppcode)\\})", + "begin": "((?:[uUL]8?)?R)\\\"\\(", + "end": "\\)\\\"|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "0": { "name": "punctuation.definition.string.begin.cpp" @@ -14325,18 +14174,16 @@ "name": "punctuation.definition.string.end.cpp" } }, - "name": "meta.string.quoted.double.raw.sql.cpp", + "name": "string.quoted.double.raw.cpp", "patterns": [ - { - "include": "source.sql" - } + {} ] }, { "begin": "((?:u|u8|U|L)?R)\"(?:([^ ()\\\\\\t]{0,16})|([^ ()\\\\\\t]*))\\(", "beginCaptures": { "0": { - "name": "punctuation.definition.string.begin" + "name": "punctuation.definition.string.$2.begin" }, "1": { "name": "meta.encoding" @@ -14345,25 +14192,29 @@ "name": "invalid.illegal.delimiter-too-long" } }, - "end": "(\\)\\2(\\3)\")(?:((?:[a-zA-Z]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|(_(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))?|(?=\\\\end\\{(?:minted|cppcode)\\})", + "end": "(\\)(\\2)(\\3)\")(?:((?:[a-zA-Z]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)|(_(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*))?|(?=\\\\end\\{(?:minted|cppcode)\\})", "endCaptures": { "1": { - "name": "punctuation.definition.string.end" + "name": "punctuation.definition.string.$2.end" }, - "2": { + "3": { "name": "invalid.illegal.delimiter-too-long" }, - "3": { + "4": { "name": "keyword.other.suffix.literal.user-defined.reserved.string.cpp" }, - "4": { + "5": { "name": "keyword.other.suffix.literal.user-defined.string.cpp" } }, - "name": "string.quoted.double.raw" + "name": "string.quoted.double.raw.$2" } ] }, + "string_escaped_char": { + "match": "(?x)\\\\ (\n\\\\\t\t\t |\n[abefnprtv'\"?] |\n[0-3][0-7]{,2}\t |\n[4-7]\\d?\t\t|\nx[a-fA-F0-9]{,2} |\nu[a-fA-F0-9]{,4} |\nU[a-fA-F0-9]{,8} )", + "name": "constant.character.escape" + }, "string_escapes_context_c": { "patterns": [ { @@ -14575,7 +14426,7 @@ "include": "#inheritance_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -14886,134 +14737,743 @@ { "include": "#switch_conditional_parentheses" }, - { - "include": "$self" - } - ] - }, - { - "begin": "(?<=\\{|<%|\\?\\?<)", - "end": "\\}|%>|\\?\\?>|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": {}, - "endCaptures": { - "0": { - "name": "punctuation.section.block.end.bracket.curly.switch.cpp" - } - }, - "name": "meta.body.switch.cpp", - "patterns": [ - { - "include": "#default_statement" + { + "include": "$self" + } + ] + }, + { + "begin": "(?<=\\{|<%|\\?\\?<)", + "end": "\\}|%>|\\?\\?>|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": {}, + "endCaptures": { + "0": { + "name": "punctuation.section.block.end.bracket.curly.switch.cpp" + } + }, + "name": "meta.body.switch.cpp", + "patterns": [ + { + "include": "#default_statement" + }, + { + "include": "#case_statement" + }, + { + "include": "$self" + } + ] + }, + { + "begin": "(?<=\\}|%>|\\?\\?>)[\\s]*", + "end": "[\\s]*(?=;)|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": {}, + "endCaptures": {}, + "name": "meta.tail.switch.cpp", + "patterns": [ + { + "include": "$self" + } + ] + } + ] + }, + "template_argument_defaulted": { + "match": "(?<=<|,)(?:\\s+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s+((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(\\=)", + "captures": { + "1": { + "name": "storage.type.template.argument.$1.cpp" + }, + "2": { + "name": "entity.name.type.template.cpp" + }, + "3": { + "name": "keyword.operator.assignment.cpp" + } + } + }, + "template_call_context": { + "patterns": [ + { + "include": "#ever_present_context" + }, + { + "include": "#template_call_range_helper" + }, + { + "include": "#storage_types" + }, + { + "include": "#language_constants" + }, + { + "include": "#scope_resolution_template_call_inner_generated" + }, + { + "include": "#operators" + }, + { + "include": "#number_literal" + }, + { + "include": "#string_context" + }, + { + "include": "#comma_in_template_argument" + }, + { + "include": "#qualified_type" + } + ] + }, + "template_call_innards": { + "match": "((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+", + "captures": { + "0": { + "patterns": [ + { + "include": "#template_call_range_helper" + } + ] + }, + "2": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "3": { + "name": "comment.block.cpp" + }, + "4": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + } + }, + "name": "meta.template.call.cpp" + }, + "template_call_range": { + "begin": "<", + "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", + "beginCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.begin.template.call.cpp" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.section.angle-brackets.end.template.call.cpp" + } + }, + "name": "meta.template.call.cpp", + "patterns": [ + { + "include": "#template_call_context" + } + ] + }, + "template_call_range_helper": { + "patterns": [ + { + "match": "\\b((?|\\?\\?>)[\\s]*", - "end": "[\\s]*(?=;)|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": {}, - "endCaptures": {}, - "name": "meta.tail.switch.cpp", - "patterns": [ - { - "include": "$self" + "4": { + "name": "keyword.operator.comparison.cpp" } - ] - } - ] - }, - "template_argument_defaulted": { - "match": "(?<=<|,)(?:\\s+)?((?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)\\s+((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(\\=)", - "captures": { - "1": { - "name": "storage.type.template.argument.$1.cpp" - }, - "2": { - "name": "entity.name.type.template.cpp" - }, - "3": { - "name": "keyword.operator.assignment.cpp" - } - } - }, - "template_call_context": { - "patterns": [ - { - "include": "#ever_present_context" + } }, { "include": "#template_call_range" - }, - { - "include": "#storage_types" - }, - { - "include": "#language_constants" - }, - { - "include": "#scope_resolution_template_call_inner_generated" - }, - { - "include": "#operators" - }, - { - "include": "#number_literal" - }, - { - "include": "#string_context" - }, - { - "include": "#comma_in_template_argument" - }, - { - "include": "#qualified_type" - } - ] - }, - "template_call_innards": { - "match": "((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+", - "captures": { - "0": { - "patterns": [ - { - "include": "#template_call_range" - } - ] - }, - "2": { - "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" - }, - "3": { - "name": "comment.block.cpp" - }, - "4": { - "name": "comment.block.cpp punctuation.definition.comment.end.cpp" - } - }, - "name": "meta.template.call.cpp" - }, - "template_call_range": { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" } ] }, @@ -15400,24 +15860,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -15496,14 +15939,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "15": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -15906,7 +16349,7 @@ "include": "#inheritance_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -16065,7 +16508,7 @@ "patterns": [ { "begin": "(\\s*+((?:(?:(?:\\[\\[.*?\\]\\]|__attribute(?:__)?\\s*\\(\\s*\\(.*?\\)\\s*\\))|__declspec\\(.*?\\))|alignas\\(.*?\\))(?!\\)))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:(?:(?:unsigned)|(?:signed)|(?:short)|(?:long))|(?:(?:struct)|(?:class)|(?:union)|(?:enum)))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:((?:::)?(?:(?!\\b(?:__has_cpp_attribute|reinterpret_cast|atomic_noexcept|atomic_commit|atomic_cancel|__has_include|thread_local|dynamic_cast|synchronized|static_cast|const_cast|consteval|co_return|protected|constinit|constexpr|co_return|consteval|namespace|constexpr|constexpr|co_await|explicit|volatile|noexcept|co_yield|noexcept|noexcept|requires|typename|decltype|operator|template|continue|co_await|co_yield|volatile|register|restrict|reflexpr|mutable|alignof|include|private|defined|typedef|_Pragma|__asm__|concept|mutable|warning|default|virtual|alignas|public|sizeof|delete|not_eq|bitand|and_eq|xor_eq|typeid|switch|return|struct|static|extern|inline|friend|ifndef|define|pragma|export|import|module|catch|throw|const|or_eq|compl|while|ifdef|const|bitor|union|class|undef|error|break|using|endif|goto|line|enum|this|case|else|elif|else|not|try|for|asm|and|xor|new|do|if|or|if)\\b)(?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)\\s*+)?::)*+)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))?(?!(?:(?:transaction_safe_dynamic)|(?:__has_cpp_attribute)|(?:reinterpret_cast)|(?:transaction_safe)|(?:atomic_noexcept)|(?:atomic_commit)|(?:__has_include)|(?:atomic_cancel)|(?:synchronized)|(?:thread_local)|(?:dynamic_cast)|(?:static_cast)|(?:const_cast)|(?:constexpr)|(?:co_return)|(?:constinit)|(?:namespace)|(?:protected)|(?:consteval)|(?:constexpr)|(?:constexpr)|(?:co_return)|(?:consteval)|(?:co_await)|(?:continue)|(?:template)|(?:reflexpr)|(?:volatile)|(?:register)|(?:co_await)|(?:co_yield)|(?:restrict)|(?:noexcept)|(?:volatile)|(?:override)|(?:explicit)|(?:decltype)|(?:operator)|(?:noexcept)|(?:noexcept)|(?:typename)|(?:requires)|(?:co_yield)|(?:nullptr)|(?:alignof)|(?:alignas)|(?:default)|(?:mutable)|(?:virtual)|(?:mutable)|(?:private)|(?:include)|(?:warning)|(?:_Pragma)|(?:defined)|(?:typedef)|(?:__asm__)|(?:concept)|(?:define)|(?:module)|(?:sizeof)|(?:switch)|(?:delete)|(?:pragma)|(?:and_eq)|(?:inline)|(?:xor_eq)|(?:typeid)|(?:import)|(?:extern)|(?:public)|(?:bitand)|(?:static)|(?:export)|(?:return)|(?:friend)|(?:ifndef)|(?:not_eq)|(?:false)|(?:final)|(?:break)|(?:const)|(?:catch)|(?:endif)|(?:ifdef)|(?:undef)|(?:error)|(?:audit)|(?:while)|(?:using)|(?:axiom)|(?:or_eq)|(?:compl)|(?:throw)|(?:bitor)|(?:const)|(?:line)|(?:case)|(?:else)|(?:this)|(?:true)|(?:goto)|(?:else)|(?:NULL)|(?:elif)|(?:new)|(?:asm)|(?:xor)|(?:and)|(?:try)|(?:not)|(?:for)|(?:do)|(?:if)|(?:or)|(?:if))\\b)(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*\\b((?|(?:(?:[^'\"<>\\/]|\\/[^*])++))*>)?(?![\\w<:.]))(((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))?(?:(?:&|\\*)((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z)))*(?:&|\\*))?((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(\\()(\\*)(?:\\s+)?((?:(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*)?)(?:\\s+)?(?:(\\[)(\\w*)(\\])(?:\\s+)?)*(\\))(?:\\s+)?(\\()", - "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?=[{=,);>]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", + "end": "(\\))((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))(?:(?:((?:(?:(?:\\s*+(\\/\\*)((?:[^\\*]++|\\*+(?!\\/))*+(\\*\\/))\\s*+)+)|(?:\\s++)|(?<=\\W)|(?=\\W)|^|(?:\\n?$)|\\A|\\Z))((?]|\\n)(?!\\()|(?=\\\\end\\{(?:minted|cppcode)\\})", "beginCaptures": { "1": { "name": "meta.qualified_type.cpp", @@ -16097,24 +16540,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -16175,14 +16601,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -16334,6 +16760,41 @@ }, "5": { "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "6": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "7": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "8": { + "name": "comment.block.cpp" + }, + "9": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" + }, + "10": { + "name": "storage.modifier.specifier.functional.post-parameters.$10.cpp" + }, + "11": { + "patterns": [ + { + "include": "#inline_comment" + } + ] + }, + "12": { + "name": "comment.block.cpp punctuation.definition.comment.begin.cpp" + }, + "13": { + "name": "comment.block.cpp" + }, + "14": { + "name": "comment.block.cpp punctuation.definition.comment.end.cpp" } }, "patterns": [ @@ -16549,7 +17010,7 @@ "include": "#inheritance_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -16901,7 +17362,7 @@ "include": "#inheritance_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -17173,24 +17634,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -17269,14 +17713,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "13": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -17569,7 +18013,7 @@ "include": "#inheritance_context" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -17828,7 +18272,7 @@ "5": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -17931,24 +18375,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -18027,14 +18454,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, @@ -18296,24 +18723,7 @@ "include": "#scope_resolution_inner_generated" }, { - "begin": "<", - "end": ">|(?=\\\\end\\{(?:minted|cppcode)\\})", - "beginCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.begin.template.call.cpp" - } - }, - "endCaptures": { - "0": { - "name": "punctuation.section.angle-brackets.end.template.call.cpp" - } - }, - "name": "meta.template.call.cpp", - "patterns": [ - { - "include": "#template_call_context" - } - ] + "include": "#template_call_range_helper" }, { "match": "(?:[a-zA-Z_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))(?:[a-zA-Z0-9_]|(?:\\\\u[0-9a-fA-F]{4}|\\\\U[0-9a-fA-F]{8}))*", @@ -18392,14 +18802,14 @@ "name": "entity.name.scope-resolution.type.cpp" }, { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, "12": { "patterns": [ { - "include": "#template_call_range" + "include": "#template_call_range_helper" } ] }, diff --git a/extensions/less/cgmanifest.json b/extensions/less/cgmanifest.json index 663e1cc4dafde..caf908bbcc05d 100644 --- a/extensions/less/cgmanifest.json +++ b/extensions/less/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "language-less", "repositoryUrl": "/~https://github.com/radium-v/Better-Less", - "commitHash": "05de79f600227201e35f07a49f07acce80e49dbf" + "commitHash": "24047277622c245dbe9309f0004d0ccb8f02636f" } }, "license": "MIT", diff --git a/extensions/less/syntaxes/less.tmLanguage.json b/extensions/less/syntaxes/less.tmLanguage.json index 3d2c6bdaeb018..2acac68838544 100644 --- a/extensions/less/syntaxes/less.tmLanguage.json +++ b/extensions/less/syntaxes/less.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/radium-v/Better-Less/commit/05de79f600227201e35f07a49f07acce80e49dbf", + "version": "/~https://github.com/radium-v/Better-Less/commit/24047277622c245dbe9309f0004d0ccb8f02636f", "name": "Less", "scopeName": "source.css.less", "patterns": [ @@ -3966,7 +3966,7 @@ ] }, { - "begin": "\\bfilter\\b", + "begin": "\\b(?:backdrop-)?filter\\b", "beginCaptures": { "0": { "name": "support.type.property-name.less" diff --git a/extensions/lua/cgmanifest.json b/extensions/lua/cgmanifest.json index 50afc15d1cf83..f35219d5a615c 100644 --- a/extensions/lua/cgmanifest.json +++ b/extensions/lua/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "sumneko/lua.tmbundle", "repositoryUrl": "/~https://github.com/sumneko/lua.tmbundle", - "commitHash": "94ce82cc4d45f82641a5252d7a7fd9e28c875adc" + "commitHash": "1483add845ebfb3e1e631fe372603e5fed2cdd42" } }, "licenseDetail": [ diff --git a/extensions/lua/syntaxes/lua.tmLanguage.json b/extensions/lua/syntaxes/lua.tmLanguage.json index e578b8846cbab..61875d06cf85b 100644 --- a/extensions/lua/syntaxes/lua.tmLanguage.json +++ b/extensions/lua/syntaxes/lua.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/sumneko/lua.tmbundle/commit/94ce82cc4d45f82641a5252d7a7fd9e28c875adc", + "version": "/~https://github.com/sumneko/lua.tmbundle/commit/1483add845ebfb3e1e631fe372603e5fed2cdd42", "name": "Lua", "scopeName": "source.lua", "patterns": [ @@ -141,8 +141,8 @@ { "match": "<\\s*(const|close)\\s*>", "captures": { - "1": { - "name": "string.tag.lua" + "0": { + "name": "storage.type.attribute.lua" } } }, @@ -155,7 +155,7 @@ "name": "keyword.control.lua" }, { - "match": "\\b(local|global)\\b", + "match": "\\b(local)\\b", "name": "keyword.local.lua" }, { @@ -363,7 +363,7 @@ "name": "punctuation.definition.comment.begin.lua" } }, - "end": "\\]\\1\\]", + "end": "(--)?\\]\\1\\]", "endCaptures": { "0": { "name": "punctuation.definition.comment.end.lua" @@ -383,7 +383,7 @@ "name": "punctuation.definition.comment.begin.lua" } }, - "end": "\\]\\1\\]", + "end": "(--)?\\]\\1\\]", "endCaptures": { "0": { "name": "punctuation.definition.comment.end.lua" @@ -472,7 +472,7 @@ "emmydoc": { "patterns": [ { - "begin": "(?<=---[ \\t]*)@class", + "begin": "(?<=---)[ \\t]*@class", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -491,7 +491,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@enum", + "begin": "(?<=---)[ \\t]*@enum", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -511,7 +511,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@type", + "begin": "(?<=---)[ \\t]*@type", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -525,7 +525,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@alias", + "begin": "(?<=---)[ \\t]*@alias", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -550,7 +550,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)(@operator)\\s*(\\b[a-z]+)?", + "begin": "(?<=---)[ \\t]*(@operator)\\s*(\\b[a-z]+)?", "beginCaptures": { "1": { "name": "storage.type.annotation.lua" @@ -567,7 +567,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@cast", + "begin": "(?<=---)[ \\t]*@cast", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -596,7 +596,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@param", + "begin": "(?<=---)[ \\t]*@param", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -624,7 +624,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@return", + "begin": "(?<=---)[ \\t]*@return", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -642,7 +642,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@field", + "begin": "(?<=---)[ \\t]*@field", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -677,7 +677,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@generic", + "begin": "(?<=---)[ \\t]*@generic", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -711,7 +711,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@vararg", + "begin": "(?<=---)[ \\t]*@vararg", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -725,7 +725,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@overload", + "begin": "(?<=---)[ \\t]*@overload", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -739,7 +739,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@deprecated", + "begin": "(?<=---)[ \\t]*@deprecated", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -748,7 +748,7 @@ "end": "(?=[\\n@#])" }, { - "begin": "(?<=---[ \\t]*)@meta", + "begin": "(?<=---)[ \\t]*@meta", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -757,7 +757,7 @@ "end": "(?=[\\n@#])" }, { - "begin": "(?<=---[ \\t]*)@private", + "begin": "(?<=---)[ \\t]*@private", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -766,7 +766,7 @@ "end": "(?=[\\n@#])" }, { - "begin": "(?<=---[ \\t]*)@protected", + "begin": "(?<=---)[ \\t]*@protected", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -775,7 +775,7 @@ "end": "(?=[\\n@#])" }, { - "begin": "(?<=---[ \\t]*)@package", + "begin": "(?<=---)[ \\t]*@package", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -784,7 +784,7 @@ "end": "(?=[\\n@#])" }, { - "begin": "(?<=---[ \\t]*)@version", + "begin": "(?<=---)[ \\t]*@version", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -803,7 +803,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@see", + "begin": "(?<=---)[ \\t]*@see", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -822,7 +822,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@diagnostic", + "begin": "(?<=---)[ \\t]*@diagnostic", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -855,7 +855,7 @@ ] }, { - "begin": "(?<=---[ \\t]*)@module", + "begin": "(?<=---)[ \\t]*@module", "beginCaptures": { "0": { "name": "storage.type.annotation.lua" @@ -869,7 +869,7 @@ ] }, { - "match": "(?<=---[ \\t]*)@(async|nodiscard)", + "match": "(?<=---)[ \\t]*@(async|nodiscard)", "name": "storage.type.annotation.lua" }, { diff --git a/extensions/markdown-language-features/server/package.json b/extensions/markdown-language-features/server/package.json index 866e1a796e158..df9124c936b9b 100644 --- a/extensions/markdown-language-features/server/package.json +++ b/extensions/markdown-language-features/server/package.json @@ -1,7 +1,7 @@ { "name": "vscode-markdown-languageserver", "description": "Markdown language server", - "version": "0.5.0-alpha.5", + "version": "0.5.0-alpha.6", "author": "Microsoft Corporation", "license": "MIT", "engines": { @@ -18,7 +18,7 @@ "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.5.0-alpha.5", + "vscode-markdown-languageservice": "^0.5.0-alpha.6", "vscode-uri": "^3.0.7" }, "devDependencies": { diff --git a/extensions/markdown-language-features/server/src/protocol.ts b/extensions/markdown-language-features/server/src/protocol.ts index 3a195755e056b..d06edbd4303ef 100644 --- a/extensions/markdown-language-features/server/src/protocol.ts +++ b/extensions/markdown-language-features/server/src/protocol.ts @@ -8,7 +8,7 @@ import type * as lsp from 'vscode-languageserver-types'; import type * as md from 'vscode-markdown-languageservice'; //#region From server -export const parse = new RequestType<{ uri: string }, md.Token[], any>('markdown/parse'); +export const parse = new RequestType<{ uri: string; text?: string }, md.Token[], any>('markdown/parse'); export const fs_readFile = new RequestType<{ uri: string }, number[], any>('markdown/fs/readFile'); export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory'); diff --git a/extensions/markdown-language-features/server/src/server.ts b/extensions/markdown-language-features/server/src/server.ts index d0b17ab138187..f1df5494f3b7c 100644 --- a/extensions/markdown-language-features/server/src/server.ts +++ b/extensions/markdown-language-features/server/src/server.ts @@ -29,7 +29,13 @@ export async function startVsCodeServer(connection: Connection) { slugifier = md.githubSlugifier; tokenize(document: md.ITextDocument): Promise { - return connection.sendRequest(protocol.parse, { uri: document.uri.toString() }); + return connection.sendRequest(protocol.parse, { + uri: document.uri, + + // Clients won't be able to read temp documents. + // Send along the full text for parsing. + text: document.version < 0 ? document.getText() : undefined + }); } }; diff --git a/extensions/markdown-language-features/server/yarn.lock b/extensions/markdown-language-features/server/yarn.lock index 87a251842eaee..0768663fe0d50 100644 --- a/extensions/markdown-language-features/server/yarn.lock +++ b/extensions/markdown-language-features/server/yarn.lock @@ -151,10 +151,10 @@ vscode-languageserver@^8.1.0: dependencies: vscode-languageserver-protocol "3.17.3" -vscode-markdown-languageservice@^0.5.0-alpha.5: - version "0.5.0-alpha.5" - resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.5.tgz#d4c5a4b7ab8d03a9dbbdf64ce51862686ca05cf8" - integrity sha512-yu2TbIj2alrgh7JAzGQS4YadSBX4MDT7UjgrT4BQvtGfXOPC4G76llP4iZpkDWPBvAXywxnMZ9eZ3N15f81InA== +vscode-markdown-languageservice@^0.5.0-alpha.6: + version "0.5.0-alpha.6" + resolved "https://registry.yarnpkg.com/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.6.tgz#3aa5fc94fea3d5d7f0cd970e64348e2791643dc0" + integrity sha512-mA1JCA7aHHSek5gr8Yv7C3esEPo2hRrgxmoZUDRro+pnwbdsJuRaWOKWtCWxejRUVVVhc/5yTK2X64Jx9OCmFQ== dependencies: "@vscode/l10n" "^0.0.10" node-html-parser "^6.1.5" diff --git a/extensions/markdown-language-features/src/client/client.ts b/extensions/markdown-language-features/src/client/client.ts index 5964b43c3774a..a2f7861550809 100644 --- a/extensions/markdown-language-features/src/client/client.ts +++ b/extensions/markdown-language-features/src/client/client.ts @@ -9,6 +9,7 @@ import { IMdParser } from '../markdownEngine'; import { IDisposable } from '../util/dispose'; import { looksLikeMarkdownPath, markdownFileExtensions } from '../util/file'; import { FileWatcherManager } from './fileWatchingManager'; +import { InMemoryDocument } from './inMemoryDocument'; import * as proto from './protocol'; import { VsCodeMdWorkspace } from './workspace'; @@ -101,11 +102,15 @@ export async function startClient(factory: LanguageClientConstructor, parser: IM client.onRequest(proto.parse, async (e) => { const uri = vscode.Uri.parse(e.uri); - const doc = await workspace.getOrLoadMarkdownDocument(uri); - if (doc) { - return parser.tokenize(doc); + if (typeof e.text === 'string') { + return parser.tokenize(new InMemoryDocument(uri, e.text, -1)); } else { - return []; + const doc = await workspace.getOrLoadMarkdownDocument(uri); + if (doc) { + return parser.tokenize(doc); + } else { + return []; + } } }); diff --git a/extensions/markdown-language-features/src/client/protocol.ts b/extensions/markdown-language-features/src/client/protocol.ts index 2f6c48b371d7c..69d162f8262f3 100644 --- a/extensions/markdown-language-features/src/client/protocol.ts +++ b/extensions/markdown-language-features/src/client/protocol.ts @@ -16,7 +16,7 @@ export type ResolvedDocumentLinkTarget = | { readonly kind: 'external'; readonly uri: vscode.Uri }; //#region From server -export const parse = new RequestType<{ uri: string }, Token[], any>('markdown/parse'); +export const parse = new RequestType<{ uri: string; text?: string }, Token[], any>('markdown/parse'); export const fs_readFile = new RequestType<{ uri: string }, number[], any>('markdown/fs/readFile'); export const fs_readDirectory = new RequestType<{ uri: string }, [string, { isDirectory: boolean }][], any>('markdown/fs/readDirectory'); diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 0ef05452e4f89..103cbc191e419 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -55,7 +55,7 @@ class TokenCache { public tryGetCached(document: ITextDocument, config: MarkdownItConfig): Token[] | undefined { if (this._cachedDocument && this._cachedDocument.uri.toString() === document.uri.toString() - && this._cachedDocument.version === document.version + && document.version >= 0 && this._cachedDocument.version === document.version && this._cachedDocument.config.breaks === config.breaks && this._cachedDocument.config.linkify === config.linkify ) { diff --git a/extensions/microsoft-authentication/src/AADHelper.ts b/extensions/microsoft-authentication/src/AADHelper.ts index 0417defe534d8..df36686dc9a3d 100644 --- a/extensions/microsoft-authentication/src/AADHelper.ts +++ b/extensions/microsoft-authentication/src/AADHelper.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import * as path from 'path'; import { isSupportedEnvironment } from './common/uri'; -import { IntervalTimer, SequencerByKey } from './common/async'; +import { IntervalTimer, raceCancellationAndTimeoutError, SequencerByKey } from './common/async'; import { generateCodeChallenge, generateCodeVerifier, randomUUID } from './cryptoUtils'; import { BetterTokenStorage, IDidChangeInOtherWindowEvent } from './betterSecretStorage'; import { LoopbackAuthServer } from './node/authServer'; @@ -314,25 +314,27 @@ export class AzureActiveDirectoryService { throw new Error('Sign in to non-public clouds is not supported on the web.'); } - if (runsRemote || runsServerless) { - return this.createSessionWithoutLocalServer(scopeData); - } + return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: vscode.l10n.t('Signing in to your account...'), cancellable: true }, async (_progress, token) => { + if (runsRemote || runsServerless) { + return await this.createSessionWithoutLocalServer(scopeData, token); + } - try { - return await this.createSessionWithLocalServer(scopeData); - } catch (e) { - this._logger.error(`[${scopeData.scopeStr}] Error creating session: ${e}`); + try { + return await this.createSessionWithLocalServer(scopeData, token); + } catch (e) { + this._logger.error(`[${scopeData.scopeStr}] Error creating session: ${e}`); - // If the error was about starting the server, try directly hitting the login endpoint instead - if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') { - return this.createSessionWithoutLocalServer(scopeData); - } + // If the error was about starting the server, try directly hitting the login endpoint instead + if (e.message === 'Error listening to server' || e.message === 'Closed' || e.message === 'Timeout waiting for port') { + return this.createSessionWithoutLocalServer(scopeData, token); + } - throw e; - } + throw e; + } + }); } - private async createSessionWithLocalServer(scopeData: IScopeData) { + private async createSessionWithLocalServer(scopeData: IScopeData, token: vscode.CancellationToken): Promise { this._logger.trace(`[${scopeData.scopeStr}] Starting login flow with local server`); const codeVerifier = generateCodeVerifier(); const codeChallenge = await generateCodeChallenge(codeVerifier); @@ -353,7 +355,7 @@ export class AzureActiveDirectoryService { let codeToExchange; try { vscode.env.openExternal(vscode.Uri.parse(`http://127.0.0.1:${server.port}/signin?nonce=${encodeURIComponent(server.nonce)}`)); - const { code } = await server.waitForOAuthResponse(); + const { code } = await raceCancellationAndTimeoutError(server.waitForOAuthResponse(), token, 1000 * 60 * 5); // 5 minutes codeToExchange = code; } finally { setTimeout(() => { @@ -368,7 +370,7 @@ export class AzureActiveDirectoryService { return session; } - private async createSessionWithoutLocalServer(scopeData: IScopeData): Promise { + private async createSessionWithoutLocalServer(scopeData: IScopeData, token: vscode.CancellationToken): Promise { this._logger.trace(`[${scopeData.scopeStr}] Starting login flow without local server`); let callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.microsoft-authentication`)); const nonce = generateCodeVerifier(); @@ -395,14 +397,6 @@ export class AzureActiveDirectoryService { const uri = vscode.Uri.parse(signInUrl.toString()); vscode.env.openExternal(uri); - let inputBox: vscode.InputBox | undefined; - const timeoutPromise = new Promise((_: (value: vscode.AuthenticationSession) => void, reject) => { - const wait = setTimeout(() => { - clearTimeout(wait); - inputBox?.dispose(); - reject('Login timed out.'); - }, 1000 * 60 * 5); - }); const existingNonces = this._pendingNonces.get(scopeData.scopeStr) || []; this._pendingNonces.set(scopeData.scopeStr, [...existingNonces, nonce]); @@ -410,6 +404,7 @@ export class AzureActiveDirectoryService { // Register a single listener for the URI callback, in case the user starts the login process multiple times // before completing it. let existingPromise = this._codeExchangePromises.get(scopeData.scopeStr); + let inputBox: vscode.InputBox | undefined; if (!existingPromise) { if (isSupportedEnvironment(callbackUri)) { existingPromise = this.handleCodeResponse(scopeData); @@ -422,11 +417,12 @@ export class AzureActiveDirectoryService { this._codeVerfifiers.set(nonce, codeVerifier); - return Promise.race([existingPromise, timeoutPromise]) + return await raceCancellationAndTimeoutError(existingPromise, token, 1000 * 60 * 5) // 5 minutes .finally(() => { this._pendingNonces.delete(scopeData.scopeStr); this._codeExchangePromises.delete(scopeData.scopeStr); this._codeVerfifiers.delete(nonce); + inputBox?.dispose(); }); } diff --git a/extensions/microsoft-authentication/src/common/async.ts b/extensions/microsoft-authentication/src/common/async.ts index 527b5bbb39942..641faaff0ddaa 100644 --- a/extensions/microsoft-authentication/src/common/async.ts +++ b/extensions/microsoft-authentication/src/common/async.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vscode'; +import { CancellationError, CancellationToken, Disposable } from 'vscode'; export class SequencerByKey { @@ -47,3 +47,36 @@ export class IntervalTimer extends Disposable { }, interval); } } + +/** + * Returns a promise that rejects with an {@CancellationError} as soon as the passed token is cancelled. + * @see {@link raceCancellation} + */ +export function raceCancellationError(promise: Promise, token: CancellationToken): Promise { + return new Promise((resolve, reject) => { + const ref = token.onCancellationRequested(() => { + ref.dispose(); + reject(new CancellationError()); + }); + promise.then(resolve, reject).finally(() => ref.dispose()); + }); +} + +export class TimeoutError extends Error { + constructor() { + super('Timed out'); + } +} + +export function raceTimeoutError(promise: Promise, timeout: number): Promise { + return new Promise((resolve, reject) => { + const ref = setTimeout(() => { + reject(new CancellationError()); + }, timeout); + promise.then(resolve, reject).finally(() => clearTimeout(ref)); + }); +} + +export function raceCancellationAndTimeoutError(promise: Promise, token: CancellationToken, timeout: number): Promise { + return raceCancellationError(raceTimeoutError(promise, timeout), token); +} diff --git a/extensions/razor/cgmanifest.json b/extensions/razor/cgmanifest.json index d3685974bdb62..b8b0e5dae4f77 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": "f01e110af179981942987384d2b5d4e489eab014" + "commitHash": "39159764277f3c80a786d8872eba7730da3d7ef0" } }, "license": "MIT", diff --git a/extensions/razor/syntaxes/cshtml.tmLanguage.json b/extensions/razor/syntaxes/cshtml.tmLanguage.json index 389a6daf249e7..71055e66e102a 100644 --- a/extensions/razor/syntaxes/cshtml.tmLanguage.json +++ b/extensions/razor/syntaxes/cshtml.tmLanguage.json @@ -4,9 +4,31 @@ "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/f01e110af179981942987384d2b5d4e489eab014", + "version": "/~https://github.com/dotnet/razor/commit/39159764277f3c80a786d8872eba7730da3d7ef0", "name": "ASP.NET Razor", "scopeName": "text.html.cshtml", + "injections": { + "string.quoted.double.html": { + "patterns": [ + { + "include": "#explicit-razor-expression" + }, + { + "include": "#implicit-expression" + } + ] + }, + "string.quoted.single.html": { + "patterns": [ + { + "include": "#explicit-razor-expression" + }, + { + "include": "#implicit-expression" + } + ] + } + }, "patterns": [ { "include": "#razor-control-structures" diff --git a/extensions/shellscript/cgmanifest.json b/extensions/shellscript/cgmanifest.json index 01ceff5f55aaa..48f939ecc4570 100644 --- a/extensions/shellscript/cgmanifest.json +++ b/extensions/shellscript/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jeff-hykin/better-shell-syntax", "repositoryUrl": "/~https://github.com/jeff-hykin/better-shell-syntax", - "commitHash": "21748db7c7fd6ccd660c5bc770212836e58385ae" + "commitHash": "6d0bc37a6b8023a5fddf75bd2b4eb1e1f962e4c2" } }, "license": "MIT", - "version": "1.8.3" + "version": "1.8.7" } ], "version": 1 diff --git a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json b/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json index 28492442703b5..7aae970d227cd 100644 --- a/extensions/shellscript/syntaxes/shell-unix-bash.tmLanguage.json +++ b/extensions/shellscript/syntaxes/shell-unix-bash.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/jeff-hykin/better-shell-syntax/commit/21748db7c7fd6ccd660c5bc770212836e58385ae", + "version": "/~https://github.com/jeff-hykin/better-shell-syntax/commit/6d0bc37a6b8023a5fddf75bd2b4eb1e1f962e4c2", "name": "Shell Script", "scopeName": "source.shell", "patterns": [ @@ -14,7 +14,7 @@ ], "repository": { "alias_statement": { - "begin": "(?:(alias)(?:[ \\t]*+)((?:(?:((?\\(\\)\\$`\\\\\"\\|]+)(?!>))", + "match": "(?:[ \\t]*+)((?:[^ \t\n>&;<>\\(\\)\\$`\\\\\"'<\\|]+)(?!>))", "captures": { "1": { "name": "string.unquoted.argument.shell", @@ -296,7 +296,7 @@ ] }, "basic_command_name": { - "match": "(?:(?:(?!(?:!|&|\\||\\(|\\)|\\{|\\[|<|>|#|\\n|$|;|[ \\t]))(?!nocorrect |nocorrect\t|nocorrect$|readonly |readonly\t|readonly$|function |function\t|function$|foreach |foreach\t|foreach$|coproc |coproc\t|coproc$|logout |logout\t|logout$|export |export\t|export$|select |select\t|select$|repeat |repeat\t|repeat$|pushd |pushd\t|pushd$|until |until\t|until$|while |while\t|while$|local |local\t|local$|case |case\t|case$|done |done\t|done$|elif |elif\t|elif$|else |else\t|else$|esac |esac\t|esac$|popd |popd\t|popd$|then |then\t|then$|time |time\t|time$|for |for\t|for$|end |end\t|end$|fi |fi\t|fi$|do |do\t|do$|in |in\t|in$|if |if\t|if$))(?:((?<=^|;|&|[ \\t])(?:readonly|declare|typeset|export|local)(?=[ \\t]|;|&|$))|((?!\"|'|\\\\\\n?$)(?:[^!'\" \\t\\n\\r]+?)))(?:(?= |\\t)|(?:(?=;|\\||&|\\n|\\)|\\`|\\{|\\}|[ \\t]*#|\\])(?|#|\\n|$|;|[ \\t]))(?!nocorrect |nocorrect\t|nocorrect$|readonly |readonly\t|readonly$|function |function\t|function$|foreach |foreach\t|foreach$|coproc |coproc\t|coproc$|logout |logout\t|logout$|export |export\t|export$|select |select\t|select$|repeat |repeat\t|repeat$|pushd |pushd\t|pushd$|until |until\t|until$|while |while\t|while$|local |local\t|local$|case |case\t|case$|done |done\t|done$|elif |elif\t|elif$|else |else\t|else$|esac |esac\t|esac$|popd |popd\t|popd$|then |then\t|then$|time |time\t|time$|for |for\t|for$|end |end\t|end$|fi |fi\t|fi$|do |do\t|do$|in |in\t|in$|if |if\t|if$))(?:((?<=^|;|&|[ \\t])(?:readonly|declare|typeset|export|local)(?=[ \\t]|;|&|$))|((?!\"|'|\\\\\\n?$)(?:[^!'\"<> \\t\\n\\r]+?)))(?:(?= |\\t)|(?:(?=;|\\||&|\\n|\\)|\\`|\\{|\\}|[ \\t]*#|\\])(?]+))", "captures": { "1": { "name": "entity.name.function.call.shell entity.name.command.shell" @@ -564,6 +572,9 @@ }, { "include": "#string" + }, + { + "include": "#heredoc" } ] }, @@ -867,7 +878,7 @@ }, "endCaptures": { "0": { - "name": "punctuation.definition.string.heredoc.delimiter.shell" + "name": "punctuation.definition.string.heredoc.$0.shell" } }, "contentName": "string.quoted.heredoc.indent.$3", @@ -1500,6 +1511,30 @@ } ] }, + "math_operators": { + "patterns": [ + { + "match": "\\+{1,2}|-{1,2}|!|~|\\*{1,2}|/|%|<[<=]?|>[>=]?|==|!=|^|\\|{1,2}|&{1,2}|\\?|\\:|,|=|[*/%+\\-&^|]=|<<=|>>=", + "name": "keyword.operator.arithmetic.shell" + }, + { + "match": "0[xX][0-9A-Fa-f]+", + "name": "constant.numeric.hex.shell" + }, + { + "match": "0\\d+", + "name": "constant.numeric.octal.shell" + }, + { + "match": "\\d{1,2}#[0-9a-zA-Z@_]+", + "name": "constant.numeric.other.shell" + }, + { + "match": "\\d+", + "name": "constant.numeric.integer.shell" + } + ] + }, "misc_ranges": { "patterns": [ { @@ -1912,7 +1947,7 @@ ] }, "redirect_fix": { - "match": "(?:(>>?)(?:[ \\t]*+)([^ \t\n'&;<>\\(\\)\\$`\\\\\"\\|]+))", + "match": "(?:(>>?)(?:[ \\t]*+)([^ \t\n>&;<>\\(\\)\\$`\\\\\"'<\\|]+))", "captures": { "1": { "name": "keyword.operator.redirect.shell" @@ -1996,7 +2031,7 @@ } }, "simple_unquoted": { - "match": "[^ \\t\\n'&;<>\\(\\)\\$`\\\\\"\\|]", + "match": "[^ \\t\\n>&;<>\\(\\)\\$`\\\\\"'<\\|]", "name": "string.unquoted.shell" }, "special_expansion": { @@ -2135,6 +2170,9 @@ { "include": "#for_statement" }, + { + "include": "#while_statement" + }, { "include": "#function_definition" }, @@ -2275,6 +2313,41 @@ } } ] + }, + "while_statement": { + "patterns": [ + { + "begin": "(\\bwhile\\b)", + "end": "(?=;|\\||&|\\n|\\)|\\`|\\{|\\}|[ \\t]*#|\\])(? { const { line, character } = args.position; const position = new vscode.Position(line, character); - await vscode.commands.executeCommand('vscode.open', vscode.Uri.from(args.file), { + await vscode.commands.executeCommand('vscode.open', vscode.Uri.from(args.file), { selection: new vscode.Range(position, position), - }); + } satisfies vscode.TextDocumentShowOptions); } } diff --git a/extensions/typescript-language-features/src/filesystems/memFs.ts b/extensions/typescript-language-features/src/filesystems/memFs.ts index 02476ec180426..eeeb60e957d3b 100644 --- a/extensions/typescript-language-features/src/filesystems/memFs.ts +++ b/extensions/typescript-language-features/src/filesystems/memFs.ts @@ -8,7 +8,7 @@ import { basename, dirname } from 'path'; export class MemFs implements vscode.FileSystemProvider { - private readonly root = new FsEntry( + private readonly root = new FsDirectoryEntry( new Map(), 0, 0, @@ -31,8 +31,11 @@ export class MemFs implements vscode.FileSystemProvider { if (!entry) { throw vscode.FileSystemError.FileNotFound(); } + if (!(entry instanceof FsDirectoryEntry)) { + throw vscode.FileSystemError.FileNotADirectory(); + } - return [...entry.contents.entries()].map(([name, entry]) => [name, entry.type]); + return Array.from(entry.contents.entries(), ([name, entry]) => [name, entry.type]); } readFile(uri: vscode.Uri): Uint8Array { @@ -43,6 +46,10 @@ export class MemFs implements vscode.FileSystemProvider { throw vscode.FileSystemError.FileNotFound(); } + if (!(entry instanceof FsFileEntry)) { + throw vscode.FileSystemError.FileIsADirectory(uri); + } + return entry.data; } @@ -58,12 +65,16 @@ export class MemFs implements vscode.FileSystemProvider { const entry = dirContents.get(basename(uri.path)); if (!entry) { if (create) { - dirContents.set(fileName, new FsEntry(content, time, time)); + dirContents.set(fileName, new FsFileEntry(content, time, time)); this._emitter.fire([{ type: vscode.FileChangeType.Created, uri }]); } else { throw vscode.FileSystemError.FileNotFound(); } } else { + if (entry instanceof FsDirectoryEntry) { + throw vscode.FileSystemError.FileIsADirectory(uri); + } + if (overwrite) { entry.mtime = time; entry.data = content; @@ -90,10 +101,10 @@ export class MemFs implements vscode.FileSystemProvider { // console.log('createDirectory', uri.toString()); const dir = this.getParent(uri); const now = Date.now() / 1000; - dir.contents.set(basename(uri.path), new FsEntry(new Map(), now, now)); + dir.contents.set(basename(uri.path), new FsDirectoryEntry(new Map(), now, now)); } - private getEntry(uri: vscode.Uri): FsEntry | void { + private getEntry(uri: vscode.Uri): FsEntry | undefined { // TODO: have this throw FileNotFound itself? // TODO: support configuring case sensitivity let node: FsEntry = this.root; @@ -104,13 +115,12 @@ export class MemFs implements vscode.FileSystemProvider { continue; } - if (node.type !== vscode.FileType.Directory) { + if (!(node instanceof FsDirectoryEntry)) { // We're looking at a File or such, so bail. return; } const next = node.contents.get(component); - if (!next) { // not found! return; @@ -121,11 +131,14 @@ export class MemFs implements vscode.FileSystemProvider { return node; } - private getParent(uri: vscode.Uri) { + private getParent(uri: vscode.Uri): FsDirectoryEntry { const dir = this.getEntry(uri.with({ path: dirname(uri.path) })); if (!dir) { throw vscode.FileSystemError.FileNotFound(); } + if (!(dir instanceof FsDirectoryEntry)) { + throw vscode.FileSystemError.FileNotADirectory(); + } return dir; } @@ -153,46 +166,32 @@ export class MemFs implements vscode.FileSystemProvider { } } -class FsEntry { - get type(): vscode.FileType { - if (this._data instanceof Uint8Array) { - return vscode.FileType.File; - } else { - return vscode.FileType.Directory; - } - } +class FsFileEntry { + readonly type = vscode.FileType.File; get size(): number { - if (this.type === vscode.FileType.Directory) { - return [...this.contents.values()].reduce((acc: number, entry: FsEntry) => acc + entry.size, 0); - } else { - return this.data.length; - } + return this.data.length; } constructor( - private _data: Uint8Array | Map, - public ctime: number, + public data: Uint8Array, + public readonly ctime: number, public mtime: number, ) { } +} - get data() { - if (this.type === vscode.FileType.Directory) { - throw vscode.FileSystemError.FileIsADirectory; - } - return this._data; - } - set data(val: Uint8Array) { - if (this.type === vscode.FileType.Directory) { - throw vscode.FileSystemError.FileIsADirectory; - } - this._data = val; - } +class FsDirectoryEntry { + readonly type = vscode.FileType.Directory; - get contents() { - if (this.type !== vscode.FileType.Directory) { - throw vscode.FileSystemError.FileNotADirectory; - } - return >this._data; + get size(): number { + return [...this.contents.values()].reduce((acc: number, entry: FsEntry) => acc + entry.size, 0); } + + constructor( + public readonly contents: Map, + public readonly ctime: number, + public readonly mtime: number, + ) { } } + +type FsEntry = FsFileEntry | FsDirectoryEntry; diff --git a/extensions/typescript-language-features/src/languageFeatures/refactor.ts b/extensions/typescript-language-features/src/languageFeatures/refactor.ts index 075fee940180d..8dc062357533e 100644 --- a/extensions/typescript-language-features/src/languageFeatures/refactor.ts +++ b/extensions/typescript-language-features/src/languageFeatures/refactor.ts @@ -40,6 +40,7 @@ function toWorkspaceEdit(client: ITypeScriptServiceClient, edits: readonly Proto namespace DidApplyRefactoringCommand { export interface Args { readonly action: string; + readonly trigger: vscode.CodeActionTriggerKind; } } @@ -56,6 +57,7 @@ class DidApplyRefactoringCommand implements Command { "refactor.execute" : { "owner": "mjbvz", "action" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, + "trigger" : { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, "${include}": [ "${TypeScriptCommonProperties}" ] @@ -63,6 +65,7 @@ class DidApplyRefactoringCommand implements Command { */ this.telemetryReporter.logTelemetry('refactor.execute', { action: args.action, + trigger: args.trigger, }); } } @@ -71,6 +74,7 @@ namespace SelectRefactorCommand { readonly document: vscode.TextDocument; readonly refactor: Proto.ApplicableRefactorInfo; readonly rangeOrSelection: vscode.Range | vscode.Selection; + readonly trigger: vscode.CodeActionTriggerKind; } } @@ -97,7 +101,7 @@ class SelectRefactorCommand implements Command { return; } - const tsAction = new InlinedCodeAction(this.client, args.document, args.refactor, selected.action, args.rangeOrSelection); + const tsAction = new InlinedCodeAction(this.client, args.document, args.refactor, selected.action, args.rangeOrSelection, args.trigger); await tsAction.resolve(nulToken); if (tsAction.edit) { @@ -118,6 +122,7 @@ namespace MoveToFileRefactorCommand { readonly document: vscode.TextDocument; readonly action: Proto.RefactorActionInfo; readonly range: vscode.Range; + readonly trigger: vscode.CodeActionTriggerKind; } } @@ -158,7 +163,7 @@ class MoveToFileRefactorCommand implements Command { return; } - await this.didApplyCommand.execute({ action: args.action.name }); + await this.didApplyCommand.execute({ action: args.action.name, trigger: args.trigger }); } private async getTargetFile(document: vscode.TextDocument, file: string, range: vscode.Range): Promise { @@ -347,6 +352,7 @@ class InlinedCodeAction extends vscode.CodeAction { public readonly refactor: Proto.ApplicableRefactorInfo, public readonly action: Proto.RefactorActionInfo, public readonly range: vscode.Range, + trigger: vscode.CodeActionTriggerKind, ) { const title = action.description; super(title, InlinedCodeAction.getKind(action)); @@ -358,7 +364,7 @@ class InlinedCodeAction extends vscode.CodeAction { this.command = { title, command: DidApplyRefactoringCommand.ID, - arguments: [{ action: action.name }], + arguments: [{ action: action.name, trigger } satisfies DidApplyRefactoringCommand.Args], }; } @@ -420,6 +426,7 @@ class MoveToFileCodeAction extends vscode.CodeAction { document: vscode.TextDocument, action: Proto.RefactorActionInfo, range: vscode.Range, + trigger: vscode.CodeActionTriggerKind, ) { super(action.description, Move_File.kind); @@ -430,7 +437,7 @@ class MoveToFileCodeAction extends vscode.CodeAction { this.command = { title: action.description, command: MoveToFileRefactorCommand.ID, - arguments: [{ action, document, range }] + arguments: [{ action, document, range, trigger } satisfies MoveToFileRefactorCommand.Args] }; } } @@ -439,13 +446,14 @@ class SelectCodeAction extends vscode.CodeAction { constructor( info: Proto.ApplicableRefactorInfo, document: vscode.TextDocument, - rangeOrSelection: vscode.Range | vscode.Selection + rangeOrSelection: vscode.Range | vscode.Selection, + trigger: vscode.CodeActionTriggerKind, ) { super(info.description, vscode.CodeActionKind.Refactor); this.command = { title: info.description, command: SelectRefactorCommand.ID, - arguments: [{ action: this, document, refactor: info, rangeOrSelection }] + arguments: [{ document, refactor: info, rangeOrSelection, trigger } satisfies SelectRefactorCommand.Args] }; } } @@ -552,7 +560,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { if (this.client.apiVersion.lt(API.v430)) { // Don't show 'infer return type' refactoring unless it has been explicitly requested @@ -603,15 +611,16 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider { for (const refactor of refactors) { if (refactor.inlineable === false) { - yield new SelectCodeAction(refactor, document, rangeOrSelection); + yield new SelectCodeAction(refactor, document, rangeOrSelection, context.triggerKind); } else { for (const action of refactor.actions) { - for (const codeAction of this.refactorActionToCodeActions(document, refactor, action, rangeOrSelection, refactor.actions)) { + for (const codeAction of this.refactorActionToCodeActions(document, context, refactor, action, rangeOrSelection, refactor.actions)) { yield codeAction; } } @@ -621,6 +630,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider( } export function coalesce(array: ReadonlyArray): T[] { - return array.filter(e => !!e); + return array.filter((e): e is T => !!e); } diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 27e25dbf17fe0..7411d9c094a30 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -953,8 +953,8 @@ function sanitizeData(data: string): string { // Strip escape sequences so winpty/conpty doesn't cause flakiness, do for all platforms for // consistency - const terminalCodesRegex = /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g; - data = data.replace(terminalCodesRegex, ''); + const CSI_SEQUENCE = /(:?(:?\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; + data = data.replace(CSI_SEQUENCE, ''); return data; } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_go.json b/extensions/vscode-colorize-tests/test/colorize-results/test_go.json index 4d399b0c0ec00..d6b2ef38ebb96 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_go.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_go.json @@ -1065,7 +1065,7 @@ }, { "c": "nil", - "t": "source.go constant.language.go", + "t": "source.go constant.language.null.go", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", diff --git a/package.json b/package.json index 2fca1caae582e..ce9dd822377b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.89.0", + "version": "1.90.0", "distro": "25c9442cad1fee4c5b7fd96dd526ae00022f881b", "author": { "name": "Microsoft Corporation" @@ -81,7 +81,6 @@ "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-canvas": "0.8.0-beta.17", "@xterm/addon-image": "0.9.0-beta.17", "@xterm/addon-search": "0.16.0-beta.17", "@xterm/addon-serialize": "0.14.0-beta.17", @@ -99,7 +98,7 @@ "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta11", - "tas-client-umd": "0.1.8", + "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", diff --git a/remote/package.json b/remote/package.json index 0bec8279e6612..c0397ce197f62 100644 --- a/remote/package.json +++ b/remote/package.json @@ -13,7 +13,6 @@ "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-canvas": "0.8.0-beta.17", "@xterm/addon-image": "0.9.0-beta.17", "@xterm/addon-search": "0.16.0-beta.17", "@xterm/addon-serialize": "0.14.0-beta.17", @@ -30,7 +29,7 @@ "minimist": "^1.2.6", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta11", - "tas-client-umd": "0.1.8", + "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.0.0", diff --git a/remote/web/package.json b/remote/web/package.json index 326cf519ada67..76375094a7dae 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -7,7 +7,6 @@ "@microsoft/1ds-post-js": "^3.2.13", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-canvas": "0.8.0-beta.17", "@xterm/addon-image": "0.9.0-beta.17", "@xterm/addon-search": "0.16.0-beta.17", "@xterm/addon-serialize": "0.14.0-beta.17", @@ -15,7 +14,7 @@ "@xterm/addon-webgl": "0.19.0-beta.17", "@xterm/xterm": "5.6.0-beta.17", "jschardet": "3.0.0", - "tas-client-umd": "0.1.8", + "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0" } diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index ed86d85e39cf6..81215235ac655 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -48,11 +48,6 @@ resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3" integrity sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g== -"@xterm/addon-canvas@0.8.0-beta.17": - version "0.8.0-beta.17" - resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.8.0-beta.17.tgz#309630c738aa2e44742cdce5ee88fb8079fa652c" - integrity sha512-1km1RE02rxdbJWp1sev6Um6T/4tWlpEhJ88OP7xwfUFuedhFEby0JXmKzP7qB0cFzEvFTCq1bOAHSA3DX8vlFQ== - "@xterm/addon-image@0.9.0-beta.17": version "0.9.0-beta.17" resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.17.tgz#343d0665a6060d4f893b4f2d32de6ccbbd00bb63" @@ -88,10 +83,10 @@ jschardet@3.0.0: resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.0.0.tgz#898d2332e45ebabbdb6bf2feece9feea9a99e882" integrity sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ== -tas-client-umd@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.8.tgz#38bd32d49545417a0ea67fb618e646298e1b67cc" - integrity sha512-0jAAujLmjjGXf9PzrNpjOrr/6CTpSOp8jX80NOHK5nlOTWWpwaZ16EOyrPdHnm2bVfPHvT0/RAD0xyiQHGQvCQ== +tas-client-umd@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.2.0.tgz#b71cc28f4c9b14f7b62f1ca4669886aa197e390c" + integrity sha512-oezN7mJVm5qZDVEby7OzxCLKUpUN5of0rY4dvOWaDF2JZBlGpd3BXceFN8B53qlTaIkVSzP65aAMT0Vc+/N25Q== vscode-oniguruma@1.7.0: version "1.7.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 66fe13a5c4c51..88d688b59549a 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -114,11 +114,6 @@ resolved "https://registry.yarnpkg.com/@vscode/windows-registry/-/windows-registry-1.1.0.tgz#03dace7c29c46f658588b9885b9580e453ad21f9" integrity sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw== -"@xterm/addon-canvas@0.8.0-beta.17": - version "0.8.0-beta.17" - resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.8.0-beta.17.tgz#309630c738aa2e44742cdce5ee88fb8079fa652c" - integrity sha512-1km1RE02rxdbJWp1sev6Um6T/4tWlpEhJ88OP7xwfUFuedhFEby0JXmKzP7qB0cFzEvFTCq1bOAHSA3DX8vlFQ== - "@xterm/addon-image@0.9.0-beta.17": version "0.9.0-beta.17" resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.17.tgz#343d0665a6060d4f893b4f2d32de6ccbbd00bb63" @@ -586,10 +581,10 @@ tar-stream@^2.1.4: inherits "^2.0.3" readable-stream "^3.1.1" -tas-client-umd@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.8.tgz#38bd32d49545417a0ea67fb618e646298e1b67cc" - integrity sha512-0jAAujLmjjGXf9PzrNpjOrr/6CTpSOp8jX80NOHK5nlOTWWpwaZ16EOyrPdHnm2bVfPHvT0/RAD0xyiQHGQvCQ== +tas-client-umd@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.2.0.tgz#b71cc28f4c9b14f7b62f1ca4669886aa197e390c" + integrity sha512-oezN7mJVm5qZDVEby7OzxCLKUpUN5of0rY4dvOWaDF2JZBlGpd3BXceFN8B53qlTaIkVSzP65aAMT0Vc+/N25Q== to-regex-range@^5.0.1: version "5.0.1" diff --git a/scripts/xterm-update.js b/scripts/xterm-update.js index 747d7af3ea802..851b296af62c5 100644 --- a/scripts/xterm-update.js +++ b/scripts/xterm-update.js @@ -8,7 +8,6 @@ const path = require('path'); const moduleNames = [ '@xterm/xterm', - '@xterm/addon-canvas', '@xterm/addon-image', '@xterm/addon-search', '@xterm/addon-serialize', diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index 85409cc5a212d..fa3bc5eb839dc 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -124,7 +124,6 @@ 'vscode-oniguruma': `${baseNodeModulesPath}/vscode-oniguruma/release/main.js`, 'vsda': `${baseNodeModulesPath}/vsda/index.js`, '@xterm/xterm': `${baseNodeModulesPath}/@xterm/xterm/lib/xterm.js`, - '@xterm/addon-canvas': `${baseNodeModulesPath}/@xterm/addon-canvas/lib/addon-canvas.js`, '@xterm/addon-image': `${baseNodeModulesPath}/@xterm/addon-image/lib/addon-image.js`, '@xterm/addon-search': `${baseNodeModulesPath}/@xterm/addon-search/lib/addon-search.js`, '@xterm/addon-serialize': `${baseNodeModulesPath}/@xterm/addon-serialize/lib/addon-serialize.js`, diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index fe822fe1a61c1..76c1afb4e3948 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -588,34 +588,62 @@ function mergeRawTokenText(tokens: marked.Token[]): string { return mergedTokenText; } -function completeSingleLinePattern(token: marked.Tokens.ListItem | marked.Tokens.Paragraph): marked.Token | undefined { - for (let i = 0; i < token.tokens.length; i++) { +function completeSingleLinePattern(token: marked.Tokens.Text | marked.Tokens.Paragraph): marked.Token | undefined { + if (!token.tokens) { + return undefined; + } + + for (let i = token.tokens.length - 1; i >= 0; i--) { const subtoken = token.tokens[i]; if (subtoken.type === 'text') { const lines = subtoken.raw.split('\n'); const lastLine = lines[lines.length - 1]; if (lastLine.includes('`')) { return completeCodespan(token); - } else if (lastLine.includes('**')) { + } + + else if (lastLine.includes('**')) { return completeDoublestar(token); - } else if (lastLine.match(/\*\w/)) { + } + + else if (lastLine.match(/\*\w/)) { return completeStar(token); - } else if (lastLine.match(/(^|\s)__\w/)) { + } + + else if (lastLine.match(/(^|\s)__\w/)) { return completeDoubleUnderscore(token); - } else if (lastLine.match(/(^|\s)_\w/)) { + } + + else if (lastLine.match(/(^|\s)_\w/)) { return completeUnderscore(token); - } else if (lastLine.match(/(^|\s)\[.*\]\(\w*/)) { + } + + else if ( + // Text with start of link target + hasLinkTextAndStartOfLinkTarget(lastLine) || + // This token doesn't have the link text, eg if it contains other markdown constructs that are in other subtokens. + // But some preceding token does have an unbalanced [ at least + hasStartOfLinkTargetAndNoLinkText(lastLine) && token.tokens.slice(0, i).some(t => t.type === 'text' && t.raw.match(/\[[^\]]*$/)) + ) { const nextTwoSubTokens = token.tokens.slice(i + 1); - if (nextTwoSubTokens[0]?.type === 'link' && nextTwoSubTokens[1]?.type === 'text' && nextTwoSubTokens[1].raw.match(/^ *"[^"]*$/)) { - // A markdown link can look like - // [link text](https://microsoft.com "more text") - // Where "more text" is a title for the link or an argument to a vscode command link + + // A markdown link can look like + // [link text](https://microsoft.com "more text") + // Where "more text" is a title for the link or an argument to a vscode command link + if ( + // If the link was parsed as a link, then look for a link token and a text token with a quote + nextTwoSubTokens[0]?.type === 'link' && nextTwoSubTokens[1]?.type === 'text' && nextTwoSubTokens[1].raw.match(/^ *"[^"]*$/) || + // And if the link was not parsed as a link (eg command link), just look for a single quote in this token + lastLine.match(/^[^"]* +"[^"]*$/) + ) { + return completeLinkTargetArg(token); } return completeLinkTarget(token); - } else if (hasStartOfLinkTarget(lastLine)) { - return completeLinkTarget(token); - } else if (lastLine.match(/(^|\s)\[\w/) && !token.tokens.slice(i + 1).some(t => hasStartOfLinkTarget(t.raw))) { + } + + // Contains the start of link text, and no following tokens contain the link target + else if (lastLine.match(/(^|\s)\[\w*/)) { return completeLinkText(token); } } @@ -624,31 +652,90 @@ function completeSingleLinePattern(token: marked.Tokens.ListItem | marked.Tokens return undefined; } -function hasStartOfLinkTarget(str: string): boolean { +function hasLinkTextAndStartOfLinkTarget(str: string): boolean { + return !!str.match(/(^|\s)\[.*\]\(\w*/); +} + +function hasStartOfLinkTargetAndNoLinkText(str: string): boolean { return !!str.match(/^[^\[]*\]\([^\)]*$/); } -// function completeListItemPattern(token: marked.Tokens.List): marked.Tokens.List | undefined { -// // Patch up this one list item -// const lastItem = token.items[token.items.length - 1]; +function completeListItemPattern(list: marked.Tokens.List): marked.Tokens.List | undefined { + // Patch up this one list item + const lastListItem = list.items[list.items.length - 1]; + const lastListSubToken = lastListItem.tokens ? lastListItem.tokens[lastListItem.tokens.length - 1] : undefined; + + /* + Example list token structures: + + list + list_item + text + text + codespan + link + list_item + text + code // Complete indented codeblock + list_item + text + space + text + text // Incomplete indented codeblock + list_item + text + list // Nested list + list_item + text + text + + Contrast with paragraph: + paragraph + text + codespan + */ + + let newToken: marked.Token | undefined; + if (lastListSubToken?.type === 'text' && !('inRawBlock' in lastListItem)) { // Why does Tag have a type of 'text' + newToken = completeSingleLinePattern(lastListSubToken as marked.Tokens.Text); + } -// const newList = completeSingleLinePattern(lastItem); -// if (!newList || newList.type !== 'list') { -// // Nothing to fix, or not a pattern we were expecting -// return; -// } + if (!newToken || newToken.type !== 'paragraph') { // 'text' item inside the list item turns into paragraph + // Nothing to fix, or not a pattern we were expecting + return; + } -// // Re-parse the whole list with the last item replaced -// const completeList = marked.lexer(mergeRawTokenText(token.items.slice(0, token.items.length - 1)) + newList.items[0].raw); -// if (completeList.length === 1 && completeList[0].type === 'list') { -// return completeList[0]; -// } + const previousListItemsText = mergeRawTokenText(list.items.slice(0, -1)); -// // Not a pattern we were expecting -// return undefined; -// } + // Grabbing the `- ` off the list item because I can't find a better way to do this + const newListItemText = lastListItem.raw.slice(0, 2) + + mergeRawTokenText(lastListItem.tokens.slice(0, -1)) + + newToken.raw; + + const newList = marked.lexer(previousListItemsText + newListItemText)[0] as marked.Tokens.List; + if (newList.type !== 'list') { + // Something went wrong + return; + } + return newList; +} + +const maxIncompleteTokensFixRounds = 3; export function fillInIncompleteTokens(tokens: marked.TokensList): marked.TokensList { + for (let i = 0; i < maxIncompleteTokensFixRounds; i++) { + const newTokens = fillInIncompleteTokensOnce(tokens); + if (newTokens) { + tokens = newTokens; + } else { + break; + } + } + + return tokens; +} + +function fillInIncompleteTokensOnce(tokens: marked.TokensList): marked.TokensList | null { let i: number; let newTokens: marked.Token[] | undefined; for (i = 0; i < tokens.length; i++) { @@ -666,13 +753,13 @@ export function fillInIncompleteTokens(tokens: marked.TokensList): marked.Tokens break; } - // if (i === tokens.length - 1 && token.type === 'list') { - // const newListToken = completeListItemPattern(token); - // if (newListToken) { - // newTokens = [newListToken]; - // break; - // } - // } + if (i === tokens.length - 1 && token.type === 'list') { + const newListToken = completeListItemPattern(token); + if (newListToken) { + newTokens = [newListToken]; + break; + } + } if (i === tokens.length - 1 && token.type === 'paragraph') { // Only operates on a single token, because any newline that follows this should break these patterns @@ -693,7 +780,7 @@ export function fillInIncompleteTokens(tokens: marked.TokensList): marked.Tokens return newTokensList as marked.TokensList; } - return tokens; + return null; } function completeCodeBlock(tokens: marked.Token[], leader: string): marked.Token[] { diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index c1e97565a1605..52e542c0eb80b 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -341,7 +341,7 @@ function topStep(array: ReadonlyArray, compare: (a: T, b: T) => number, re * @returns New array with all falsy values removed. The original array IS NOT modified. */ export function coalesce(array: ReadonlyArray): T[] { - return array.filter(e => !!e); + return array.filter((e): e is T => !!e); } /** @@ -435,13 +435,6 @@ export function commonPrefixLength(one: ReadonlyArray, other: ReadonlyArra return result; } -/** - * @deprecated Use `[].flat()` - */ -export function flatten(arr: T[][]): T[] { - return ([]).concat(...arr); -} - export function range(to: number): number[]; export function range(from: number, to: number): number[]; export function range(arg: number, to?: number): number[] { diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 03cb85813cc83..8c02823852c7a 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -764,7 +764,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu result.push(column); } - if (wordLen === patternLen && options.boostFullMatch) { + if (wordLen - wordStart === patternLen && options.boostFullMatch) { // the word matches the pattern with all characters! // giving the score a total match boost (to come up ahead other words) result[0] += 2; diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index be372198ee714..51f74227383e1 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -786,6 +786,14 @@ export function* forAnsiStringParts(str: string) { } } +/** + * Strips ANSI escape sequences from a string. + * @param str The dastringa stringo strip the ANSI escape sequences from. + * + * @example + * removeAnsiEscapeCodes('\u001b[31mHello, World!\u001b[0m'); + * // 'Hello, World!' + */ export function removeAnsiEscapeCodes(str: string): string { if (str) { str = str.replace(CSI_SEQUENCE, ''); @@ -794,6 +802,21 @@ export function removeAnsiEscapeCodes(str: string): string { return str; } +const PROMPT_NON_PRINTABLE = /\\\[.*?\\\]/g; + +/** + * Strips ANSI escape sequences from a UNIX-style prompt string (eg. `$PS1`). + * @param str The string to strip the ANSI escape sequences from. + * + * @example + * removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'); + * // '\n\\w\n> ' + */ +export function removeAnsiEscapeCodesFromPrompt(str: string): string { + return removeAnsiEscapeCodes(str).replace(PROMPT_NON_PRINTABLE, ''); +} + + // -- UTF-8 BOM export const UTF8_BOM_CHARACTER = String.fromCharCode(CharCode.UTF8_BOM); diff --git a/src/vs/base/parts/storage/common/storage.ts b/src/vs/base/parts/storage/common/storage.ts index eb9607e6c3e6a..a1afb1f23ea83 100644 --- a/src/vs/base/parts/storage/common/storage.ts +++ b/src/vs/base/parts/storage/common/storage.ts @@ -384,8 +384,11 @@ export class Storage extends Disposable implements IStorage { } async flush(delay?: number): Promise { - if (!this.hasPending) { - return; // return early if nothing to do + if ( + this.state === StorageState.Closed || // Return early if we are already closed + this.pendingClose // return early if nothing to do + ) { + return; } return this.doFlush(delay); diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 289b3974df837..010c0d672af9f 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -362,7 +362,9 @@ suite('MarkdownRenderer', () => { const completeTableTokens = marked.lexer(completeTable); const newTokens = fillInIncompleteTokens(tokens); - ignoreRaw(newTokens, completeTableTokens); + if (newTokens) { + ignoreRaw(newTokens, completeTableTokens); + } assert.deepStrictEqual(newTokens, completeTableTokens); }); @@ -373,7 +375,9 @@ suite('MarkdownRenderer', () => { const newTokens = fillInIncompleteTokens(tokens); - ignoreRaw(newTokens, completeTableTokens); + if (newTokens) { + ignoreRaw(newTokens, completeTableTokens); + } assert.deepStrictEqual(newTokens, completeTableTokens); }); @@ -384,7 +388,9 @@ suite('MarkdownRenderer', () => { const newTokens = fillInIncompleteTokens(tokens); - ignoreRaw(newTokens, completeTableTokens); + if (newTokens) { + ignoreRaw(newTokens, completeTableTokens); + } assert.deepStrictEqual(newTokens, completeTableTokens); }); @@ -592,7 +598,7 @@ const y = 2; assert.deepStrictEqual(newTokens, completeTokens); }); - test.skip(`incomplete ${name} in list`, () => { + test(`incomplete ${name} in list`, () => { const text = `- list item one\n- list item two and ${delimiter}text`; const tokens = marked.lexer(text); const newTokens = fillInIncompleteTokens(tokens); @@ -602,6 +608,83 @@ const y = 2; }); } + suite('list', () => { + test('list with complete codeblock', () => { + const list = `- + \`\`\`js + let x = 1; + \`\`\` +- list item two +`; + const tokens = marked.lexer(list); + const newTokens = fillInIncompleteTokens(tokens); + + assert.deepStrictEqual(newTokens, tokens); + }); + + test.skip('list with incomplete codeblock', () => { + const incomplete = `- list item one + + \`\`\`js + let x = 1;` + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '\n ```'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('list with subitems', () => { + const list = `- hello + - sub item +- text + newline for some reason +` + const tokens = marked.lexer(list); + const newTokens = fillInIncompleteTokens(tokens); + + assert.deepStrictEqual(newTokens, tokens); + }); + + test('list with stuff', () => { + const list = `- list item one \`codespan\` **bold** [link](http://microsoft.com) more text`; + const tokens = marked.lexer(list); + const newTokens = fillInIncompleteTokens(tokens); + + assert.deepStrictEqual(newTokens, tokens); + }); + + test('list with incomplete link text', () => { + const incomplete = `- list item one +- item two [link` + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '](about:blank)'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('list with incomplete link target', () => { + const incomplete = `- list item one +- item two [link](` + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + ')'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('list with incomplete link with other stuff', () => { + const incomplete = `- list item one +- item two [\`link` + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '\`](about:blank)'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + }); + suite('codespan', () => { simpleMarkdownTestSuite('codespan', '`'); @@ -720,16 +803,16 @@ const y = 2; assert.deepStrictEqual(newTokens, completeTokens); }); - test('incomplete link target with extra stuff and arg', () => { + test('incomplete link target with extra stuff and incomplete arg', () => { const incomplete = '[before `text` after](http://microsoft.com "more text '; const tokens = marked.lexer(incomplete); const newTokens = fillInIncompleteTokens(tokens); - const completeTokens = marked.lexer(incomplete + ')'); + const completeTokens = marked.lexer(incomplete + '")'); assert.deepStrictEqual(newTokens, completeTokens); }); - test('incomplete link target with arg', () => { + test('incomplete link target with incomplete arg', () => { const incomplete = 'foo [text](http://microsoft.com "more text here '; const tokens = marked.lexer(incomplete); const newTokens = fillInIncompleteTokens(tokens); @@ -738,6 +821,51 @@ const y = 2; assert.deepStrictEqual(newTokens, completeTokens); }); + test('incomplete link target with incomplete arg 2', () => { + const incomplete = '[text](command:_github.copilot.openRelativePath "arg'; + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '")'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('incomplete link target with complete arg', () => { + const incomplete = 'foo [text](http://microsoft.com "more text here"'; + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + ')'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('link text with incomplete codespan', () => { + const incomplete = `text [\`codespan`; + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '`](about:blank)'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('link text with incomplete stuff', () => { + const incomplete = `text [more text \`codespan\` text **bold`; + const tokens = marked.lexer(incomplete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(incomplete + '**](about:blank)'); + assert.deepStrictEqual(newTokens, completeTokens); + }); + + test('Looks like incomplete link target but isn\'t', () => { + const complete = '**bold** `codespan` text]('; + const tokens = marked.lexer(complete); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(complete); + assert.deepStrictEqual(newTokens, completeTokens); + }); + test.skip('incomplete link in list', () => { const incomplete = '- [text'; const tokens = marked.lexer(incomplete); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index 135330f7044bd..5bfe3856226f5 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -580,8 +580,16 @@ suite('Filters', () => { const a = 'createModelServices'; const b = 'create'; - const aBoost = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); - const bBoost = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); + let aBoost = fuzzyScore(prefix, prefix, 0, a, a.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); + let bBoost = fuzzyScore(prefix, prefix, 0, b, b.toLowerCase(), 0, { boostFullMatch: true, firstMatchCanBeWeak: true }); + assert.ok(aBoost); + assert.ok(bBoost); + assert.ok(aBoost[0] < bBoost[0]); + + // also works with wordStart > 0 (/~https://github.com/microsoft/vscode/issues/187921) + const wordPrefix = '$(symbol-function) '; + aBoost = fuzzyScore(prefix, prefix, 0, `${wordPrefix}${a}`, `${wordPrefix}${a}`.toLowerCase(), wordPrefix.length, { boostFullMatch: true, firstMatchCanBeWeak: true }); + bBoost = fuzzyScore(prefix, prefix, 0, `${wordPrefix}${b}`, `${wordPrefix}${b}`.toLowerCase(), wordPrefix.length, { boostFullMatch: true, firstMatchCanBeWeak: true }); assert.ok(aBoost); assert.ok(bBoost); assert.ok(aBoost[0] < bBoost[0]); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 73c04ad723944..655bc2b42555d 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -538,6 +538,11 @@ suite('Strings', () => { } }); + test('removeAnsiEscapeCodesFromPrompt', () => { + assert.strictEqual(strings.removeAnsiEscapeCodesFromPrompt('\u001b[31m$ \u001b[0m'), '$ '); + assert.strictEqual(strings.removeAnsiEscapeCodesFromPrompt('\n\\[\u001b[01;34m\\]\\w\\[\u001b[00m\\]\n\\[\u001b[1;32m\\]> \\[\u001b[0m\\]'), '\n\\w\n> '); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts index 01e081224922a..7cf33372a9724 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterPage.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterPage.ts @@ -38,7 +38,7 @@ export default (): string => `
- + diff --git a/src/vs/code/electron-sandbox/issue/issueReporterService.ts b/src/vs/code/electron-sandbox/issue/issueReporterService.ts index bfb1d01e17d8d..6edb382d5ac7a 100644 --- a/src/vs/code/electron-sandbox/issue/issueReporterService.ts +++ b/src/vs/code/electron-sandbox/issue/issueReporterService.ts @@ -38,7 +38,8 @@ interface SearchResult { enum IssueSource { VSCode = 'vscode', Extension = 'extension', - Marketplace = 'marketplace' + Marketplace = 'marketplace', + Unknown = 'unknown' } export class IssueReporter extends Disposable { @@ -289,6 +290,13 @@ export class IssueReporter extends Disposable { this.updatePerformanceInfo(info as Partial); }); } + + // Resets placeholder + const descriptionTextArea = this.getElementById('issue-title'); + if (descriptionTextArea) { + descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title"); + } + this.updatePreviewButtonState(); this.setSourceOptions(); this.render(); @@ -334,6 +342,17 @@ export class IssueReporter extends Disposable { hide(problemSourceHelpText); } + const descriptionTextArea = this.getElementById('issue-title'); + if (value === IssueSource.VSCode) { + descriptionTextArea.placeholder = localize('vscodePlaceholder', "E.g Workbench is missing problems panel"); + } else if (value === IssueSource.Extension) { + descriptionTextArea.placeholder = localize('extensionPlaceholder', "E.g. Missing alt text on extension readme image"); + } else if (value === IssueSource.Marketplace) { + descriptionTextArea.placeholder = localize('marketplacePlaceholder', "E.g Cannot disable installed extension"); + } else { + descriptionTextArea.placeholder = localize('undefinedPlaceholder', "Please enter a title"); + } + let fileOnExtension, fileOnMarketplace = false; if (value === IssueSource.Extension) { fileOnExtension = true; @@ -712,7 +731,7 @@ export class IssueReporter extends Disposable { reset(typeSelect, makeOption(IssueType.Bug, localize('bugReporter', "Bug Report")), makeOption(IssueType.FeatureRequest, localize('featureRequest', "Feature Request")), - makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue")) + makeOption(IssueType.PerformanceIssue, localize('performanceIssue', "Performance Issue (freeze, slow, crash)")) ); typeSelect.value = issueType.toString(); @@ -747,14 +766,14 @@ export class IssueReporter extends Disposable { sourceSelect.innerText = ''; sourceSelect.append(this.makeOption('', localize('selectSource', "Select source"), true)); - sourceSelect.append(this.makeOption('vscode', localize('vscode', "Visual Studio Code"), false)); - sourceSelect.append(this.makeOption('extension', localize('extension', "An extension"), false)); + sourceSelect.append(this.makeOption(IssueSource.VSCode, localize('vscode', "Visual Studio Code"), false)); + sourceSelect.append(this.makeOption(IssueSource.Extension, localize('extension', "A VS Code extension"), false)); if (this.configuration.product.reportMarketplaceIssueUrl) { - sourceSelect.append(this.makeOption('marketplace', localize('marketplace', "Extensions marketplace"), false)); + sourceSelect.append(this.makeOption(IssueSource.Marketplace, localize('marketplace', "Extensions Marketplace"), false)); } if (issueType !== IssueType.FeatureRequest) { - sourceSelect.append(this.makeOption('unknown', localize('unknown', "Don't know"), false)); + sourceSelect.append(this.makeOption(IssueSource.Unknown, localize('unknown', "Don't know"), false)); } if (selected !== -1 && selected < sourceSelect.options.length) { diff --git a/src/vs/editor/browser/controller/pointerHandler.ts b/src/vs/editor/browser/controller/pointerHandler.ts index 1ae9ecab36124..2019016ac4ce2 100644 --- a/src/vs/editor/browser/controller/pointerHandler.ts +++ b/src/vs/editor/browser/controller/pointerHandler.ts @@ -33,7 +33,7 @@ export class PointerEventHandler extends MouseHandler { this._lastPointerType = 'mouse'; this._register(dom.addDisposableListener(this.viewHelper.linesContentDomNode, 'pointerdown', (e: any) => { - const pointerType = e.pointerType; + const pointerType = e.pointerType; if (pointerType === 'mouse') { this._lastPointerType = 'mouse'; return; diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 82fdcc21a7a2d..df179da3007bc 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -19,7 +19,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import { LanguageId } from 'vs/editor/common/encodedTokenAttributes'; -import { LanguageFilter } from 'vs/editor/common/languageSelector'; +import { LanguageSelector } from 'vs/editor/common/languageSelector'; import * as model from 'vs/editor/common/model'; import { TokenizationRegistry as TokenizationRegistryImpl } from 'vs/editor/common/tokenizationRegistry'; import { ContiguousMultilineTokens } from 'vs/editor/common/tokens/contiguousMultilineTokens'; @@ -1068,7 +1068,7 @@ export interface DocumentHighlightProvider { * A provider that can provide document highlights across multiple documents. */ export interface MultiDocumentHighlightProvider { - selector: LanguageFilter; + readonly selector: LanguageSelector; /** * Provide a Map of URI --> document highlights, like all occurrences of a variable or diff --git a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts index 68420ef0fd107..3e62c4c02811f 100644 --- a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts @@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { Gesture } from 'vs/base/browser/touch'; import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; +import { HierarchicalKind } from 'vs/base/common/hierarchicalKind'; import { Disposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/base/common/themables'; import 'vs/css!./lightBulbWidget'; @@ -15,10 +16,11 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IPosition } from 'vs/editor/common/core/position'; import { computeIndentLevel } from 'vs/editor/common/model/utils'; import { autoFixCommandId, quickFixCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; -import type { CodeActionSet, CodeActionTrigger } from 'vs/editor/contrib/codeAction/common/types'; +import { CodeActionKind, CodeActionSet, CodeActionTrigger } from 'vs/editor/contrib/codeAction/common/types'; import * as nls from 'vs/nls'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; namespace LightBulbState { @@ -64,6 +66,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { private readonly _editor: ICodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ICommandService commandService: ICommandService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { super(); @@ -189,6 +192,33 @@ export class LightBulbWidget extends Disposable implements IContentWidget { position: { lineNumber: effectiveLineNumber, column: effectiveColumnNumber }, preference: LightBulbWidget._posPref }); + + const validActions = actions.validActions; + const actionKind = actions.validActions[0].action.kind; + if (validActions.length !== 1 || !actionKind) { + this._editor.layoutContentWidget(this); + return; + } + + const hierarchicalKind = new HierarchicalKind(actionKind); + + if (CodeActionKind.RefactorMove.contains(hierarchicalKind)) { + // Telemetry for showing code actions here. only log on `showLightbulb`. Logs when code action list is quit out. + type ShowCodeActionListEvent = { + codeActionListLength: number; + }; + + type ShowListEventClassification = { + codeActionListLength: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The length of the code action list when quit out. Can be from any code action menu.' }; + owner: 'justschen'; + comment: 'Event used to gain insights into how often the lightbulb only contains one code action, namely the move to code action. '; + }; + + this._telemetryService.publicLog2('lightbulbWidget.moveToCodeActions', { + codeActionListLength: validActions.length, + }); + } + this._editor.layoutContentWidget(this); } diff --git a/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts b/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts index 7415060262ab3..9443f52cc0525 100644 --- a/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts +++ b/src/vs/editor/contrib/dropOrPasteInto/browser/postEditWidget.ts @@ -7,6 +7,8 @@ import * as dom from 'vs/base/browser/dom'; import { Button } from 'vs/base/browser/ui/button/button'; import { toAction } from 'vs/base/common/actions'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { toErrorMessage } from 'vs/base/common/errorMessage'; +import { isCancellationError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./postEditWidget'; @@ -16,10 +18,12 @@ import { Range } from 'vs/editor/common/core/range'; import { DocumentDropEdit, DocumentPasteEdit } from 'vs/editor/common/languages'; import { TrackedRangeStickiness } from 'vs/editor/common/model'; import { createCombinedWorkspaceEdit } from 'vs/editor/contrib/dropOrPasteInto/browser/edit'; +import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { INotificationService } from 'vs/platform/notification/common/notification'; interface EditSet { @@ -143,6 +147,7 @@ export class PostEditWidgetManager { + const model = this._editor.getModel(); + if (!model) { + return; + } + + await model.undo(); + this.applyEditAndShowIfNeeded(ranges, { activeEditIndex: newEditIndex, allEdits: edits.allEdits }, canShowWidget, resolve, token); + }; + + const handleError = (e: Error, message: string) => { + if (isCancellationError(e)) { + return; + } + + this._notificationService.error(message); + if (canShowWidget) { + this.show(ranges[0], edits, onDidSelectEdit); + } + }; + + let resolvedEdit: T; + try { + resolvedEdit = await resolve(edit, token); + } catch (e) { + return handleError(e, localize('resolveError', "Error resolving edit '{0}':\n{1}", edit.title, toErrorMessage(e))); + } + if (token.isCancellationRequested) { return; } @@ -183,6 +215,8 @@ export class PostEditWidgetManager 1) { - this.show(editRange ?? primaryRange, edits, async (newEditIndex) => { - const model = this._editor.getModel(); - if (!model) { - return; - } - - await model.undo(); - this.applyEditAndShowIfNeeded(ranges, { activeEditIndex: newEditIndex, allEdits: edits.allEdits }, canShowWidget, resolve, token); - }); + this.show(editRange ?? primaryRange, edits, onDidSelectEdit); } } diff --git a/src/vs/editor/contrib/folding/browser/foldingRanges.ts b/src/vs/editor/contrib/folding/browser/foldingRanges.ts index 5c800345b4826..ee23abbfc7479 100644 --- a/src/vs/editor/contrib/folding/browser/foldingRanges.ts +++ b/src/vs/editor/contrib/folding/browser/foldingRanges.ts @@ -246,7 +246,7 @@ export class FoldingRegions { } public toFoldRange(index: number): FoldRange { - return { + return { startLineNumber: this._startIndexes[index] & MAX_LINE_NUMBER, endLineNumber: this._endIndexes[index] & MAX_LINE_NUMBER, type: this._types ? this._types[index] : undefined, diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 46d92a532e948..f5b8af528e3d5 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -26,6 +26,8 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { listHighlightForeground, registerColor } from 'vs/platform/theme/common/colorRegistry'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { ThemeIcon } from 'vs/base/common/themables'; +import { StopWatch } from 'vs/base/common/stopwatch'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; const $ = dom.$; @@ -61,6 +63,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @ILanguageService languageService: ILanguageService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); @@ -272,12 +275,30 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private renderMarkdownDocs(markdown: IMarkdownString | undefined): IMarkdownRenderResult { + const stopWatch = new StopWatch(); const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(markdown, { asyncRenderCallback: () => { this.domNodes?.scrollbar.scanDomNode(); } })); renderedContents.element.classList.add('markdown-docs'); + + type RenderMarkdownPerformanceClassification = { + owner: 'donjayamanne'; + comment: 'Measure the time taken to render markdown for parameter hints'; + renderDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Time in ms to render the markdown' }; + }; + + type RenderMarkdownPerformanceEvent = { + renderDuration: number; + }; + const renderDuration = stopWatch.elapsed(); + if (renderDuration > 300) { + this.telemetryService.publicLog2('parameterHints.parseMarkdown', { + renderDuration + }); + } + return renderedContents; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 42bb190155a24..740d8f5f8e6ba 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7395,7 +7395,7 @@ declare namespace monaco.languages { * A provider that can provide document highlights across multiple documents. */ export interface MultiDocumentHighlightProvider { - selector: LanguageFilter; + readonly selector: LanguageSelector; /** * Provide a Map of Uri --> document highlights, like all occurrences of a variable or * all exit-points of a function. diff --git a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts index 166c08ff25635..ba277dbc24e87 100644 --- a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts +++ b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts @@ -318,29 +318,6 @@ export class SoundSource { } } -export const enum AccessibilityAlertSettingId { - Save = 'accessibility.alert.save', - Format = 'accessibility.alert.format', - Clear = 'accessibility.alert.clear', - Breakpoint = 'accessibility.alert.breakpoint', - Error = 'accessibility.alert.error', - Warning = 'accessibility.alert.warning', - FoldedArea = 'accessibility.alert.foldedArea', - TerminalQuickFix = 'accessibility.alert.terminalQuickFix', - TerminalBell = 'accessibility.alert.terminalBell', - TerminalCommandFailed = 'accessibility.alert.terminalCommandFailed', - TaskCompleted = 'accessibility.alert.taskCompleted', - TaskFailed = 'accessibility.alert.taskFailed', - ChatRequestSent = 'accessibility.alert.chatRequestSent', - NotebookCellCompleted = 'accessibility.alert.notebookCellCompleted', - NotebookCellFailed = 'accessibility.alert.notebookCellFailed', - OnDebugBreak = 'accessibility.alert.onDebugBreak', - NoInlayHints = 'accessibility.alert.noInlayHints', - LineHasBreakpoint = 'accessibility.alert.lineHasBreakpoint', - Progress = 'accessibility.alert.chatResponseProgress' -} - - export class AccessibilitySignal { private constructor( public readonly sound: SoundSource, @@ -363,7 +340,7 @@ export class AccessibilitySignal { }; legacySoundSettingsKey?: string; settingsKey: string; - legacyAnnouncementSettingsKey?: AccessibilityAlertSettingId; + legacyAnnouncementSettingsKey?: string; announcementMessage?: string; }): AccessibilitySignal { const soundSource = new SoundSource('randomOneOf' in options.sound ? options.sound.randomOneOf : [options.sound]); @@ -400,7 +377,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.lineHasError.name', 'Error on Line'), sound: Sound.error, legacySoundSettingsKey: 'audioCues.lineHasError', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Error, + legacyAnnouncementSettingsKey: 'accessibility.alert.error', announcementMessage: localize('accessibility.signals.lineHasError', 'Error on Line'), settingsKey: 'accessibility.signals.lineHasError', }); @@ -409,7 +386,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.lineHasWarning.name', 'Warning on Line'), sound: Sound.warning, legacySoundSettingsKey: 'audioCues.lineHasWarning', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Warning, + legacyAnnouncementSettingsKey: 'accessibility.alert.warning', announcementMessage: localize('accessibility.signals.lineHasWarning', 'Warning on Line'), settingsKey: 'accessibility.signals.lineHasWarning', }); @@ -417,7 +394,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.lineHasFoldedArea.name', 'Folded Area on Line'), sound: Sound.foldedArea, legacySoundSettingsKey: 'audioCues.lineHasFoldedArea', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.FoldedArea, + legacyAnnouncementSettingsKey: 'accessibility.alert.foldedArea', announcementMessage: localize('accessibility.signals.lineHasFoldedArea', 'Folded'), settingsKey: 'accessibility.signals.lineHasFoldedArea', }); @@ -425,7 +402,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.lineHasBreakpoint.name', 'Breakpoint on Line'), sound: Sound.break, legacySoundSettingsKey: 'audioCues.lineHasBreakpoint', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Breakpoint, + legacyAnnouncementSettingsKey: 'accessibility.alert.breakpoint', announcementMessage: localize('accessibility.signals.lineHasBreakpoint', 'Breakpoint'), settingsKey: 'accessibility.signals.lineHasBreakpoint', }); @@ -440,7 +417,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.terminalQuickFix.name', 'Terminal Quick Fix'), sound: Sound.quickFixes, legacySoundSettingsKey: 'audioCues.terminalQuickFix', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.TerminalQuickFix, + legacyAnnouncementSettingsKey: 'accessibility.alert.terminalQuickFix', announcementMessage: localize('accessibility.signals.terminalQuickFix', 'Quick Fix'), settingsKey: 'accessibility.signals.terminalQuickFix', }); @@ -449,7 +426,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.onDebugBreak.name', 'Debugger Stopped on Breakpoint'), sound: Sound.break, legacySoundSettingsKey: 'audioCues.onDebugBreak', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.OnDebugBreak, + legacyAnnouncementSettingsKey: 'accessibility.alert.onDebugBreak', announcementMessage: localize('accessibility.signals.onDebugBreak', 'Breakpoint'), settingsKey: 'accessibility.signals.onDebugBreak', }); @@ -458,7 +435,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.noInlayHints', 'No Inlay Hints on Line'), sound: Sound.error, legacySoundSettingsKey: 'audioCues.noInlayHints', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.NoInlayHints, + legacyAnnouncementSettingsKey: 'accessibility.alert.noInlayHints', announcementMessage: localize('accessibility.signals.noInlayHints', 'No Inlay Hints'), settingsKey: 'accessibility.signals.noInlayHints', }); @@ -467,7 +444,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.taskCompleted', 'Task Completed'), sound: Sound.taskCompleted, legacySoundSettingsKey: 'audioCues.taskCompleted', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.TaskCompleted, + legacyAnnouncementSettingsKey: 'accessibility.alert.taskCompleted', announcementMessage: localize('accessibility.signals.taskCompleted', 'Task Completed'), settingsKey: 'accessibility.signals.taskCompleted', }); @@ -476,7 +453,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.taskFailed', 'Task Failed'), sound: Sound.taskFailed, legacySoundSettingsKey: 'audioCues.taskFailed', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.TaskFailed, + legacyAnnouncementSettingsKey: 'accessibility.alert.taskFailed', announcementMessage: localize('accessibility.signals.taskFailed', 'Task Failed'), settingsKey: 'accessibility.signals.taskFailed', }); @@ -485,7 +462,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.terminalCommandFailed', 'Terminal Command Failed'), sound: Sound.error, legacySoundSettingsKey: 'audioCues.terminalCommandFailed', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.TerminalCommandFailed, + legacyAnnouncementSettingsKey: 'accessibility.alert.terminalCommandFailed', announcementMessage: localize('accessibility.signals.terminalCommandFailed', 'Command Failed'), settingsKey: 'accessibility.signals.terminalCommandFailed', }); @@ -494,7 +471,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.terminalBell', 'Terminal Bell'), sound: Sound.terminalBell, legacySoundSettingsKey: 'audioCues.terminalBell', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.TerminalBell, + legacyAnnouncementSettingsKey: 'accessibility.alert.terminalBell', announcementMessage: localize('accessibility.signals.terminalBell', 'Terminal Bell'), settingsKey: 'accessibility.signals.terminalBell', }); @@ -503,7 +480,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.notebookCellCompleted', 'Notebook Cell Completed'), sound: Sound.taskCompleted, legacySoundSettingsKey: 'audioCues.notebookCellCompleted', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.NotebookCellCompleted, + legacyAnnouncementSettingsKey: 'accessibility.alert.notebookCellCompleted', announcementMessage: localize('accessibility.signals.notebookCellCompleted', 'Notebook Cell Completed'), settingsKey: 'accessibility.signals.notebookCellCompleted', }); @@ -512,7 +489,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.notebookCellFailed', 'Notebook Cell Failed'), sound: Sound.taskFailed, legacySoundSettingsKey: 'audioCues.notebookCellFailed', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.NotebookCellFailed, + legacyAnnouncementSettingsKey: 'accessibility.alert.notebookCellFailed', announcementMessage: localize('accessibility.signals.notebookCellFailed', 'Notebook Cell Failed'), settingsKey: 'accessibility.signals.notebookCellFailed', }); @@ -542,7 +519,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.chatRequestSent', 'Chat Request Sent'), sound: Sound.chatRequestSent, legacySoundSettingsKey: 'audioCues.chatRequestSent', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.ChatRequestSent, + legacyAnnouncementSettingsKey: 'accessibility.alert.chatRequestSent', announcementMessage: localize('accessibility.signals.chatRequestSent', 'Chat Request Sent'), settingsKey: 'accessibility.signals.chatRequestSent', }); @@ -565,7 +542,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.progress', 'Progress'), sound: Sound.progress, legacySoundSettingsKey: 'audioCues.chatResponsePending', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Progress, + legacyAnnouncementSettingsKey: 'accessibility.alert.progress', announcementMessage: localize('accessibility.signals.progress', 'Progress'), settingsKey: 'accessibility.signals.progress' }); @@ -574,7 +551,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.clear', 'Clear'), sound: Sound.clear, legacySoundSettingsKey: 'audioCues.clear', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Clear, + legacyAnnouncementSettingsKey: 'accessibility.alert.clear', announcementMessage: localize('accessibility.signals.clear', 'Clear'), settingsKey: 'accessibility.signals.clear' }); @@ -583,7 +560,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.save', 'Save'), sound: Sound.save, legacySoundSettingsKey: 'audioCues.save', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Save, + legacyAnnouncementSettingsKey: 'accessibility.alert.save', announcementMessage: localize('accessibility.signals.save', 'Save'), settingsKey: 'accessibility.signals.save' }); @@ -592,7 +569,7 @@ export class AccessibilitySignal { name: localize('accessibilitySignals.format', 'Format'), sound: Sound.format, legacySoundSettingsKey: 'audioCues.format', - legacyAnnouncementSettingsKey: AccessibilityAlertSettingId.Format, + legacyAnnouncementSettingsKey: 'accessibility.alert.format', announcementMessage: localize('accessibility.signals.format', 'Format'), settingsKey: 'accessibility.signals.format' }); diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 06d5d687dfc2f..f8e502eb9086f 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -97,10 +97,6 @@ export const enum TerminalSettingId { EnableFileLinks = 'terminal.integrated.enableFileLinks', AllowedLinkSchemes = 'terminal.integrated.allowedLinkSchemes', UnicodeVersion = 'terminal.integrated.unicodeVersion', - LocalEchoLatencyThreshold = 'terminal.integrated.localEchoLatencyThreshold', - LocalEchoEnabled = 'terminal.integrated.localEchoEnabled', - LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms', - LocalEchoStyle = 'terminal.integrated.localEchoStyle', EnablePersistentSessions = 'terminal.integrated.enablePersistentSessions', PersistentSessionReviveProcess = 'terminal.integrated.persistentSessionReviveProcess', HideOnStartup = 'terminal.integrated.hideOnStartup', @@ -115,16 +111,10 @@ export const enum TerminalSettingId { ShellIntegrationShowWelcome = 'terminal.integrated.shellIntegration.showWelcome', ShellIntegrationDecorationsEnabled = 'terminal.integrated.shellIntegration.decorationsEnabled', ShellIntegrationCommandHistory = 'terminal.integrated.shellIntegration.history', - ShellIntegrationSuggestEnabled = 'terminal.integrated.shellIntegration.suggestEnabled', EnableImages = 'terminal.integrated.enableImages', SmoothScrolling = 'terminal.integrated.smoothScrolling', IgnoreBracketedPasteMode = 'terminal.integrated.ignoreBracketedPasteMode', FocusAfterRun = 'terminal.integrated.focusAfterRun', - AccessibleViewPreserveCursorPosition = 'terminal.integrated.accessibleViewPreserveCursorPosition', - AccessibleViewFocusOnCommandExecution = 'terminal.integrated.accessibleViewFocusOnCommandExecution', - StickyScrollEnabled = 'terminal.integrated.stickyScroll.enabled', - StickyScrollMaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount', - MouseWheelZoom = 'terminal.integrated.mouseWheelZoom', // Debug settings that are hidden from user diff --git a/src/vs/platform/terminal/common/terminalRecorder.ts b/src/vs/platform/terminal/common/terminalRecorder.ts index d8fcb0269484b..79a828cc220a7 100644 --- a/src/vs/platform/terminal/common/terminalRecorder.ts +++ b/src/vs/platform/terminal/common/terminalRecorder.ts @@ -7,7 +7,7 @@ import { IPtyHostProcessReplayEvent } from 'vs/platform/terminal/common/capabili import { ReplayEntry } from 'vs/platform/terminal/common/terminalProcess'; const enum Constants { - MaxRecorderDataSize = 1024 * 1024 // 1MB + MaxRecorderDataSize = 10 * 1024 * 1024 // 10MB } interface RecorderEntry { diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index da8d1dd3fa055..b49916fa232a2 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -21,6 +21,7 @@ import { BufferMarkCapability } from 'vs/platform/terminal/common/capabilities/b import type { ITerminalAddon, Terminal } from '@xterm/headless'; import { URI } from 'vs/base/common/uri'; import { sanitizeCwd } from 'vs/platform/terminal/common/terminalEnvironment'; +import { removeAnsiEscapeCodesFromPrompt } from 'vs/base/common/strings'; /** @@ -382,12 +383,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } switch (key) { case 'ContinuationPrompt': { - // Exclude escape sequences and values between \[ and \] - const sanitizedValue = (value - .replace(/\x1b\[[0-9;]*m/g, '') - .replace(/\\\[.*?\\\]/g, '') - ); - this._updateContinuationPrompt(sanitizedValue); + this._updateContinuationPrompt(removeAnsiEscapeCodesFromPrompt(value)); return true; } case 'Cwd': { diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 2b991c2b2039f..8adf80ad9c78f 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -221,6 +221,8 @@ export function getTitleBarStyle(configurationService: IConfigurationService): T return isLinux ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM; // default to custom on all macOS and Windows } +export const DEFAULT_CUSTOM_TITLEBAR_HEIGHT = 35; // includes space for command center + export function useWindowControlsOverlay(configurationService: IConfigurationService): boolean { if (!isWindows || isWeb) { return false; // only supported on a desktop Windows instance diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index ee03669982cbc..dcb3a1776ecfa 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -32,7 +32,7 @@ import { IApplicationStorageMainService, IStorageMainService } from 'vs/platform import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ThemeIcon } from 'vs/base/common/themables'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; -import { getMenuBarVisibility, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, hasNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { getMenuBarVisibility, IFolderToOpen, INativeWindowConfiguration, IWindowSettings, IWorkspaceToOpen, MenuBarVisibility, hasNativeTitlebar, useNativeFullScreen, useWindowControlsOverlay, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window'; import { defaultBrowserWindowOptions, IWindowsMainService, OpenContext, WindowStateValidator } from 'vs/platform/windows/electron-main/windows'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, toWorkspaceIdentifier } from 'vs/platform/workspace/common/workspace'; import { IWorkspacesManagementMainService } from 'vs/platform/workspaces/electron-main/workspacesManagementMainService'; @@ -136,11 +136,13 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { win.setSheetOffset(isBigSurOrNewer(release()) ? 28 : 22); // offset dialogs by the height of the custom title bar if we have any } - // Update the window controls immediately based on cached values + // Update the window controls immediately based on cached or default values if (useCustomTitleStyle && ((isWindows && useWindowControlsOverlay(this.configurationService)) || isMacintosh)) { const cachedWindowControlHeight = this.stateService.getItem((BaseWindow.windowControlHeightStateStorageKey)); if (cachedWindowControlHeight) { this.updateWindowControls({ height: cachedWindowControlHeight }); + } else { + this.updateWindowControls({ height: DEFAULT_CUSTOM_TITLEBAR_HEIGHT }); } } diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 25a017e19a95f..a6f7fc430bcdd 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -300,7 +300,8 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa // Exclude some very common files from the dock/taskbar private static readonly COMMON_FILES_FILTER = [ 'COMMIT_EDITMSG', - 'MERGE_MSG' + 'MERGE_MSG', + 'git-rebase-todo' ]; private readonly macOSRecentDocumentsUpdater = this._register(new ThrottledDelayer(800)); diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index e956f7f056540..0253cba2d4e44 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -163,7 +163,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- outline $registerDocumentSymbolProvider(handle: number, selector: IDocumentFilterDto[], displayName: string): void { - this._registrations.set(handle, this._languageFeaturesService.documentSymbolProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.documentSymbolProvider.register(selector, { displayName, provideDocumentSymbols: (model: ITextModel, token: CancellationToken): Promise => { return this._proxy.$provideDocumentSymbols(handle, model.uri, token); @@ -175,7 +175,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread $registerCodeLensSupport(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { - const provider = { + const provider: languages.CodeLensProvider = { provideCodeLenses: async (model: ITextModel, token: CancellationToken): Promise => { const listDto = await this._proxy.$provideCodeLenses(handle, model.uri, token); if (!listDto) { @@ -217,7 +217,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- declaration $registerDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.definitionProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.definitionProvider.register(selector, { provideDefinition: (model, position, token): Promise => { return this._proxy.$provideDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } @@ -225,7 +225,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } $registerDeclarationSupport(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.declarationProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.declarationProvider.register(selector, { provideDeclaration: (model, position, token) => { return this._proxy.$provideDeclaration(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } @@ -233,7 +233,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } $registerImplementationSupport(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.implementationProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.implementationProvider.register(selector, { provideImplementation: (model, position, token): Promise => { return this._proxy.$provideImplementation(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } @@ -241,7 +241,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } $registerTypeDefinitionSupport(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.typeDefinitionProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.typeDefinitionProvider.register(selector, { provideTypeDefinition: (model, position, token): Promise => { return this._proxy.$provideTypeDefinition(handle, model.uri, position, token).then(MainThreadLanguageFeatures._reviveLocationLinkDto); } @@ -256,7 +256,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread this._proxy.$releaseHover(handle, hoverId); }); */ - this._registrations.set(handle, this._languageFeaturesService.hoverProvider.register(selector, >{ + this._registrations.set(handle, this._languageFeaturesService.hoverProvider.register(selector, { provideHover: async (model: ITextModel, position: EditorPosition, token: CancellationToken, context?: languages.HoverContext): Promise => { const serializedContext: languages.HoverContext<{ id: number }> = { verbosityRequest: context?.verbosityRequest ? { @@ -274,7 +274,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- debug hover $registerEvaluatableExpressionProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.evaluatableExpressionProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.evaluatableExpressionProvider.register(selector, { provideEvaluatableExpression: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { return this._proxy.$provideEvaluatableExpression(handle, model.uri, position, token); } @@ -284,7 +284,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- inline values $registerInlineValuesProvider(handle: number, selector: IDocumentFilterDto[], eventHandle: number | undefined): void { - const provider = { + const provider: languages.InlineValuesProvider = { provideInlineValues: (model: ITextModel, viewPort: EditorRange, context: languages.InlineValueContext, token: CancellationToken): Promise => { return this._proxy.$provideInlineValues(handle, model.uri, viewPort, context, token); } @@ -309,7 +309,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- occurrences $registerDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.documentHighlightProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.documentHighlightProvider.register(selector, { provideDocumentHighlights: (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { return this._proxy.$provideDocumentHighlights(handle, model.uri, position, token); } @@ -317,7 +317,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread } $registerMultiDocumentHighlightProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.multiDocumentHighlightProvider.register(selector, { selector: selector, provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise | undefined> => { return this._proxy.$provideMultiDocumentHighlights(handle, model.uri, position, otherModels.map(model => model.uri), token).then(dto => { @@ -343,7 +343,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- linked editing $registerLinkedEditingRangeProvider(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.linkedEditingRangeProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.linkedEditingRangeProvider.register(selector, { provideLinkedEditingRanges: async (model: ITextModel, position: EditorPosition, token: CancellationToken): Promise => { const res = await this._proxy.$provideLinkedEditingRanges(handle, model.uri, position, token); if (res) { @@ -360,7 +360,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- references $registerReferenceSupport(handle: number, selector: IDocumentFilterDto[]): void { - this._registrations.set(handle, this._languageFeaturesService.referenceProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.referenceProvider.register(selector, { provideReferences: (model: ITextModel, position: EditorPosition, context: languages.ReferenceContext, token: CancellationToken): Promise => { return this._proxy.$provideReferences(handle, model.uri, position, context, token).then(MainThreadLanguageFeatures._reviveLocationDto); } @@ -376,7 +376,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread if (!listDto) { return undefined; } - return { + return { actions: MainThreadLanguageFeatures._reviveCodeActionDto(listDto.actions, this._uriIdentService), dispose: () => { if (typeof listDto.cacheId === 'number') { @@ -649,7 +649,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- parameter hints $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void { - this._registrations.set(handle, this._languageFeaturesService.signatureHelpProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.signatureHelpProvider.register(selector, { signatureHelpTriggerCharacters: metadata.triggerCharacters, signatureHelpRetriggerCharacters: metadata.retriggerCharacters, @@ -672,7 +672,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- inline hints $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean, eventHandle: number | undefined, displayName: string | undefined): void { - const provider = { + const provider: languages.InlayHintsProvider = { displayName, provideInlayHints: async (model: ITextModel, range: EditorRange, token: CancellationToken): Promise => { const result = await this._proxy.$provideInlayHints(handle, model.uri, range, token); @@ -764,7 +764,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread $registerDocumentColorProvider(handle: number, selector: IDocumentFilterDto[]): void { const proxy = this._proxy; - this._registrations.set(handle, this._languageFeaturesService.colorProvider.register(selector, { + this._registrations.set(handle, this._languageFeaturesService.colorProvider.register(selector, { provideDocumentColors: (model, token) => { return proxy.$provideDocumentColors(handle, model.uri, token) .then(documentColors => { @@ -797,7 +797,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread // --- folding $registerFoldingRangeProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, eventHandle: number | undefined): void { - const provider = { + const provider: languages.FoldingRangeProvider = { id: extensionId.value, provideFoldingRanges: (model, context, token) => { return this._proxy.$provideFoldingRanges(handle, model.uri, context, token); diff --git a/src/vs/workbench/api/browser/mainThreadSpeech.ts b/src/vs/workbench/api/browser/mainThreadSpeech.ts index 56ce1bca62330..fcb28dbc417c0 100644 --- a/src/vs/workbench/api/browser/mainThreadSpeech.ts +++ b/src/vs/workbench/api/browser/mainThreadSpeech.ts @@ -7,13 +7,18 @@ import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostContext, ExtHostSpeechShape, MainContext, MainThreadSpeechShape } from 'vs/workbench/api/common/extHost.protocol'; -import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechService, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; +import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechService, ISpeechToTextEvent, ITextToSpeechEvent } from 'vs/workbench/contrib/speech/common/speechService'; import { IExtHostContext, extHostNamedCustomer } from 'vs/workbench/services/extensions/common/extHostCustomers'; type SpeechToTextSession = { readonly onDidChange: Emitter; }; +type TextToSpeechSession = { + readonly onDidChange: Emitter; + synthesize(text: string): Promise; +}; + type KeywordRecognitionSession = { readonly onDidChange: Emitter; }; @@ -26,6 +31,7 @@ export class MainThreadSpeech implements MainThreadSpeechShape { private readonly providerRegistrations = new Map(); private readonly speechToTextSessions = new Map(); + private readonly textToSpeechSessions = new Map(); private readonly keywordRecognitionSessions = new Map(); constructor( @@ -66,6 +72,36 @@ export class MainThreadSpeech implements MainThreadSpeechShape { onDidChange: onDidChange.event }; }, + createTextToSpeechSession: (token) => { + if (token.isCancellationRequested) { + return { + onDidChange: Event.None, + synthesize: async () => { } + }; + } + + const disposables = new DisposableStore(); + const session = Math.random(); + + this.proxy.$createTextToSpeechSession(handle, session); + + const onDidChange = disposables.add(new Emitter()); + this.textToSpeechSessions.set(session, { + onDidChange, + synthesize: text => this.proxy.$synthesizeSpeech(session, text) + }); + + disposables.add(token.onCancellationRequested(() => { + this.proxy.$cancelTextToSpeechSession(session); + this.textToSpeechSessions.delete(session); + disposables.dispose(); + })); + + return { + onDidChange: onDidChange.event, + synthesize: text => this.proxy.$synthesizeSpeech(session, text) + }; + }, createKeywordRecognitionSession: token => { if (token.isCancellationRequested) { return { @@ -112,6 +148,11 @@ export class MainThreadSpeech implements MainThreadSpeechShape { providerSession?.onDidChange.fire(event); } + $emitTextToSpeechEvent(session: number, event: ITextToSpeechEvent): void { + const providerSession = this.textToSpeechSessions.get(session); + providerSession?.onDidChange.fire(event); + } + $emitKeywordRecognitionEvent(session: number, event: IKeywordRecognitionEvent): void { const providerSession = this.keywordRecognitionSessions.get(session); providerSession?.onDidChange.fire(event); @@ -124,6 +165,9 @@ export class MainThreadSpeech implements MainThreadSpeechShape { this.speechToTextSessions.forEach(session => session.onDidChange.dispose()); this.speechToTextSessions.clear(); + this.textToSpeechSessions.forEach(session => session.onDidChange.dispose()); + this.textToSpeechSessions.clear(); + this.keywordRecognitionSessions.forEach(session => session.onDidChange.dispose()); this.keywordRecognitionSessions.clear(); } diff --git a/src/vs/workbench/api/browser/mainThreadTunnelService.ts b/src/vs/workbench/api/browser/mainThreadTunnelService.ts index 4fb13ab62311a..5b8cf2637fcdf 100644 --- a/src/vs/workbench/api/browser/mainThreadTunnelService.ts +++ b/src/vs/workbench/api/browser/mainThreadTunnelService.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { MainThreadTunnelServiceShape, MainContext, ExtHostContext, ExtHostTunnelServiceShape, CandidatePortSource, PortAttributesSelector, TunnelDto } from 'vs/workbench/api/common/extHost.protocol'; import { TunnelDtoConverter } from 'vs/workbench/api/common/extHostTunnelService'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; -import { IRemoteExplorerService, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService'; +import { IRemoteExplorerService, PORT_AUTO_FORWARD_SETTING, PORT_AUTO_SOURCE_SETTING, PORT_AUTO_SOURCE_SETTING_HYBRID, PORT_AUTO_SOURCE_SETTING_OUTPUT } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ITunnelProvider, ITunnelService, TunnelCreationOptions, TunnelProviderFeatures, TunnelOptions, RemoteTunnel, ProvidedPortAttributes, PortAttributesProvider, TunnelProtocol } from 'vs/platform/tunnel/common/tunnel'; import { Disposable } from 'vs/base/common/lifecycle'; import type { TunnelDescription } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -223,6 +223,11 @@ export class MainThreadTunnelService extends Disposable implements MainThreadTun .registerDefaultConfigurations([{ overrides: { 'remote.autoForwardPortsSource': PORT_AUTO_SOURCE_SETTING_OUTPUT } }]); break; } + case CandidatePortSource.Hybrid: { + Registry.as(ConfigurationExtensions.Configuration) + .registerDefaultConfigurations([{ overrides: { 'remote.autoForwardPortsSource': PORT_AUTO_SOURCE_SETTING_HYBRID } }]); + break; + } default: // Do nothing, the defaults for these settings should be used. } }).catch(() => { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 117a610839515..2b49b0f59d626 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1698,6 +1698,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I DebugThread: extHostTypes.DebugThread, RelatedInformationType: extHostTypes.RelatedInformationType, SpeechToTextStatus: extHostTypes.SpeechToTextStatus, + TextToSpeechStatus: extHostTypes.TextToSpeechStatus, PartialAcceptTriggerKind: extHostTypes.PartialAcceptTriggerKind, KeywordRecognitionStatus: extHostTypes.KeywordRecognitionStatus, ChatResponseMarkdownPart: extHostTypes.ChatResponseMarkdownPart, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index e847d35fd9515..ed133c366b22d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -64,7 +64,7 @@ import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { InputValidationType } from 'vs/workbench/contrib/scm/common/scm'; import { IWorkspaceSymbol, NotebookPriorityInfo } from 'vs/workbench/contrib/search/common/search'; import { IRawClosedNotebookFileMatch } from 'vs/workbench/contrib/search/common/searchNotebookHelpers'; -import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechToTextEvent } from 'vs/workbench/contrib/speech/common/speechService'; +import { IKeywordRecognitionEvent, ISpeechProviderMetadata, ISpeechToTextEvent, ITextToSpeechEvent } from 'vs/workbench/contrib/speech/common/speechService'; import { CoverageDetails, ExtensionRunTestsRequest, ICallProfileRunHandler, IFileCoverage, ISerializedTestResults, IStartControllerTests, ITestItem, ITestMessage, ITestRunProfile, ITestRunTask, ResolvedTestRunRequest, TestResultState, TestsDiffOp } from 'vs/workbench/contrib/testing/common/testTypes'; import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor } from 'vs/workbench/contrib/timeline/common/timeline'; import { TypeHierarchyItem } from 'vs/workbench/contrib/typeHierarchy/common/typeHierarchy'; @@ -1181,6 +1181,7 @@ export interface MainThreadSpeechShape extends IDisposable { $unregisterProvider(handle: number): void; $emitSpeechToTextEvent(session: number, event: ISpeechToTextEvent): void; + $emitTextToSpeechEvent(session: number, event: ITextToSpeechEvent): void; $emitKeywordRecognitionEvent(session: number, event: IKeywordRecognitionEvent): void; } @@ -1188,6 +1189,10 @@ export interface ExtHostSpeechShape { $createSpeechToTextSession(handle: number, session: number, language?: string): Promise; $cancelSpeechToTextSession(session: number): Promise; + $createTextToSpeechSession(handle: number, session: number): Promise; + $synthesizeSpeech(session: number, text: string): Promise; + $cancelTextToSpeechSession(session: number): Promise; + $createKeywordRecognitionSession(handle: number, session: number): Promise; $cancelKeywordRecognitionSession(session: number): Promise; } @@ -1593,7 +1598,8 @@ export interface MainThreadWindowShape extends IDisposable { export enum CandidatePortSource { None = 0, Process = 1, - Output = 2 + Output = 2, + Hybrid = 3 } export interface PortAttributesSelector { diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 1c562edf76a19..16b1d4a405ba1 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -89,28 +89,28 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { }); } - $createSession(providerId: string, scopes: string[], options: vscode.AuthenticationProviderCreateSessionOptions): Promise { + async $createSession(providerId: string, scopes: string[], options: vscode.AuthenticationProviderCreateSessionOptions): Promise { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.createSession(scopes, options)); + return await providerData.provider.createSession(scopes, options); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $removeSession(providerId: string, sessionId: string): Promise { + async $removeSession(providerId: string, sessionId: string): Promise { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.removeSession(sessionId)); + return await providerData.provider.removeSession(sessionId); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); } - $getSessions(providerId: string, scopes?: string[]): Promise> { + async $getSessions(providerId: string, scopes?: string[]): Promise> { const providerData = this._authenticationProviders.get(providerId); if (providerData) { - return Promise.resolve(providerData.provider.getSessions(scopes)); + return await providerData.provider.getSessions(scopes); } throw new Error(`Unable to find authentication provider with handle: ${providerId}`); diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index abc3e0e4b7dca..b53f624362e14 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -206,7 +206,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private readonly _proxy: MainThreadChatAgentsShape2; private readonly _sessionDisposables: DisposableMap = this._register(new DisposableMap()); - private readonly _completionDisposables = this._register(new DisposableStore()); + private readonly _completionDisposables: DisposableMap = this._register(new DisposableMap()); constructor( mainContext: IMainContext, @@ -374,9 +374,18 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS return []; } + let disposables = this._completionDisposables.get(handle); + if (disposables) { + // Clear any disposables from the last invocation of this completion provider + disposables.clear(); + } else { + disposables = new DisposableStore(); + this._completionDisposables.set(handle, disposables); + } + const items = await agent.invokeCompletionProvider(query, token); - return items.map((i) => typeConvert.ChatAgentCompletionItem.from(i, this.commands.converter, this._completionDisposables)); + return items.map((i) => typeConvert.ChatAgentCompletionItem.from(i, this.commands.converter, disposables)); } async $provideWelcomeMessage(handle: number, location: ChatAgentLocation, token: CancellationToken): Promise<(string | IMarkdownString)[] | undefined> { diff --git a/src/vs/workbench/api/common/extHostDebugService.ts b/src/vs/workbench/api/common/extHostDebugService.ts index 2970a9c3729c7..d39a2dc0d8eff 100644 --- a/src/vs/workbench/api/common/extHostDebugService.ts +++ b/src/vs/workbench/api/common/extHostDebugService.ts @@ -29,6 +29,7 @@ import { toDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon as ThemeIconUtils } from 'vs/base/common/themables'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import * as Convert from 'vs/workbench/api/common/extHostTypeConverters'; +import { coalesce } from 'vs/base/common/arrays'; export const IExtHostDebugService = createDecorator('IExtHostDebugService'); @@ -935,7 +936,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E private definesDebugType(ed: IExtensionDescription, type: string) { if (ed.contributes) { - const debuggers = ed.contributes['debuggers']; + const debuggers = ed.contributes['debuggers']; if (debuggers && debuggers.length > 0) { for (const dbg of debuggers) { // only debugger contributions with a "label" are considered a "defining" debugger contribution @@ -961,7 +962,7 @@ export abstract class ExtHostDebugServiceBase implements IExtHostDebugService, E return Promise.race([ Promise.all(promises).then(result => { - const trackers = result.filter(t => !!t); // filter null + const trackers = coalesce(result); // filter null if (trackers.length > 0) { return new MultiTracker(trackers); } diff --git a/src/vs/workbench/api/common/extHostNotebookKernels.ts b/src/vs/workbench/api/common/extHostNotebookKernels.ts index 49dcbf40085f6..ecb6b4a7db3ce 100644 --- a/src/vs/workbench/api/common/extHostNotebookKernels.ts +++ b/src/vs/workbench/api/common/extHostNotebookKernels.ts @@ -433,6 +433,7 @@ export class ExtHostNotebookKernels implements ExtHostNotebookKernelsShape { if (obj.controller.interruptHandler) { // If we're interrupting all cells, we also need to cancel the notebook level execution. const items = this._activeNotebookExecutions.get(document.uri); + this._activeNotebookExecutions.delete(document.uri); if (handles.length && Array.isArray(items) && items.length) { items.forEach(d => d.dispose()); } diff --git a/src/vs/workbench/api/common/extHostQuickOpen.ts b/src/vs/workbench/api/common/extHostQuickOpen.ts index edc8a951ffce0..b850be83f8642 100644 --- a/src/vs/workbench/api/common/extHostQuickOpen.ts +++ b/src/vs/workbench/api/common/extHostQuickOpen.ts @@ -64,7 +64,7 @@ export function createExtHostQuickOpen(mainContext: IMainContext, workspace: IEx // clear state from last invocation this._onDidSelectItem = undefined; - const itemsPromise = >Promise.resolve(itemsOrItemsPromise); + const itemsPromise = Promise.resolve(itemsOrItemsPromise); const instance = ++this._instances; diff --git a/src/vs/workbench/api/common/extHostSpeech.ts b/src/vs/workbench/api/common/extHostSpeech.ts index 9093f63e3abc5..abc56cedc087a 100644 --- a/src/vs/workbench/api/common/extHostSpeech.ts +++ b/src/vs/workbench/api/common/extHostSpeech.ts @@ -17,6 +17,7 @@ export class ExtHostSpeech implements ExtHostSpeechShape { private readonly providers = new Map(); private readonly sessions = new Map(); + private readonly synthesizers = new Map(); constructor( mainContext: IMainContext @@ -52,6 +53,46 @@ export class ExtHostSpeech implements ExtHostSpeechShape { this.sessions.delete(session); } + async $createTextToSpeechSession(handle: number, session: number): Promise { + const provider = this.providers.get(handle); + if (!provider) { + return; + } + + const disposables = new DisposableStore(); + + const cts = new CancellationTokenSource(); + this.sessions.set(session, cts); + + const textToSpeech = disposables.add(provider.provideTextToSpeechSession(cts.token)); + this.synthesizers.set(session, textToSpeech); + + disposables.add(textToSpeech.onDidChange(e => { + if (cts.token.isCancellationRequested) { + return; + } + + this.proxy.$emitTextToSpeechEvent(session, e); + })); + + disposables.add(cts.token.onCancellationRequested(() => disposables.dispose())); + } + + async $synthesizeSpeech(session: number, text: string): Promise { + const synthesizer = this.synthesizers.get(session); + if (!synthesizer) { + return; + } + + synthesizer.synthesize(text); + } + + async $cancelTextToSpeechSession(session: number): Promise { + this.sessions.get(session)?.dispose(true); + this.sessions.delete(session); + this.synthesizers.delete(session); + } + async $createKeywordRecognitionSession(handle: number, session: number): Promise { const provider = this.providers.get(handle); if (!provider) { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index ff0d6f9e5b981..551e1289cc9a7 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4484,6 +4484,12 @@ export enum SpeechToTextStatus { Error = 5 } +export enum TextToSpeechStatus { + Started = 1, + Stopped = 2, + Error = 3 +} + export enum KeywordRecognitionStatus { Recognized = 1, Stopped = 2 diff --git a/src/vs/workbench/api/node/extHostDebugService.ts b/src/vs/workbench/api/node/extHostDebugService.ts index 5e50ef5c47e7e..fd03fd9ea327d 100644 --- a/src/vs/workbench/api/node/extHostDebugService.ts +++ b/src/vs/workbench/api/node/extHostDebugService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createCancelablePromise, firstParallel } from 'vs/base/common/async'; +import { createCancelablePromise, firstParallel, timeout } from 'vs/base/common/async'; import { IDisposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import * as nls from 'vs/nls'; @@ -127,6 +127,7 @@ export class ExtHostDebugService extends ExtHostDebugServiceBase { } else { if (terminal.state.isInteractedWith) { terminal.sendText('\u0003'); // Ctrl+C for #106743. Not part of the same command for #107969 + await timeout(200); // mirroring /~https://github.com/microsoft/vscode/blob/c67ccc70ece5f472ec25464d3eeb874cfccee9f1/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts#L852-L857 } if (configProvider.getConfiguration('debug.terminal').get('clearBeforeReusing')) { diff --git a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts index ada06633cf8d2..57b58ab67f6aa 100644 --- a/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadTreeViews.test.ts @@ -54,7 +54,7 @@ suite('MainThreadHostTreeView', function () { const disposables = ensureNoDisposablesAreLeakedInTestSuite(); setup(async () => { - const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); const viewDescriptorService = disposables.add(instantiationService.createInstance(ViewDescriptorService)); instantiationService.stub(IViewDescriptorService, viewDescriptorService); container = Registry.as(Extensions.ViewContainersRegistry).registerViewContainer({ id: 'testContainer', title: nls.localize2('test', 'test'), ctorDescriptor: new SyncDescriptor({}) }, ViewContainerLocation.Sidebar); diff --git a/src/vs/workbench/browser/actions/navigationActions.ts b/src/vs/workbench/browser/actions/navigationActions.ts index e0e838c85bfd3..56b88fe52d6e6 100644 --- a/src/vs/workbench/browser/actions/navigationActions.ts +++ b/src/vs/workbench/browser/actions/navigationActions.ts @@ -273,10 +273,13 @@ abstract class BaseFocusAction extends Action2 { neighbour = next ? Parts.PANEL_PART : Parts.SIDEBAR_PART; break; case Parts.PANEL_PART: - neighbour = next ? Parts.STATUSBAR_PART : Parts.EDITOR_PART; + neighbour = next ? Parts.AUXILIARYBAR_PART : Parts.EDITOR_PART; + break; + case Parts.AUXILIARYBAR_PART: + neighbour = next ? Parts.STATUSBAR_PART : Parts.PANEL_PART; break; case Parts.STATUSBAR_PART: - neighbour = next ? Parts.ACTIVITYBAR_PART : Parts.PANEL_PART; + neighbour = next ? Parts.ACTIVITYBAR_PART : Parts.AUXILIARYBAR_PART; break; case Parts.ACTIVITYBAR_PART: neighbour = next ? Parts.SIDEBAR_PART : Parts.STATUSBAR_PART; @@ -306,6 +309,8 @@ abstract class BaseFocusAction extends Action2 { currentlyFocusedPart = Parts.STATUSBAR_PART; } else if (layoutService.hasFocus(Parts.SIDEBAR_PART)) { currentlyFocusedPart = Parts.SIDEBAR_PART; + } else if (layoutService.hasFocus(Parts.AUXILIARYBAR_PART)) { + currentlyFocusedPart = Parts.AUXILIARYBAR_PART; } else if (layoutService.hasFocus(Parts.PANEL_PART)) { currentlyFocusedPart = Parts.PANEL_PART; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 94ec1087453c1..4e2809e149c2a 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -427,12 +427,12 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // The menu bar toggles the title bar in web because it does not need to be shown for window controls only if (isWeb && menuBarVisibility === 'toggle') { - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive())); } // The menu bar toggles the title bar in full screen for toggle and classic settings else if (this.state.runtime.mainWindowFullscreen && (menuBarVisibility === 'toggle' || menuBarVisibility === 'classic')) { - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive())); } // Move layout call to any time the menubar @@ -468,8 +468,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.mainContainer.classList.remove(LayoutClasses.FULLSCREEN); const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO); - const zenModeActive = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - if (zenModeExitInfo.transitionedToFullScreen && zenModeActive) { + if (zenModeExitInfo.transitionedToFullScreen && this.isZenModeActive()) { this.toggleZenMode(); } } @@ -482,7 +481,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (hasCustomTitlebar(this.configurationService)) { // Propagate to grid - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive())); this.updateWindowsBorder(true); } @@ -1072,11 +1071,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi })()); // Restore Zen Mode - const zenModeWasActive = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + const zenModeWasActive = this.isZenModeActive(); const restoreZenMode = getZenModeConfiguration(this.configurationService).restore; if (zenModeWasActive) { - this.stateModel.setRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE, !restoreZenMode); + this.setZenModeActive(!restoreZenMode); this.toggleZenMode(false, true); } @@ -1148,6 +1147,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.Sidebar)?.focus(); break; } + case Parts.AUXILIARYBAR_PART: { + this.paneCompositeService.getActivePaneComposite(ViewContainerLocation.AuxiliaryBar)?.focus(); + break; + } case Parts.ACTIVITYBAR_PART: (this.getPart(Parts.SIDEBAR_PART) as SidebarPart).focusActivityBar(); break; @@ -1221,7 +1224,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi switch (part) { case Parts.TITLEBAR_PART: - return shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled); + return shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive()); case Parts.SIDEBAR_PART: return !this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN); case Parts.PANEL_PART: @@ -1287,8 +1290,16 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } + private isZenModeActive(): boolean { + return this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); + } + + private setZenModeActive(active: boolean) { + this.stateModel.setRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE, active); + } + toggleZenMode(skipLayout?: boolean, restoring = false): void { - this.stateModel.setRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE, !this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)); + this.setZenModeActive(!this.isZenModeActive()); this.state.runtime.zenMode.transitionDisposables.clearAndDisposeAll(); const setLineNumbers = (lineNumbers?: LineNumbersType) => { @@ -1314,7 +1325,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const zenModeExitInfo = this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_EXIT_INFO); // Zen Mode Active - if (this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)) { + if (this.isZenModeActive()) { toggleMainWindowFullScreen = !this.state.runtime.mainWindowFullscreen && config.fullScreen && !isIOS; @@ -1446,7 +1457,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } // Event - this._onDidChangeZenMode.fire(this.stateModel.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE)); + this._onDidChangeZenMode.fire(this.isZenModeActive()); } private setStatusBarHidden(hidden: boolean, skipLayout?: boolean): void { @@ -1673,7 +1684,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private setActivityBarHidden(hidden: boolean, skipLayout?: boolean): void { this.stateModel.setRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, hidden); - // Propagate to grid this.workbenchGrid.setViewVisible(this.activityBarPartView, !hidden); } @@ -2024,14 +2034,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } updateMenubarVisibility(skipLayout: boolean): void { - const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled); + const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive()); if (!skipLayout && this.workbenchGrid && shouldShowTitleBar !== this.isVisible(Parts.TITLEBAR_PART, mainWindow)) { this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowTitleBar); } } updateCustomTitleBarVisibility(): void { - const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled); + const shouldShowTitleBar = shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive()); const titlebarVisible = this.isVisible(Parts.TITLEBAR_PART); if (shouldShowTitleBar !== titlebarVisible) { this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowTitleBar); @@ -2189,7 +2199,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.moveView(this.bannerPartView, Sizing.Distribute, this.titleBarPartView, shouldBannerBeFirst ? Direction.Up : Direction.Down); } - this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled)); + this.workbenchGrid.setViewVisible(this.titleBarPartView, shouldShowCustomTitleBar(this.configurationService, mainWindow, this.state.runtime.menuBar.toggled, this.isZenModeActive())); } private arrangeEditorNodes(nodes: { editor: ISerializedNode; sideBar?: ISerializedNode; auxiliaryBar?: ISerializedNode }, availableHeight: number, availableWidth: number): ISerializedNode { @@ -2565,13 +2575,11 @@ class LayoutStateModel extends Disposable { } private updateStateFromLegacySettings(configurationChangeEvent: IConfigurationChangeEvent): void { - const isZenMode = this.getRuntimeValue(LayoutStateKeys.ZEN_MODE_ACTIVE); - - if (configurationChangeEvent.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION) && !isZenMode) { + if (configurationChangeEvent.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) { this.setRuntimeValueAndFire(LayoutStateKeys.ACTIVITYBAR_HIDDEN, this.isActivityBarHidden()); } - if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE) && !isZenMode) { + if (configurationChangeEvent.affectsConfiguration(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)) { this.setRuntimeValueAndFire(LayoutStateKeys.STATUSBAR_HIDDEN, !this.configurationService.getValue(LegacyWorkbenchLayoutSettings.STATUSBAR_VISIBLE)); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 15e58a5817990..4610ddff2c38f 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -614,42 +614,24 @@ registerThemingParticipant((theme, collector) => { const outline = theme.getColor(activeContrastBorder); if (outline) { collector.addRule(` - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:before { - content: ""; - position: absolute; - top: 8px; - left: 8px; - height: 32px; - width: 32px; - z-index: 1; + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label::before{ + padding: 6px; } - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.profile-activity-item:before { - top: -6px; + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active .action-label::before, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:hover .action-label::before, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .action-label::before, + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:hover .action-label::before { + outline: 1px solid ${outline}; } - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:hover:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:hover:before { - outline: 1px solid; - } - - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover:before { - outline: 1px dashed; + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover .action-label::before { + outline: 1px dashed ${outline}; } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before { border-left-color: ${outline}; } - - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.active:hover:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked:hover:before, - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:hover:before { - outline-color: ${outline}; - } `); } @@ -658,7 +640,7 @@ registerThemingParticipant((theme, collector) => { const focusBorderColor = theme.getColor(focusBorder); if (focusBorderColor) { collector.addRule(` - .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before { + .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator::before { border-left-color: ${focusBorderColor}; } `); diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index a47bbe794a0f4..f42d3307fb75d 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -26,26 +26,6 @@ display: block; } -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item::before { - top: 1px; - margin-top: -2px; -} - -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item::after { - bottom: 1px; - margin-bottom: -2px; -} - -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item:first-of-type::before { - top: 2px; - margin-top: -2px; -} - -.monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item:last-of-type::after { - bottom: 2px; - margin-bottom: -2px; -} - .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item.top::before, .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item.top::after, .monaco-workbench .activitybar > .content > .composite-bar > .monaco-action-bar .action-item.bottom::before, @@ -109,7 +89,7 @@ .monaco-workbench.hc-black .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before, .monaco-workbench.hc-black .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item:focus .active-item-indicator:before { - border-color: var(--vscode-focusBorder); + border-color: var(--vscode-activityBar-activeBorder); } .monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.checked .active-item-indicator:before { diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 4cbc1def33568..16c40e836f214 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -71,7 +71,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { return Math.max(width, 300); } - readonly priority: LayoutPriority = LayoutPriority.Low; + readonly priority = LayoutPriority.Low; constructor( @INotificationService notificationService: INotificationService, @@ -245,6 +245,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { if (this.getCompositeBarPosition() === CompositeBarPosition.TOP) { return 22; } + return super.getToolbarWidth(); } diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index a3062e62d472c..f0341568cf00f 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -121,11 +121,11 @@ export class AuxiliaryEditorPart { const useCustomTitle = isNative && hasCustomTitlebar(this.configurationService); // custom title in aux windows only enabled in native if (useCustomTitle) { titlebarPart = disposables.add(this.titleService.createAuxiliaryTitlebarPart(auxiliaryWindow.container, editorPart)); - titlebarVisible = shouldShowCustomTitleBar(this.configurationService, auxiliaryWindow.window); + titlebarVisible = shouldShowCustomTitleBar(this.configurationService, auxiliaryWindow.window, undefined, false); const handleTitleBarVisibilityEvent = () => { const oldTitlebarPartVisible = titlebarVisible; - titlebarVisible = shouldShowCustomTitleBar(this.configurationService, auxiliaryWindow.window); + titlebarVisible = shouldShowCustomTitleBar(this.configurationService, auxiliaryWindow.window, undefined, false); if (oldTitlebarPartVisible !== titlebarVisible) { updateTitlebarVisibility(true); } diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 12d77fb90547c..706924c23e71a 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/multieditortabscontrol'; -import { isMacintosh, isWindows } from 'vs/base/common/platform'; +import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { shorten } from 'vs/base/common/labels'; import { EditorResourceAccessor, Verbosity, IEditorPartOptions, SideBySideEditor, DEFAULT_EDITOR_ASSOCIATION, EditorInputCapabilities, IUntypedEditorInput, preventEditorClose, EditorCloseMethod, EditorsOrder, IToolbarActions } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; @@ -316,6 +316,15 @@ export class MultiEditorTabsControl extends EditorTabsControl { } })); + // Prevent auto-pasting (/~https://github.com/microsoft/vscode/issues/201696) + if (isLinux) { + this._register(addDisposableListener(tabsContainer, EventType.MOUSE_UP, e => { + if (e.button === 1) { + e.preventDefault(); + } + })); + } + // Drag & Drop support let lastDragEvent: DragEvent | undefined = undefined; let isNewWindowOperation = false; diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index e7f574aca2492..68e797d69e125 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -526,7 +526,7 @@ export abstract class AbstractPaneCompositePart extends CompositePart { if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) { @@ -116,7 +115,7 @@ export class SidebarPart extends AbstractPaneCompositePart { } private onDidChangeActivityBarLocation(): void { - this.acitivityBarPart.hide(); + this.activityBarPart.hide(); this.updateCompositeBar(); @@ -126,7 +125,7 @@ export class SidebarPart extends AbstractPaneCompositePart { } if (this.shouldShowActivityBar()) { - this.acitivityBarPart.show(); + this.activityBarPart.show(); } this.rememberActivityBarVisiblePosition(); @@ -135,7 +134,6 @@ export class SidebarPart extends AbstractPaneCompositePart { override updateStyles(): void { super.updateStyles(); - // Part container const container = assertIsDefined(this.getContainer()); container.style.backgroundColor = this.getColor(SIDE_BAR_BACKGROUND) || ''; @@ -213,6 +211,7 @@ export class SidebarPart extends AbstractPaneCompositePart { if (this.shouldShowCompositeBar()) { return false; } + return this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) !== ActivityBarPosition.HIDDEN; } @@ -244,25 +243,28 @@ export class SidebarPart extends AbstractPaneCompositePart { } override getPinnedPaneCompositeIds(): string[] { - return this.shouldShowCompositeBar() ? super.getPinnedPaneCompositeIds() : this.acitivityBarPart.getPinnedPaneCompositeIds(); + return this.shouldShowCompositeBar() ? super.getPinnedPaneCompositeIds() : this.activityBarPart.getPinnedPaneCompositeIds(); } override getVisiblePaneCompositeIds(): string[] { - return this.shouldShowCompositeBar() ? super.getVisiblePaneCompositeIds() : this.acitivityBarPart.getVisiblePaneCompositeIds(); + return this.shouldShowCompositeBar() ? super.getVisiblePaneCompositeIds() : this.activityBarPart.getVisiblePaneCompositeIds(); } async focusActivityBar(): Promise { if (this.configurationService.getValue(LayoutSettings.ACTIVITY_BAR_LOCATION) === ActivityBarPosition.HIDDEN) { await this.configurationService.updateValue(LayoutSettings.ACTIVITY_BAR_LOCATION, this.getRememberedActivityBarVisiblePosition()); + this.onDidChangeActivityBarLocation(); } + if (this.shouldShowCompositeBar()) { - this.focusComositeBar(); + this.focusCompositeBar(); } else { if (!this.layoutService.isVisible(Parts.ACTIVITYBAR_PART)) { this.layoutService.setPartHidden(false, Parts.ACTIVITYBAR_PART); } - this.acitivityBarPart.show(true); + + this.activityBarPart.show(true); } } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 5c2ff6882c44b..0bee377dce951 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from 'vs/nls'; import { MultiWindowParts, Part } from 'vs/workbench/browser/part'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser'; -import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar } from 'vs/platform/window/common/window'; +import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; @@ -200,7 +200,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { readonly maximumWidth: number = Number.POSITIVE_INFINITY; get minimumHeight(): number { - const value = this.isCommandCenterVisible || (isWeb && isWCOEnabled()) ? 35 : 30; + const value = this.isCommandCenterVisible || (isWeb && isWCOEnabled()) ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30; return value / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1); } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 9ac5d9dbad240..6bf667dda1074 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -13,7 +13,7 @@ import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list import { ElementsDragAndDropData, ListViewTargetSector } from 'vs/base/browser/ui/list/listView'; import { IAsyncDataSource, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeNode, ITreeRenderer, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults'; -import { ActionRunner, IAction } from 'vs/base/common/actions'; +import { ActionRunner, IAction, Separator } from 'vs/base/common/actions'; import { timeout } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; @@ -801,9 +801,10 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this.tree!.setFocus([node]); let selected = this.canSelectMany ? this.getSelection() : []; - if (selected.length === 0) { + if (!selected.find(item => item.handle === node.handle)) { selected = [node]; } + const actions = treeMenus.getResourceContextActions(selected); if (!actions.length) { return; @@ -1567,8 +1568,8 @@ class MultipleSelectionActionRunner extends ActionRunner { }); } - if (!actionInSelected && selectionHandleArgs && selectionHandleArgs.length > 0) { - context = selectionHandleArgs[0]; + if (!actionInSelected && selectionHandleArgs) { + selectionHandleArgs = undefined; } await action.run(context, selectionHandleArgs); @@ -1598,13 +1599,53 @@ class TreeMenus implements IDisposable { this.contextKeyService = service; } + private filterNonUniversalActions(groups: Map[], newActions: IAction[]) { + const newActionsSet: Set = new Set(newActions.map(a => a.id)); + for (const group of groups) { + const actions = group.keys(); + for (const action of actions) { + if (!newActionsSet.has(action)) { + group.delete(action); + } + } + } + } + + private buildMenu(groups: Map[]): IAction[] { + const result: IAction[] = []; + for (const group of groups) { + if (group.size > 0) { + if (result.length) { + result.push(new Separator()); + } + result.push(...group.values()); + } + } + return result; + } + + private createGroups(actions: IAction[]): Map[] { + const groups: Map[] = []; + let group: Map = new Map(); + for (const action of actions) { + if (action instanceof Separator) { + groups.push(group); + group = new Map(); + } else { + group.set(action.id, action); + } + } + groups.push(group); + return groups; + } + private getActions(menuId: MenuId, elements: ITreeItem[], listen?: DisposableStore): { primary: IAction[]; secondary: IAction[] } { if (!this.contextKeyService) { return { primary: [], secondary: [] }; } - const allowedPrimary = new Map(); - const allowedSecondary = new Map(); + let primaryGroups: Map[] = []; + let secondaryGroups: Map[] = []; for (let i = 0; i < elements.length; i++) { const element = elements[i]; const contextKeyService = this.contextKeyService.createOverlay([ @@ -1618,25 +1659,11 @@ class TreeMenus implements IDisposable { const result = { primary, secondary, menu }; createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, 'inline'); if (i === 0) { - for (const action of result.primary) { - allowedPrimary.set(action.id, action); - } - for (const action of result.secondary) { - allowedSecondary.set(action.id, action); - } + primaryGroups = this.createGroups(result.primary); + secondaryGroups = this.createGroups(result.secondary); } else { - const primaryKeys = allowedPrimary.keys(); - for (const key of primaryKeys) { - if (!result.primary.some(action => action.id === key)) { - allowedPrimary.delete(key); - } - } - const secondaryKeys = allowedSecondary.keys(); - for (const key of secondaryKeys) { - if (!result.secondary.some(action => action.id === key)) { - allowedSecondary.delete(key); - } - } + this.filterNonUniversalActions(primaryGroups, result.primary); + this.filterNonUniversalActions(secondaryGroups, result.secondary); } if (listen && elements.length === 1) { listen.add(menu.onDidChange(() => this._onDidChange.fire(element))); @@ -1646,7 +1673,7 @@ class TreeMenus implements IDisposable { } } - return { primary: Array.from(allowedPrimary.values()), secondary: Array.from(allowedSecondary.values()) }; + return { primary: this.buildMenu(primaryGroups), secondary: this.buildMenu(secondaryGroups) }; } dispose() { diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts index b6285e45c7199..3331c892d0c29 100644 --- a/src/vs/workbench/browser/parts/views/viewFilter.ts +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -81,6 +81,7 @@ export class FilterWidget extends Widget { private moreFiltersActionViewItem: MoreFiltersActionViewItem | undefined; private isMoreFiltersChecked: boolean = false; + private lastWidth?: number; private focusTracker: DOM.IFocusTracker; public get onDidFocus() { return this.focusTracker.onDidFocus; } @@ -147,6 +148,13 @@ export class FilterWidget extends Widget { this.element.parentElement?.classList.toggle('grow', width > 700); this.element.classList.toggle('small', width < 400); this.adjustInputBox(); + this.lastWidth = width; + } + + relayout() { + if (this.lastWidth) { + this.layout(this.lastWidth); + } } checkMoreFilters(checked: boolean): void { diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index 6eae565005161..c62f1b0ab4745 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -688,7 +688,9 @@ export abstract class ViewPane extends Pane implements IView { override get trapsArrowNavigation(): boolean { return true; } override render(container: HTMLElement): void { container.classList.add('viewpane-filter-container'); - append(container, that.getFilterWidget()!.element); + const filter = that.getFilterWidget()!; + append(container, filter.element); + filter.relayout(); } }; } diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 9a0f4dda2c576..b6a90af346973 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -677,7 +677,7 @@ export const ACTIVITY_BAR_BORDER = registerColor('activityBar.border', { export const ACTIVITY_BAR_ACTIVE_BORDER = registerColor('activityBar.activeBorder', { dark: ACTIVITY_BAR_FOREGROUND, light: ACTIVITY_BAR_FOREGROUND, - hcDark: null, + hcDark: contrastBorder, hcLight: contrastBorder }, localize('activityBarActiveBorder', "Activity bar border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 9703c105d0e7e..7d0bb72c307a7 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -15,7 +15,6 @@ import { getOrSet, SetMap } from 'vs/base/common/map'; import { Registry } from 'vs/platform/registry/common/platform'; import { IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { flatten } from 'vs/base/common/arrays'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IProgressIndicator } from 'vs/platform/progress/common/progress'; import Severity from 'vs/base/common/severity'; @@ -204,7 +203,7 @@ class ViewContainersRegistryImpl extends Disposable implements IViewContainersRe private readonly defaultViewContainers: ViewContainer[] = []; get all(): ViewContainer[] { - return flatten([...this.viewContainers.values()]); + return [...this.viewContainers.values()].flat(); } registerViewContainer(viewContainerDescriptor: IViewContainerDescriptor, viewContainerLocation: ViewContainerLocation, options?: { isDefault?: boolean; doNotRegisterOpenCommand?: boolean }): ViewContainer { diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts index ebd81e486052e..aa516a2853776 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibility.contribution.ts @@ -17,11 +17,9 @@ import { SaveAccessibilitySignalContribution } from 'vs/workbench/contrib/access import { CommentsAccessibilityHelpContribution } from 'vs/workbench/contrib/comments/browser/commentsAccessibility'; import { DiffEditorActiveAnnouncementContribution } from 'vs/workbench/contrib/accessibilitySignals/browser/openDiffEditorAnnouncement'; import { SpeechAccessibilitySignalContribution } from 'vs/workbench/contrib/speech/browser/speechAccessibilitySignal'; -import { registerAudioCueConfiguration } from 'vs/workbench/contrib/accessibility/browser/audioCueConfiguration'; import { AccessibleViewInformationService, IAccessibleViewInformationService } from 'vs/workbench/services/accessibility/common/accessibleViewInformationService'; registerAccessibilityConfiguration(); -registerAudioCueConfiguration(); registerSingleton(IAccessibleViewService, AccessibleViewService, InstantiationType.Delayed); registerSingleton(IAccessibleViewInformationService, AccessibleViewInformationService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 478dc21bf4ead..ef8197fa3b01c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -8,7 +8,7 @@ import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPrope import { Registry } from 'vs/platform/registry/common/platform'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { workbenchConfigurationNodeBase, Extensions as WorkbenchExtensions, IConfigurationMigrationRegistry, ConfigurationKeyValuePairs, ConfigurationMigration } from 'vs/workbench/common/configuration'; -import { AccessibilityAlertSettingId, AccessibilitySignal } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; +import { AccessibilitySignal } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; import { ISpeechService, SPEECH_LANGUAGES, SPEECH_LANGUAGE_CONFIG } from 'vs/workbench/contrib/speech/common/speechService'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -81,13 +81,6 @@ const baseVerbosityProperty: IConfigurationPropertySchema = { default: true, tags: ['accessibility'] }; -const markdownDeprecationMessage = localize('accessibility.announcement.deprecationMessage', "This setting is deprecated. Use the `signals` settings instead."); -const baseAlertProperty: IConfigurationPropertySchema = { - type: 'boolean', - default: true, - tags: ['accessibility'], - markdownDeprecationMessage -}; export const accessibilityConfigurationNodeBase = Object.freeze({ id: 'accessibility', @@ -189,135 +182,51 @@ const configuration: IConfigurationNode = { description: localize('verbosity.diffEditorActive', 'Indicate when a diff editor becomes the active editor.'), ...baseVerbosityProperty }, - [AccessibilityAlertSettingId.Save]: { - 'markdownDescription': localize('announcement.save', "Indicates when a file is saved. Also see {0}.", '`#audioCues.save#`'), - 'enum': ['userGesture', 'always', 'never'], - 'default': 'always', - 'enumDescriptions': [ - localize('announcement.save.userGesture', "Indicates when a file is saved via user gesture."), - localize('announcement.save.always', "Indicates whenever is a file is saved, including auto save."), - localize('announcement.save.never', "Never alerts.") - ], - tags: ['accessibility'], - markdownDeprecationMessage - }, - [AccessibilityAlertSettingId.Clear]: { - 'markdownDescription': localize('announcement.clear', "Indicates when a feature is cleared (for example, the terminal, Debug Console, or Output channel). Also see {0}.", '`#audioCues.clear#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.Format]: { - 'markdownDescription': localize('announcement.format', "Indicates when a file or notebook cell is formatted. Also see {0}.", '`#audioCues.format#`'), - 'type': 'string', - 'enum': ['userGesture', 'always', 'never'], - 'default': 'always', - 'enumDescriptions': [ - localize('announcement.format.userGesture', "Indicates when a file is formatted via user gesture."), - localize('announcement.format.always', "Indicates whenever is a file is formatted, including auto save, on cell execution, and more."), - localize('announcement.format.never', "Never alerts.") - ], - tags: ['accessibility'], - markdownDeprecationMessage - }, - [AccessibilityAlertSettingId.Breakpoint]: { - 'markdownDescription': localize('announcement.breakpoint', "Indicates when the debugger breaks. Also see {0}.", '`#audioCues.onDebugBreak#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.Error]: { - 'markdownDescription': localize('announcement.error', "Indicates when the active line has an error. Also see {0}.", '`#audioCues.lineHasError#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.Warning]: { - 'markdownDescription': localize('announcement.warning', "Indicates when the active line has a warning. Also see {0}.", '`#audioCues.lineHasWarning#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.FoldedArea]: { - 'markdownDescription': localize('announcement.foldedArea', "Indicates when the active line has a folded area that can be unfolded. Also see {0}.", '`#audioCues.lineHasFoldedArea#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.TerminalQuickFix]: { - 'markdownDescription': localize('announcement.terminalQuickFix', "Indicates when there is an available terminal quick fix. Also see {0}.", '`#audioCues.terminalQuickFix#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.TerminalBell]: { - 'markdownDescription': localize('announcement.terminalBell', "Indicates when the terminal bell is activated."), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.TerminalCommandFailed]: { - 'markdownDescription': localize('announcement.terminalCommandFailed', "Indicates when a terminal command fails (non-zero exit code). Also see {0}.", '`#audioCues.terminalCommandFailed#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.TaskFailed]: { - 'markdownDescription': localize('announcement.taskFailed', "Indicates when a task fails (non-zero exit code). Also see {0}.", '`#audioCues.taskFailed#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.TaskCompleted]: { - 'markdownDescription': localize('announcement.taskCompleted', "Indicates when a task completes successfully (zero exit code). Also see {0}.", '`#audioCues.taskCompleted#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.ChatRequestSent]: { - 'markdownDescription': localize('announcement.chatRequestSent', "Indicates when a chat request is sent. Also see {0}.", '`#audioCues.chatRequestSent#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.Progress]: { - 'markdownDescription': localize('announcement.progress', "Indicates when a chat response is pending. Also see {0}.", '`#audioCues.chatResponsePending#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.NoInlayHints]: { - 'markdownDescription': localize('announcement.noInlayHints', "Indicates when there are no inlay hints. Also see {0}.", '`#audioCues.noInlayHints#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.LineHasBreakpoint]: { - 'markdownDescription': localize('announcement.lineHasBreakpoint', "Indicates when on a line with a breakpoint. Also see {0}.", '`#audioCues.lineHasBreakpoint#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.NotebookCellCompleted]: { - 'markdownDescription': localize('announcement.notebookCellCompleted', "Indicates when a notebook cell completes successfully. Also see {0}.", '`#audioCues.notebookCellCompleted#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.NotebookCellFailed]: { - 'markdownDescription': localize('announcement.notebookCellFailed', "Indicates when a notebook cell fails. Also see {0}.", '`#audioCues.notebookCellFailed#`'), - ...baseAlertProperty - }, - [AccessibilityAlertSettingId.OnDebugBreak]: { - 'markdownDescription': localize('announcement.onDebugBreak', "Indicates when the debugger breaks. Also see {0}.", '`#audioCues.onDebugBreak#`'), - ...baseAlertProperty - }, [AccessibilityWorkbenchSettingId.AccessibleViewCloseOnKeyPress]: { markdownDescription: localize('terminal.integrated.accessibleView.closeOnKeyPress', "On keypress, close the Accessible View and focus the element from which it was invoked."), type: 'boolean', default: true }, - 'accessibility.signals.sounds.volume': { - 'description': localize('accessibility.signals.sounds.volume', "The volume of the sounds in percent (0-100)."), - 'type': 'number', - 'minimum': 0, - 'maximum': 100, - 'default': 70, - tags: ['accessibility'] - }, - 'accessibility.signals.debouncePositionChanges': { - 'description': localize('accessibility.signals.debouncePositionChanges', "Whether or not position changes should be debounced"), - 'type': 'boolean', - 'default': false, + 'accessibility.signalOptions': { + type: 'object', + additionalProperties: false, + properties: { + 'volume': { + 'description': localize('accessibility.signalOptions.volume', "The volume of the sounds in percent (0-100)."), + 'type': 'number', + 'minimum': 0, + 'maximum': 100, + 'default': 70, + }, + 'debouncePositionChanges': { + 'description': localize('accessibility.signalOptions.debouncePositionChanges', "Whether or not position changes should be debounced"), + 'type': 'boolean', + 'default': false, + }, + }, + default: { + 'volume': 70, + 'debouncePositionChanges': false + }, tags: ['accessibility'] }, 'accessibility.signals.lineHasBreakpoint': { ...signalFeatureBase, - 'description': localize('accessibility.signals.lineHasBreakpoint', "Plays a signal when the active line has a breakpoint."), + 'description': localize('accessibility.signals.lineHasBreakpoint', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the active line has a breakpoint."), 'properties': { 'sound': { 'description': localize('accessibility.signals.lineHasBreakpoint.sound', "Plays a sound when the active line has a breakpoint."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.lineHasBreakpoint.announcement', "Indicates when the active line has a breakpoint."), + 'description': localize('accessibility.signals.lineHasBreakpoint.announcement', "Announces when the active line has a breakpoint."), ...announcementFeatureBase }, }, }, 'accessibility.signals.lineHasInlineSuggestion': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.lineHasInlineSuggestion', "Indicates when the active line has an inline suggestion."), + 'description': localize('accessibility.signals.lineHasInlineSuggestion', "Plays a sound / audio cue when the active line has an inline suggestion."), 'properties': { 'sound': { 'description': localize('accessibility.signals.lineHasInlineSuggestion.sound', "Plays a sound when the active line has an inline suggestion."), @@ -328,14 +237,14 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.lineHasError': { ...signalFeatureBase, - 'description': localize('accessibility.signals.lineHasError', "Indicates when the active line has an error."), + 'description': localize('accessibility.signals.lineHasError', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the active line has an error."), 'properties': { 'sound': { 'description': localize('accessibility.signals.lineHasError.sound', "Plays a sound when the active line has an error."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.lineHasError.announcement', "Indicates when the active line has an error."), + 'description': localize('accessibility.signals.lineHasError.announcement', "Announces when the active line has an error."), ...announcementFeatureBase, default: 'off' }, @@ -343,7 +252,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.lineHasFoldedArea': { ...signalFeatureBase, - 'description': localize('accessibility.signals.lineHasFoldedArea', "Indicates when the active line has a folded area that can be unfolded."), + 'description': localize('accessibility.signals.lineHasFoldedArea', "Plays a signal - sound (audio cue) and/or announcement (alert) - the active line has a folded area that can be unfolded."), 'properties': { 'sound': { 'description': localize('accessibility.signals.lineHasFoldedArea.sound', "Plays a sound when the active line has a folded area that can be unfolded."), @@ -351,21 +260,21 @@ const configuration: IConfigurationNode = { default: 'off' }, 'announcement': { - 'description': localize('accessibility.signals.lineHasFoldedArea.announcement', "Indicates when the active line has a folded area that can be unfolded."), + 'description': localize('accessibility.signals.lineHasFoldedArea.announcement', "Announces when the active line has a folded area that can be unfolded."), ...announcementFeatureBase }, } }, 'accessibility.signals.lineHasWarning': { ...signalFeatureBase, - 'description': localize('accessibility.signals.lineHasWarning', "Plays a signal when the active line has a warning."), + 'description': localize('accessibility.signals.lineHasWarning', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the active line has a warning."), 'properties': { 'sound': { 'description': localize('accessibility.signals.lineHasWarning.sound', "Plays a sound when the active line has a warning."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.lineHasWarning.announcement', "Indicates when the active line has a warning."), + 'description': localize('accessibility.signals.lineHasWarning.announcement', "Announces when the active line has a warning."), ...announcementFeatureBase, default: 'off' }, @@ -373,14 +282,14 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.positionHasError': { ...signalFeatureBase, - 'description': localize('accessibility.signals.positionHasError', "Plays a signal when the active line has a warning."), + 'description': localize('accessibility.signals.positionHasError', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the active line has a warning."), 'properties': { 'sound': { 'description': localize('accessibility.signals.positionHasError.sound', "Plays a sound when the active line has a warning."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.positionHasError.announcement', "Indicates when the active line has a warning."), + 'description': localize('accessibility.signals.positionHasError.announcement', "Announces when the active line has a warning."), ...announcementFeatureBase, default: 'on' }, @@ -388,14 +297,14 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.positionHasWarning': { ...signalFeatureBase, - 'description': localize('accessibility.signals.positionHasWarning', "Plays a signal when the active line has a warning."), + 'description': localize('accessibility.signals.positionHasWarning', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the active line has a warning."), 'properties': { 'sound': { 'description': localize('accessibility.signals.positionHasWarning.sound', "Plays a sound when the active line has a warning."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.positionHasWarning.announcement', "Indicates when the active line has a warning."), + 'description': localize('accessibility.signals.positionHasWarning.announcement', "Announces when the active line has a warning."), ...announcementFeatureBase, default: 'on' }, @@ -403,105 +312,105 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.onDebugBreak': { ...signalFeatureBase, - 'description': localize('accessibility.signals.onDebugBreak', "Plays a signal when the debugger stopped on a breakpoint."), + 'description': localize('accessibility.signals.onDebugBreak', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the debugger stopped on a breakpoint."), 'properties': { 'sound': { 'description': localize('accessibility.signals.onDebugBreak.sound', "Plays a sound when the debugger stopped on a breakpoint."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.onDebugBreak.announcement', "Indicates when the debugger stopped on a breakpoint."), + 'description': localize('accessibility.signals.onDebugBreak.announcement', "Announces when the debugger stopped on a breakpoint."), ...announcementFeatureBase }, } }, 'accessibility.signals.noInlayHints': { ...signalFeatureBase, - 'description': localize('accessibility.signals.noInlayHints', "Plays a signal when trying to read a line with inlay hints that has no inlay hints."), + 'description': localize('accessibility.signals.noInlayHints', "Plays a signal - sound (audio cue) and/or announcement (alert) - when trying to read a line with inlay hints that has no inlay hints."), 'properties': { 'sound': { 'description': localize('accessibility.signals.noInlayHints.sound', "Plays a sound when trying to read a line with inlay hints that has no inlay hints."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.noInlayHints.announcement', "Indicates when trying to read a line with inlay hints that has no inlay hints."), + 'description': localize('accessibility.signals.noInlayHints.announcement', "Announces when trying to read a line with inlay hints that has no inlay hints."), ...announcementFeatureBase }, } }, 'accessibility.signals.taskCompleted': { ...signalFeatureBase, - 'description': localize('accessibility.signals.taskCompleted', "Plays a signal when a task is completed."), + 'description': localize('accessibility.signals.taskCompleted', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a task is completed."), 'properties': { 'sound': { 'description': localize('accessibility.signals.taskCompleted.sound', "Plays a sound when a task is completed."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.taskCompleted.announcement', "Indicates when a task is completed."), + 'description': localize('accessibility.signals.taskCompleted.announcement', "Announces when a task is completed."), ...announcementFeatureBase }, } }, 'accessibility.signals.taskFailed': { ...signalFeatureBase, - 'description': localize('accessibility.signals.taskFailed', "Plays a signal when a task fails (non-zero exit code)."), + 'description': localize('accessibility.signals.taskFailed', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a task fails (non-zero exit code)."), 'properties': { 'sound': { 'description': localize('accessibility.signals.taskFailed.sound', "Plays a sound when a task fails (non-zero exit code)."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.taskFailed.announcement', "Indicates when a task fails (non-zero exit code)."), + 'description': localize('accessibility.signals.taskFailed.announcement', "Announces when a task fails (non-zero exit code)."), ...announcementFeatureBase }, } }, 'accessibility.signals.terminalCommandFailed': { ...signalFeatureBase, - 'description': localize('accessibility.signals.terminalCommandFailed', "Plays a signal when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view."), + 'description': localize('accessibility.signals.terminalCommandFailed', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view."), 'properties': { 'sound': { 'description': localize('accessibility.signals.terminalCommandFailed.sound', "Plays a sound when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.terminalCommandFailed.announcement', "Indicates when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view."), + 'description': localize('accessibility.signals.terminalCommandFailed.announcement', "Announces when a terminal command fails (non-zero exit code) or when a command with such an exit code is navigated to in the accessible view."), ...announcementFeatureBase }, } }, 'accessibility.signals.terminalQuickFix': { ...signalFeatureBase, - 'description': localize('accessibility.signals.terminalQuickFix', "Plays a signal when terminal Quick Fixes are available."), + 'description': localize('accessibility.signals.terminalQuickFix', "Plays a signal - sound (audio cue) and/or announcement (alert) - when terminal Quick Fixes are available."), 'properties': { 'sound': { 'description': localize('accessibility.signals.terminalQuickFix.sound', "Plays a sound when terminal Quick Fixes are available."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.terminalQuickFix.announcement', "Indicates when terminal Quick Fixes are available."), + 'description': localize('accessibility.signals.terminalQuickFix.announcement', "Announces when terminal Quick Fixes are available."), ...announcementFeatureBase }, } }, 'accessibility.signals.terminalBell': { ...signalFeatureBase, - 'description': localize('accessibility.signals.terminalBell', "Plays a signal when the terminal bell is ringing."), + 'description': localize('accessibility.signals.terminalBell', "Plays a signal - sound (audio cue) and/or announcement (alert) - when the terminal bell is ringing."), 'properties': { 'sound': { 'description': localize('accessibility.signals.terminalBell.sound', "Plays a sound when the terminal bell is ringing."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.terminalBell.announcement', "Indicates when the terminal bell is ringing."), + 'description': localize('accessibility.signals.terminalBell.announcement', "Announces when the terminal bell is ringing."), ...announcementFeatureBase }, } }, 'accessibility.signals.diffLineInserted': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.diffLineInserted', "Indicates when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change."), + 'description': localize('accessibility.signals.diffLineInserted', "Plays a sound / audio cue when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change."), 'properties': { 'sound': { 'description': localize('accessibility.signals.sound', "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change."), @@ -511,7 +420,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.diffLineModified': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.diffLineModified', "Indicates when the focus moves to an modified line in Accessible Diff Viewer mode or to the next/previous change."), + 'description': localize('accessibility.signals.diffLineModified', "Plays a sound / audio cue when the focus moves to an modified line in Accessible Diff Viewer mode or to the next/previous change."), 'properties': { 'sound': { 'description': localize('accessibility.signals.diffLineModified.sound', "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change."), @@ -521,7 +430,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.diffLineDeleted': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.diffLineDeleted', "Indicates when the focus moves to an deleted line in Accessible Diff Viewer mode or to the next/previous change."), + 'description': localize('accessibility.signals.diffLineDeleted', "Plays a sound / audio cue when the focus moves to an deleted line in Accessible Diff Viewer mode or to the next/previous change."), 'properties': { 'sound': { 'description': localize('accessibility.signals.diffLineDeleted.sound', "Plays a sound when the focus moves to an deleted line in Accessible Diff Viewer mode or to the next/previous change."), @@ -531,49 +440,49 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.notebookCellCompleted': { ...signalFeatureBase, - 'description': localize('accessibility.signals.notebookCellCompleted', "Plays a signal when a notebook cell execution is successfully completed."), + 'description': localize('accessibility.signals.notebookCellCompleted', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a notebook cell execution is successfully completed."), 'properties': { 'sound': { 'description': localize('accessibility.signals.notebookCellCompleted.sound', "Plays a sound when a notebook cell execution is successfully completed."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.notebookCellCompleted.announcement', "Indicates when a notebook cell execution is successfully completed."), + 'description': localize('accessibility.signals.notebookCellCompleted.announcement', "Announces when a notebook cell execution is successfully completed."), ...announcementFeatureBase }, } }, 'accessibility.signals.notebookCellFailed': { ...signalFeatureBase, - 'description': localize('accessibility.signals.notebookCellFailed', "Plays a signal when a notebook cell execution fails."), + 'description': localize('accessibility.signals.notebookCellFailed', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a notebook cell execution fails."), 'properties': { 'sound': { 'description': localize('accessibility.signals.notebookCellFailed.sound', "Plays a sound when a notebook cell execution fails."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.notebookCellFailed.announcement', "Indicates when a notebook cell execution fails."), + 'description': localize('accessibility.signals.notebookCellFailed.announcement', "Announces when a notebook cell execution fails."), ...announcementFeatureBase }, } }, 'accessibility.signals.chatRequestSent': { ...signalFeatureBase, - 'description': localize('accessibility.signals.chatRequestSent', "Plays a signal when a chat request is made."), + 'description': localize('accessibility.signals.chatRequestSent', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a chat request is made."), 'properties': { 'sound': { 'description': localize('accessibility.signals.chatRequestSent.sound', "Plays a sound when a chat request is made."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.chatRequestSent.announcement', "Indicates when a chat request is made."), + 'description': localize('accessibility.signals.chatRequestSent.announcement', "Announces when a chat request is made."), ...announcementFeatureBase }, } }, 'accessibility.signals.progress': { ...signalFeatureBase, - 'description': localize('accessibility.signals.progress', "Plays a signal on loop while progress is occurring."), + 'description': localize('accessibility.signals.progress', "Plays a signal - sound (audio cue) and/or announcement (alert) - on loop while progress is occurring."), 'properties': { 'sound': { 'description': localize('accessibility.signals.progress.sound', "Plays a sound on loop while progress is occurring."), @@ -587,7 +496,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.chatResponseReceived': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.chatResponseReceived', "Indicates when the response has been received."), + 'description': localize('accessibility.signals.chatResponseReceived', "Plays a sound / audio cue when the response has been received."), 'properties': { 'sound': { 'description': localize('accessibility.signals.chatResponseReceived.sound', "Plays a sound on loop while the response has been received."), @@ -597,7 +506,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.voiceRecordingStarted': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.voiceRecordingStarted', "Indicates when the voice recording has started."), + 'description': localize('accessibility.signals.voiceRecordingStarted', "Plays a sound / audio cue when the voice recording has started."), 'properties': { 'sound': { 'description': localize('accessibility.signals.voiceRecordingStarted.sound', "Plays a sound when the voice recording has started."), @@ -610,7 +519,7 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.voiceRecordingStopped': { ...defaultNoAnnouncement, - 'description': localize('accessibility.signals.voiceRecordingStopped', "Indicates when the voice recording has stopped."), + 'description': localize('accessibility.signals.voiceRecordingStopped', "Plays a sound / audio cue when the voice recording has stopped."), 'properties': { 'sound': { 'description': localize('accessibility.signals.voiceRecordingStopped.sound', "Plays a sound when the voice recording has stopped."), @@ -621,14 +530,14 @@ const configuration: IConfigurationNode = { }, 'accessibility.signals.clear': { ...signalFeatureBase, - 'description': localize('accessibility.signals.clear', "Plays a signal when a feature is cleared (for example, the terminal, Debug Console, or Output channel)."), + 'description': localize('accessibility.signals.clear', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a feature is cleared (for example, the terminal, Debug Console, or Output channel)."), 'properties': { 'sound': { 'description': localize('accessibility.signals.clear.sound', "Plays a sound when a feature is cleared."), ...soundFeatureBase }, 'announcement': { - 'description': localize('accessibility.signals.clear.announcement', "Indicates when a feature is cleared."), + 'description': localize('accessibility.signals.clear.announcement', "Announces when a feature is cleared."), ...announcementFeatureBase }, }, @@ -637,7 +546,7 @@ const configuration: IConfigurationNode = { 'type': 'object', 'tags': ['accessibility'], additionalProperties: false, - 'markdownDescription': localize('accessibility.signals.save', "Plays a signal when a file is saved."), + 'markdownDescription': localize('accessibility.signals.save', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a file is saved."), 'properties': { 'sound': { 'description': localize('accessibility.signals.save.sound', "Plays a sound when a file is saved."), @@ -645,20 +554,20 @@ const configuration: IConfigurationNode = { 'enum': ['userGesture', 'always', 'never'], 'default': 'never', 'enumDescriptions': [ - localize('accessibility.signals.save.sound.userGesture', "Plays the audio cue when a user explicitly saves a file."), - localize('accessibility.signals.save.sound.always', "Plays the audio cue whenever a file is saved, including auto save."), - localize('accessibility.signals.save.sound.never', "Never plays the audio cue.") + localize('accessibility.signals.save.sound.userGesture', "Plays the sound when a user explicitly saves a file."), + localize('accessibility.signals.save.sound.always', "Plays the sound whenever a file is saved, including auto save."), + localize('accessibility.signals.save.sound.never', "Never plays the sound.") ], }, 'announcement': { - 'description': localize('accessibility.signals.save.announcement', "Indicates when a file is saved."), + 'description': localize('accessibility.signals.save.announcement', "Announces when a file is saved."), 'type': 'string', 'enum': ['userGesture', 'always', 'never'], 'default': 'never', 'enumDescriptions': [ localize('accessibility.signals.save.announcement.userGesture', "Announces when a user explicitly saves a file."), localize('accessibility.signals.save.announcement.always', "Announces whenever a file is saved, including auto save."), - localize('accessibility.signals.save.announcement.never', "Never plays the audio cue.") + localize('accessibility.signals.save.announcement.never', "Never plays the announcement.") ], }, }, @@ -671,7 +580,7 @@ const configuration: IConfigurationNode = { 'type': 'object', 'tags': ['accessibility'], additionalProperties: false, - 'markdownDescription': localize('accessibility.signals.format', "Plays a signal when a file or notebook is formatted."), + 'markdownDescription': localize('accessibility.signals.format', "Plays a signal - sound (audio cue) and/or announcement (alert) - when a file or notebook is formatted."), 'properties': { 'sound': { 'description': localize('accessibility.signals.format.sound', "Plays a sound when a file or notebook is formatted."), @@ -679,18 +588,18 @@ const configuration: IConfigurationNode = { 'enum': ['userGesture', 'always', 'never'], 'default': 'never', 'enumDescriptions': [ - localize('accessibility.signals.format.userGesture', "Plays the audio cue when a user explicitly formats a file."), - localize('accessibility.signals.format.always', "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell."), - localize('accessibility.signals.format.never', "Never plays the audio cue.") + localize('accessibility.signals.format.userGesture', "Plays the sound when a user explicitly formats a file."), + localize('accessibility.signals.format.always', "Plays the sound whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell."), + localize('accessibility.signals.format.never', "Never plays the sound.") ], }, 'announcement': { - 'description': localize('accessibility.signals.format.announcement', "Indicates when a file or notebook is formatted."), + 'description': localize('accessibility.signals.format.announcement', "Announces when a file or notebook is formatted."), 'type': 'string', 'enum': ['userGesture', 'always', 'never'], 'default': 'never', 'enumDescriptions': [ - localize('accessibility.signals.format.announcement.userGesture', "Announceswhen a user explicitly formats a file."), + localize('accessibility.signals.format.announcement.userGesture', "Announces when a user explicitly formats a file."), localize('accessibility.signals.format.announcement.always', "Announces whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell."), localize('accessibility.signals.format.announcement.never', "Never announces.") ], @@ -802,9 +711,10 @@ export class DynamicSpeechAccessibilityConfiguration extends Disposable implemen Registry.as(WorkbenchExtensions.ConfigurationMigration) .registerConfigurationMigrations([{ key: 'audioCues.volume', - migrateFn: (value, accessor) => { + migrateFn: (volume, accessor) => { + const debouncePositionChanges = getDebouncePositionChangesFromConfig(accessor); return [ - ['accessibility.signals.sounds.volume', { value }], + ['accessibility.signalOptions', { value: debouncePositionChanges !== undefined ? { volume, debouncePositionChanges } : { volume } }], ['audioCues.volume', { value: undefined }] ]; } @@ -813,14 +723,46 @@ Registry.as(WorkbenchExtensions.ConfigurationMi Registry.as(WorkbenchExtensions.ConfigurationMigration) .registerConfigurationMigrations([{ key: 'audioCues.debouncePositionChanges', - migrateFn: (value, accessor) => { + migrateFn: (debouncePositionChanges, accessor) => { + const volume = getVolumeFromConfig(accessor); return [ - ['accessibility.signals.debouncePositionChanges', { value }], + ['accessibility.signalOptions', { value: volume !== undefined ? { volume, debouncePositionChanges } : { debouncePositionChanges } }], ['audioCues.debouncePositionChanges', { value: undefined }] ]; } }]); +Registry.as(WorkbenchExtensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'accessibility.signals.sounds.volume', + migrateFn: (volume, accessor) => { + const debouncePositionChanges = getDebouncePositionChangesFromConfig(accessor); + return [ + ['accessibility.signalOptions', { value: debouncePositionChanges !== undefined ? { volume, debouncePositionChanges } : { volume } }], + ['accessibility.signals.sounds.volume', { value: undefined }] + ]; + } + }]); + +Registry.as(WorkbenchExtensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: 'accessibility.signals.debouncePositionChanges', + migrateFn: (debouncePositionChanges, accessor) => { + const volume = getVolumeFromConfig(accessor); + return [ + ['accessibility.signalOptions', { value: volume !== undefined ? { volume, debouncePositionChanges } : { debouncePositionChanges } }], + ['accessibility.signals.debouncePositionChanges', { value: undefined }] + ]; + } + }]); + +function getVolumeFromConfig(accessor: (key: string) => any): string | undefined { + return accessor('accessibility.signalOptions')?.volume || accessor('accessibility.signals.sounds.volume') || accessor('audioCues.volume'); +} + +function getDebouncePositionChangesFromConfig(accessor: (key: string) => any): number | undefined { + return accessor('accessibility.signalOptions')?.debouncePositionChanges || accessor('accessibility.signals.debouncePositionChanges') || accessor('audioCues.debouncePositionChanges'); +} Registry.as(WorkbenchExtensions.ConfigurationMigration) .registerConfigurationMigrations([{ diff --git a/src/vs/workbench/contrib/accessibility/browser/audioCueConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/audioCueConfiguration.ts deleted file mode 100644 index a1114ff859cf1..0000000000000 --- a/src/vs/workbench/contrib/accessibility/browser/audioCueConfiguration.ts +++ /dev/null @@ -1,163 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { localize } from 'vs/nls'; -import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurationPropertySchema, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; -import { Registry } from 'vs/platform/registry/common/platform'; - -export const audioCueFeatureBase: IConfigurationPropertySchema = { - 'type': 'string', - 'enum': ['auto', 'on', 'off'], - 'default': 'auto', - 'enumDescriptions': [ - localize('audioCues.enabled.auto', "Enable audio cue when a screen reader is attached."), - localize('audioCues.enabled.on', "Enable audio cue."), - localize('audioCues.enabled.off', "Disable audio cue.") - ], - tags: ['accessibility'], -}; -const markdownDeprecationMessage = localize('audioCues.enabled.deprecated', "This setting is deprecated. Use `signals` settings instead."); -const soundDeprecatedFeatureBase: IConfigurationPropertySchema = { - ...audioCueFeatureBase, - markdownDeprecationMessage -}; -export function registerAudioCueConfiguration() { - Registry.as(ConfigurationExtensions.Configuration).registerConfiguration({ - scope: ConfigurationScope.RESOURCE, - 'properties': { - 'audioCues.enabled': { - markdownDeprecationMessage: 'Deprecated. Use the specific setting for each audio cue instead (`audioCues.*`).', - tags: ['accessibility'] - }, - 'audioCues.volume': { - markdownDeprecationMessage: 'Deprecated. Use `accessibility.signals.sounds.volume` instead.', - tags: ['accessibility'] - }, - 'audioCues.debouncePositionChanges': { - 'description': localize('audioCues.debouncePositionChanges', "Whether or not position changes should be debounced"), - 'type': 'boolean', - 'default': false, - tags: ['accessibility'], - 'markdownDeprecationMessage': localize('audioCues.debouncePositionChangesDeprecated', 'This setting is deprecated, instead use the `signals.debouncePositionChanges` setting.') - }, - 'audioCues.lineHasBreakpoint': { - 'description': localize('audioCues.lineHasBreakpoint', "Plays a sound when the active line has a breakpoint."), - ...soundDeprecatedFeatureBase - }, - 'audioCues.lineHasInlineSuggestion': { - 'description': localize('audioCues.lineHasInlineSuggestion', "Plays a sound when the active line has an inline suggestion."), - ...soundDeprecatedFeatureBase - }, - 'audioCues.lineHasError': { - 'description': localize('audioCues.lineHasError', "Plays a sound when the active line has an error."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.lineHasFoldedArea': { - 'description': localize('audioCues.lineHasFoldedArea', "Plays a sound when the active line has a folded area that can be unfolded."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.lineHasWarning': { - 'description': localize('audioCues.lineHasWarning', "Plays a sound when the active line has a warning."), - ...soundDeprecatedFeatureBase, - default: 'off', - }, - 'audioCues.onDebugBreak': { - 'description': localize('audioCues.onDebugBreak', "Plays a sound when the debugger stopped on a breakpoint."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.noInlayHints': { - 'description': localize('audioCues.noInlayHints', "Plays a sound when trying to read a line with inlay hints that has no inlay hints."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.taskCompleted': { - 'description': localize('audioCues.taskCompleted', "Plays a sound when a task is completed."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.taskFailed': { - 'description': localize('audioCues.taskFailed', "Plays a sound when a task fails (non-zero exit code)."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.terminalCommandFailed': { - 'description': localize('audioCues.terminalCommandFailed', "Plays a sound when a terminal command fails (non-zero exit code)."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.terminalQuickFix': { - 'description': localize('audioCues.terminalQuickFix', "Plays a sound when terminal Quick Fixes are available."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.terminalBell': { - 'description': localize('audioCues.terminalBell', "Plays a sound when the terminal bell is ringing."), - ...soundDeprecatedFeatureBase, - default: 'on' - }, - 'audioCues.diffLineInserted': { - 'description': localize('audioCues.diffLineInserted', "Plays a sound when the focus moves to an inserted line in Accessible Diff Viewer mode or to the next/previous change."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.diffLineDeleted': { - 'description': localize('audioCues.diffLineDeleted', "Plays a sound when the focus moves to a deleted line in Accessible Diff Viewer mode or to the next/previous change."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.diffLineModified': { - 'description': localize('audioCues.diffLineModified', "Plays a sound when the focus moves to a modified line in Accessible Diff Viewer mode or to the next/previous change."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.notebookCellCompleted': { - 'description': localize('audioCues.notebookCellCompleted', "Plays a sound when a notebook cell execution is successfully completed."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.notebookCellFailed': { - 'description': localize('audioCues.notebookCellFailed', "Plays a sound when a notebook cell execution fails."), - ...soundDeprecatedFeatureBase, - }, - 'audioCues.chatRequestSent': { - 'description': localize('audioCues.chatRequestSent', "Plays a sound when a chat request is made."), - ...soundDeprecatedFeatureBase, - default: 'off' - }, - 'audioCues.chatResponsePending': { - 'description': localize('audioCues.chatResponsePending', "Plays a sound on loop while the response is pending."), - ...soundDeprecatedFeatureBase, - default: 'auto' - }, - 'audioCues.chatResponseReceived': { - 'description': localize('audioCues.chatResponseReceived', "Plays a sound on loop while the response has been received."), - ...soundDeprecatedFeatureBase, - default: 'off' - }, - 'audioCues.clear': { - 'description': localize('audioCues.clear', "Plays a sound when a feature is cleared (for example, the terminal, Debug Console, or Output channel). When this is disabled, an ARIA alert will announce 'Cleared'."), - ...soundDeprecatedFeatureBase, - default: 'off' - }, - 'audioCues.save': { - 'markdownDescription': localize('audioCues.save', "Plays a sound when a file is saved. Also see {0}", '`#accessibility.alert.save#`'), - 'type': 'string', - 'enum': ['userGesture', 'always', 'never'], - 'default': 'never', - 'enumDescriptions': [ - localize('audioCues.save.userGesture', "Plays the audio cue when a user explicitly saves a file."), - localize('audioCues.save.always', "Plays the audio cue whenever a file is saved, including auto save."), - localize('audioCues.save.never', "Never plays the audio cue.") - ], - tags: ['accessibility'], - markdownDeprecationMessage - }, - 'audioCues.format': { - 'markdownDescription': localize('audioCues.format', "Plays a sound when a file or notebook is formatted. Also see {0}", '`#accessibility.alert.format#`'), - 'type': 'string', - 'enum': ['userGesture', 'always', 'never'], - 'default': 'never', - 'enumDescriptions': [ - localize('audioCues.format.userGesture', "Plays the audio cue when a user explicitly formats a file."), - localize('audioCues.format.always', "Plays the audio cue whenever a file is formatted, including if it is set to format on save, type, or, paste, or run of a cell."), - localize('audioCues.format.never', "Never plays the audio cue.") - ], - tags: ['accessibility'], - markdownDeprecationMessage - }, - }, - }); -} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 8bb76ed3bd1ea..455d4f15b1744 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -370,7 +370,7 @@ export function registerChatCodeBlockActions() { const editorService = accessor.get(IEditorService); const chatService = accessor.get(IChatService); - editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined }); + editorService.openEditor({ contents: context.code, languageId: context.languageId, resource: undefined } satisfies IUntitledTextResourceEditorInput); if (isResponseVM(context.element)) { chatService.notifyUserAction({ diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 20c87e73203ce..e6d70783da26d 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -269,7 +269,7 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { .filter(a => a.locations.includes(ChatAgentLocation.Panel)) .map(async a => { const agentWithLeader = `${chatAgentLeader}${a.name}`; - const actionArg: IChatExecuteActionContext = { inputValue: `${agentWithLeader} ${a.metadata.sampleRequest}` }; + const actionArg: IChatExecuteActionContext = { inputValue: `${agentWithLeader} ${a.metadata.sampleRequest ?? ''}` }; const urlSafeArg = encodeURIComponent(JSON.stringify(actionArg)); const description = a.description ? `- ${a.description}` : ''; const agentLine = `* [\`${agentWithLeader}\`](command:${SubmitAction.ID}?${urlSafeArg}) ${description}`; diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 641ebdb8407bd..ed6193be46ac1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -619,6 +619,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer>this.instantiationService.createInstance( - WorkbenchCompressibleAsyncDataTree, + const tree = this.instantiationService.createInstance( + WorkbenchCompressibleAsyncDataTree, 'ChatListRenderer', container, new ChatListTreeDelegate(), @@ -1313,8 +1318,8 @@ class ContentReferencesListPool extends Disposable { const container = $('.chat-used-context-list'); this._register(createFileIconThemableTreeContainerScope(container, this.themeService)); - const list = >this.instantiationService.createInstance( - WorkbenchList, + const list = this.instantiationService.createInstance( + WorkbenchList, 'ChatListRenderer', container, new ContentReferencesListDelegate(), diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts index e419f12a0ff26..3ba58f37bf98a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts @@ -54,6 +54,10 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi description: localize('chatCommandSticky', "Whether invoking the command puts the chat into a persistent mode, where the command is automatically added to the chat input for the next message."), type: 'boolean' }, + sampleRequest: { + description: localize('chatSampleRequest', "When the user clicks this participant in `/help`, this text will be submitted to the participant."), + type: 'string' + }, defaultImplicitVariables: { markdownDescription: '**Only** allowed for extensions that have the `chatParticipantAdditions` proposal. The names of the variables that are invoked by default', type: 'array', @@ -83,7 +87,7 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi type: 'string' }, sampleRequest: { - description: localize('chatCommandSampleRequest', "When the user clicks this command in `/help`, this text will be submitted to this participant."), + description: localize('chatCommandSampleRequest', "When the user clicks this command in `/help`, this text will be submitted to the participant."), type: 'string' }, isSticky: { @@ -209,6 +213,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { description: providerDescriptor.description, metadata: { isSticky: providerDescriptor.isSticky, + sampleRequest: providerDescriptor.sampleRequest, }, name: providerDescriptor.name, isDefault: providerDescriptor.isDefault, diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 9058da7ea036d..d0a0a088e9c44 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -661,7 +661,6 @@ } .interactive-item-container .rendered-markdown.progress-step > p .codicon.codicon-check { - font-size: 14px; color: var(--vscode-debugIcon-startForeground) !important; } diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index b34f77970879b..6c400c395acce 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -227,10 +227,14 @@ export class Response implements IResponse { return part.command.title; } else if (part.kind === 'textEditGroup') { return ''; + } else if (part.kind === 'progressMessage') { + return ''; } else { return part.content.value; } - }).join('\n\n'); + }) + .filter(s => s.length > 0) + .join('\n\n'); if (!quiet) { this._onDidChangeValue.fire(); diff --git a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts index 3a190bf172b4d..4c4449bbaf92b 100644 --- a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts @@ -20,6 +20,7 @@ export interface IRawChatParticipantContribution { description?: string; isDefault?: boolean; isSticky?: boolean; + sampleRequest?: string; commands?: IRawChatCommandContribution[]; defaultImplicitVariables?: string[]; locations?: RawChatParticipantLocation[]; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index d2ac621f3b5a1..f937415cf67d3 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Action } from 'vs/base/common/actions'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { ErrorNoTelemetry } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; @@ -13,9 +14,10 @@ import { revive } from 'vs/base/common/marshalling'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { Progress } from 'vs/platform/progress/common/progress'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -145,7 +147,9 @@ export class ChatService extends Disposable implements IChatService { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IChatSlashCommandService private readonly chatSlashCommandService: IChatSlashCommandService, @IChatVariablesService private readonly chatVariablesService: IChatVariablesService, - @IChatAgentService private readonly chatAgentService: IChatAgentService + @IChatAgentService private readonly chatAgentService: IChatAgentService, + @INotificationService private readonly notificationService: INotificationService, + @ICommandService private readonly commandService: ICommandService, ) { super(); @@ -349,6 +353,17 @@ export class ChatService extends Disposable implements IChatService { const defaultAgent = this.chatAgentService.getActivatedAgents().find(agent => agent.id === defaultAgentData.id); if (!defaultAgent) { // Should have been registered during activation above! + this.notificationService.notify({ + severity: Severity.Error, + message: localize('chatFailErrorMessage', "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date."), + actions: { + primary: [ + new Action('showExtension', localize('action.showExtension', "Show Extension"), undefined, true, () => { + return this.commandService.executeCommand('workbench.extensions.action.showExtensionsWithIds', ['GitHub.copilot-chat']); + }) + ] + } + }); throw new ErrorNoTelemetry('No default agent registered'); } const welcomeMessage = model.welcomeMessage ? undefined : await defaultAgent.provideWelcomeMessage?.(model.initialLocation, token) ?? undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 687e8f390a5b9..8a39c4d2e63de 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -17,6 +17,7 @@ import { IParsedChatRequest } from 'vs/workbench/contrib/chat/common/chatParserT import { IChatCommandButton, IChatContentReference, IChatFollowup, IChatProgressMessage, IChatResponseErrorDetails, IChatResponseProgressFileTreeData, IChatUsedContext, InteractiveSessionVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; import { CodeBlockModelCollection } from './codeBlockModelCollection'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; export function isRequestVM(item: unknown): item is IChatRequestViewModel { return !!item && typeof item === 'object' && 'message' in item; @@ -74,6 +75,7 @@ export interface IChatResponseMarkdownRenderData { renderedWordCount: number; lastRenderTime: number; isFullyRendered: boolean; + originalMarkdown: IMarkdownString; } export interface IChatProgressMessageRenderData { diff --git a/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts b/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts index 10b5b660fd4ee..5e94b169a0647 100644 --- a/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/voiceChat.test.ts @@ -15,7 +15,7 @@ import { ChatAgentLocation, IChatAgent, IChatAgentCommand, IChatAgentData, IChat import { IChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { IChatProgress, IChatFollowup } from 'vs/workbench/contrib/chat/common/chatService'; import { IVoiceChatSessionOptions, IVoiceChatTextEvent, VoiceChatService } from 'vs/workbench/contrib/chat/common/voiceChat'; -import { ISpeechProvider, ISpeechService, ISpeechToTextEvent, ISpeechToTextSession, KeywordRecognitionStatus, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; +import { ISpeechProvider, ISpeechService, ISpeechToTextEvent, ISpeechToTextSession, ITextToSpeechSession, KeywordRecognitionStatus, SpeechToTextStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; suite('VoiceChat', () => { @@ -75,6 +75,7 @@ suite('VoiceChat', () => { readonly hasSpeechProvider = true; readonly hasActiveSpeechToTextSession = false; + readonly hasActiveTextToSpeechSession = false; readonly hasActiveKeywordRecognition = false; registerSpeechProvider(identifier: string, provider: ISpeechProvider): IDisposable { throw new Error('Method not implemented.'); } @@ -87,6 +88,16 @@ suite('VoiceChat', () => { }; } + onDidStartTextToSpeechSession = Event.None; + onDidEndTextToSpeechSession = Event.None; + + async createTextToSpeechSession(token: CancellationToken): Promise { + return { + onDidChange: Event.None, + synthesize: async () => { } + }; + } + onDidStartKeywordRecognition = Event.None; onDidEndKeywordRecognition = Event.None; recognizeKeyword(token: CancellationToken): Promise { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts index 87d153a08df77..7151a0ffa5d01 100644 --- a/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts +++ b/src/vs/workbench/contrib/debug/browser/debugConfigurationManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct, flatten } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import { sequence } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Emitter, Event } from 'vs/base/common/event'; @@ -258,7 +258,7 @@ export class ConfigurationManager implements IConfigurationManager { }); const nestedPicks = await Promise.all(picks); - const items = flatten(nestedPicks); + const items = nestedPicks.flat(); input.items = items; input.busy = false; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index d7e5dc80a3cf5..c5db527c52649 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -111,7 +111,7 @@ export class DebugService implements IDebugService { @ICommandService private readonly commandService: ICommandService, @IQuickInputService private readonly quickInputService: IQuickInputService, @IWorkspaceTrustRequestService private readonly workspaceTrustRequestService: IWorkspaceTrustRequestService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, ) { this.breakpointsToSendOnResourceSaved = new Set(); @@ -200,6 +200,13 @@ export class DebugService implements IDebugService { } })); + this.disposables.add(extensionService.onWillStop(evt => { + evt.veto( + this.stopSession(undefined).then(() => false), + nls.localize('stoppingDebug', 'Stopping debug sessions...'), + ); + })); + this.initContextKeys(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts index 5df2308f69154..075791be12aef 100644 --- a/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/baseDebugView.test.ts @@ -31,7 +31,7 @@ suite('Debug - Base Debug View', () => { * Instantiate services for use by the functions being tested. */ setup(() => { - const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposables); linkDetector = instantiationService.createInstance(LinkDetector); }); diff --git a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts index c0603a64412ee..f385cbbb7b3de 100644 --- a/src/vs/workbench/contrib/debug/test/browser/repl.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/repl.test.ts @@ -239,9 +239,9 @@ suite('Debug - REPL', () => { const repl = new ReplModel(configurationService); const replFilter = new ReplFilter(); - const getFilteredElements = () => { + const getFilteredElements = (): ReplOutputElement[] => { const elements = repl.getReplElements(); - return elements.filter(e => { + return elements.filter((e): e is ReplOutputElement => { const filterResult = replFilter.filter(e, TreeVisibility.Visible); return filterResult === true || filterResult === TreeVisibility.Visible; }); @@ -253,19 +253,19 @@ suite('Debug - REPL', () => { repl.appendToRepl(session, { output: 'fourth line\n', sev: severity.Info }); replFilter.filterQuery = 'first'; - const r1 = getFilteredElements(); + const r1 = getFilteredElements(); assert.strictEqual(r1.length, 1); assert.strictEqual(r1[0].value, 'first line\n'); replFilter.filterQuery = '!first'; - const r2 = getFilteredElements(); + const r2 = getFilteredElements(); assert.strictEqual(r1.length, 1); assert.strictEqual(r2[0].value, 'second line\n'); assert.strictEqual(r2[1].value, 'third line\n'); assert.strictEqual(r2[2].value, 'fourth line\n'); replFilter.filterQuery = 'first, line'; - const r3 = getFilteredElements(); + const r3 = getFilteredElements(); assert.strictEqual(r3.length, 4); assert.strictEqual(r3[0].value, 'first line\n'); assert.strictEqual(r3[1].value, 'second line\n'); @@ -273,22 +273,22 @@ suite('Debug - REPL', () => { assert.strictEqual(r3[3].value, 'fourth line\n'); replFilter.filterQuery = 'line, !second'; - const r4 = getFilteredElements(); + const r4 = getFilteredElements(); assert.strictEqual(r4.length, 3); assert.strictEqual(r4[0].value, 'first line\n'); assert.strictEqual(r4[1].value, 'third line\n'); assert.strictEqual(r4[2].value, 'fourth line\n'); replFilter.filterQuery = '!second, line'; - const r4_same = getFilteredElements(); + const r4_same = getFilteredElements(); assert.strictEqual(r4.length, r4_same.length); replFilter.filterQuery = '!line'; - const r5 = getFilteredElements(); + const r5 = getFilteredElements(); assert.strictEqual(r5.length, 0); replFilter.filterQuery = 'smth'; - const r6 = getFilteredElements(); + const r6 = getFilteredElements(); assert.strictEqual(r6.length, 0); }); }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index f5144870fa71d..1fd2e6152b3fb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -63,7 +63,6 @@ import { isVirtualWorkspace } from 'vs/platform/workspace/common/virtualWorkspac import { escapeMarkdownSyntaxTokens, IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { ViewContainerLocation } from 'vs/workbench/common/views'; -import { flatten } from 'vs/base/common/arrays'; import { fromNow } from 'vs/base/common/date'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { getLocale } from 'vs/platform/languagePacks/common/languagePacks'; @@ -251,7 +250,7 @@ export class ActionWithDropDownAction extends ExtensionAction { private readonly actionsGroups: ExtensionAction[][], ) { super(id, label); - this.extensionActions = flatten(actionsGroups); + this.extensionActions = actionsGroups.flat(); this.update(); this._register(Event.any(...this.extensionActions.map(a => a.onDidChange))(() => this.update(true))); this.extensionActions.forEach(a => this._register(a)); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index f6451dd55c0b5..6b0e7319bc160 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -31,7 +31,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ViewPane, IViewPaneOptions, ViewPaneShowActions } from 'vs/workbench/browser/parts/views/viewPane'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { coalesce, distinct, flatten } from 'vs/base/common/arrays'; +import { coalesce, distinct } from 'vs/base/common/arrays'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -978,12 +978,12 @@ export class ExtensionsListView extends ViewPane { .map(extensionId => isString(extensionId) ? extensionId.toLowerCase() : extensionId); return distinct( - flatten(await Promise.all([ + (await Promise.all([ // Order is important this.extensionRecommendationsService.getImportantRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() - ])).filter(extensionId => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) + ])).flat().filter(extensionId => !local.includes(extensionId.toLowerCase()) && !workspaceRecommendations.includes(extensionId.toLowerCase()) ), extensionId => extensionId.toLowerCase()); } @@ -993,13 +993,13 @@ export class ExtensionsListView extends ViewPane { const localExtensionIds = localExtensions.map(e => e.identifier.id.toLowerCase()); const allRecommendations = distinct( - flatten(await Promise.all([ + (await Promise.all([ // Order is important this.getWorkspaceRecommendations(), this.extensionRecommendationsService.getImportantRecommendations(), this.extensionRecommendationsService.getFileBasedRecommendations(), this.extensionRecommendationsService.getOtherRecommendations() - ])).filter(extensionId => { + ])).flat().filter(extensionId => { if (isString(extensionId)) { return !localExtensionIds.includes(extensionId.toLowerCase()); } diff --git a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts index eda5f43eac0ba..586c9f83b2f09 100644 --- a/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts +++ b/src/vs/workbench/contrib/extensions/browser/workspaceRecommendations.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { EXTENSION_IDENTIFIER_PATTERN } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { distinct, equals, flatten } from 'vs/base/common/arrays'; +import { distinct, equals } from 'vs/base/common/arrays'; import { ExtensionRecommendations, ExtensionRecommendation } from 'vs/workbench/contrib/extensions/browser/extensionRecommendations'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ExtensionRecommendationReason } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; @@ -150,7 +150,7 @@ export class WorkspaceRecommendations extends ExtensionRecommendations { const invalidExtensions: string[] = []; let message = ''; - const allRecommendations = distinct(flatten(contents.map(({ recommendations }) => recommendations || []))); + const allRecommendations = distinct(contents.flatMap(({ recommendations }) => recommendations || [])); const regEx = new RegExp(EXTENSION_IDENTIFIER_PATTERN); for (const extensionId of allRecommendations) { if (regEx.test(extensionId)) { diff --git a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts index b74676a210ff4..d916401a6a713 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionQuery.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionQuery.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten } from 'vs/base/common/arrays'; import { EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; export class Query { @@ -26,19 +25,18 @@ export class Query { const hasSort = subcommands.sort.some(subcommand => queryContains(`@sort:${subcommand}`)); const hasCategory = subcommands.category.some(subcommand => queryContains(`@category:${subcommand}`)); - return flatten( - commands.map(command => { - if (hasSort && command === 'sort' || hasCategory && command === 'category') { - return []; - } - if (command in subcommands) { - return (subcommands as Record)[command] - .map(subcommand => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`); - } - else { - return queryContains(`@${command}`) ? [] : [`@${command} `]; - } - })); + return commands.flatMap(command => { + if (hasSort && command === 'sort' || hasCategory && command === 'category') { + return []; + } + if (command in subcommands) { + return (subcommands as Record)[command] + .map(subcommand => `@${command}:${subcommand}${subcommand === '' ? '' : ' '}`); + } + else { + return queryContains(`@${command}`) ? [] : [`@${command} `]; + } + }); } static parse(value: string): Query { 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 057adbf762273..5bf79cc39715b 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -58,6 +58,8 @@ import { ChatWidgetHistoryService, IChatWidgetHistoryService } from 'vs/workbenc import { IHoverService } from 'vs/platform/hover/browser/hover'; import { NullHoverService } from 'vs/platform/hover/test/browser/nullHoverService'; import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; suite('InteractiveChatController', function () { class TestController extends InlineChatController { @@ -136,6 +138,7 @@ suite('InteractiveChatController', function () { [IInlineChatService, new SyncDescriptor(InlineChatServiceImpl)], [IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)], [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)], + [ICommandService, new SyncDescriptor(TestCommandService)], [IInlineChatSavingService, new class extends mock() { override markChanged(session: Session): void { // noop diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index 043ea8c80abcb..b28ae6f4ad7ef 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -55,6 +55,8 @@ import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { TestExtensionService, TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; import { IChatAgentService, ChatAgentService, ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ChatVariablesService } from 'vs/workbench/contrib/chat/browser/chatVariables'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; suite('InlineChatSession', function () { @@ -89,6 +91,7 @@ suite('InlineChatSession', function () { [IContextKeyService, contextKeyService], [IDiffProviderFactoryService, new SyncDescriptor(TestDiffProviderFactoryService)], [IInlineChatSessionService, new SyncDescriptor(InlineChatSessionServiceImpl)], + [ICommandService, new SyncDescriptor(TestCommandService)], [IInlineChatSavingService, new class extends mock() { override markChanged(session: Session): void { // noop diff --git a/src/vs/workbench/contrib/markers/browser/markersModel.ts b/src/vs/workbench/contrib/markers/browser/markersModel.ts index ad0e29114f2db..0e4fbdfb8024f 100644 --- a/src/vs/workbench/contrib/markers/browser/markersModel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersModel.ts @@ -7,7 +7,7 @@ import { basename, extUri } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Range, IRange } from 'vs/editor/common/core/range'; import { IMarker, MarkerSeverity, IRelatedInformation, IMarkerData } from 'vs/platform/markers/common/markers'; -import { isNonEmptyArray, flatten } from 'vs/base/common/arrays'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; import { ResourceMap } from 'vs/base/common/map'; import { Emitter, Event } from 'vs/base/common/event'; import { Hasher } from 'vs/base/common/hash'; @@ -52,7 +52,7 @@ export class ResourceMarkers { get markers(): readonly Marker[] { if (!this._cachedMarkers) { - this._cachedMarkers = flatten([...this._markersMap.values()]).sort(ResourceMarkers._compareMarkers); + this._cachedMarkers = [...this._markersMap.values()].flat().sort(ResourceMarkers._compareMarkers); } return this._cachedMarkers; } diff --git a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts index 74fa00a81ae6a..169dc418962fa 100644 --- a/src/vs/workbench/contrib/markers/browser/markersViewActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersViewActions.ts @@ -59,7 +59,7 @@ export class MarkersFilters extends Disposable { set excludedFiles(filesExclude: boolean) { if (this._excludedFiles.get() !== filesExclude) { this._excludedFiles.set(filesExclude); - this._onDidChange.fire({ excludedFiles: true }); + this._onDidChange.fire({ excludedFiles: true }); } } @@ -70,7 +70,7 @@ export class MarkersFilters extends Disposable { set activeFile(activeFile: boolean) { if (this._activeFile.get() !== activeFile) { this._activeFile.set(activeFile); - this._onDidChange.fire({ activeFile: true }); + this._onDidChange.fire({ activeFile: true }); } } @@ -81,7 +81,7 @@ export class MarkersFilters extends Disposable { set showWarnings(showWarnings: boolean) { if (this._showWarnings.get() !== showWarnings) { this._showWarnings.set(showWarnings); - this._onDidChange.fire({ showWarnings: true }); + this._onDidChange.fire({ showWarnings: true }); } } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts index a7d5280085fde..c73973e433ee5 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts @@ -450,7 +450,7 @@ registerAction2(class ChangeCellLanguageAction extends NotebookCellAction{ + const item: ILanguagePickInput = { label: languageName, iconClasses: getIconClasses(modelService, languageService, this.getFakeResource(languageName, languageService)), description, diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint.ts index 25b3967c84628..04d42f4ad1eb4 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/foldedCellHint.ts @@ -14,7 +14,7 @@ import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewM import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { executingStateIcon } from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, NotebookCellExecutionState } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { MutableDisposable } from 'vs/base/common/lifecycle'; export class FoldedCellHint extends CellContentPart { @@ -49,7 +49,13 @@ export class FoldedCellHint extends CellContentPart { const idx = this._notebookEditor.getViewModel().getCellIndex(element); const length = this._notebookEditor.getViewModel().getFoldedLength(idx); - DOM.reset(this._container, this.getRunFoldedSectionButton({ start: idx, end: idx + length + 1 }), this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element)); + const runSectionButton = this.getRunFoldedSectionButton({ start: idx, end: idx + length + 1 }); + if (!runSectionButton) { + DOM.reset(this._container, this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element)); + } else { + DOM.reset(this._container, runSectionButton, this.getHiddenCellsLabel(length), this.getHiddenCellHintButton(element)); + } + DOM.show(this._container); const foldHintTop = element.layoutInfo.previewHeight; @@ -83,10 +89,16 @@ export class FoldedCellHint extends CellContentPart { return expandIcon; } - private getRunFoldedSectionButton(range: ICellRange): HTMLElement { + private getRunFoldedSectionButton(range: ICellRange): HTMLElement | undefined { const runAllContainer = DOM.$('span.folded-cell-run-section-button'); const cells = this._notebookEditor.getCellsInRange(range); + // Check if any cells are code cells, if not, we won't show the run button + const hasCodeCells = cells.some(cell => cell.cellKind === CellKind.Code); + if (!hasCodeCells) { + return undefined; + } + const isRunning = cells.some(cell => { const cellExecution = this._notebookExecutionStateService.getCellExecution(cell.uri); return cellExecution && cellExecution.state === NotebookCellExecutionState.Executing; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts index ded82f216173b..00157aaa86ce5 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineEntryFactory.ts @@ -45,7 +45,7 @@ function getMarkdownHeadersInCellFallbackToHtmlTags(fullContent: string) { export class NotebookOutlineEntryFactory { private cellOutlineEntryCache: Record = {}; - private readonly cachedMarkdownOutlineEntries = new WeakMap(); + private readonly cachedMarkdownOutlineEntries = new WeakMap(); constructor( private readonly executionStateService: INotebookExecutionStateService ) { } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider.ts index 999113306e6c9..1ac843597abab 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProvider.ts @@ -50,7 +50,7 @@ export class NotebookCellOutlineProvider { } private readonly _outlineEntryFactory: NotebookOutlineEntryFactory; - private readonly delayedOutlineRecompute: Delayer;; + private readonly delayedOutlineRecompute: Delayer; constructor( private readonly _editor: INotebookEditor, private readonly _target: OutlineTarget, @@ -65,7 +65,7 @@ export class NotebookCellOutlineProvider { const delayerRecomputeActive = this._disposables.add(new Delayer(200)); this._disposables.add(_editor.onDidChangeSelection(() => { delayerRecomputeActive.trigger(() => this._recomputeActive()); - }, this)) + }, this)); // .3s of a delay is sufficient, 100-200s is too quick and will unnecessarily block the ui thread. // Given we're only updating the outline when the user types, we can afford to wait a bit. @@ -115,10 +115,10 @@ export class NotebookCellOutlineProvider { if (!this._entries.length) { this._recomputeState(); } - } + }; this._disposables.add(this._editor.onDidChangeModel(monitorModelChanges)); monitorModelChanges(); - this._recomputeState() + this._recomputeState(); } dispose(): void { @@ -260,7 +260,10 @@ export class NotebookCellOutlineProvider { } })); - this._recomputeActive(); + const { changeEventTriggered } = this._recomputeActive(); + if (!changeEventTriggered) { + this._onDidChange.fire({}); + } } private _recomputeActive(): { changeEventTriggered: boolean } { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProviderFactory.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProviderFactory.ts index d5908204c940b..54411bcd29679 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProviderFactory.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookOutlineProviderFactory.ts @@ -24,7 +24,7 @@ class NotebookCellOutlineProviderReferenceCollection extends ReferenceCollection export const INotebookCellOutlineProviderFactory = createDecorator('INotebookCellOutlineProviderFactory'); export interface INotebookCellOutlineProviderFactory { - getOrCreate(editor: INotebookEditor, target: OutlineTarget): IReference + getOrCreate(editor: INotebookEditor, target: OutlineTarget): IReference; } export class NotebookCellOutlineProviderFactory implements INotebookCellOutlineProviderFactory { diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 521b8d908f735..52e6889c4bc76 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -202,7 +202,7 @@ export class NotebookStickyScroll extends Disposable { } private init() { - const { object: notebookOutlineReference } = this.notebookOutlineReference = this.instantiationService.invokeFunction((accessor) => accessor.get(INotebookCellOutlineProviderFactory).getOrCreate(this.notebookEditor, OutlineTarget.QuickPick)); + const { object: notebookOutlineReference } = this.notebookOutlineReference = this.instantiationService.invokeFunction((accessor) => accessor.get(INotebookCellOutlineProviderFactory).getOrCreate(this.notebookEditor, OutlineTarget.OutlinePane)); this._register(this.notebookOutlineReference); this.updateContent(computeContent(this.notebookEditor, this.notebookCellList, notebookOutlineReference.entries, this.getCurrentStickyHeight())); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index 6bee52d8257ab..72f128ed81f6c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -149,7 +149,7 @@ export const tocData: ITOCEntry = { { id: 'features/accessibilitySignals', label: localize('accessibility.signals', 'Accessibility Signals'), - settings: ['accessibility.signals.*', 'audioCues.*'] + settings: ['accessibility.signals.*', 'accessibility.signalOptions.*'] }, { id: 'features/accessibility', diff --git a/src/vs/workbench/contrib/remote/browser/urlFinder.ts b/src/vs/workbench/contrib/remote/browser/urlFinder.ts index 7fed46eb3a704..e2995bb5f2d5b 100644 --- a/src/vs/workbench/contrib/remote/browser/urlFinder.ts +++ b/src/vs/workbench/contrib/remote/browser/urlFinder.ts @@ -7,9 +7,9 @@ import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/termin import { Emitter } from 'vs/base/common/event'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { IDebugService, IDebugSession, IReplElement } from 'vs/workbench/contrib/debug/common/debug'; +import { removeAnsiEscapeCodes } from 'vs/base/common/strings'; export class UrlFinder extends Disposable { - private static readonly terminalCodesRegex = /(?:\u001B|\u009B)[\[\]()#;?]*(?:(?:(?:[a-zA-Z0-9]*(?:;[a-zA-Z0-9]*)*)?\u0007)|(?:(?:\d{1,4}(?:;\d{0,4})*)?[0-9A-PR-TZcf-ntqry=><~]))/g; /** * Local server url pattern matching following urls: * http://localhost:3000/ - commonly used across multiple frameworks @@ -99,7 +99,7 @@ export class UrlFinder extends Disposable { private processData(data: string) { // strip ANSI terminal codes - data = data.replace(UrlFinder.terminalCodesRegex, ''); + data = removeAnsiEscapeCodes(data); const urlMatches = data.match(UrlFinder.localUrlRegex) || []; if (urlMatches && urlMatches.length > 0) { urlMatches.forEach((match) => { diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 47a1db3d92c89..7537f80f534cd 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -58,10 +58,6 @@ import { AccessibilitySignal, IAccessibilitySignalService } from 'vs/platform/ac import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IQuickDiffService, QuickDiff } from 'vs/workbench/contrib/scm/common/quickDiff'; import { IQuickDiffSelectItem, SwitchQuickDiffBaseAction, SwitchQuickDiffViewItem } from 'vs/workbench/contrib/scm/browser/dirtyDiffSwitcher'; -import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; -import { IEditorControl } from 'vs/workbench/common/editor'; -import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; -import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; class DiffActionRunner extends ActionRunner { @@ -1653,28 +1649,8 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor this.enabled = false; } - private getVisibleEditorControls(): IEditorControl[] { - const controls: IEditorControl[] = []; - const addControl = (control: IEditorControl | undefined) => { - if (control) { - controls.push(control); - } - }; - - for (const editorPane of this.editorService.visibleEditorPanes) { - if (editorPane instanceof TextDiffEditor || editorPane instanceof TextFileEditor) { - addControl(editorPane.getControl()); - } else if (editorPane instanceof SideBySideEditor) { - addControl(editorPane.getPrimaryEditorPane()?.getControl()); - addControl(editorPane.getSecondaryEditorPane()?.getControl()); - } - } - return controls; - } - private onEditorsChanged(): void { - const visibleControls = this.getVisibleEditorControls(); - for (const editor of visibleControls) { + for (const editor of this.editorService.visibleTextEditorControls) { if (isCodeEditor(editor)) { const textModel = editor.getModel(); const controller = DirtyDiffController.get(editor); @@ -1700,7 +1676,7 @@ export class DirtyDiffWorkbenchController extends Disposable implements ext.IWor for (const [uri, item] of this.items) { for (const editorId of item.keys()) { - if (!this.getVisibleEditorControls().find(editor => isCodeEditor(editor) && editor.getModel()?.uri.toString() === uri.toString() && editor.getId() === editorId)) { + if (!this.editorService.visibleTextEditorControls.find(editor => isCodeEditor(editor) && editor.getModel()?.uri.toString() === uri.toString() && editor.getId() === editorId)) { if (item.has(editorId)) { const dirtyDiffItem = item.get(editorId); dirtyDiffItem?.dispose(); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 7ffaa617d4ab3..bdff99acf7988 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -39,7 +39,6 @@ import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; -import { flatten } from 'vs/base/common/arrays'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; @@ -589,7 +588,7 @@ class RepositoryPaneActionRunner extends ActionRunner { const selection = this.getSelectedResources(); const contextIsSelected = selection.some(s => s === context); const actualContext = contextIsSelected ? selection : [context]; - const args = flatten(actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e])); + const args = actualContext.map(e => ResourceTree.isResourceNode(e) ? ResourceTree.collect(e) : [e]).flat(); await action.run(...args); } } diff --git a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts index 2051b1d13741b..bd4676d881c63 100644 --- a/src/vs/workbench/contrib/search/browser/searchActionsFind.ts +++ b/src/vs/workbench/contrib/search/browser/searchActionsFind.ts @@ -88,7 +88,7 @@ registerAction2(class ExpandSelectedTreeCommandAction extends Action2 { menu: [{ id: MenuId.SearchContext, when: ContextKeyExpr.and( - ContextKeyExpr.or(Constants.SearchContext.FileFocusKey, Constants.SearchContext.FolderFocusKey), + Constants.SearchContext.FolderFocusKey, Constants.SearchContext.HasSearchResults ), group: 'search', @@ -97,8 +97,8 @@ registerAction2(class ExpandSelectedTreeCommandAction extends Action2 { }); } - override async run(accessor: any): Promise { - await expandSelectSubtree(accessor); + override run(accessor: any) { + expandSelectSubtree(accessor); } }); diff --git a/src/vs/workbench/contrib/search/browser/searchModel.ts b/src/vs/workbench/contrib/search/browser/searchModel.ts index 97a018558d74d..8d982d4737e5b 100644 --- a/src/vs/workbench/contrib/search/browser/searchModel.ts +++ b/src/vs/workbench/contrib/search/browser/searchModel.ts @@ -2154,7 +2154,7 @@ export class SearchModel extends Disposable { const resolvedNotebookResults = await notebookResult.completeData; tokenSource.dispose(); const searchLength = Date.now() - searchStart; - const resolvedResult = { + const resolvedResult: ISearchComplete = { results: [...allClosedEditorResults.results, ...resolvedNotebookResults.results], messages: [...allClosedEditorResults.messages, ...resolvedNotebookResults.messages], limitHit: allClosedEditorResults.limitHit || resolvedNotebookResults.limitHit, diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts index c017b2db0eda8..62bbb3386a52f 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorSerialization.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce, flatten } from 'vs/base/common/arrays'; +import { coalesce } from 'vs/base/common/arrays'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/searchEditor'; import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; @@ -255,10 +255,9 @@ export const serializeSearchResultForEditor = const allResults = flattenSearchResultSerializations( - flatten( - searchResult.folderMatches().sort(matchComparer) - .map(folderMatch => folderMatch.allDownstreamFileMatches().sort(matchComparer) - .flatMap(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))))); + searchResult.folderMatches().sort(matchComparer) + .map(folderMatch => folderMatch.allDownstreamFileMatches().sort(matchComparer) + .flatMap(fileMatch => fileMatchToSearchResultFormat(fileMatch, labelFormatter))).flat()); return { matchRanges: allResults.matchRanges.map(translateRangeLines(info.length)), diff --git a/src/vs/workbench/contrib/speech/browser/speechService.ts b/src/vs/workbench/contrib/speech/browser/speechService.ts index 94a45671ea3e1..25d5c0ce95198 100644 --- a/src/vs/workbench/contrib/speech/browser/speechService.ts +++ b/src/vs/workbench/contrib/speech/browser/speechService.ts @@ -12,7 +12,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ILogService } from 'vs/platform/log/common/log'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { DeferredPromise } from 'vs/base/common/async'; -import { ISpeechService, ISpeechProvider, HasSpeechProvider, ISpeechToTextSession, SpeechToTextInProgress, IKeywordRecognitionSession, KeywordRecognitionStatus, SpeechToTextStatus, speechLanguageConfigToLanguage, SPEECH_LANGUAGE_CONFIG } from 'vs/workbench/contrib/speech/common/speechService'; +import { ISpeechService, ISpeechProvider, HasSpeechProvider, ISpeechToTextSession, SpeechToTextInProgress, IKeywordRecognitionSession, KeywordRecognitionStatus, SpeechToTextStatus, speechLanguageConfigToLanguage, SPEECH_LANGUAGE_CONFIG, ITextToSpeechSession, TextToSpeechInProgress, TextToSpeechStatus } from 'vs/workbench/contrib/speech/common/speechService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; @@ -126,6 +126,8 @@ export class SpeechService extends Disposable implements ISpeechService { this._onDidChangeHasSpeechProvider.fire(); } + //#region Transcription + private readonly _onDidStartSpeechToTextSession = this._register(new Emitter()); readonly onDidStartSpeechToTextSession = this._onDidStartSpeechToTextSession.event; @@ -236,6 +238,89 @@ export class SpeechService extends Disposable implements ISpeechService { return provider; } + //#endregion + + //#region Synthesizer + + private readonly _onDidStartTextToSpeechSession = this._register(new Emitter()); + readonly onDidStartTextToSpeechSession = this._onDidStartTextToSpeechSession.event; + + private readonly _onDidEndTextToSpeechSession = this._register(new Emitter()); + readonly onDidEndTextToSpeechSession = this._onDidEndTextToSpeechSession.event; + + private _activeTextToSpeechSession: ITextToSpeechSession | undefined = undefined; + get hasActiveTextToSpeechSession() { return !!this._activeTextToSpeechSession; } + + private readonly textToSpeechInProgress = TextToSpeechInProgress.bindTo(this.contextKeyService); + + async createTextToSpeechSession(token: CancellationToken, context: string = 'speech'): Promise { + const provider = await this.getProvider(); + + const session = this._activeTextToSpeechSession = provider.createTextToSpeechSession(token); + + const sessionStart = Date.now(); + let sessionError = false; + + const disposables = new DisposableStore(); + + const onSessionStoppedOrCanceled = () => { + if (session === this._activeTextToSpeechSession) { + this._activeTextToSpeechSession = undefined; + this.textToSpeechInProgress.reset(); + this._onDidEndTextToSpeechSession.fire(); + + type TextToSpeechSessionClassification = { + owner: 'bpasero'; + comment: 'An event that fires when a text to speech session is created'; + context: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Context of the session.' }; + sessionDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Duration of the session.' }; + sessionError: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If speech resulted in error.' }; + }; + type TextToSpeechSessionEvent = { + context: string; + sessionDuration: number; + sessionError: boolean; + }; + this.telemetryService.publicLog2('textToSpeechSession', { + context, + sessionDuration: Date.now() - sessionStart, + sessionError + }); + } + + disposables.dispose(); + }; + + disposables.add(token.onCancellationRequested(() => onSessionStoppedOrCanceled())); + if (token.isCancellationRequested) { + onSessionStoppedOrCanceled(); + } + + disposables.add(session.onDidChange(e => { + switch (e.status) { + case TextToSpeechStatus.Started: + if (session === this._activeTextToSpeechSession) { + this.textToSpeechInProgress.set(true); + this._onDidStartTextToSpeechSession.fire(); + } + break; + case TextToSpeechStatus.Stopped: + onSessionStoppedOrCanceled(); + break; + case TextToSpeechStatus.Error: + this.logService.error(`Speech provider error in text to speech session: ${e.text}`); + sessionError = true; + break; + } + })); + + return session; + } + + //#endregion + + //#region Keyword Recognition + private readonly _onDidStartKeywordRecognition = this._register(new Emitter()); readonly onDidStartKeywordRecognition = this._onDidStartKeywordRecognition.event; @@ -344,4 +429,6 @@ export class SpeechService extends Disposable implements ISpeechService { onSessionStoppedOrCanceled(); } } + + //#endregion } diff --git a/src/vs/workbench/contrib/speech/common/speechService.ts b/src/vs/workbench/contrib/speech/common/speechService.ts index 4f260469983c3..4bd76b641aa42 100644 --- a/src/vs/workbench/contrib/speech/common/speechService.ts +++ b/src/vs/workbench/contrib/speech/common/speechService.ts @@ -16,6 +16,7 @@ export const ISpeechService = createDecorator('speechService'); export const HasSpeechProvider = new RawContextKey('hasSpeechProvider', false, { type: 'string', description: localize('hasSpeechProvider', "A speech provider is registered to the speech service.") }); export const SpeechToTextInProgress = new RawContextKey('speechToTextInProgress', false, { type: 'string', description: localize('speechToTextInProgress', "A speech-to-text session is in progress.") }); +export const TextToSpeechInProgress = new RawContextKey('textToSpeechInProgress', false, { type: 'string', description: localize('textToSpeechInProgress', "A text-to-speech session is in progress.") }); export interface ISpeechProviderMetadata { readonly extension: ExtensionIdentifier; @@ -39,6 +40,23 @@ export interface ISpeechToTextSession { readonly onDidChange: Event; } +export enum TextToSpeechStatus { + Started = 1, + Stopped = 2, + Error = 3 +} + +export interface ITextToSpeechEvent { + readonly status: TextToSpeechStatus; + readonly text?: string; +} + +export interface ITextToSpeechSession { + readonly onDidChange: Event; + + synthesize(text: string): void; +} + export enum KeywordRecognitionStatus { Recognized = 1, Stopped = 2, @@ -62,6 +80,7 @@ export interface ISpeechProvider { readonly metadata: ISpeechProviderMetadata; createSpeechToTextSession(token: CancellationToken, options?: ISpeechToTextSessionOptions): ISpeechToTextSession; + createTextToSpeechSession(token: CancellationToken): ITextToSpeechSession; createKeywordRecognitionSession(token: CancellationToken): IKeywordRecognitionSession; } @@ -86,6 +105,18 @@ export interface ISpeechService { */ createSpeechToTextSession(token: CancellationToken, context?: string): Promise; + readonly onDidStartTextToSpeechSession: Event; + readonly onDidEndTextToSpeechSession: Event; + + readonly hasActiveTextToSpeechSession: boolean; + + /** + * Creates a synthesizer to synthesize speech from text. The returned + * session object provides a method to synthesize text and listen for + * events. + */ + createTextToSpeechSession(token: CancellationToken, context?: string): Promise; + readonly onDidStartKeywordRecognition: Event; readonly onDidEndKeywordRecognition: Event; diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index cd473790977de..1a02a11503320 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -1286,7 +1286,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (type === undefined) { return true; } - const settingValueMap: IStringDictionary = settingValue; + const settingValueMap: IStringDictionary = settingValue as any; return !settingValueMap[type]; } @@ -2574,7 +2574,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private _handleError(err: any): void { let showOutput = true; if (err instanceof TaskError) { - const buildError = err; + const buildError = err; const needsConfig = buildError.code === TaskErrors.NotConfigured || buildError.code === TaskErrors.NoBuildTask || buildError.code === TaskErrors.NoTestTask; const needsTerminate = buildError.code === TaskErrors.RunningTask; if (needsConfig || needsTerminate) { @@ -2592,7 +2592,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._notificationService.notify({ severity: buildError.severity, message: buildError.message }); } } else if (err instanceof Error) { - const error = err; + const error = err; this._notificationService.error(error.message); showOutput = false; } else if (Types.isString(err)) { @@ -3404,7 +3404,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer })]); if (!timeout && ((await entries).length === 1) && this._configurationService.getValue(QUICKOPEN_SKIP_CONFIG)) { - const entry: any = ((await entries)[0]); + const entry: any = (await entries)[0]; if (entry.task) { this._handleSelection(entry); return; diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 5e0eed955c802..c4d6e65f891b1 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -460,7 +460,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { public terminateAll(): Promise { const promises: Promise[] = []; for (const [key, terminalData] of Object.entries(this._activeTasks)) { - const terminal = terminalData.terminal; + const terminal = terminalData?.terminal; if (terminal) { promises.push(new Promise((resolve, reject) => { const onExit = terminal.onExit(() => { diff --git a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts index 12b5058d65a98..d375636a4c949 100644 --- a/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts +++ b/src/vs/workbench/contrib/terminal/browser/baseTerminalBackend.ts @@ -9,11 +9,14 @@ import { Schemas } from 'vs/base/common/network'; import { localize } from 'vs/nls'; import { ICrossVersionSerializedTerminalState, IPtyHostController, ISerializedTerminalState, ITerminalLogService } from 'vs/platform/terminal/common/terminal'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar'; +// HACK: This file should not depend on terminalContrib +// eslint-disable-next-line local/code-import-patterns +import { TerminalDeveloperCommandId } from 'vs/workbench/contrib/terminalContrib/developer/common/terminal.developer'; + export abstract class BaseTerminalBackend extends Disposable { private _isPtyHostUnresponsive: boolean = false; @@ -65,7 +68,7 @@ export abstract class BaseTerminalBackend extends Disposable { text: `$(debug-disconnect) ${localize('ptyHostStatus.short', 'Pty Host')}`, tooltip: localize('nonResponsivePtyHost', "The connection to the terminal's pty host process is unresponsive, terminals may stop working. Click to manually restart the pty host."), ariaLabel: localize('ptyHostStatus.ariaLabel', 'Pty Host is unresponsive'), - command: TerminalCommandId.RestartPtyHost, + command: TerminalDeveloperCommandId.RestartPtyHost, kind: 'warning' }; } diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 index 4e68338d26f5a..9669ee56d97c8 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 @@ -163,7 +163,6 @@ function Set-MappedKeyHandler { } } -$Global:__VSCodeHaltCompletions = $false function Set-MappedKeyHandlers { Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a' Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b' @@ -179,43 +178,55 @@ function Set-MappedKeyHandlers { Send-Completions } - # Suggest trigger characters - Set-PSReadLineKeyHandler -Chord "-" -ScriptBlock { - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("-") - if (!$Global:__VSCodeHaltCompletions) { - Send-Completions - } - } - - Set-PSReadLineKeyHandler -Chord 'F12,y' -ScriptBlock { - $Global:__VSCodeHaltCompletions = $true - } - - Set-PSReadLineKeyHandler -Chord 'F12,z' -ScriptBlock { - $Global:__VSCodeHaltCompletions = $false - } + # TODO: When does this invalidate? Installing a new module could add new commands. We could expose a command to update? Track `(Get-Module).Count`? + # Commands are expensive to complete and send over, do this once for the empty string so we + # don't need to do it each time the user requests. Additionally we also want to do filtering + # and ranking on the client side with the full list of results. + $result = "$([char]0x1b)]633;CompletionsPwshCommands;commands;" + $result += [System.Management.Automation.CompletionCompleters]::CompleteCommand('') | ConvertTo-Json -Compress + $result += "`a" + Write-Host -NoNewLine $result } } function Send-Completions { $commandLine = "" $cursorIndex = 0 - # TODO: Since fuzzy matching exists, should completions be provided only for character after the - # last space and then filter on the client side? That would let you trigger ctrl+space - # anywhere on a word and have full completions available [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$commandLine, [ref]$cursorIndex) $completionPrefix = $commandLine - # Get completions + # Start completions sequence $result = "$([char]0x1b)]633;Completions" - if ($completionPrefix.Length -gt 0) { - # Get and send completions + + # If there is a space in the input, defer to TabExpansion2 as it's more complicated to + # determine what type of completions to use + # `[` is included here as namespace commands are not included in CompleteCommand(''), + # additionally for some reason CompleteVariable('[') causes the prompt to clear and reprint + # multiple times + if ($completionPrefix.Contains(' ') -or $completionPrefix.Contains('[')) { $completions = TabExpansion2 -inputScript $completionPrefix -cursorColumn $cursorIndex if ($null -ne $completions.CompletionMatches) { $result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);" $result += $completions.CompletionMatches | ConvertTo-Json -Compress } } + # If there is no space, get completions using CompletionCompleters as it gives us more + # control and works on the empty string + else { + # Note that CompleteCommand isn't included here as it's expensive + $completions = $( + ([System.Management.Automation.CompletionCompleters]::CompleteFilename($completionPrefix)); + ([System.Management.Automation.CompletionCompleters]::CompleteVariable($completionPrefix)); + ) + if ($null -ne $completions) { + $result += ";$($completions.ReplacementIndex);$($completions.ReplacementLength);$($cursorIndex);" + $result += $completions | ConvertTo-Json -Compress + } else { + $result += ";0;$($completionPrefix.Length);$($completionPrefix.Length);[]" + } + } + + # End completions sequence $result += "`a" Write-Host -NoNewLine $result diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index e28872179e047..c3ee3fb42d7ef 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -513,22 +513,6 @@ .terminal-scroll-highlight.terminal-scroll-highlight-outline { border-color: var(--vscode-focusBorder); } -.hc-black .xterm-find-result-decoration, -.hc-light .xterm-find-result-decoration { - outline-style: dotted !important; -} - -.hc-black .xterm-find-result-decoration, -.hc-light .xterm-find-result-decoration { - outline-style: solid !important; -} - -.xterm-find-active-result-decoration { - outline-style: solid !important; - outline-width: 2px !important; - /* Ensure the active decoration is above the regular decoration */ - z-index: 7 !important; -} .monaco-workbench.hc-black .editor-instance .xterm.focus::before, .monaco-workbench.hc-black .pane-body.integrated-terminal .xterm.focus::before, diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index b9f06d1546f00..96657abf2ac17 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -22,7 +22,7 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { IKeybindings, KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from 'vs/platform/quickinput/common/quickAccess'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ITerminalLogService, TerminalSettingId, WindowsShellType } from 'vs/platform/terminal/common/terminal'; +import { ITerminalLogService, WindowsShellType } from 'vs/platform/terminal/common/terminal'; import { TerminalLogService } from 'vs/platform/terminal/common/terminalLogService'; import { registerTerminalPlatformConfiguration } from 'vs/platform/terminal/common/terminalPlatformConfiguration'; import { EditorPaneDescriptor, IEditorPaneRegistry } from 'vs/workbench/browser/editor'; @@ -56,6 +56,10 @@ import { registerTerminalConfiguration } from 'vs/workbench/contrib/terminal/com import { TerminalContextKeyStrings, TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; +// HACK: This file should not depend on terminalContrib +// eslint-disable-next-line local/code-import-patterns +import { TerminalSuggestSettingId } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration'; + // Register services registerSingleton(ITerminalLogService, TerminalLogService, InstantiationType.Delayed); registerSingleton(ITerminalConfigurationService, TerminalConfigurationService, InstantiationType.Delayed); @@ -206,7 +210,7 @@ registerSendSequenceKeybinding('\x1b[24~d', { // F12,d -> shift+end (SelectLine) mac: { primary: KeyMod.Shift | KeyMod.CtrlCmd | KeyCode.RightArrow } }); registerSendSequenceKeybinding('\x1b[24~e', { // F12,e -> ctrl+space (Native suggest) - when: ContextKeyExpr.and(TerminalContextKeys.focus, ContextKeyExpr.equals(TerminalContextKeyStrings.ShellType, WindowsShellType.PowerShell), TerminalContextKeys.terminalShellIntegrationEnabled, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), ContextKeyExpr.equals(`config.${TerminalSettingId.ShellIntegrationSuggestEnabled}`, true)), + when: ContextKeyExpr.and(TerminalContextKeys.focus, ContextKeyExpr.equals(TerminalContextKeyStrings.ShellType, WindowsShellType.PowerShell), TerminalContextKeys.terminalShellIntegrationEnabled, CONTEXT_ACCESSIBILITY_MODE_ENABLED.negate(), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.Enabled}`, true), ContextKeyExpr.equals(`config.${TerminalSuggestSettingId.EnabledLegacy}`, true))), primary: KeyMod.CtrlCmd | KeyCode.Space, mac: { primary: KeyMod.WinCtrl | KeyCode.Space } }); diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 8fdc2d6c5dee4..77d22d4aaf36a 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -22,7 +22,6 @@ import { IEditableData } from 'vs/workbench/common/views'; import { ITerminalStatusList } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { XtermTerminal } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; import { IRegisterContributedProfileArgs, IRemoteTerminalAttachTarget, IStartExtensionTerminalRequest, ITerminalConfiguration, ITerminalFont, ITerminalProcessExtHostProxy, ITerminalProcessInfo } from 'vs/workbench/contrib/terminal/common/terminal'; -import { ISimpleSelectedSuggestion } from 'vs/workbench/services/suggest/browser/simpleSuggestWidget'; import type { IMarker, ITheme, Terminal as RawXtermTerminal, IBufferRange } from '@xterm/xterm'; import { ScrollPosition } from 'vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -1239,12 +1238,3 @@ export const enum LinuxDistro { export const enum TerminalDataTransfers { Terminals = 'Terminals' } - -export interface ISuggestController { - selectPreviousSuggestion(): void; - selectPreviousPageSuggestion(): void; - selectNextSuggestion(): void; - selectNextPageSuggestion(): void; - acceptSelectedSuggestion(suggestion?: Pick): void; - hideSuggestWidget(): void; -} diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 1166023453d13..f4f0233b5cc5b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -1520,24 +1520,6 @@ export function registerTerminalActions() { } }); - registerTerminalAction({ - id: TerminalCommandId.ToggleStickyScroll, - title: localize2('workbench.action.terminal.toggleStickyScroll', 'Toggle Sticky Scroll'), - toggled: { - condition: ContextKeyExpr.equals('config.terminal.integrated.stickyScroll.enabled', true), - title: localize('stickyScroll', "Sticky Scroll"), - mnemonicTitle: localize({ key: 'miStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Sticky Scroll"), - }, - run: (c, accessor) => { - const configurationService = accessor.get(IConfigurationService); - const newValue = !configurationService.getValue(TerminalSettingId.StickyScrollEnabled); - return configurationService.updateValue(TerminalSettingId.StickyScrollEnabled, newValue); - }, - menu: [ - { id: MenuId.TerminalStickyScrollContext } - ] - }); - // Some commands depend on platform features if (BrowserFeatures.clipboard.writeText) { registerActiveXtermAction({ diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index 97f735e675c7b..17c4bf1ffd8ed 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -89,6 +89,10 @@ import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalSt import { shouldPasteTerminalText } from 'vs/workbench/contrib/terminal/common/terminalClipboard'; import { TerminalIconPicker } from 'vs/workbench/contrib/terminal/browser/terminalIconPicker'; +// HACK: This file should not depend on terminalContrib +// eslint-disable-next-line local/code-import-patterns +import { TerminalAccessibilityCommandId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility'; + const enum Constants { /** * The maximum amount of milliseconds to wait for a container before starting to create the @@ -702,7 +706,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { get shouldPersist(): boolean { return this._processManager.shouldPersist && !this.shellLaunchConfig.isTransient && (!this.reconnectionProperties || this._configurationService.getValue('task.reconnection') === true); } public static getXtermConstructor(keybindingService: IKeybindingService, contextKeyService: IContextKeyService) { - const keybinding = keybindingService.lookupKeybinding(TerminalCommandId.FocusAccessibleBuffer, contextKeyService); + const keybinding = keybindingService.lookupKeybinding(TerminalAccessibilityCommandId.FocusAccessibleBuffer, contextKeyService); if (xtermConstructor) { return xtermConstructor; } @@ -1242,6 +1246,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } // Send it to the process + this._logService.debug('sending data (vscode)', text); await this._processManager.write(text); this._onDidInputData.fire(this); this._onDidSendText.fire(text); @@ -1452,6 +1457,9 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { } } }); + if (this.isDisposed) { + return; + } if (this.xterm?.shellIntegration) { this.capabilities.add(this.xterm.shellIntegration.capabilities); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 2e51964baaf89..01db326f019be 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -42,6 +42,10 @@ import { getActiveWindow, runWhenWindowIdle } from 'vs/base/browser/dom'; import { mainWindow } from 'vs/base/browser/window'; import { shouldUseEnvironmentVariableCollection } from 'vs/platform/terminal/common/terminalEnvironment'; +// HACK: This file should not depend on terminalContrib +// eslint-disable-next-line local/code-import-patterns +import { TerminalSuggestSettingId } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration'; + const enum ProcessConstants { /** * The amount of time to consider terminal errors to be related to the launch. @@ -285,7 +289,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const options: ITerminalProcessOptions = { shellIntegration: { enabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationEnabled), - suggestEnabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationSuggestEnabled), + suggestEnabled: this._configurationService.getValue(TerminalSuggestSettingId.Enabled) || this._configurationService.getValue(TerminalSuggestSettingId.EnabledLegacy), nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, @@ -485,7 +489,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const options: ITerminalProcessOptions = { shellIntegration: { enabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationEnabled), - suggestEnabled: this._configurationService.getValue(TerminalSettingId.ShellIntegrationSuggestEnabled), + suggestEnabled: this._configurationService.getValue(TerminalSuggestSettingId.Enabled) || this._configurationService.getValue(TerminalSuggestSettingId.EnabledLegacy), nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts index e2ada5a5a1053..1e82b5badc317 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/decorationStyles.ts @@ -29,7 +29,6 @@ export const enum DecorationSelector { Codicon = 'codicon', XtermDecoration = 'xterm-decoration', OverviewRuler = '.xterm-decoration-overview-ruler', - QuickFix = 'quick-fix' } export class TerminalDecorationHoverManager extends Disposable { diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts index e0d8a498fa3c5..0c0048cc757f1 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/markNavigationAddon.ts @@ -14,7 +14,10 @@ import { TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR } from 'vs/workbench/co import { getWindow } from 'vs/base/browser/dom'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; + +// HACK: Mark navigation currently depends on terminalContrib/stickyScroll +// eslint-disable-next-line local/code-import-patterns +import { TerminalStickyScrollSettingId } from 'vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration'; enum Boundary { Top, @@ -282,7 +285,7 @@ export class MarkNavigationAddon extends Disposable implements IMarkTracker, ITe { bufferRange: range, // Ensure scroll shows the line when sticky scroll is enabled - forceScroll: !!this._configurationService.getValue(TerminalSettingId.StickyScrollEnabled) + forceScroll: !!this._configurationService.getValue(TerminalStickyScrollSettingId.Enabled) } ); } diff --git a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts index 7aeb0a5941744..67136657ac519 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm/xtermTerminal.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import type { IBuffer, ITerminalOptions, ITheme, Terminal as RawXtermTerminal, LogLevel as XtermLogLevel } from '@xterm/xterm'; -import type { CanvasAddon as CanvasAddonType } from '@xterm/addon-canvas'; import type { ISearchOptions, SearchAddon as SearchAddonType } from '@xterm/addon-search'; import type { Unicode11Addon as Unicode11AddonType } from '@xterm/addon-unicode11'; import type { WebglAddon as WebglAddonType } from '@xterm/addon-webgl'; @@ -45,7 +44,6 @@ const enum RenderConstants { SmoothScrollDuration = 125 } -let CanvasAddon: typeof CanvasAddonType; let ImageAddon: typeof ImageAddonType; let SearchAddon: typeof SearchAddonType; let SerializeAddon: typeof SerializeAddonType; @@ -121,7 +119,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach private _decorationAddon: DecorationAddon; // Optional addons - private _canvasAddon?: CanvasAddonType; private _searchAddon?: SearchAddonType; private _unicode11Addon?: Unicode11AddonType; private _webglAddon?: WebglAddonType; @@ -136,7 +133,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach get findResult(): { resultIndex: number; resultCount: number } | undefined { return this._lastFindResult; } get isStdinDisabled(): boolean { return !!this.raw.options.disableStdin; } - get isGpuAccelerated(): boolean { return !!(this._canvasAddon || this._webglAddon); } + get isGpuAccelerated(): boolean { return !!this._webglAddon; } private readonly _onDidRequestRunCommand = this._register(new Emitter<{ command: ITerminalCommand; copyAsHtml?: boolean; noNewLine?: boolean }>()); readonly onDidRequestRunCommand = this._onDidRequestRunCommand.event; @@ -159,7 +156,7 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach get shellIntegration(): IShellIntegration { return this._shellIntegrationAddon; } get textureAtlas(): Promise | undefined { - const canvas = this._webglAddon?.textureAtlas || this._canvasAddon?.textureAtlas; + const canvas = this._webglAddon?.textureAtlas; if (!canvas) { return undefined; } @@ -332,8 +329,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach if (options.enableGpu) { if (this._shouldLoadWebgl()) { this._enableWebglRenderer(); - } else if (this._shouldLoadCanvas()) { - this._enableCanvasRenderer(); } } @@ -406,11 +401,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach this._enableWebglRenderer(); } else { this._disposeOfWebglRenderer(); - if (this._shouldLoadCanvas()) { - this._enableCanvasRenderer(); - } else { - this._disposeOfCanvasRenderer(); - } } } } @@ -423,10 +413,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach return (this._terminalConfigurationService.config.gpuAcceleration === 'auto' && XtermTerminal._suggestedRendererType === undefined) || this._terminalConfigurationService.config.gpuAcceleration === 'on'; } - private _shouldLoadCanvas(): boolean { - return this._terminalConfigurationService.config.gpuAcceleration === 'canvas'; - } - forceRedraw() { this.raw.clearTextureAtlas(); } @@ -680,7 +666,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach const Addon = await this._getWebglAddonConstructor(); this._webglAddon = new Addon(); - this._disposeOfCanvasRenderer(); try { this.raw.loadAddon(this._webglAddon); this._logService.trace('Webgl was loaded'); @@ -706,38 +691,10 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach this._disposeOfWebglRenderer(); } - /** - * @deprecated This will be removed in the future, see /~https://github.com/microsoft/vscode/issues/209276 - */ - private async _enableCanvasRenderer(): Promise { - if (!this.raw.element || this._canvasAddon) { - return; - } - const Addon = await this._getCanvasAddonConstructor(); - this._canvasAddon = new Addon(); - this._disposeOfWebglRenderer(); - try { - this.raw.loadAddon(this._canvasAddon); - this._logService.trace('Canvas renderer was loaded'); - } catch (e) { - this._logService.warn(`Canvas renderer could not be loaded, falling back to dom renderer`, e); - XtermTerminal._suggestedRendererType = 'dom'; - this._disposeOfCanvasRenderer(); - } - this._refreshImageAddon(); - } - - protected async _getCanvasAddonConstructor(): Promise { - if (!CanvasAddon) { - CanvasAddon = (await importAMDNodeModule('@xterm/addon-canvas', 'lib/xterm-addon-canvas.js')).CanvasAddon; - } - return CanvasAddon; - } - @debounce(100) private async _refreshImageAddon(): Promise { - // Only allow the image addon when a canvas is being used to avoid possible GPU issues - if (this._terminalConfigurationService.config.enableImages && (this._canvasAddon || this._webglAddon)) { + // Only allow the image addon when webgl is being used to avoid possible GPU issues + if (this._terminalConfigurationService.config.enableImages && this._webglAddon) { if (!this._imageAddon) { const AddonCtor = await this._getImageAddonConstructor(); this._imageAddon = new AddonCtor(); @@ -788,16 +745,6 @@ export class XtermTerminal extends Disposable implements IXtermTerminal, IDetach return SerializeAddon; } - private _disposeOfCanvasRenderer(): void { - try { - this._canvasAddon?.dispose(); - } catch { - // ignore - } - this._canvasAddon = undefined; - this._refreshImageAddon(); - } - private _disposeOfWebglRenderer(): void { try { this._webglAddon?.dispose(); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index a2c8e2a804ed1..406bfbb41cf89 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -19,6 +19,12 @@ import { AccessibilityCommandId } from 'vs/workbench/contrib/accessibility/commo import { IEnvironmentVariableInfo } from 'vs/workbench/contrib/terminal/common/environmentVariable'; import { IExtensionPointDescriptor } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +// Import commands to skip shell from terminalContrib - this is an exception to the eslint rule +// since they need to be included in the terminal module +import { defaultTerminalAccessibilityCommandsToSkipShell } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility'; // eslint-disable-line local/code-import-patterns +import { defaultTerminalFindCommandToSkipShell } from 'vs/workbench/contrib/terminalContrib/find/common/terminal.find'; // eslint-disable-line local/code-import-patterns +import { defaultTerminalSuggestCommandsToSkipShell } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest'; // eslint-disable-line local/code-import-patterns + export const TERMINAL_VIEW_ID = 'terminal'; export const TERMINAL_CREATION_COMMANDS = ['workbench.action.terminal.toggleTerminal', 'workbench.action.terminal.new', 'workbench.action.togglePanel', 'workbench.action.terminal.focus']; @@ -135,7 +141,7 @@ export interface ITerminalConfiguration { altClickMovesCursor: boolean; macOptionIsMeta: boolean; macOptionClickForcesSelection: boolean; - gpuAcceleration: 'auto' | 'on' | 'canvas' | 'off'; + gpuAcceleration: 'auto' | 'on' | 'off'; rightClickBehavior: 'default' | 'copyPaste' | 'paste' | 'selectWord' | 'nothing'; middleClickBehavior: 'default' | 'paste'; cursorBlinking: boolean; @@ -178,10 +184,6 @@ export interface ITerminalConfiguration { enableFileLinks: 'off' | 'on' | 'notRemote'; allowedLinkSchemes: string[]; unicodeVersion: '6' | '11'; - localEchoLatencyThreshold: number; - localEchoExcludePrograms: ReadonlyArray; - localEchoEnabled: 'auto' | 'on' | 'off'; - localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; enablePersistentSessions: boolean; tabs: { enabled: boolean; @@ -202,6 +204,8 @@ export interface ITerminalConfiguration { shellIntegration?: { enabled: boolean; decorationsEnabled: boolean; + // TODO: Legacy - remove soon + suggestEnabled: boolean; }; enableImages: boolean; smoothScrolling: boolean; @@ -209,8 +213,6 @@ export interface ITerminalConfiguration { rescaleOverlappingGlyphs: boolean; } -export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; - export interface ITerminalFont { fontFamily: string; fontSize: number; @@ -385,8 +387,6 @@ export interface ISerializedTerminalInstanceContext { export const QUICK_LAUNCH_PROFILE_CHOICE = 'workbench.action.terminal.profile.choice'; export const enum TerminalCommandId { - FindNext = 'workbench.action.terminal.findNext', - FindPrevious = 'workbench.action.terminal.findPrevious', Toggle = 'workbench.action.terminal.toggleTerminal', Kill = 'workbench.action.terminal.kill', KillViewOrEditor = 'workbench.action.terminal.killViewOrEditor', @@ -395,15 +395,8 @@ export const enum TerminalCommandId { KillAll = 'workbench.action.terminal.killAll', QuickKill = 'workbench.action.terminal.quickKill', ConfigureTerminalSettings = 'workbench.action.terminal.openSettings', - OpenDetectedLink = 'workbench.action.terminal.openDetectedLink', - OpenWordLink = 'workbench.action.terminal.openWordLink', ShellIntegrationLearnMore = 'workbench.action.terminal.learnMore', - OpenFileLink = 'workbench.action.terminal.openFileLink', - OpenWebLink = 'workbench.action.terminal.openUrlLink', RunRecentCommand = 'workbench.action.terminal.runRecentCommand', - FocusAccessibleBuffer = 'workbench.action.terminal.focusAccessibleBuffer', - AccessibleBufferGoToNextCommand = 'workbench.action.terminal.accessibleBufferGoToNextCommand', - AccessibleBufferGoToPreviousCommand = 'workbench.action.terminal.accessibleBufferGoToPreviousCommand', CopyLastCommand = 'workbench.action.terminal.copyLastCommand', CopyLastCommandOutput = 'workbench.action.terminal.copyLastCommandOutput', CopyLastCommandAndLastCommandOutput = 'workbench.action.terminal.copyLastCommandAndLastCommandOutput', @@ -425,7 +418,6 @@ export const enum TerminalCommandId { Split = 'workbench.action.terminal.split', SplitActiveTab = 'workbench.action.terminal.splitActiveTab', SplitInActiveWorkspace = 'workbench.action.terminal.splitInActiveWorkspace', - ShowQuickFixes = 'workbench.action.terminal.showQuickFixes', Unsplit = 'workbench.action.terminal.unsplit', JoinActiveTab = 'workbench.action.terminal.joinActiveTab', Join = 'workbench.action.terminal.join', @@ -454,11 +446,9 @@ export const enum TerminalCommandId { ScrollDownLine = 'workbench.action.terminal.scrollDown', ScrollDownPage = 'workbench.action.terminal.scrollDownPage', ScrollToBottom = 'workbench.action.terminal.scrollToBottom', - ScrollToBottomAccessibleView = 'workbench.action.terminal.scrollToBottomAccessibleView', ScrollUpLine = 'workbench.action.terminal.scrollUp', ScrollUpPage = 'workbench.action.terminal.scrollUpPage', ScrollToTop = 'workbench.action.terminal.scrollToTop', - ScrollToTopAccessibleView = 'workbench.action.terminal.scrollToTopAccessibleView', Clear = 'workbench.action.terminal.clear', ClearSelection = 'workbench.action.terminal.clearSelection', ChangeIcon = 'workbench.action.terminal.changeIcon', @@ -468,8 +458,6 @@ export const enum TerminalCommandId { Rename = 'workbench.action.terminal.rename', RenameActiveTab = 'workbench.action.terminal.renameActiveTab', RenameWithArgs = 'workbench.action.terminal.renameWithArg', - FindFocus = 'workbench.action.terminal.focusFind', - FindHide = 'workbench.action.terminal.hideFind', QuickOpenTerm = 'workbench.action.quickOpenTerm', ScrollToPreviousCommand = 'workbench.action.terminal.scrollToPreviousCommand', ScrollToNextCommand = 'workbench.action.terminal.scrollToNextCommand', @@ -478,10 +466,6 @@ export const enum TerminalCommandId { SelectToPreviousLine = 'workbench.action.terminal.selectToPreviousLine', SelectToNextLine = 'workbench.action.terminal.selectToNextLine', SendSequence = 'workbench.action.terminal.sendSequence', - ToggleFindRegex = 'workbench.action.terminal.toggleFindRegex', - ToggleFindWholeWord = 'workbench.action.terminal.toggleFindWholeWord', - ToggleFindCaseSensitive = 'workbench.action.terminal.toggleFindCaseSensitive', - SearchWorkspace = 'workbench.action.terminal.searchWorkspace', AttachToSession = 'workbench.action.terminal.attachToSession', DetachSession = 'workbench.action.terminal.detachSession', MoveToEditor = 'workbench.action.terminal.moveToEditor', @@ -489,26 +473,10 @@ export const enum TerminalCommandId { MoveIntoNewWindow = 'workbench.action.terminal.moveIntoNewWindow', SetDimensions = 'workbench.action.terminal.setDimensions', ClearPreviousSessionHistory = 'workbench.action.terminal.clearPreviousSessionHistory', - SelectPrevSuggestion = 'workbench.action.terminal.selectPrevSuggestion', - SelectPrevPageSuggestion = 'workbench.action.terminal.selectPrevPageSuggestion', - SelectNextSuggestion = 'workbench.action.terminal.selectNextSuggestion', - SelectNextPageSuggestion = 'workbench.action.terminal.selectNextPageSuggestion', - AcceptSelectedSuggestion = 'workbench.action.terminal.acceptSelectedSuggestion', - HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', FocusHover = 'workbench.action.terminal.focusHover', ShowEnvironmentContributions = 'workbench.action.terminal.showEnvironmentContributions', - ToggleStickyScroll = 'workbench.action.terminal.toggleStickyScroll', StartVoice = 'workbench.action.terminal.startVoice', StopVoice = 'workbench.action.terminal.stopVoice', - FontZoomIn = 'workbench.action.terminal.fontZoomIn', - FontZoomOut = 'workbench.action.terminal.fontZoomOut', - FontZoomReset = 'workbench.action.terminal.fontZoomReset', - - // Developer commands - - WriteDataToTerminal = 'workbench.action.terminal.writeDataToTerminal', - ShowTextureAtlas = 'workbench.action.terminal.showTextureAtlas', - RestartPtyHost = 'workbench.action.terminal.restartPtyHost', } export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ @@ -523,14 +491,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.DeleteToLineStart, TerminalCommandId.DeleteWordLeft, TerminalCommandId.DeleteWordRight, - TerminalCommandId.FindFocus, - TerminalCommandId.FindHide, - TerminalCommandId.FindNext, - TerminalCommandId.FindPrevious, TerminalCommandId.GoToRecentDirectory, - TerminalCommandId.ToggleFindRegex, - TerminalCommandId.ToggleFindWholeWord, - TerminalCommandId.ToggleFindCaseSensitive, TerminalCommandId.FocusNextPane, TerminalCommandId.FocusNext, TerminalCommandId.FocusPreviousPane, @@ -571,14 +532,7 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ TerminalCommandId.SplitInActiveWorkspace, TerminalCommandId.Split, TerminalCommandId.Toggle, - TerminalCommandId.SelectPrevSuggestion, - TerminalCommandId.SelectPrevPageSuggestion, - TerminalCommandId.SelectNextSuggestion, - TerminalCommandId.SelectNextPageSuggestion, - TerminalCommandId.AcceptSelectedSuggestion, - TerminalCommandId.HideSuggestWidget, TerminalCommandId.FocusHover, - TerminalCommandId.FocusAccessibleBuffer, AccessibilityCommandId.OpenAccessibilityHelp, 'editor.action.toggleTabFocusMode', 'notifications.hideList', @@ -664,6 +618,9 @@ export const DEFAULT_COMMANDS_TO_SKIP_SHELL: string[] = [ 'workbench.action.terminal.chat.runCommand', 'workbench.action.terminal.chat.insertCommand', 'workbench.action.terminal.chat.viewInChat', + ...defaultTerminalAccessibilityCommandsToSkipShell, + ...defaultTerminalFindCommandToSkipShell, + ...defaultTerminalSuggestCommandsToSkipShell, ]; export const terminalContributionsDescriptor: IExtensionPointDescriptor = { diff --git a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts index cb9ab6eb930f4..3ccbefd5c2d0d 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalConfiguration.ts @@ -5,7 +5,7 @@ import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { localize } from 'vs/nls'; -import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT, DEFAULT_LOCAL_ECHO_EXCLUDE } from 'vs/workbench/contrib/terminal/common/terminal'; +import { DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, DEFAULT_COMMANDS_TO_SKIP_SHELL, SUGGESTIONS_FONT_WEIGHT, MINIMUM_FONT_WEIGHT, MAXIMUM_FONT_WEIGHT } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalLocationString, TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -14,6 +14,14 @@ import { terminalColorSchema, terminalIconSchema } from 'vs/platform/terminal/co import product from 'vs/platform/product/common/product'; import { Extensions as WorkbenchExtensions, IConfigurationMigrationRegistry, ConfigurationKeyValuePairs } from 'vs/workbench/common/configuration'; +// Import configuration schemes from terminalContrib - this is an exception to the eslint rule since +// they need to be declared at part of the rest of the terminal configuration +import { terminalAccessibilityConfiguration } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration'; // eslint-disable-line local/code-import-patterns +import { terminalStickyScrollConfiguration } from 'vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration'; // eslint-disable-line local/code-import-patterns +import { terminalTypeAheadConfiguration } from 'vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration'; // eslint-disable-line local/code-import-patterns +import { terminalZoomConfiguration } from 'vs/workbench/contrib/terminalContrib/zoom/common/terminal.zoom'; // eslint-disable-line local/code-import-patterns +import { terminalSuggestConfiguration } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration'; // eslint-disable-line local/code-import-patterns + const terminalDescriptors = '\n- ' + [ '`\${cwd}`: ' + localize("cwd", "the terminal's current working directory"), '`\${cwdFolder}`: ' + localize('cwdFolder', "the terminal's current working directory, displayed for multi-root workspaces or in a single root workspace when the value differs from the initial working directory. On Windows, this will only be displayed when shell integration is enabled."), @@ -296,12 +304,11 @@ const terminalConfiguration: IConfigurationNode = { }, [TerminalSettingId.GpuAcceleration]: { type: 'string', - enum: ['auto', 'on', 'off', 'canvas'], + enum: ['auto', 'on', 'off'], markdownEnumDescriptions: [ localize('terminal.integrated.gpuAcceleration.auto', "Let VS Code detect which renderer will give the best experience."), localize('terminal.integrated.gpuAcceleration.on', "Enable GPU acceleration within the terminal."), localize('terminal.integrated.gpuAcceleration.off', "Disable GPU acceleration within the terminal. The terminal will render much slower when GPU acceleration is off but it should reliably work on all systems."), - localize('terminal.integrated.gpuAcceleration.canvas', "Use the terminal's fallback canvas renderer which uses a 2d context instead of webgl which may perform better on some systems. Note that some features are limited in the canvas renderer like opaque selection.") ], default: 'auto', description: localize('terminal.integrated.gpuAcceleration', "Controls whether the terminal will leverage the GPU to do its rendering.") @@ -514,45 +521,6 @@ const terminalConfiguration: IConfigurationNode = { default: '11', description: localize('terminal.integrated.unicodeVersion', "Controls what version of Unicode to use when evaluating the width of characters in the terminal. If you experience emoji or other wide characters not taking up the right amount of space or backspace either deleting too much or too little then you may want to try tweaking this setting.") }, - [TerminalSettingId.LocalEchoLatencyThreshold]: { - description: localize('terminal.integrated.localEchoLatencyThreshold', "Length of network delay, in milliseconds, where local edits will be echoed on the terminal without waiting for server acknowledgement. If '0', local echo will always be on, and if '-1' it will be disabled."), - type: 'integer', - minimum: -1, - default: 30, - }, - [TerminalSettingId.LocalEchoEnabled]: { - markdownDescription: localize('terminal.integrated.localEchoEnabled', "When local echo should be enabled. This will override {0}", '`#terminal.integrated.localEchoLatencyThreshold#`'), - type: 'string', - enum: ['on', 'off', 'auto'], - enumDescriptions: [ - localize('terminal.integrated.localEchoEnabled.on', "Always enabled"), - localize('terminal.integrated.localEchoEnabled.off', "Always disabled"), - localize('terminal.integrated.localEchoEnabled.auto', "Enabled only for remote workspaces") - ], - default: 'auto' - }, - [TerminalSettingId.LocalEchoExcludePrograms]: { - description: localize('terminal.integrated.localEchoExcludePrograms', "Local echo will be disabled when any of these program names are found in the terminal title."), - type: 'array', - items: { - type: 'string', - uniqueItems: true - }, - default: DEFAULT_LOCAL_ECHO_EXCLUDE, - }, - [TerminalSettingId.LocalEchoStyle]: { - description: localize('terminal.integrated.localEchoStyle', "Terminal style of locally echoed text; either a font style or an RGB color."), - default: 'dim', - anyOf: [ - { - enum: ['bold', 'dim', 'italic', 'underlined', 'inverted', '#ff0000'], - }, - { - type: 'string', - format: 'color-hex', - } - ] - }, [TerminalSettingId.EnablePersistentSessions]: { description: localize('terminal.integrated.enablePersistentSessions', "Persist terminal sessions/history for the workspace across window reloads."), type: 'boolean', @@ -627,13 +595,6 @@ const terminalConfiguration: IConfigurationNode = { type: 'number', default: 100 }, - [TerminalSettingId.ShellIntegrationSuggestEnabled]: { - restricted: true, - markdownDescription: localize('terminal.integrated.shellIntegration.suggestEnabled', "Enables experimental terminal Intellisense suggestions for supported shells when {0} is set to {1}. If shell integration is installed manually, {2} needs to be set to {3} before calling the script.", '`#terminal.integrated.shellIntegration.enabled#`', '`true`', '`VSCODE_SUGGEST`', '`1`'), - type: 'boolean', - default: false, - markdownDeprecationMessage: localize('suggestEnabled.deprecated', 'This is an experimental setting and may break the terminal! Use at your own risk.') - }, [TerminalSettingId.SmoothScrolling]: { markdownDescription: localize('terminal.integrated.smoothScrolling', "Controls whether the terminal will scroll using an animation."), type: 'boolean', @@ -661,35 +622,11 @@ const terminalConfiguration: IConfigurationNode = { localize('terminal.integrated.focusAfterRun.none', "Do nothing."), ] }, - [TerminalSettingId.AccessibleViewPreserveCursorPosition]: { - markdownDescription: localize('terminal.integrated.accessibleViewPreserveCursorPosition', "Preserve the cursor position on reopen of the terminal's accessible view rather than setting it to the bottom of the buffer."), - type: 'boolean', - default: false - }, - [TerminalSettingId.AccessibleViewFocusOnCommandExecution]: { - markdownDescription: localize('terminal.integrated.accessibleViewFocusOnCommandExecution', "Focus the terminal accessible view when a command is executed."), - type: 'boolean', - default: false - }, - [TerminalSettingId.StickyScrollEnabled]: { - markdownDescription: localize('terminal.integrated.stickyScroll.enabled', "Shows the current command at the top of the terminal."), - type: 'boolean', - default: product.quality !== 'stable' - }, - [TerminalSettingId.StickyScrollMaxLineCount]: { - markdownDescription: localize('terminal.integrated.stickyScroll.maxLineCount', "Defines the maximum number of sticky lines to show. Sticky scroll lines will never exceed 40% of the viewport regardless of this setting."), - type: 'number', - default: 5, - minimum: 1, - maximum: 10 - }, - [TerminalSettingId.MouseWheelZoom]: { - markdownDescription: isMacintosh - ? localize('terminal.integrated.mouseWheelZoom.mac', "Zoom the font of the terminal when using mouse wheel and holding `Cmd`.") - : localize('terminal.integrated.mouseWheelZoom', "Zoom the font of the terminal when using mouse wheel and holding `Ctrl`."), - type: 'boolean', - default: false - }, + ...terminalAccessibilityConfiguration, + ...terminalStickyScrollConfiguration, + ...terminalSuggestConfiguration, + ...terminalTypeAheadConfiguration, + ...terminalZoomConfiguration, } }; diff --git a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts index a8602f7d2f6de..7b726a7c1d888 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalStrings.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalStrings.ts @@ -39,7 +39,6 @@ export const terminalStrings = { sendSequence: localize2('workbench.action.terminal.sendSequence', "Send Custom Sequence To Terminal"), newWithCwd: localize2('workbench.action.terminal.newWithCwd', "Create New Terminal Starting in a Custom Working Directory"), renameWithArgs: localize2('workbench.action.terminal.renameWithArg', "Rename the Currently Active Terminal"), - stickyScroll: localize2('stickyScroll', "Sticky Scroll"), scrollToPreviousCommand: localize2('workbench.action.terminal.scrollToPreviousCommand', "Scroll To Previous Command"), scrollToNextCommand: localize2('workbench.action.terminal.scrollToNextCommand', "Scroll To Next Command") }; 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 2f231b15c43f8..a77db21d05444 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 @@ -19,7 +19,7 @@ import { ITerminalContribution, ITerminalInstance, ITerminalService, IXtermTermi import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { BufferContentTracker } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker'; import { TerminalAccessibilityHelpProvider } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp'; @@ -33,6 +33,10 @@ import { Event } from 'vs/base/common/event'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; import { AccessibilitySignal, IAccessibilitySignalService } from 'vs/platform/accessibilitySignal/browser/accessibilitySignalService'; import { alert } from 'vs/base/browser/ui/aria/aria'; +import { TerminalAccessibilitySettingId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration'; +import { TerminalAccessibilityCommandId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility'; + +// #region Terminal Contributions class TextAreaSyncContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.textAreaSync'; @@ -59,7 +63,6 @@ class TextAreaSyncContribution extends DisposableStore implements ITerminalContr } registerTerminalContribution(TextAreaSyncContribution.ID, TextAreaSyncContribution); - export class TerminalAccessibleViewContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.accessibleBufferProvider'; static get(instance: ITerminalInstance): TerminalAccessibleViewContribution | null { @@ -97,7 +100,7 @@ export class TerminalAccessibleViewContribution extends Disposable implements IT } })); this._register(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TerminalSettingId.AccessibleViewFocusOnCommandExecution)) { + if (e.affectsConfiguration(TerminalAccessibilitySettingId.AccessibleViewFocusOnCommandExecution)) { this._updateCommandExecutedListener(); } })); @@ -137,7 +140,7 @@ export class TerminalAccessibleViewContribution extends Disposable implements IT if (!this._instance.capabilities.has(TerminalCapability.CommandDetection)) { return; } - if (!this._configurationService.getValue(TerminalSettingId.AccessibleViewFocusOnCommandExecution)) { + if (!this._configurationService.getValue(TerminalAccessibilitySettingId.AccessibleViewFocusOnCommandExecution)) { this._onDidRunCommand.clear(); return; } else if (this._onDidRunCommand.value) { @@ -168,7 +171,7 @@ export class TerminalAccessibleViewContribution extends Disposable implements IT return this._register(this._instantiationService.createInstance(TerminalAccessibilityHelpProvider, this._instance, this._xterm!)).provideContent(); })); } - const position = this._configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? this._accessibleViewService.getPosition(AccessibleViewProviderId.Terminal) : undefined; + const position = this._configurationService.getValue(TerminalAccessibilitySettingId.AccessibleViewPreserveCursorPosition) ? this._accessibleViewService.getPosition(AccessibleViewProviderId.Terminal) : undefined; this._accessibleViewService.show(this._bufferProvider, position); } navigateToCommand(type: NavigationType): void { @@ -261,11 +264,14 @@ export class TerminalAccessibilityHelpContribution extends Disposable { } registerTerminalContribution(TerminalAccessibilityHelpContribution.ID, TerminalAccessibilityHelpContribution); +// #endregion + +// #region Actions class FocusAccessibleBufferAction extends Action2 { constructor() { super({ - id: TerminalCommandId.FocusAccessibleBuffer, + id: TerminalAccessibilityCommandId.FocusAccessibleBuffer, title: localize2('workbench.action.terminal.focusAccessibleBuffer', "Focus Accessible Terminal View"), precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), keybinding: [ @@ -294,7 +300,7 @@ class FocusAccessibleBufferAction extends Action2 { registerAction2(FocusAccessibleBufferAction); registerTerminalAction({ - id: TerminalCommandId.AccessibleBufferGoToNextCommand, + id: TerminalAccessibilityCommandId.AccessibleBufferGoToNextCommand, title: localize2('workbench.action.terminal.accessibleBufferGoToNextCommand', "Accessible Buffer Go to Next Command"), precondition: ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated, ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.Terminal))), keybinding: [ @@ -313,9 +319,8 @@ registerTerminalAction({ } }); - registerTerminalAction({ - id: TerminalCommandId.AccessibleBufferGoToPreviousCommand, + id: TerminalAccessibilityCommandId.AccessibleBufferGoToPreviousCommand, title: localize2('workbench.action.terminal.accessibleBufferGoToPreviousCommand', "Accessible Buffer Go to Previous Command"), precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.Terminal))), keybinding: [ @@ -335,7 +340,7 @@ registerTerminalAction({ }); registerTerminalAction({ - id: TerminalCommandId.ScrollToBottomAccessibleView, + id: TerminalAccessibilityCommandId.ScrollToBottomAccessibleView, title: localize2('workbench.action.terminal.scrollToBottomAccessibleView', 'Scroll to Accessible View Bottom'), precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.Terminal))), keybinding: { @@ -355,7 +360,7 @@ registerTerminalAction({ }); registerTerminalAction({ - id: TerminalCommandId.ScrollToTopAccessibleView, + id: TerminalAccessibilityCommandId.ScrollToTopAccessibleView, title: localize2('workbench.action.terminal.scrollToTopAccessibleView', 'Scroll to Accessible View Top'), precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.Terminal))), keybinding: { @@ -369,3 +374,5 @@ registerTerminalAction({ accessibleViewService.setPosition({ lineNumber: 1, column: 1 } as Position, true); } }); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts index 4a3d33ff9ca91..ad967c57b5aa1 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibilityHelp.ts @@ -19,6 +19,9 @@ import { ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal } from '@xterm/xterm'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { TerminalAccessibilitySettingId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration'; +import { TerminalAccessibilityCommandId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility'; +import { TerminalLinksCommandId } from 'vs/workbench/contrib/terminalContrib/links/common/terminal.links'; export const enum ClassName { Active = 'active', @@ -31,7 +34,7 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc onClose() { const expr = ContextKeyExpr.and(accessibleViewIsShown, ContextKeyExpr.equals(accessibleViewCurrentProviderId.key, AccessibleViewProviderId.TerminalHelp)); if (expr?.evaluate(this._contextKeyService.getContext(null))) { - this._commandService.executeCommand(TerminalCommandId.FocusAccessibleBuffer); + this._commandService.executeCommand(TerminalAccessibilityCommandId.FocusAccessibleBuffer); } else { this._instance.focus(); } @@ -77,9 +80,9 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc provideContent(): string { const content = []; - 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(this._descriptionForCommand(TerminalAccessibilityCommandId.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._configurationService.getValue(TerminalSettingId.AccessibleViewFocusOnCommandExecution)) { + if (!this._configurationService.getValue(TerminalAccessibilitySettingId.AccessibleViewFocusOnCommandExecution)) { content.push(localize('focusViewOnExecution', 'Enable `terminal.integrated.accessibleViewFocusOnCommandExecution` to automatically focus the terminal accessible view when a command is executed in the terminal.')); } if (this._instance.shellType === WindowsShellType.CommandPrompt) { @@ -88,8 +91,8 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc if (this._hasShellIntegration) { const shellIntegrationCommandList = []; shellIntegrationCommandList.push(localize('shellIntegration', "The terminal has a feature called shell integration that offers an enhanced experience and provides useful commands for screen readers such as:")); - shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToNextCommand, localize('goToNextCommand', 'Go to Next Command ({0}) in the accessible view'), localize('goToNextCommandNoKb', 'Go to Next Command in the accessible view is currently not triggerable by a keybinding.'))); - shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.AccessibleBufferGoToPreviousCommand, localize('goToPreviousCommand', 'Go to Previous Command ({0}) in the accessible view'), localize('goToPreviousCommandNoKb', 'Go to Previous Command in the accessible view is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalAccessibilityCommandId.AccessibleBufferGoToNextCommand, localize('goToNextCommand', 'Go to Next Command ({0}) in the accessible view'), localize('goToNextCommandNoKb', 'Go to Next Command in the accessible view is currently not triggerable by a keybinding.'))); + shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalAccessibilityCommandId.AccessibleBufferGoToPreviousCommand, localize('goToPreviousCommand', 'Go to Previous Command ({0}) in the accessible view'), localize('goToPreviousCommandNoKb', 'Go to Previous Command in the accessible view is currently not triggerable by a keybinding.'))); shellIntegrationCommandList.push('- ' + this._descriptionForCommand(AccessibilityCommandId.GoToSymbol, localize('goToSymbol', 'Go to Symbol ({0})'), localize('goToSymbolNoKb', 'Go to symbol is currently not triggerable by a keybinding.'))); shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.RunRecentCommand, localize('runRecentCommand', 'Run Recent Command ({0})'), localize('runRecentCommandNoKb', 'Run Recent Command is currently not triggerable by a keybinding.'))); shellIntegrationCommandList.push('- ' + this._descriptionForCommand(TerminalCommandId.GoToRecentDirectory, localize('goToRecentDirectory', 'Go to Recent Directory ({0})'), localize('goToRecentDirectoryNoKb', 'Go to Recent Directory is currently not triggerable by a keybinding.'))); @@ -97,7 +100,7 @@ export class TerminalAccessibilityHelpProvider extends Disposable implements IAc } else { content.push(this._descriptionForCommand(TerminalCommandId.RunRecentCommand, localize('goToRecentDirectoryNoShellIntegration', 'The Go to Recent Directory command ({0}) enables screen readers to easily navigate to a directory that has been used in the terminal.'), localize('goToRecentDirectoryNoKbNoShellIntegration', 'The Go to Recent Directory command enables screen readers to easily navigate to a directory that has been used in the terminal and is currently not triggerable by a keybinding.'))); } - content.push(this._descriptionForCommand(TerminalCommandId.OpenDetectedLink, localize('openDetectedLink', 'The Open Detected Link ({0}) command enables screen readers to easily open links found in the terminal.'), localize('openDetectedLinkNoKb', 'The Open Detected Link command enables screen readers to easily open links found in the terminal and is currently not triggerable by a keybinding.'))); + content.push(this._descriptionForCommand(TerminalLinksCommandId.OpenDetectedLink, localize('openDetectedLink', 'The Open Detected Link ({0}) command enables screen readers to easily open links found in the terminal.'), localize('openDetectedLinkNoKb', 'The Open Detected Link command enables screen readers to easily open links found in the terminal and is currently not triggerable by a keybinding.'))); content.push(this._descriptionForCommand(TerminalCommandId.NewWithProfile, localize('newWithProfile', 'The Create New Terminal (With Profile) ({0}) command allows for easy terminal creation using a specific profile.'), localize('newWithProfileNoKb', 'The Create New Terminal (With Profile) command allows for easy terminal creation using a specific profile and is currently not triggerable by a keybinding.'))); content.push(localize('focusAfterRun', 'Configure what gets focused after running selected text in the terminal with `{0}`.', TerminalSettingId.FocusAfterRun)); return content.join('\n\n'); diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts index 399e846921fe9..05a1f753a815a 100644 --- a/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/browser/terminalAccessibleBufferProvider.ts @@ -10,11 +10,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { TerminalCapability, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { AccessibilityVerbositySettingId, AccessibleViewProviderId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; import { AccessibleViewType, IAccessibleViewContentProvider, IAccessibleViewOptions, IAccessibleViewSymbol } from 'vs/workbench/contrib/accessibility/browser/accessibleView'; import { ITerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BufferContentTracker } from 'vs/workbench/contrib/terminalContrib/accessibility/browser/bufferContentTracker'; +import { TerminalAccessibilitySettingId } from 'vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration'; export class TerminalAccessibleBufferProvider extends DisposableStore implements IAccessibleViewContentProvider { id = AccessibleViewProviderId.Terminal; @@ -34,11 +34,11 @@ export class TerminalAccessibleBufferProvider extends DisposableStore implements ) { super(); this.options.customHelp = customHelp; - this.options.position = configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; + this.options.position = configurationService.getValue(TerminalAccessibilitySettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; this.add(this._instance.onDisposed(() => this._onDidRequestClearProvider.fire(AccessibleViewProviderId.Terminal))); this.add(configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TerminalSettingId.AccessibleViewPreserveCursorPosition)) { - this.options.position = configurationService.getValue(TerminalSettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; + if (e.affectsConfiguration(TerminalAccessibilitySettingId.AccessibleViewPreserveCursorPosition)) { + this.options.position = configurationService.getValue(TerminalAccessibilitySettingId.AccessibleViewPreserveCursorPosition) ? 'initial-bottom' : 'bottom'; } })); this._focusedInstance = _terminalService.activeInstance; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility.ts new file mode 100644 index 0000000000000..492b11f19ab99 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminal.accessibility.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum TerminalAccessibilityCommandId { + FocusAccessibleBuffer = 'workbench.action.terminal.focusAccessibleBuffer', + AccessibleBufferGoToNextCommand = 'workbench.action.terminal.accessibleBufferGoToNextCommand', + AccessibleBufferGoToPreviousCommand = 'workbench.action.terminal.accessibleBufferGoToPreviousCommand', + ScrollToBottomAccessibleView = 'workbench.action.terminal.scrollToBottomAccessibleView', + ScrollToTopAccessibleView = 'workbench.action.terminal.scrollToTopAccessibleView', +} + +export const defaultTerminalAccessibilityCommandsToSkipShell = [ + TerminalAccessibilityCommandId.FocusAccessibleBuffer +]; diff --git a/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration.ts new file mode 100644 index 0000000000000..29d5be506d501 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/accessibility/common/terminalAccessibilityConfiguration.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IStringDictionary } from 'vs/base/common/collections'; +import { localize } from 'vs/nls'; +import type { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; + +export const enum TerminalAccessibilitySettingId { + AccessibleViewPreserveCursorPosition = 'terminal.integrated.accessibleViewPreserveCursorPosition', + AccessibleViewFocusOnCommandExecution = 'terminal.integrated.accessibleViewFocusOnCommandExecution', +} + +export interface ITerminalAccessibilityConfiguration { + accessibleViewPreserveCursorPosition: boolean; + accessibleViewFocusOnCommandExecution: number; +} + +export const terminalAccessibilityConfiguration: IStringDictionary = { + [TerminalAccessibilitySettingId.AccessibleViewPreserveCursorPosition]: { + markdownDescription: localize('terminal.integrated.accessibleViewPreserveCursorPosition', "Preserve the cursor position on reopen of the terminal's accessible view rather than setting it to the bottom of the buffer."), + type: 'boolean', + default: false + }, + [TerminalAccessibilitySettingId.AccessibleViewFocusOnCommandExecution]: { + markdownDescription: localize('terminal.integrated.accessibleViewFocusOnCommandExecution', "Focus the terminal accessible view when a command is executed."), + type: 'boolean', + default: false + }, +}; diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts index 44eabbf13f6b1..14a3330d3f0e8 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.chat.contribution.ts @@ -7,11 +7,23 @@ import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/com import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalInlineChatAccessibleViewContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibleView'; import { TerminalChatController } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController'; - -import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; import { TerminalChatAccessibilityHelpContribution } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatAccessibilityHelp'; +// #region Terminal Contributions + registerTerminalContribution(TerminalChatController.ID, TerminalChatController, false); +// #endregion + +// #region Contributions + registerWorkbenchContribution2(TerminalInlineChatAccessibleViewContribution.ID, TerminalInlineChatAccessibleViewContribution, WorkbenchPhase.Eventually); registerWorkbenchContribution2(TerminalChatAccessibilityHelpContribution.ID, TerminalChatAccessibilityHelpContribution, WorkbenchPhase.Eventually); + +// #endregion + +// #region Actions + +import 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChatActions'; + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts index 692c2588c4a0a..3ac4007888441 100644 --- a/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/developer/browser/terminal.developer.contribution.ts @@ -20,14 +20,15 @@ import { IInternalXtermTerminal, ITerminalContribution, ITerminalInstance, IXter import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import type { Terminal } from '@xterm/xterm'; import { ITerminalCommand, TerminalCapability, type ICommandDetectionCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; import { IStatusbarService, StatusbarAlignment, type IStatusbarEntry, type IStatusbarEntryAccessor } from 'vs/workbench/services/statusbar/browser/statusbar'; +import { TerminalDeveloperCommandId } from 'vs/workbench/contrib/terminalContrib/developer/common/terminal.developer'; registerTerminalAction({ - id: TerminalCommandId.ShowTextureAtlas, + id: TerminalDeveloperCommandId.ShowTextureAtlas, title: localize2('workbench.action.terminal.showTextureAtlas', 'Show Terminal Texture Atlas'), category: Categories.Developer, precondition: ContextKeyExpr.or(TerminalContextKeys.isOpen), @@ -59,7 +60,7 @@ registerTerminalAction({ }); registerTerminalAction({ - id: TerminalCommandId.WriteDataToTerminal, + id: TerminalDeveloperCommandId.WriteDataToTerminal, title: localize2('workbench.action.terminal.writeDataToTerminal', 'Write Data to Terminal'), category: Categories.Developer, run: async (c, accessor) => { @@ -95,7 +96,7 @@ registerTerminalAction({ registerTerminalAction({ - id: TerminalCommandId.RestartPtyHost, + id: TerminalDeveloperCommandId.RestartPtyHost, title: localize2('workbench.action.terminal.restartPtyHost', 'Restart Pty Host'), category: Categories.Developer, run: async (c, accessor) => { diff --git a/src/vs/workbench/contrib/terminalContrib/developer/common/terminal.developer.ts b/src/vs/workbench/contrib/terminalContrib/developer/common/terminal.developer.ts new file mode 100644 index 0000000000000..39d0f040af1ce --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/developer/common/terminal.developer.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum TerminalDeveloperCommandId { + WriteDataToTerminal = 'workbench.action.terminal.writeDataToTerminal', + ShowTextureAtlas = 'workbench.action.terminal.showTextureAtlas', + RestartPtyHost = 'workbench.action.terminal.restartPtyHost', +} diff --git a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts index e12eb04ebd5b0..faf67e936151b 100644 --- a/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/environmentChanges/browser/terminal.environmentChanges.contribution.ts @@ -17,6 +17,8 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic // TODO: The rest of the terminal environment changes feature should move here /~https://github.com/microsoft/vscode/issues/177241 +// #region Actions + registerActiveInstanceAction({ id: TerminalCommandId.ShowEnvironmentContributions, title: localize2('workbench.action.terminal.showEnvironmentContributions', 'Show Environment Contributions'), @@ -45,6 +47,7 @@ registerActiveInstanceAction({ } }); +// #endregion function describeEnvironmentChanges(collection: IMergedEnvironmentVariableCollection, scope: EnvironmentVariableScope | undefined): string { let content = `# ${localize('envChanges', 'Terminal Environment Changes')}`; diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/media/terminalFind.css b/src/vs/workbench/contrib/terminalContrib/find/browser/media/terminalFind.css new file mode 100644 index 0000000000000..544d4e639f8ff --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/media/terminalFind.css @@ -0,0 +1,21 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.hc-black .xterm-find-result-decoration, +.hc-light .xterm-find-result-decoration { + outline-style: dotted !important; +} + +.hc-black .xterm-find-result-decoration, +.hc-light .xterm-find-result-decoration { + outline-style: solid !important; +} + +.xterm-find-active-result-decoration { + outline-style: solid !important; + outline-width: 2px !important; + /* Ensure the active decoration is above the regular decoration */ + z-index: 7 !important; +} diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts index 5556b7e5d7f4c..c93f1236a78c5 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminal.find.contribution.ts @@ -16,10 +16,14 @@ import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IT import { registerActiveInstanceAction, registerActiveXtermAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { TerminalFindWidget } from 'vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { TerminalFindCommandId } from 'vs/workbench/contrib/terminalContrib/find/common/terminal.find'; +import 'vs/css!./media/terminalFind'; + +// #region Terminal Contributions class TerminalFindContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.find'; @@ -97,8 +101,12 @@ class TerminalFindContribution extends Disposable implements ITerminalContributi } registerTerminalContribution(TerminalFindContribution.ID, TerminalFindContribution, true); +// #endregion + +// #region Actions + registerActiveXtermAction({ - id: TerminalCommandId.FindFocus, + id: TerminalFindCommandId.FindFocus, title: localize2('workbench.action.terminal.focusFind', 'Focus Find'), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.KeyF, @@ -113,7 +121,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.FindHide, + id: TerminalFindCommandId.FindHide, title: localize2('workbench.action.terminal.hideFind', 'Hide Find'), keybinding: { primary: KeyCode.Escape, @@ -129,7 +137,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ToggleFindRegex, + id: TerminalFindCommandId.ToggleFindRegex, title: localize2('workbench.action.terminal.toggleFindRegex', 'Toggle Find Using Regex'), keybinding: { primary: KeyMod.Alt | KeyCode.KeyR, @@ -146,7 +154,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ToggleFindWholeWord, + id: TerminalFindCommandId.ToggleFindWholeWord, title: localize2('workbench.action.terminal.toggleFindWholeWord', 'Toggle Find Using Whole Word'), keybinding: { primary: KeyMod.Alt | KeyCode.KeyW, @@ -163,7 +171,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.ToggleFindCaseSensitive, + id: TerminalFindCommandId.ToggleFindCaseSensitive, title: localize2('workbench.action.terminal.toggleFindCaseSensitive', 'Toggle Find Using Case Sensitive'), keybinding: { primary: KeyMod.Alt | KeyCode.KeyC, @@ -180,7 +188,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.FindNext, + id: TerminalFindCommandId.FindNext, title: localize2('workbench.action.terminal.findNext', 'Find Next'), keybinding: [ { @@ -207,7 +215,7 @@ registerActiveXtermAction({ }); registerActiveXtermAction({ - id: TerminalCommandId.FindPrevious, + id: TerminalFindCommandId.FindPrevious, title: localize2('workbench.action.terminal.findPrevious', 'Find Previous'), keybinding: [ { @@ -235,7 +243,7 @@ registerActiveXtermAction({ // Global workspace file search registerActiveInstanceAction({ - id: TerminalCommandId.SearchWorkspace, + id: TerminalFindCommandId.SearchWorkspace, title: localize2('workbench.action.terminal.searchWorkspace', 'Search Workspace'), keybinding: [ { @@ -246,3 +254,5 @@ registerActiveInstanceAction({ ], run: (activeInstance, c, accessor) => findInFilesCommand(accessor, { query: activeInstance.selection }) }); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts index eee6410fd9b01..eec3455a76584 100644 --- a/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/find/browser/terminalFindWidget.ts @@ -14,11 +14,11 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; import type { ISearchOptions } from '@xterm/addon-search'; -import { TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { openContextMenu } from 'vs/workbench/contrib/terminalContrib/find/browser/textInputContextMenu'; import { IDisposable } from 'vs/base/common/lifecycle'; import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { TerminalFindCommandId } from 'vs/workbench/contrib/terminalContrib/find/common/terminal.find'; const TERMINAL_FIND_WIDGET_INITIAL_WIDTH = 419; @@ -46,12 +46,12 @@ export class TerminalFindWidget extends SimpleFindWidget { showResultCount: true, initialWidth: TERMINAL_FIND_WIDGET_INITIAL_WIDTH, enableSash: true, - appendCaseSensitiveActionId: TerminalCommandId.ToggleFindCaseSensitive, - appendRegexActionId: TerminalCommandId.ToggleFindRegex, - appendWholeWordsActionId: TerminalCommandId.ToggleFindWholeWord, - previousMatchActionId: TerminalCommandId.FindPrevious, - nextMatchActionId: TerminalCommandId.FindNext, - closeWidgetActionId: TerminalCommandId.FindHide, + appendCaseSensitiveActionId: TerminalFindCommandId.ToggleFindCaseSensitive, + appendRegexActionId: TerminalFindCommandId.ToggleFindRegex, + appendWholeWordsActionId: TerminalFindCommandId.ToggleFindWholeWord, + previousMatchActionId: TerminalFindCommandId.FindPrevious, + nextMatchActionId: TerminalFindCommandId.FindNext, + closeWidgetActionId: TerminalFindCommandId.FindHide, type: 'Terminal', matchesLimit: XtermTerminalConstants.SearchHighlightLimit }, _contextViewService, _contextKeyService, hoverService, keybindingService); diff --git a/src/vs/workbench/contrib/terminalContrib/find/common/terminal.find.ts b/src/vs/workbench/contrib/terminalContrib/find/common/terminal.find.ts new file mode 100644 index 0000000000000..99d0cdf130a79 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/find/common/terminal.find.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum TerminalFindCommandId { + FindFocus = 'workbench.action.terminal.focusFind', + FindHide = 'workbench.action.terminal.hideFind', + FindNext = 'workbench.action.terminal.findNext', + FindPrevious = 'workbench.action.terminal.findPrevious', + ToggleFindRegex = 'workbench.action.terminal.toggleFindRegex', + ToggleFindWholeWord = 'workbench.action.terminal.toggleFindWholeWord', + ToggleFindCaseSensitive = 'workbench.action.terminal.toggleFindCaseSensitive', + SearchWorkspace = 'workbench.action.terminal.searchWorkspace', +} + +export const defaultTerminalFindCommandToSkipShell = [ + TerminalFindCommandId.FindFocus, + TerminalFindCommandId.FindHide, + TerminalFindCommandId.FindNext, + TerminalFindCommandId.FindPrevious, + TerminalFindCommandId.ToggleFindRegex, + TerminalFindCommandId.ToggleFindWholeWord, + TerminalFindCommandId.ToggleFindCaseSensitive, + TerminalFindCommandId.SearchWorkspace, +]; diff --git a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts index d2dde144b7609..2217fb1aed13b 100644 --- a/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/highlight/browser/terminal.highlight.contribution.ts @@ -12,7 +12,7 @@ import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/brow import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; - +// #region Terminal Contributions class TerminalHighlightContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.highlight'; @@ -55,3 +55,5 @@ class TerminalHighlightContribution extends Disposable implements ITerminalContr } registerTerminalContribution(TerminalHighlightContribution.ID, TerminalHighlightContribution, false); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts index dbdaf9200a000..89d73f49f2508 100644 --- a/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/links/browser/terminal.links.contribution.ts @@ -16,7 +16,7 @@ import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IX import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId, isTerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessInfo, ITerminalProcessManager, isTerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; import { ITerminalLinkProviderService } from 'vs/workbench/contrib/terminalContrib/links/browser/links'; @@ -25,9 +25,16 @@ import { TerminalLinkProviderService } from 'vs/workbench/contrib/terminalContri import { TerminalLinkQuickpick } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkQuickpick'; import { TerminalLinkResolver } from 'vs/workbench/contrib/terminalContrib/links/browser/terminalLinkResolver'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { TerminalLinksCommandId } from 'vs/workbench/contrib/terminalContrib/links/common/terminal.links'; + +// #region Services registerSingleton(ITerminalLinkProviderService, TerminalLinkProviderService, InstantiationType.Delayed); +// #endregion + +// #region Terminal Contributions + class TerminalLinkContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.link'; @@ -103,10 +110,14 @@ class TerminalLinkContribution extends DisposableStore implements ITerminalContr registerTerminalContribution(TerminalLinkContribution.ID, TerminalLinkContribution, true); +// #endregion + +// #region Actions + const category = terminalStrings.actionCategory; registerActiveInstanceAction({ - id: TerminalCommandId.OpenDetectedLink, + id: TerminalLinksCommandId.OpenDetectedLink, title: localize2('workbench.action.terminal.openDetectedLink', 'Open Detected Link...'), f1: true, category, @@ -124,7 +135,7 @@ registerActiveInstanceAction({ run: (activeInstance) => TerminalLinkContribution.get(activeInstance)?.showLinkQuickpick() }); registerActiveInstanceAction({ - id: TerminalCommandId.OpenWebLink, + id: TerminalLinksCommandId.OpenWebLink, title: localize2('workbench.action.terminal.openLastUrlLink', 'Open Last URL Link'), metadata: { description: localize2('workbench.action.terminal.openLastUrlLink.description', 'Opens the last detected URL/URI link in the terminal') @@ -135,10 +146,12 @@ registerActiveInstanceAction({ run: (activeInstance) => TerminalLinkContribution.get(activeInstance)?.openRecentLink('url') }); registerActiveInstanceAction({ - id: TerminalCommandId.OpenFileLink, + id: TerminalLinksCommandId.OpenFileLink, title: localize2('workbench.action.terminal.openLastLocalFileLink', 'Open Last Local File Link'), f1: true, category, precondition: TerminalContextKeys.terminalHasBeenCreated, run: (activeInstance) => TerminalLinkContribution.get(activeInstance)?.openRecentLink('localFile') }); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/links/common/terminal.links.ts b/src/vs/workbench/contrib/terminalContrib/links/common/terminal.links.ts new file mode 100644 index 0000000000000..2c0be865dea16 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/links/common/terminal.links.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum TerminalLinksCommandId { + OpenDetectedLink = 'workbench.action.terminal.openDetectedLink', + OpenWordLink = 'workbench.action.terminal.openWordLink', + OpenFileLink = 'workbench.action.terminal.openFileLink', + OpenWebLink = 'workbench.action.terminal.openUrlLink', +} diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts index 21c627770546a..9444cf1090b1e 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon.ts @@ -36,8 +36,12 @@ import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; import { ICommandService } from 'vs/platform/commands/common/commands'; +const enum QuickFixDecorationSelector { + QuickFix = 'quick-fix' +} + const quickFixClasses = [ - DecorationSelector.QuickFix, + QuickFixDecorationSelector.QuickFix, DecorationSelector.Codicon, DecorationSelector.CommandDecoration, DecorationSelector.XtermDecoration @@ -268,7 +272,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon, height: rect.height }; - if (e.classList.contains(DecorationSelector.QuickFix)) { + if (e.classList.contains(QuickFixDecorationSelector.QuickFix)) { if (this._currentRenderContext) { this._currentRenderContext.anchor = anchor; } diff --git a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts index faa206bdcc28f..9533aa208d50f 100644 --- a/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/quickFix/browser/terminal.quickFix.contribution.ts @@ -14,7 +14,7 @@ import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/wor import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; import { ITerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFix'; import { TerminalQuickFixAddon } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/quickFixAddon'; @@ -22,10 +22,14 @@ import { freePort, gitCreatePr, gitPull, gitPushSetUpstream, gitSimilar, gitTwoD import { TerminalQuickFixService } from 'vs/workbench/contrib/terminalContrib/quickFix/browser/terminalQuickFixService'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -// Services +// #region Services + registerSingleton(ITerminalQuickFixService, TerminalQuickFixService, InstantiationType.Delayed); -// Contributions +// #endregion + +// #region Contributions + class TerminalQuickFixContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'quickFix'; @@ -70,9 +74,16 @@ class TerminalQuickFixContribution extends DisposableStore implements ITerminalC } registerTerminalContribution(TerminalQuickFixContribution.ID, TerminalQuickFixContribution); -// Actions +// #endregion + +// #region Actions + +const enum TerminalQuickFixCommandId { + ShowQuickFixes = 'workbench.action.terminal.showQuickFixes', +} + registerActiveInstanceAction({ - id: TerminalCommandId.ShowQuickFixes, + id: TerminalQuickFixCommandId.ShowQuickFixes, title: localize2('workbench.action.terminal.showQuickFixes', 'Show Terminal Quick Fixes'), precondition: TerminalContextKeys.focus, keybinding: { @@ -81,3 +92,5 @@ registerActiveInstanceAction({ }, run: (activeInstance) => TerminalQuickFixContribution.get(activeInstance)?.addon?.showMenu() }); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminal.stickyScroll.contribution.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminal.stickyScroll.contribution.ts index 93a1102635457..6f6b7193334f1 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminal.stickyScroll.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminal.stickyScroll.contribution.ts @@ -3,10 +3,50 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/stickyScroll'; +import { localize, localize2 } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalStickyScrollContribution } from 'vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution'; +import { TerminalStickyScrollSettingId } from 'vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration'; -import 'vs/css!./media/stickyScroll'; -import './terminalStickyScrollColorRegistry'; +// #region Terminal Contributions registerTerminalContribution(TerminalStickyScrollContribution.ID, TerminalStickyScrollContribution, true); + +// #endregion + +// #region Actions + +const enum TerminalStickyScrollCommandId { + ToggleStickyScroll = 'workbench.action.terminal.toggleStickyScroll', +} + +registerTerminalAction({ + id: TerminalStickyScrollCommandId.ToggleStickyScroll, + title: localize2('workbench.action.terminal.toggleStickyScroll', 'Toggle Sticky Scroll'), + toggled: { + condition: ContextKeyExpr.equals(`config.${TerminalStickyScrollSettingId.Enabled}`, true), + title: localize('stickyScroll', "Sticky Scroll"), + mnemonicTitle: localize({ key: 'miStickyScroll', comment: ['&& denotes a mnemonic'] }, "&&Sticky Scroll"), + }, + run: (c, accessor) => { + const configurationService = accessor.get(IConfigurationService); + const newValue = !configurationService.getValue(TerminalStickyScrollSettingId.Enabled); + return configurationService.updateValue(TerminalStickyScrollSettingId.Enabled, newValue); + }, + menu: [ + { id: MenuId.TerminalStickyScrollContext } + ] +}); + +// #endregion + +// #region Colors + +import './terminalStickyScrollColorRegistry'; + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts index 3fb4a9f9322d6..5ab1af0d0eb3c 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { Color } from 'vs/base/common/color'; -import 'vs/css!./media/stickyScroll'; import { localize } from 'vs/nls'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor } from 'vs/platform/theme/common/colorUtils'; export const terminalStickyScrollBackground = registerColor('terminalStickyScroll.background', { light: null, diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution.ts index a3730a5300469..69a38b8142e40 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollContribution.ts @@ -12,11 +12,11 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalInstance, TerminalInstanceColorProvider } from 'vs/workbench/contrib/terminal/browser/terminalInstance'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TerminalStickyScrollSettingId } from 'vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration'; import { TerminalStickyScrollOverlay } from 'vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay'; export class TerminalStickyScrollContribution extends Disposable implements ITerminalContribution { @@ -45,7 +45,7 @@ export class TerminalStickyScrollContribution extends Disposable implements ITer super(); this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { - if (!e || e.affectsConfiguration(TerminalSettingId.StickyScrollEnabled)) { + if (!e || e.affectsConfiguration(TerminalStickyScrollSettingId.Enabled)) { this._refreshState(); } })); @@ -118,6 +118,6 @@ export class TerminalStickyScrollContribution extends Disposable implements ITer private _shouldBeEnabled(): boolean { const capability = this._instance.capabilities.get(TerminalCapability.CommandDetection); - return !!(this._configurationService.getValue(TerminalSettingId.StickyScrollEnabled) && capability && this._xterm?.raw?.element); + return !!(this._configurationService.getValue(TerminalStickyScrollSettingId.Enabled) && capability && this._xterm?.raw?.element); } } diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index eac420466bbf8..194ee23694e42 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -2,12 +2,11 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { CanvasAddon as CanvasAddonType } from '@xterm/addon-canvas'; + import type { SerializeAddon as SerializeAddonType } from '@xterm/addon-serialize'; import type { IBufferLine, IMarker, ITerminalOptions, ITheme, Terminal as RawXtermTerminal, Terminal as XTermTerminal } from '@xterm/xterm'; import { importAMDNodeModule } from 'vs/amdX'; import { $, addDisposableListener, addStandardDisposableListener, getWindow } from 'vs/base/browser/dom'; -import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { memoize, throttle } from 'vs/base/common/decorators'; import { Event } from 'vs/base/common/event'; import { Disposable, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -21,13 +20,13 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandDetectionCapability, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITerminalInstance, IXtermColorProvider, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { openContextMenu } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { TERMINAL_CONFIG_SECTION, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; import { terminalStrings } from 'vs/workbench/contrib/terminal/common/terminalStrings'; +import { TerminalStickyScrollSettingId } from 'vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration'; import { terminalStickyScrollBackground, terminalStickyScrollHoverBackground } from 'vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry'; const enum OverlayState { @@ -48,9 +47,6 @@ export class TerminalStickyScrollOverlay extends Disposable { private _stickyScrollOverlay?: RawXtermTerminal; private _serializeAddon?: SerializeAddonType; - private readonly _canvasAddon = this._register(new MutableDisposable()); - private _pendingCanvasAddon?: CancelablePromise; - private _element?: HTMLElement; private _currentStickyCommand?: ITerminalCommand | ICurrentPartialCommand; private _currentContent?: string; @@ -86,8 +82,8 @@ export class TerminalStickyScrollOverlay extends Disposable { // React to configuration changes this._register(Event.runAndSubscribe(configurationService.onDidChangeConfiguration, e => { - if (!e || e.affectsConfiguration(TerminalSettingId.StickyScrollMaxLineCount)) { - this._rawMaxLineCount = configurationService.getValue(TerminalSettingId.StickyScrollMaxLineCount); + if (!e || e.affectsConfiguration(TerminalStickyScrollSettingId.MaxLineCount)) { + this._rawMaxLineCount = configurationService.getValue(TerminalStickyScrollSettingId.MaxLineCount); } })); @@ -119,13 +115,14 @@ export class TerminalStickyScrollOverlay extends Disposable { })); this._getSerializeAddonConstructor().then(SerializeAddon => { + if (this._store.isDisposed) { + return; + } this._serializeAddon = this._register(new SerializeAddon()); this._xterm.raw.loadAddon(this._serializeAddon); // Trigger a render as the serialize addon is required to render this._refresh(); }); - - this._syncGpuAccelerationState(); }); } @@ -177,9 +174,6 @@ export class TerminalStickyScrollOverlay extends Disposable { private _setVisible(isVisible: boolean) { if (isVisible) { this._ensureElement(); - // The GPU acceleration state may be changes at any time and there is no event to listen - // to currently. - this._syncGpuAccelerationState(); } this._element?.classList.toggle(CssClasses.Visible, isVisible); } @@ -419,36 +413,6 @@ export class TerminalStickyScrollOverlay extends Disposable { } this._stickyScrollOverlay.resize(this._xterm.raw.cols, this._stickyScrollOverlay.rows); this._stickyScrollOverlay.options = this._getOptions(); - this._syncGpuAccelerationState(); - } - - private _syncGpuAccelerationState() { - if (!this._stickyScrollOverlay) { - return; - } - const overlay = this._stickyScrollOverlay; - - // The Webgl renderer isn't used here as there are a limited number of webgl contexts - // available within a given page. This is a single row that isn't rendered too often so the - // performance isn't as important - if (this._xterm.isGpuAccelerated) { - if (!this._canvasAddon.value && !this._pendingCanvasAddon) { - this._pendingCanvasAddon = createCancelablePromise(async token => { - const CanvasAddon = await this._getCanvasAddonConstructor(); - if (!token.isCancellationRequested && !this._store.isDisposed) { - this._canvasAddon.value = new CanvasAddon(); - if (this._canvasAddon.value) { // The MutableDisposable could be disposed - overlay.loadAddon(this._canvasAddon.value); - } - } - this._pendingCanvasAddon = undefined; - }); - } - } else { - this._canvasAddon.clear(); - this._pendingCanvasAddon?.cancel(); - this._pendingCanvasAddon = undefined; - } } private _getOptions(): ITerminalOptions { @@ -486,12 +450,6 @@ export class TerminalStickyScrollOverlay extends Disposable { }; } - @memoize - private async _getCanvasAddonConstructor(): Promise { - const m = await importAMDNodeModule('@xterm/addon-canvas', 'lib/xterm-addon-canvas.js'); - return m.CanvasAddon; - } - @memoize private async _getSerializeAddonConstructor(): Promise { const m = await importAMDNodeModule('@xterm/addon-serialize', 'lib/addon-serialize.js'); diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts new file mode 100644 index 0000000000000..8e9c34d32f401 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/common/terminalStickyScrollConfiguration.ts @@ -0,0 +1,34 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IStringDictionary } from 'vs/base/common/collections'; +import { localize } from 'vs/nls'; +import type { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import product from 'vs/platform/product/common/product'; + +export const enum TerminalStickyScrollSettingId { + Enabled = 'terminal.integrated.stickyScroll.enabled', + MaxLineCount = 'terminal.integrated.stickyScroll.maxLineCount', +} + +export interface ITerminalStickyScrollConfiguration { + enabled: boolean; + maxLineCount: number; +} + +export const terminalStickyScrollConfiguration: IStringDictionary = { + [TerminalStickyScrollSettingId.Enabled]: { + markdownDescription: localize('stickyScroll.enabled', "Shows the current command at the top of the terminal."), + type: 'boolean', + default: product.quality !== 'stable' + }, + [TerminalStickyScrollSettingId.MaxLineCount]: { + markdownDescription: localize('stickyScroll.maxLineCount', "Defines the maximum number of sticky lines to show. Sticky scroll lines will never exceed 40% of the viewport regardless of this setting."), + type: 'number', + default: 5, + minimum: 1, + maximum: 10 + }, +}; diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts index 512373ba4a296..7a781570251a5 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminal.suggest.contribution.ts @@ -3,23 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; import * as dom from 'vs/base/browser/dom'; -import { DisposableStore, toDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { localize2 } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; -import { SuggestAddon } from 'vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon'; -import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; -import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; -import { ContextKeyExpr, IContextKey, IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey'; +import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; -import { registerActiveInstanceAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; -import { localize2 } from 'vs/nls'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode } from 'vs/base/common/keyCodes'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; +import { SuggestAddon } from 'vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon'; +import { TerminalSuggestCommandId } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest'; + +// #region Terminal Contributions class TerminalSuggestContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.suggest'; @@ -84,9 +87,12 @@ class TerminalSuggestContribution extends DisposableStore implements ITerminalCo registerTerminalContribution(TerminalSuggestContribution.ID, TerminalSuggestContribution); -// Actions +// #endregion + +// #region Actions + registerActiveInstanceAction({ - id: TerminalCommandId.SelectPrevSuggestion, + id: TerminalSuggestCommandId.SelectPrevSuggestion, title: localize2('workbench.action.terminal.selectPrevSuggestion', 'Select the Previous Suggestion'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -99,7 +105,7 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalCommandId.SelectPrevPageSuggestion, + id: TerminalSuggestCommandId.SelectPrevPageSuggestion, title: localize2('workbench.action.terminal.selectPrevPageSuggestion', 'Select the Previous Page Suggestion'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -112,7 +118,7 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalCommandId.SelectNextSuggestion, + id: TerminalSuggestCommandId.SelectNextSuggestion, title: localize2('workbench.action.terminal.selectNextSuggestion', 'Select the Next Suggestion'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -125,7 +131,7 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalCommandId.SelectNextPageSuggestion, + id: TerminalSuggestCommandId.SelectNextPageSuggestion, title: localize2('workbench.action.terminal.selectNextPageSuggestion', 'Select the Next Page Suggestion'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -138,7 +144,7 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalCommandId.AcceptSelectedSuggestion, + id: TerminalSuggestCommandId.AcceptSelectedSuggestion, title: localize2('workbench.action.terminal.acceptSelectedSuggestion', 'Accept Selected Suggestion'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -152,7 +158,7 @@ registerActiveInstanceAction({ }); registerActiveInstanceAction({ - id: TerminalCommandId.HideSuggestWidget, + id: TerminalSuggestCommandId.HideSuggestWidget, title: localize2('workbench.action.terminal.hideSuggestWidget', 'Hide Suggest Widget'), f1: false, precondition: ContextKeyExpr.and(ContextKeyExpr.or(TerminalContextKeys.processSupported, TerminalContextKeys.terminalHasBeenCreated), TerminalContextKeys.focus, TerminalContextKeys.isOpen, TerminalContextKeys.suggestWidgetVisible), @@ -163,3 +169,5 @@ registerActiveInstanceAction({ }, run: (activeInstance) => TerminalSuggestContribution.get(activeInstance)?.addon?.hideSuggestWidget() }); + +// #endregion diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts index c861284ce4729..d46c7f3ae04e8 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts @@ -16,16 +16,21 @@ import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { activeContrastBorder } from 'vs/platform/theme/common/colorRegistry'; -import { ISuggestController } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalConfigurationService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { TerminalStorageKeys } from 'vs/workbench/contrib/terminal/common/terminalStorageKeys'; import type { ITerminalAddon, Terminal } from '@xterm/xterm'; + import { getListStyles } from 'vs/platform/theme/browser/defaultStyles'; import { TerminalCapability, type ITerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/capabilities'; import type { IPromptInputModel, IPromptInputModelState } from 'vs/platform/terminal/common/capabilities/commandDetection/promptInputModel'; import { ShellIntegrationOscPs } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; +import type { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { terminalSuggestConfigSection, type ITerminalSuggestConfiguration } from 'vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration'; const enum VSCodeOscPt { Completions = 'Completions', + CompletionsPwshCommands = 'CompletionsPwshCommands', CompletionsBash = 'CompletionsBash', CompletionsBashFirstWord = 'CompletionsBashFirstWord' } @@ -69,6 +74,15 @@ const pwshTypeToIconMap: { [type: string]: ThemeIcon | undefined } = { 13: Codicon.symbolKeyword }; +export interface ISuggestController { + selectPreviousSuggestion(): void; + selectPreviousPageSuggestion(): void; + selectNextSuggestion(): void; + selectNextPageSuggestion(): void; + acceptSelectedSuggestion(suggestion?: Pick): void; + hideSuggestWidget(): void; +} + export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggestController { private _terminal?: Terminal; @@ -88,6 +102,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest private _leadingLineContent?: string; private _cursorIndexDelta: number = 0; + static requestCompletionsSequence = '\x1b[24~e'; // F12,e + private readonly _onBell = this._register(new Emitter()); readonly onBell = this._onBell.event; private readonly _onAcceptedCompletion = this._register(new Emitter()); @@ -96,7 +112,9 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest constructor( private readonly _capabilities: ITerminalCapabilityStore, private readonly _terminalSuggestWidgetVisibleContextKey: IContextKey, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService ) { super(); @@ -134,7 +152,43 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._screen = screen; } + private _requestCompletions(): void { + // TODO: Debounce? Prevent this flooding the channel + this._onAcceptedCompletion.fire(SuggestAddon.requestCompletionsSequence); + } + private _sync(promptInputState: IPromptInputModelState): void { + const config = this._configurationService.getValue(terminalSuggestConfigSection); + const enabled = config.enabled || this._terminalConfigurationService.config.shellIntegration?.suggestEnabled; + if (!enabled) { + return; + } + + if (!this._terminalSuggestWidgetVisibleContextKey.get()) { + // If input has been added + if (!this._mostRecentPromptInputState || promptInputState.cursorIndex > this._mostRecentPromptInputState.cursorIndex) { + let sent = false; + + // Quick suggestions + if (config.quickSuggestions) { + const completionPrefix = promptInputState.value.substring(0, promptInputState.cursorIndex); + if (promptInputState.cursorIndex === 1 || completionPrefix.match(/([\s\[])[^\s]$/)) { + this._requestCompletions(); + sent = true; + } + } + + // Trigger characters + if (config.suggestOnTriggerCharacters && !sent) { + const lastChar = promptInputState.value.at(promptInputState.cursorIndex - 1); + if (lastChar?.match(/[\\\/\-]/)) { + this._requestCompletions(); + sent = true; + } + } + } + } + this._mostRecentPromptInputState = promptInputState; if (!this._promptInputModel || !this._terminal || !this._suggestWidget || !this._initialPromptInputState) { return; @@ -142,6 +196,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest this._currentPromptInputState = promptInputState; + // Hide the widget if the cursor moves to the left of the initial position as the // completions are no longer valid if (this._currentPromptInputState.cursorIndex < this._initialPromptInputState.cursorIndex) { @@ -152,19 +207,15 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest if (this._terminalSuggestWidgetVisibleContextKey.get()) { const inputBeforeCursor = this._currentPromptInputState.value.substring(0, this._currentPromptInputState.cursorIndex); this._cursorIndexDelta = this._currentPromptInputState.cursorIndex - this._initialPromptInputState.cursorIndex; - this._suggestWidget.setLineContext(new LineContext(inputBeforeCursor, this._cursorIndexDelta)); } // Hide and clear model if there are no more items if (!this._suggestWidget.hasCompletions()) { this.hideSuggestWidget(); - // TODO: Don't request every time; refine completions - // this._onAcceptedCompletion.fire('\x1b[24~e'); return; } - // TODO: Expose on xterm.js const dimensions = this._getTerminalDimensions(); if (!dimensions.width || !dimensions.height) { return; @@ -191,6 +242,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest case VSCodeOscPt.Completions: this._handleCompletionsSequence(this._terminal, data, command, args); return true; + case VSCodeOscPt.CompletionsPwshCommands: + this._handleCompletionsPwshCommandsSequence(this._terminal, data, command, args); case VSCodeOscPt.CompletionsBash: this._handleCompletionsBashSequence(this._terminal, data, command, args); return true; @@ -204,42 +257,68 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest private _handleCompletionsSequence(terminal: Terminal, data: string, command: string, args: string[]): void { // Nothing to handle if the terminal is not attached - if (!terminal.element || !this._enableWidget) { + if (!terminal.element || !this._enableWidget || !this._promptInputModel) { return; } - const replacementIndex = parseInt(args[0]); - const replacementLength = parseInt(args[1]); - if (!args[3]) { - this._onBell.fire(); - return; - } + let replacementIndex = 0; + let replacementLength = this._promptInputModel.cursorIndex; - let completionList: IPwshCompletion[] | IPwshCompletion = JSON.parse(data.slice(command.length + args[0].length + args[1].length + args[2].length + 4/*semi-colons*/)); + const payload = data.slice(command.length + args[0].length + args[1].length + args[2].length + 4/*semi-colons*/); + let completionList: IPwshCompletion[] | IPwshCompletion = args.length === 0 || payload.length === 0 ? [] : JSON.parse(payload); if (!Array.isArray(completionList)) { completionList = [completionList]; } const completions = completionList.map((e: any) => { return new SimpleCompletionItem({ - label: e.CompletionText, + label: e.ListItemText, + completionText: e.CompletionText, icon: pwshTypeToIconMap[e.ResultType], detail: e.ToolTip }); }); - this._leadingLineContent = completions[0].completion.label.slice(0, replacementLength); - this._cursorIndexDelta = 0; - const model = new SimpleCompletionModel(completions, new LineContext(this._leadingLineContent, replacementIndex), replacementIndex, replacementLength); - if (completions.length === 1) { - const insertText = completions[0].completion.label.substring(replacementLength); - if (insertText.length === 0) { - this._onBell.fire(); - return; - } + this._leadingLineContent = this._promptInputModel.value.substring(0, this._promptInputModel.cursorIndex); + + // If there's no space it means this is a command, add cached commands list to completions + const firstChar = this._leadingLineContent.length === 0 ? '' : this._leadingLineContent[0]; + if (this._leadingLineContent.trim().includes(' ') || firstChar === '[') { + replacementIndex = parseInt(args[0]); + replacementLength = parseInt(args[1]); + this._leadingLineContent = completions[0]?.completion.label.slice(0, replacementLength) ?? ''; + } else { + completions.push(...this._cachedPwshCommands); } + this._cursorIndexDelta = replacementIndex; + + const model = new SimpleCompletionModel(completions, new LineContext(this._leadingLineContent, replacementIndex), replacementIndex, replacementLength); this._handleCompletionModel(model); } + // TODO: These aren't persisted across reloads + private _cachedPwshCommands: Set = new Set(); + private _handleCompletionsPwshCommandsSequence(terminal: Terminal, data: string, command: string, args: string[]): void { + const type = args[0]; + let completionList: IPwshCompletion[] | IPwshCompletion = JSON.parse(data.slice(command.length + type.length + 2/*semi-colons*/)); + if (!Array.isArray(completionList)) { + completionList = [completionList]; + } + const set = this._cachedPwshCommands; + set.clear(); + + const completions = completionList.map((e: any) => { + return new SimpleCompletionItem({ + label: e.ListItemText, + completionText: e.CompletionText, + icon: pwshTypeToIconMap[e.ResultType], + detail: e.ToolTip + }); + }); + for (const c of completions) { + set.add(c); + } + } + // TODO: These aren't persisted across reloads // TODO: Allow triggering anywhere in the first word based on the cached completions private _cachedBashAliases: Set = new Set(); @@ -338,9 +417,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest } private _getTerminalDimensions(): { width: number; height: number } { + const cssCellDims = (this._terminal as any as { _core: IXtermCore })._core._renderService.dimensions.css.cell; return { - width: (this._terminal as any)._core._renderService.dimensions.css.cell.width, - height: (this._terminal as any)._core._renderService.dimensions.css.cell.height, + width: cssCellDims.width, + height: cssCellDims.height, }; } @@ -382,16 +462,9 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest listInactiveFocusBackground: editorSuggestWidgetSelectedBackground, listInactiveFocusOutline: activeContrastBorder })); - this._suggestWidget.onDidSelect(async e => this.acceptSelectedSuggestion(e)); - this._suggestWidget.onDidHide(() => this._terminalSuggestWidgetVisibleContextKey.set(false)); - this._suggestWidget.onDidShow(() => { - this._initialPromptInputState = { - value: this._promptInputModel!.value, - cursorIndex: this._promptInputModel!.cursorIndex, - ghostTextIndex: this._promptInputModel!.ghostTextIndex - }; - this._terminalSuggestWidgetVisibleContextKey.set(true); - }); + this._register(this._suggestWidget.onDidSelect(async e => this.acceptSelectedSuggestion(e))); + this._register(this._suggestWidget.onDidHide(() => this._terminalSuggestWidgetVisibleContextKey.set(false))); + this._register(this._suggestWidget.onDidShow(() => this._terminalSuggestWidgetVisibleContextKey.set(true))); } return this._suggestWidget; } @@ -426,21 +499,21 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest const additionalInput = currentPromptInputState.value.substring(initialPromptInputState.cursorIndex, currentPromptInputState.cursorIndex); // Get the final completion on the right side of the cursor - const initialInput = initialPromptInputState.value.substring(0, initialPromptInputState.cursorIndex); + const initialInput = initialPromptInputState.value.substring(0, (this._leadingLineContent?.length ?? 0)); const lastSpaceIndex = initialInput.lastIndexOf(' '); - const finalCompletionRightSide = suggestion.item.completion.label.substring(initialPromptInputState.cursorIndex - (lastSpaceIndex === -1 ? 0 : lastSpaceIndex + 1)); + const completion = suggestion.item.completion; + const completionText = completion.completionText ?? completion.label; + const finalCompletionRightSide = completionText.substring((this._leadingLineContent?.length ?? 0) - (lastSpaceIndex === -1 ? 0 : lastSpaceIndex + 1)); // Get the final completion on the right side of the cursor if it differs from the initial // propmt input state - let finalCompletionLeftSide = suggestion.item.completion.label.substring(0, initialPromptInputState.cursorIndex - (lastSpaceIndex === -1 ? 0 : lastSpaceIndex + 1)); + let finalCompletionLeftSide = completionText.substring(0, (this._leadingLineContent?.length ?? 0) - (lastSpaceIndex === -1 ? 0 : lastSpaceIndex + 1)); if (initialInput.endsWith(finalCompletionLeftSide)) { finalCompletionLeftSide = ''; } // Send the completion this._onAcceptedCompletion.fire([ - // Disable suggestions - '\x1b[24~y', // Backspace to remove all additional input '\x7F'.repeat(additionalInput.length), // Backspace to remove left side of completion @@ -449,8 +522,6 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest finalCompletionLeftSide, // Write the completion finalCompletionRightSide, - // Enable suggestions - '\x1b[24~z', ].join('')); this.hideSuggestWidget(); diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts new file mode 100644 index 0000000000000..ab4b902ee654c --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminal.suggest.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const enum TerminalSuggestCommandId { + SelectPrevSuggestion = 'workbench.action.terminal.selectPrevSuggestion', + SelectPrevPageSuggestion = 'workbench.action.terminal.selectPrevPageSuggestion', + SelectNextSuggestion = 'workbench.action.terminal.selectNextSuggestion', + SelectNextPageSuggestion = 'workbench.action.terminal.selectNextPageSuggestion', + AcceptSelectedSuggestion = 'workbench.action.terminal.acceptSelectedSuggestion', + HideSuggestWidget = 'workbench.action.terminal.hideSuggestWidget', +} + +export const defaultTerminalSuggestCommandsToSkipShell = [ + TerminalSuggestCommandId.SelectPrevSuggestion, + TerminalSuggestCommandId.SelectPrevPageSuggestion, + TerminalSuggestCommandId.SelectNextSuggestion, + TerminalSuggestCommandId.SelectNextPageSuggestion, + TerminalSuggestCommandId.AcceptSelectedSuggestion, + TerminalSuggestCommandId.HideSuggestWidget, +]; diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts new file mode 100644 index 0000000000000..f465e37f07653 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/suggest/common/terminalSuggestConfiguration.ts @@ -0,0 +1,52 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IStringDictionary } from 'vs/base/common/collections'; +import { localize } from 'vs/nls'; +import type { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; +import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; + +export const enum TerminalSuggestSettingId { + Enabled = 'terminal.integrated.suggest.enabled', + EnabledLegacy = 'terminal.integrated.shellIntegration.suggestEnabled', + QuickSuggestions = 'terminal.integrated.suggest.quickSuggestions', + SuggestOnTriggerCharacters = 'terminal.integrated.suggest.suggestOnTriggerCharacters', +} + +export const terminalSuggestConfigSection = 'terminal.integrated.suggest'; + +export interface ITerminalSuggestConfiguration { + enabled: boolean; + quickSuggestions: boolean; + suggestOnTriggerCharacters: boolean; +} + +export const terminalSuggestConfiguration: IStringDictionary = { + [TerminalSuggestSettingId.Enabled]: { + restricted: true, + markdownDescription: localize('suggest.enabled', "Enables experimental terminal Intellisense suggestions for supported shells ({0}) when {1} is set to {2}.\n\nIf shell integration is installed manually, {3} needs to be set to {4} before calling the shell integration script.", 'PowerShell', `\`#${TerminalSettingId.ShellIntegrationEnabled}#\``, '`true`', '`VSCODE_SUGGEST`', '`1`'), + type: 'boolean', + default: false, + }, + [TerminalSuggestSettingId.EnabledLegacy]: { + restricted: true, + markdownDescription: localize('suggest.enabled', "Enables experimental terminal Intellisense suggestions for supported shells ({0}) when {1} is set to {2}.\n\nIf shell integration is installed manually, {3} needs to be set to {4} before calling the shell integration script.", 'PowerShell', `\`#${TerminalSettingId.ShellIntegrationEnabled}#\``, '`true`', '`VSCODE_SUGGEST`', '`1`'), + type: 'boolean', + default: false, + markdownDeprecationMessage: localize('suggest.enabled.deprecated', 'This setting is deprecated, please use `{0}` instead.', `\`#${TerminalSuggestSettingId.Enabled}#\``) + }, + [TerminalSuggestSettingId.QuickSuggestions]: { + restricted: true, + markdownDescription: localize('suggest.quickSuggestions', "Controls whether suggestions should automatically show up while typing. Also be aware of the {0}-setting which controls if suggestions are triggered by special characters.", `\`#${TerminalSuggestSettingId.SuggestOnTriggerCharacters}#\``), + type: 'boolean', + default: true, + }, + [TerminalSuggestSettingId.SuggestOnTriggerCharacters]: { + restricted: true, + markdownDescription: localize('suggest.suggestOnTriggerCharacters', "Controls whether suggestions should automatically show up when typing trigger characters."), + type: 'boolean', + default: true, + }, +}; diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts index 1851d36cf7d3b..6977568149c0d 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminal.typeAhead.contribution.ts @@ -6,13 +6,13 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { TypeAheadAddon } from 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon'; -import { ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import type { Terminal as RawXtermTerminal } from '@xterm/xterm'; +import { TerminalTypeAheadSettingId, type ITerminalTypeAheadConfiguration } from 'vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration'; class TerminalTypeAheadContribution extends DisposableStore implements ITerminalContribution { static readonly ID = 'terminal.typeAhead'; @@ -37,7 +37,7 @@ class TerminalTypeAheadContribution extends DisposableStore implements ITerminal xtermReady(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { this._loadTypeAheadAddon(xterm.raw); this.add(this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(TerminalSettingId.LocalEchoEnabled)) { + if (e.affectsConfiguration(TerminalTypeAheadSettingId.LocalEchoEnabled)) { this._loadTypeAheadAddon(xterm.raw); } })); @@ -49,7 +49,7 @@ class TerminalTypeAheadContribution extends DisposableStore implements ITerminal } private _loadTypeAheadAddon(xterm: RawXtermTerminal): void { - const enabled = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoEnabled; + const enabled = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoEnabled; const isRemote = !!this._processManager.remoteAuthority; if (enabled === 'off' || enabled === 'auto' && !isRemote) { this._addon?.dispose(); diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts index d6d672c9aca09..6a0656d8f1b96 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts @@ -12,8 +12,9 @@ import { escapeRegExpCharacters } from 'vs/base/common/strings'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { XtermAttributes, IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; -import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IBeforeProcessDataEvent, ITerminalProcessManager, TERMINAL_CONFIG_SECTION } from 'vs/workbench/contrib/terminal/common/terminal'; import type { IBuffer, IBufferCell, IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, type ITerminalTypeAheadConfiguration } from 'vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration'; const enum VT { Esc = '\x1b', @@ -1134,7 +1135,7 @@ class TypeAheadStyle implements IDisposable { undo!: string; private _csiHandler?: IDisposable; - constructor(value: ITerminalConfiguration['localEchoStyle'], private readonly _terminal: Terminal) { + constructor(value: ITerminalTypeAheadConfiguration['localEchoStyle'], private readonly _terminal: Terminal) { this.onUpdate(value); } @@ -1240,7 +1241,7 @@ class TypeAheadStyle implements IDisposable { /** * Updates the current typeahead style. */ - onUpdate(style: ITerminalConfiguration['localEchoStyle']) { + onUpdate(style: ITerminalTypeAheadConfiguration['localEchoStyle']) { const { applyArgs, undoArgs } = this._getArgs(style); this._applyArgs = applyArgs; this._undoArgs = this._originalUndoArgs = undoArgs; @@ -1248,7 +1249,7 @@ class TypeAheadStyle implements IDisposable { this.undo = TypeAheadStyle._compileArgs(this._undoArgs); } - private _getArgs(style: ITerminalConfiguration['localEchoStyle']) { + private _getArgs(style: ITerminalTypeAheadConfiguration['localEchoStyle']) { switch (style) { case 'bold': return { applyArgs: [1], undoArgs: [22] }; @@ -1289,8 +1290,8 @@ export const enum CharPredictState { export class TypeAheadAddon extends Disposable implements ITerminalAddon { private _typeaheadStyle?: TypeAheadStyle; - private _typeaheadThreshold = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoLatencyThreshold; - private _excludeProgramRe = compileExcludeRegexp(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoExcludePrograms); + private _typeaheadThreshold = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoLatencyThreshold; + private _excludeProgramRe = compileExcludeRegexp(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoExcludePrograms); protected _lastRow?: { y: number; startingX: number; endingX: number; charState: CharPredictState }; protected _timeline?: PredictionTimeline; private _terminalTitle = ''; @@ -1311,7 +1312,7 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { } activate(terminal: Terminal): void { - const style = this._typeaheadStyle = this._register(new TypeAheadStyle(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoStyle, terminal)); + const style = this._typeaheadStyle = this._register(new TypeAheadStyle(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoStyle, terminal)); const timeline = this._timeline = new PredictionTimeline(terminal, this._typeaheadStyle); const stats = this.stats = this._register(new PredictionStats(this._timeline)); @@ -1328,9 +1329,9 @@ export class TypeAheadAddon extends Disposable implements ITerminalAddon { })); this._register(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TERMINAL_CONFIG_SECTION)) { - style.onUpdate(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoStyle); - this._typeaheadThreshold = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoLatencyThreshold; - this._excludeProgramRe = compileExcludeRegexp(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoExcludePrograms); + style.onUpdate(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoStyle); + this._typeaheadThreshold = this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoLatencyThreshold; + this._excludeProgramRe = compileExcludeRegexp(this._configurationService.getValue(TERMINAL_CONFIG_SECTION).localEchoExcludePrograms); this._reevaluatePredictorState(stats, timeline); } })); diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration.ts new file mode 100644 index 0000000000000..6b4b2d9f917e4 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IStringDictionary } from 'vs/base/common/collections'; +import { localize } from 'vs/nls'; +import type { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; + +export const DEFAULT_LOCAL_ECHO_EXCLUDE: ReadonlyArray = ['vim', 'vi', 'nano', 'tmux']; + +export const enum TerminalTypeAheadSettingId { + LocalEchoLatencyThreshold = 'terminal.integrated.localEchoLatencyThreshold', + LocalEchoEnabled = 'terminal.integrated.localEchoEnabled', + LocalEchoExcludePrograms = 'terminal.integrated.localEchoExcludePrograms', + LocalEchoStyle = 'terminal.integrated.localEchoStyle', +} + +export interface ITerminalTypeAheadConfiguration { + localEchoLatencyThreshold: number; + localEchoExcludePrograms: ReadonlyArray; + localEchoEnabled: 'auto' | 'on' | 'off'; + localEchoStyle: 'bold' | 'dim' | 'italic' | 'underlined' | 'inverted' | string; +} + +export const terminalTypeAheadConfiguration: IStringDictionary = { + [TerminalTypeAheadSettingId.LocalEchoLatencyThreshold]: { + description: localize('terminal.integrated.localEchoLatencyThreshold', "Length of network delay, in milliseconds, where local edits will be echoed on the terminal without waiting for server acknowledgement. If '0', local echo will always be on, and if '-1' it will be disabled."), + type: 'integer', + minimum: -1, + default: 30, + }, + [TerminalTypeAheadSettingId.LocalEchoEnabled]: { + markdownDescription: localize('terminal.integrated.localEchoEnabled', "When local echo should be enabled. This will override {0}", '`#terminal.integrated.localEchoLatencyThreshold#`'), + type: 'string', + enum: ['on', 'off', 'auto'], + enumDescriptions: [ + localize('terminal.integrated.localEchoEnabled.on', "Always enabled"), + localize('terminal.integrated.localEchoEnabled.off', "Always disabled"), + localize('terminal.integrated.localEchoEnabled.auto', "Enabled only for remote workspaces") + ], + default: 'auto' + }, + [TerminalTypeAheadSettingId.LocalEchoExcludePrograms]: { + description: localize('terminal.integrated.localEchoExcludePrograms', "Local echo will be disabled when any of these program names are found in the terminal title."), + type: 'array', + items: { + type: 'string', + uniqueItems: true + }, + default: DEFAULT_LOCAL_ECHO_EXCLUDE, + }, + [TerminalTypeAheadSettingId.LocalEchoStyle]: { + description: localize('terminal.integrated.localEchoStyle', "Terminal style of locally echoed text; either a font style or an RGB color."), + default: 'dim', + anyOf: [ + { + enum: ['bold', 'dim', 'italic', 'underlined', 'inverted', '#ff0000'], + }, + { + type: 'string', + format: 'color-hex', + } + ] + }, +}; diff --git a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts index c6524b94eb2bd..86972f65fa702 100644 --- a/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/typeAhead/test/browser/terminalTypeAhead.test.ts @@ -8,11 +8,12 @@ import type { IBuffer, Terminal } from '@xterm/xterm'; import { SinonStub, stub, useFakeTimers } from 'sinon'; import { Emitter } from 'vs/base/common/event'; import { CharPredictState, IPrediction, PredictionStats, TypeAheadAddon } from 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon'; -import { DEFAULT_LOCAL_ECHO_EXCLUDE, IBeforeProcessDataEvent, ITerminalConfiguration, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; +import { IBeforeProcessDataEvent, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DEFAULT_LOCAL_ECHO_EXCLUDE, type ITerminalTypeAheadConfiguration } from 'vs/workbench/contrib/terminalContrib/typeAhead/common/terminalTypeAheadConfiguration'; const CSI = `\x1b[`; @@ -84,7 +85,7 @@ suite('Workbench - Terminal Typeahead', () => { suite('timeline', () => { let onBeforeProcessData: Emitter; let publicLog: SinonStub; - let config: ITerminalConfiguration; + let config: ITerminalTypeAheadConfiguration; let addon: TestTypeAheadAddon; const predictedHelloo = [ @@ -103,7 +104,7 @@ suite('Workbench - Terminal Typeahead', () => { setup(() => { onBeforeProcessData = ds.add(new Emitter()); - config = upcastPartial({ + config = upcastPartial({ localEchoStyle: 'italic', localEchoLatencyThreshold: 0, localEchoExcludePrograms: DEFAULT_LOCAL_ECHO_EXCLUDE, diff --git a/src/vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution.ts b/src/vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution.ts index aed5f68e6bbeb..12f2a06f6870c 100644 --- a/src/vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/zoom/browser/terminal.zoom.contribution.ts @@ -13,12 +13,13 @@ import { TerminalSettingId } from 'vs/platform/terminal/common/terminal'; import { IDetachedTerminalInstance, ITerminalContribution, ITerminalInstance, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { registerTerminalContribution } from 'vs/workbench/contrib/terminal/browser/terminalExtensions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ITerminalProcessInfo, ITerminalProcessManager, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; +import { ITerminalProcessInfo, ITerminalProcessManager } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalWidgetManager } from 'vs/workbench/contrib/terminal/browser/widgets/widgetManager'; import { registerTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { localize2 } from 'vs/nls'; import { isNumber } from 'vs/base/common/types'; import { defaultTerminalFontSize } from 'vs/workbench/contrib/terminal/common/terminalConfiguration'; +import { TerminalZoomCommandId, TerminalZoomSettingId } from 'vs/workbench/contrib/terminalContrib/zoom/common/terminal.zoom'; class TerminalMouseWheelZoomContribution extends Disposable implements ITerminalContribution { static readonly ID = 'terminal.mouseWheelZoom'; @@ -46,8 +47,8 @@ class TerminalMouseWheelZoomContribution extends Disposable implements ITerminal xtermOpen(xterm: IXtermTerminal & { raw: RawXtermTerminal }): void { this._register(Event.runAndSubscribe(this._configurationService.onDidChangeConfiguration, e => { - if (!e || e.affectsConfiguration(TerminalSettingId.MouseWheelZoom)) { - if (!!this._configurationService.getValue(TerminalSettingId.MouseWheelZoom)) { + if (!e || e.affectsConfiguration(TerminalZoomSettingId.MouseWheelZoom)) { + if (!!this._configurationService.getValue(TerminalZoomSettingId.MouseWheelZoom)) { this._setupMouseWheelZoomListener(xterm.raw); } else { this._listener.clear(); @@ -125,7 +126,7 @@ class TerminalMouseWheelZoomContribution extends Disposable implements ITerminal registerTerminalContribution(TerminalMouseWheelZoomContribution.ID, TerminalMouseWheelZoomContribution, true); registerTerminalAction({ - id: TerminalCommandId.FontZoomIn, + id: TerminalZoomCommandId.FontZoomIn, title: localize2('fontZoomIn', 'Increase Font Size'), run: async (c, accessor) => { const configurationService = accessor.get(IConfigurationService); @@ -137,7 +138,7 @@ registerTerminalAction({ }); registerTerminalAction({ - id: TerminalCommandId.FontZoomOut, + id: TerminalZoomCommandId.FontZoomOut, title: localize2('fontZoomOut', 'Decrease Font Size'), run: async (c, accessor) => { const configurationService = accessor.get(IConfigurationService); @@ -149,7 +150,7 @@ registerTerminalAction({ }); registerTerminalAction({ - id: TerminalCommandId.FontZoomReset, + id: TerminalZoomCommandId.FontZoomReset, title: localize2('fontZoomReset', 'Reset Font Size'), run: async (c, accessor) => { const configurationService = accessor.get(IConfigurationService); diff --git a/src/vs/workbench/contrib/terminalContrib/zoom/common/terminal.zoom.ts b/src/vs/workbench/contrib/terminalContrib/zoom/common/terminal.zoom.ts new file mode 100644 index 0000000000000..e7da97b2dc4f9 --- /dev/null +++ b/src/vs/workbench/contrib/terminalContrib/zoom/common/terminal.zoom.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IStringDictionary } from 'vs/base/common/collections'; +import { isMacintosh } from 'vs/base/common/platform'; +import { localize } from 'vs/nls'; +import type { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; + +export const enum TerminalZoomCommandId { + FontZoomIn = 'workbench.action.terminal.fontZoomIn', + FontZoomOut = 'workbench.action.terminal.fontZoomOut', + FontZoomReset = 'workbench.action.terminal.fontZoomReset', +} + +export const enum TerminalZoomSettingId { + MouseWheelZoom = 'terminal.integrated.mouseWheelZoom', +} + +export const terminalZoomConfiguration: IStringDictionary = { + [TerminalZoomSettingId.MouseWheelZoom]: { + markdownDescription: isMacintosh + ? localize('terminal.integrated.mouseWheelZoom.mac', "Zoom the font of the terminal when using mouse wheel and holding `Cmd`.") + : localize('terminal.integrated.mouseWheelZoom', "Zoom the font of the terminal when using mouse wheel and holding `Ctrl`."), + type: 'boolean', + default: false + }, +}; diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index e26b2f2bd80e3..7af1ee2c33128 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -198,6 +198,8 @@ export const testsUnderUri = async function* (testService: ITestService, ident: } else if (!test.item.uri) { queue.push(test.children.values()); continue; + } else if (ident.extUri.isEqualOrParent(test.item.uri, uri)) { + yield test; } else if (ident.extUri.isEqualOrParent(uri, test.item.uri)) { if (test.expand === TestItemExpandState.Expandable) { await testService.collection.expand(test.item.extId, 1); @@ -206,8 +208,6 @@ export const testsUnderUri = async function* (testService: ITestService, ident: await waitForTestToBeIdle(testService, test); } queue.push(test.children.values()); - } else if (ident.extUri.isEqualOrParent(test.item.uri, uri)) { - yield test; } } } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 147ad6fc7cc8b..1d374e644c3c0 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -10,7 +10,7 @@ import { Button } from 'vs/base/browser/ui/button/button'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; -import { coalesce, equals, flatten } from 'vs/base/common/arrays'; +import { coalesce, equals } from 'vs/base/common/arrays'; import { Delayer, Throttler } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; @@ -570,7 +570,7 @@ export class GettingStartedPage extends EditorPane { this.updateMediaSourceForColorMode(mediaElement, media.path); this.stepDisposables.add(addDisposableListener(this.stepMediaComponent, 'click', () => { - const hrefs = flatten(stepToExpand.description.map(lt => lt.nodes.filter((node): node is ILink => typeof node !== 'string').map(node => node.href))); + const hrefs = stepToExpand.description.map(lt => lt.nodes.filter((node): node is ILink => typeof node !== 'string').map(node => node.href)).flat(); if (hrefs.length === 1) { const href = hrefs[0]; if (href.startsWith('http')) { @@ -602,7 +602,7 @@ export class GettingStartedPage extends EditorPane { })); this.stepDisposables.add(addDisposableListener(this.stepMediaComponent, 'click', () => { - const hrefs = flatten(stepToExpand.description.map(lt => lt.nodes.filter((node): node is ILink => typeof node !== 'string').map(node => node.href))); + const hrefs = stepToExpand.description.map(lt => lt.nodes.filter((node): node is ILink => typeof node !== 'string').map(node => node.href)).flat(); if (hrefs.length === 1) { const href = hrefs[0]; if (href.startsWith('http')) { @@ -644,7 +644,7 @@ export class GettingStartedPage extends EditorPane { if (serializedContextKeyExprs) { const contextKeyExprs = coalesce(serializedContextKeyExprs.map(expr => ContextKeyExpr.deserialize(expr))); - const watchingKeys = new Set(flatten(contextKeyExprs.map(expr => expr.keys()))); + const watchingKeys = new Set(contextKeyExprs.flatMap(expr => expr.keys())); this.stepDisposables.add(this.contextService.onDidChangeContext(e => { if (e.affectsSome(watchingKeys)) { postTrueKeysMessage(); } diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts index db46c01330b3c..d70c6800b7784 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService.ts @@ -26,7 +26,7 @@ import { ILink, LinkedText, parseLinkedText } from 'vs/base/common/linkedText'; import { walkthroughsExtensionPoint } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedExtensionPoint'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { dirname } from 'vs/base/common/path'; -import { coalesce, flatten } from 'vs/base/common/arrays'; +import { coalesce } from 'vs/base/common/arrays'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { localize, localize2 } from 'vs/nls'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -567,10 +567,10 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ } if (!step.completionEvents.length) { - step.completionEvents = coalesce(flatten( + step.completionEvents = coalesce( step.description .filter(linkedText => linkedText.nodes.length === 1) // only buttons - .map(linkedText => + .flatMap(linkedText => linkedText.nodes .filter(((node): node is ILink => typeof node !== 'string')) .map(({ href }) => { @@ -581,7 +581,7 @@ export class WalkthroughsService extends Disposable implements IWalkthroughsServ return 'onLink:' + href; } return undefined; - })))); + }))); } if (!step.completionEvents.length) { diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 4e4e8f5b7d0e2..12609c51916d1 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -18,7 +18,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { INativeHostService } from 'vs/platform/native/common/native'; -import { hasNativeTitlebar, useWindowControlsOverlay } from 'vs/platform/window/common/window'; +import { hasNativeTitlebar, useWindowControlsOverlay, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -37,7 +37,7 @@ export class NativeTitlebarPart extends BrowserTitlebarPart { return super.minimumHeight; } - return (this.isCommandCenterVisible ? 35 : this.macTitlebarSize) / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1); + return (this.isCommandCenterVisible ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : this.macTitlebarSize) / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1); } override get maximumHeight(): number { return this.minimumHeight; } diff --git a/src/vs/workbench/services/authentication/browser/authenticationExtensionsService.ts b/src/vs/workbench/services/authentication/browser/authenticationExtensionsService.ts index 3ffce7c44d9ce..1cdb2d5a5aeeb 100644 --- a/src/vs/workbench/services/authentication/browser/authenticationExtensionsService.ts +++ b/src/vs/workbench/services/authentication/browser/authenticationExtensionsService.ts @@ -246,23 +246,30 @@ export class AuthenticationExtensionsService extends Disposable implements IAuth quickPick.placeholder = nls.localize('getSessionPlateholder', "Select an account for '{0}' to use or Esc to cancel", extensionName); quickPick.onDidAccept(async _ => { - const session = quickPick.selectedItems[0].session ?? await this._authenticationService.createSession(providerId, scopes); + quickPick.dispose(); + let session = quickPick.selectedItems[0].session; + if (!session) { + try { + session = await this._authenticationService.createSession(providerId, scopes); + } catch (e) { + reject(e); + return; + } + } const accountName = session.account.label; this._authenticationAccessService.updateAllowedExtensions(providerId, accountName, [{ id: extensionId, name: extensionName, allowed: true }]); this.updateSessionPreference(providerId, extensionId, session); this.removeAccessRequest(providerId, extensionId); - quickPick.dispose(); resolve(session); }); quickPick.onDidHide(_ => { + quickPick.dispose(); if (!quickPick.selectedItems[0]) { reject('User did not consent to account access'); } - - quickPick.dispose(); }); quickPick.show(); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts index 3056d9dec2665..31139bf5737cc 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationEditing.test.ts @@ -108,7 +108,7 @@ suite('ConfigurationEditing', () => { const workspaceFolder = joinPath(ROOT, uuid.generateUuid()); await fileService.createFolder(workspaceFolder); - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; environmentService.policyFile = joinPath(workspaceFolder, 'policies.json'); instantiationService.stub(IEnvironmentService, environmentService); diff --git a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts index a7ef7154baa46..eab8d6064f58c 100644 --- a/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configurationService.test.ts @@ -188,7 +188,7 @@ suite('WorkspaceContextService - Folder', () => { uriIdentityService, new NullLogService(), new NullPolicyService())); - await (testObject).initialize(convertToWorkspacePayload(folder)); + await testObject.initialize(convertToWorkspacePayload(folder)); const actual = testObject.getWorkspaceFolder(joinPath(folder, 'a').with({ query: 'myquery=1' })); @@ -228,7 +228,7 @@ suite('WorkspaceContextService - Workspace', () => { await fileService.createFolder(folderB); await fileService.writeFile(configResource, VSBuffer.fromString(JSON.stringify(workspace, null, '\t'))); - const instantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService = workbenchInstantiationService(undefined, disposables); const environmentService = TestEnvironmentService; const remoteAgentService = disposables.add(disposables.add(instantiationService.createInstance(RemoteAgentService))); instantiationService.stub(IRemoteAgentService, remoteAgentService); @@ -290,7 +290,7 @@ suite('WorkspaceContextService - Workspace Editing', () => { await fileService.createFolder(folderB); await fileService.writeFile(configResource, VSBuffer.fromString(JSON.stringify(workspace, null, '\t'))); - const instantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService = workbenchInstantiationService(undefined, disposables); const environmentService = TestEnvironmentService; const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService)); instantiationService.stub(IRemoteAgentService, remoteAgentService); @@ -538,7 +538,7 @@ suite('WorkspaceService - Initialization', () => { await fileService.createFolder(folderB); await fileService.writeFile(configResource, VSBuffer.fromString(JSON.stringify(workspace, null, '\t'))); - const instantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService)); instantiationService.stub(IRemoteAgentService, remoteAgentService); @@ -558,7 +558,7 @@ suite('WorkspaceService - Initialization', () => { await testObject.initialize({ id: '' }); instantiationService.stub(ITextFileService, disposables.add(instantiationService.createInstance(TestTextFileService))); - instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); + instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); testObject.acquireInstantiationService(instantiationService); }); @@ -800,7 +800,7 @@ suite('WorkspaceConfigurationService - Folder', () => { const folder = joinPath(ROOT, 'a'); await fileService.createFolder(folder); - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; environmentService.policyFile = joinPath(folder, 'policies.json'); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService)); @@ -1613,7 +1613,7 @@ suite('WorkspaceConfigurationService - Profiles', () => { const folder = joinPath(ROOT, 'a'); await fileService.createFolder(folder); - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; environmentService.policyFile = joinPath(folder, 'policies.json'); const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService)); @@ -1637,7 +1637,7 @@ suite('WorkspaceConfigurationService - Profiles', () => { await workspaceService.initialize(convertToWorkspacePayload(folder)); instantiationService.stub(IKeybindingEditingService, disposables.add(instantiationService.createInstance(KeybindingsEditingService))); instantiationService.stub(ITextFileService, disposables.add(instantiationService.createInstance(TestTextFileService))); - instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); + instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); workspaceService.acquireInstantiationService(instantiationService); }); @@ -1968,7 +1968,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await fileService.createFolder(folderB); await fileService.writeFile(configResource, VSBuffer.fromString(JSON.stringify(workspace, null, '\t'))); - const instantiationService = workbenchInstantiationService(undefined, disposables); + const instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; const remoteAgentService = disposables.add(instantiationService.createInstance(RemoteAgentService)); instantiationService.stub(IRemoteAgentService, remoteAgentService); @@ -1990,7 +1990,7 @@ suite('WorkspaceConfigurationService-Multiroot', () => { await workspaceService.initialize(getWorkspaceIdentifier(configResource)); instantiationService.stub(IKeybindingEditingService, disposables.add(instantiationService.createInstance(KeybindingsEditingService))); instantiationService.stub(ITextFileService, disposables.add(instantiationService.createInstance(TestTextFileService))); - instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); + instantiationService.stub(ITextModelService, disposables.add(instantiationService.createInstance(TextModelResolverService))); jsonEditingServce = instantiationService.createInstance(JSONEditingService); instantiationService.stub(IJSONEditingService, jsonEditingServce); workspaceService.acquireInstantiationService(instantiationService); @@ -2709,7 +2709,7 @@ suite('WorkspaceConfigurationService - Remote Folder', () => { machineSettingsResource = joinPath(ROOT, 'machine-settings.json'); remoteSettingsResource = machineSettingsResource.with({ scheme: Schemas.vscodeRemote, authority: remoteAuthority }); - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); environmentService = TestEnvironmentService; const remoteEnvironmentPromise = new Promise>(c => resolveRemoteEnvironment = () => c({ settingsPath: remoteSettingsResource })); const remoteAgentService = instantiationService.stub(IRemoteAgentService, >{ getEnvironment: () => remoteEnvironmentPromise }); diff --git a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts index 45311b71c557f..5439e7be42f36 100644 --- a/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts +++ b/src/vs/workbench/services/dialogs/test/electron-sandbox/fileDialogService.test.ts @@ -78,7 +78,7 @@ suite('FileDialogService', function () { const testFile: URI = URI.file('/test/file'); setup(async function () { - disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); + disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); const configurationService = new TestConfigurationService(); await configurationService.setUserConfiguration('files', { simpleDialog: { enable: true } }); instantiationService.stub(IConfigurationService, configurationService); diff --git a/src/vs/workbench/services/editor/browser/editorResolverService.ts b/src/vs/workbench/services/editor/browser/editorResolverService.ts index d18ee039799a7..8275646f62eb0 100644 --- a/src/vs/workbench/services/editor/browser/editorResolverService.ts +++ b/src/vs/workbench/services/editor/browser/editorResolverService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as glob from 'vs/base/common/glob'; -import { distinct, firstOrDefault, flatten, insert } from 'vs/base/common/arrays'; +import { distinct, firstOrDefault, insert } from 'vs/base/common/arrays'; import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { basename, extname, isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -339,7 +339,7 @@ export class EditorResolverService extends Disposable implements IEditorResolver * Returns all editors as an array. Possible to contain duplicates */ private get _registeredEditors(): RegisteredEditors { - return flatten(Array.from(this._flattenedEditors.values())); + return Array.from(this._flattenedEditors.values()).flat(); } updateUserAssociations(globPattern: string, editorID: string): void { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 6b0dc0e8e2d6d..cd6d5c6f86ee4 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -5,7 +5,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IResourceEditorInput, IEditorOptions, EditorActivation, IResourceEditorInputIdentifier, ITextResourceEditorInput } from 'vs/platform/editor/common/editor'; -import { SideBySideEditor, IEditorPane, GroupIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, EditorInputWithOptions, isEditorInputWithOptions, IEditorIdentifier, IEditorCloseEvent, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, EditorInputCapabilities, isResourceDiffEditorInput, IUntypedEditorInput, isResourceEditorInput, isEditorInput, isEditorInputWithOptionsAndGroup, IFindEditorOptions, isResourceMergeEditorInput, IEditorWillOpenEvent } from 'vs/workbench/common/editor'; +import { SideBySideEditor, IEditorPane, GroupIdentifier, IUntitledTextResourceEditorInput, IResourceDiffEditorInput, EditorInputWithOptions, isEditorInputWithOptions, IEditorIdentifier, IEditorCloseEvent, ITextDiffEditorPane, IRevertOptions, SaveReason, EditorsOrder, IWorkbenchEditorConfiguration, EditorResourceAccessor, IVisibleEditorPane, EditorInputCapabilities, isResourceDiffEditorInput, IUntypedEditorInput, isResourceEditorInput, isEditorInput, isEditorInputWithOptionsAndGroup, IFindEditorOptions, isResourceMergeEditorInput, IEditorWillOpenEvent, IEditorControl } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { ResourceMap, ResourceSet } from 'vs/base/common/map'; @@ -14,6 +14,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { SideBySideEditor as SideBySideEditorPane } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { IEditorGroupsService, IEditorGroup, GroupsOrder, IEditorReplacement, isEditorReplacement, ICloseEditorOptions, IEditorGroupsContainer } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IUntypedEditorReplacement, IEditorService, ISaveEditorsOptions, ISaveAllEditorsOptions, IRevertAllEditorsOptions, IBaseSaveRevertAllEditorOptions, IOpenEditorsOptions, PreferredGroup, isPreferredGroup, IEditorsChangeEvent, ISaveEditorsResult } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -491,9 +492,18 @@ export class EditorService extends Disposable implements EditorServiceImpl { get visibleTextEditorControls(): Array { const visibleTextEditorControls: Array = []; for (const visibleEditorPane of this.visibleEditorPanes) { - const control = visibleEditorPane.getControl(); - if (isCodeEditor(control) || isDiffEditor(control)) { - visibleTextEditorControls.push(control); + const controls: Array = []; + if (visibleEditorPane instanceof SideBySideEditorPane) { + controls.push(visibleEditorPane.getPrimaryEditorPane()?.getControl()); + controls.push(visibleEditorPane.getSecondaryEditorPane()?.getControl()); + } else { + controls.push(visibleEditorPane.getControl()); + } + + for (const control of controls) { + if (isCodeEditor(control) || isDiffEditor(control)) { + visibleTextEditorControls.push(control); + } } } diff --git a/src/vs/workbench/services/editor/common/customEditorLabelService.ts b/src/vs/workbench/services/editor/common/customEditorLabelService.ts index 3eb55a67968c5..8264adcbd91c0 100644 --- a/src/vs/workbench/services/editor/common/customEditorLabelService.ts +++ b/src/vs/workbench/services/editor/common/customEditorLabelService.ts @@ -139,7 +139,10 @@ export class CustomEditorLabelService extends Disposable implements ICustomEdito for (const pattern of this.patterns) { let relevantPath: string; if (root && !pattern.isAbsolutePath) { - relevantPath = relativePath ?? getRelativePath(resourceDirname(root.uri), resource) ?? resource.path; + if (!relativePath) { + relativePath = getRelativePath(resourceDirname(root.uri), resource) ?? resource.path; + } + relevantPath = relativePath; } else { relevantPath = resource.path; } @@ -186,7 +189,7 @@ export class CustomEditorLabelService extends Disposable implements ICustomEdito if (n < 0) { nth = Math.abs(n) - 1; } else { - nth = length - 1 - n - 1; // -1 for the filename, -1 for 0-based index + nth = length - n - 1; } const nthDir = pathFragments[nth]; diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 1be2060f98c72..a44e9fd4f4f62 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -207,6 +207,9 @@ export interface IEditorService { /** * All text editor widgets that are currently visible across all editor groups. A text editor * widget is either a text or a diff editor. + * + * This property supports side-by-side editors as well, by returning both sides if they are + * text editor widgets. */ readonly visibleTextEditorControls: readonly (IEditor | IDiffEditor)[]; diff --git a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts index e1b6f5880f881..2b6fecc8eaf41 100644 --- a/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts +++ b/src/vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { distinct, flatten } from 'vs/base/common/arrays'; +import { distinct } from 'vs/base/common/arrays'; import { Emitter, Event } from 'vs/base/common/event'; import { JSONPath, parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -81,12 +81,12 @@ export class WorkspaceExtensionsConfigService extends Disposable implements IWor async getRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.recommendations ? c.recommendations.map(c => c.toLowerCase()) : []))); + return distinct(configs.flatMap(c => c.recommendations ? c.recommendations.map(c => c.toLowerCase()) : [])); } async getUnwantedRecommendations(): Promise { const configs = await this.getExtensionsConfigs(); - return distinct(flatten(configs.map(c => c.unwantedRecommendations ? c.unwantedRecommendations.map(c => c.toLowerCase()) : []))); + return distinct(configs.flatMap(c => c.unwantedRecommendations ? c.unwantedRecommendations.map(c => c.toLowerCase()) : [])); } async toggleRecommendation(extensionId: string): Promise { diff --git a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts index f1795ab490867..ae0a893194472 100644 --- a/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts +++ b/src/vs/workbench/services/extensions/test/browser/extensionStorageMigration.test.ts @@ -33,7 +33,7 @@ suite('ExtensionStorageMigration', () => { let instantiationService: TestInstantiationService; setup(() => { - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); const fileService = disposables.add(new FileService(new NullLogService())); disposables.add(fileService.registerProvider(ROOT.scheme, disposables.add(new InMemoryFileSystemProvider()))); diff --git a/src/vs/workbench/services/history/browser/historyService.ts b/src/vs/workbench/services/history/browser/historyService.ts index 7fde1ff6a00c2..5a232af3d86cc 100644 --- a/src/vs/workbench/services/history/browser/historyService.ts +++ b/src/vs/workbench/services/history/browser/historyService.ts @@ -835,13 +835,14 @@ export class HistoryService extends Disposable implements IHistoryService { // Side-by-side editors get special treatment: we try to distill the // possibly untyped resource inputs from both sides to be able to - // offer these entries from the history to the user still. + // offer these entries from the history to the user still unless + // they are excluded. else { const resourceInputs: IResourceEditorInput[] = []; const sideInputs = editor.primary.matches(editor.secondary) ? [editor.primary] : [editor.primary, editor.secondary]; for (const sideInput of sideInputs) { const candidateResourceInput = this.editorHelper.preferResourceEditorInput(sideInput); - if (isResourceEditorInput(candidateResourceInput)) { + if (isResourceEditorInput(candidateResourceInput) && this.includeInHistory(candidateResourceInput)) { resourceInputs.push(candidateResourceInput); } } diff --git a/src/vs/workbench/services/history/test/browser/historyService.test.ts b/src/vs/workbench/services/history/test/browser/historyService.test.ts index e275f65cef64c..06478095336ca 100644 --- a/src/vs/workbench/services/history/test/browser/historyService.test.ts +++ b/src/vs/workbench/services/history/test/browser/historyService.test.ts @@ -6,7 +6,7 @@ import * as assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite, toResource } from 'vs/base/test/common/utils'; import { URI } from 'vs/base/common/uri'; -import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor, TestTextFileEditor, workbenchTeardown } from 'vs/workbench/test/browser/workbenchTestServices'; +import { workbenchInstantiationService, TestFileEditorInput, registerTestEditor, createEditorPart, registerTestFileEditor, TestServiceAccessor, TestTextFileEditor, workbenchTeardown, registerTestSideBySideEditor } from 'vs/workbench/test/browser/workbenchTestServices'; import { EditorPart } from 'vs/workbench/browser/parts/editor/editorPart'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorGroupsService, GroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -28,13 +28,14 @@ import { Selection } from 'vs/editor/common/core/selection'; import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; suite('HistoryService', function () { const TEST_EDITOR_ID = 'MyTestEditorForEditorHistory'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForHistoyService'; - async function createServices(scope = GoScope.DEFAULT): Promise<[EditorPart, HistoryService, EditorService, ITextFileService, IInstantiationService, TestConfigurationService]> { + async function createServices(scope = GoScope.DEFAULT, configureSearchExclude = false): Promise<[EditorPart, HistoryService, EditorService, ITextFileService, IInstantiationService, TestConfigurationService]> { const instantiationService = workbenchInstantiationService(undefined, disposables); const part = await createEditorPart(instantiationService, disposables); @@ -49,6 +50,9 @@ suite('HistoryService', function () { } else if (scope === GoScope.EDITOR) { configurationService.setUserConfiguration('workbench.editor.navigationScope', 'editor'); } + if (configureSearchExclude) { + configurationService.setUserConfiguration('search', { exclude: { "**/node_modules/**": true } }); + } instantiationService.stub(IConfigurationService, configurationService); const historyService = disposables.add(instantiationService.createInstance(HistoryService)); @@ -63,6 +67,7 @@ suite('HistoryService', function () { setup(() => { disposables.add(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)])); + disposables.add(registerTestSideBySideEditor()); disposables.add(registerTestFileEditor()); }); @@ -660,8 +665,7 @@ suite('HistoryService', function () { } } - const [part, historyService, , , instantiationService, configurationService] = await createServices(); - configurationService.setUserConfiguration('search.exclude', '{ "**/node_modules/**": true }'); + const [part, historyService, editorService, , instantiationService] = await createServices(undefined, true); let history = historyService.getHistory(); assert.strictEqual(history.length, 0); @@ -698,6 +702,19 @@ suite('HistoryService', function () { history = historyService.getHistory(); assert.strictEqual(history.length, 2); + // side by side + const input5 = disposables.add(new TestFileEditorInputWithUntyped(URI.parse('file://bar5'), TEST_EDITOR_INPUT_ID)); + const input6 = disposables.add(new TestFileEditorInputWithUntyped(URI.file('file://bar1/node_modules/test.txt'), TEST_EDITOR_INPUT_ID)); + const input7 = new SideBySideEditorInput(undefined, undefined, input6, input5, editorService); + await part.activeGroup.openEditor(input7, { pinned: true }); + + history = historyService.getHistory(); + assert.strictEqual(history.length, 3); + input7.dispose(); + + history = historyService.getHistory(); + assert.strictEqual(history.length, 3); // only input5 survived, input6 is excluded via search.exclude + return workbenchTeardown(instantiationService); }); diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 769664b057427..3357adabc8228 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -311,12 +311,16 @@ export interface IWorkbenchLayoutService extends ILayoutService { getVisibleNeighborPart(part: Parts, direction: Direction): Parts | undefined; } -export function shouldShowCustomTitleBar(configurationService: IConfigurationService, window: Window, menuBarToggled?: boolean): boolean { +export function shouldShowCustomTitleBar(configurationService: IConfigurationService, window: Window, menuBarToggled?: boolean, zenModeActive?: boolean): boolean { if (!hasCustomTitlebar(configurationService)) { return false; } + if (zenModeActive) { + return false; + } + const inFullscreen = isFullscreen(window); const nativeTitleBarEnabled = hasNativeTitlebar(configurationService); diff --git a/src/vs/workbench/services/preferences/common/preferencesModels.ts b/src/vs/workbench/services/preferences/common/preferencesModels.ts index f0c0b5ca0f1c1..507844e02c8ac 100644 --- a/src/vs/workbench/services/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/services/preferences/common/preferencesModels.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, tail, coalesce } from 'vs/base/common/arrays'; +import { tail, coalesce } from 'vs/base/common/arrays'; import { IStringDictionary } from 'vs/base/common/collections'; import { Emitter, Event } from 'vs/base/common/event'; import { JSONVisitor, visit } from 'vs/base/common/json'; @@ -900,19 +900,18 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements builder.pushGroup(settingsGroup); // builder has rewritten settings ranges, fix match ranges - const fixedMatches = flatten( - filterMatches - .map(m => m.matches || []) - .map((settingMatches, i) => { - const setting = settingsGroup.sections[0].settings[i]; - return settingMatches.map(range => { - return new Range( - range.startLineNumber + setting.range.startLineNumber, - range.startColumn, - range.endLineNumber + setting.range.startLineNumber, - range.endColumn); - }); - })); + const fixedMatches = filterMatches + .map(m => m.matches || []) + .flatMap((settingMatches, i) => { + const setting = settingsGroup.sections[0].settings[i]; + return settingMatches.map(range => { + return new Range( + range.startLineNumber + setting.range.startLineNumber, + range.startColumn, + range.endLineNumber + setting.range.startLineNumber, + range.endColumn); + }); + }); return fixedMatches; } diff --git a/src/vs/workbench/services/remote/common/tunnelModel.ts b/src/vs/workbench/services/remote/common/tunnelModel.ts index f0ceaf1950c32..14f20e126ad98 100644 --- a/src/vs/workbench/services/remote/common/tunnelModel.ts +++ b/src/vs/workbench/services/remote/common/tunnelModel.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { flatten } from 'vs/base/common/arrays'; import { debounce } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; @@ -988,14 +987,14 @@ export class TunnelModel extends Disposable { } // Group calls to provide attributes by pid. - const allProviderResults = await Promise.all(flatten(this.portAttributesProviders.map(provider => { + const allProviderResults = await Promise.all(this.portAttributesProviders.flatMap(provider => { return Array.from(pidToPortsMapping.entries()).map(entry => { const portGroup = entry[1]; const matchingCandidate = matchingCandidates.get(portGroup[0]); return provider.providePortAttributes(portGroup, matchingCandidate?.pid, matchingCandidate?.detail, CancellationToken.None); }); - }))); + })); const providedAttributes: Map = new Map(); allProviderResults.forEach(attributes => attributes.forEach(attribute => { if (attribute) { diff --git a/src/vs/workbench/services/search/common/queryBuilder.ts b/src/vs/workbench/services/search/common/queryBuilder.ts index 9124249957aa6..9259afa1d89c6 100644 --- a/src/vs/workbench/services/search/common/queryBuilder.ts +++ b/src/vs/workbench/services/search/common/queryBuilder.ts @@ -228,7 +228,7 @@ export class QueryBuilder { }; if (options.onlyOpenEditors) { - const openEditors = arrays.coalesce(arrays.flatten(this.editorGroupsService.groups.map(group => group.editors.map(editor => editor.resource)))); + const openEditors = arrays.coalesce(this.editorGroupsService.groups.flatMap(group => group.editors.map(editor => editor.resource))); this.logService.trace('QueryBuilder#commonQuery - openEditor URIs', JSON.stringify(openEditors)); const openEditorsInQuery = openEditors.filter(editor => pathIncludedInQuery(queryProps, editor.fsPath)); const openEditorsQueryProps = this.commonQueryFromFileList(openEditorsInQuery); @@ -358,7 +358,7 @@ export class QueryBuilder { result.searchPaths = searchPaths; } - const exprSegments = arrays.flatten(expandedExprSegments); + const exprSegments = expandedExprSegments.flat(); const includePattern = patternListToIExpression(...exprSegments); if (includePattern) { result.pattern = includePattern; diff --git a/src/vs/workbench/services/search/common/searchService.ts b/src/vs/workbench/services/search/common/searchService.ts index 8d97feed7b330..cec7fb1114ce5 100644 --- a/src/vs/workbench/services/search/common/searchService.ts +++ b/src/vs/workbench/services/search/common/searchService.ts @@ -202,8 +202,8 @@ export class SearchService extends Disposable implements ISearchService { return { limitHit: completes[0] && completes[0].limitHit, stats: completes[0].stats, - messages: arrays.coalesce(arrays.flatten(completes.map(i => i.messages))).filter(arrays.uniqueFilter(message => message.type + message.text + message.trusted)), - results: arrays.flatten(completes.map((c: ISearchComplete) => c.results)) + messages: arrays.coalesce(completes.flatMap(i => i.messages)).filter(arrays.uniqueFilter(message => message.type + message.text + message.trusted)), + results: completes.flatMap((c: ISearchComplete) => c.results) }; })(); diff --git a/src/vs/workbench/services/search/common/textSearchManager.ts b/src/vs/workbench/services/search/common/textSearchManager.ts index eb83a7ae27ee7..84808af5691ec 100644 --- a/src/vs/workbench/services/search/common/textSearchManager.ts +++ b/src/vs/workbench/services/search/common/textSearchManager.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { flatten, mapArrayOrNot } from 'vs/base/common/arrays'; +import { mapArrayOrNot } from 'vs/base/common/arrays'; import { isThenable } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -81,11 +81,11 @@ export class TextSearchManager { const someFolderHitLImit = results.some(result => !!result && !!result.limitHit); resolve({ limitHit: this.isLimitHit || someFolderHitLImit, - messages: flatten(results.map(result => { + messages: results.flatMap(result => { if (!result?.message) { return []; } if (Array.isArray(result.message)) { return result.message; } else { return [result.message]; } - })), + }), stats: { type: this.processType } diff --git a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts index 1e6c7670117dc..bc51969faef04 100644 --- a/src/vs/workbench/services/search/test/common/searchHelpers.test.ts +++ b/src/vs/workbench/services/search/test/common/searchHelpers.test.ts @@ -7,17 +7,17 @@ import * as assert from 'assert'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch, ITextModel } from 'vs/editor/common/model'; -import { ISearchRange, ITextQuery, ITextSearchContext, QueryType } from 'vs/workbench/services/search/common/search'; +import { ISearchRange, ITextQuery, ITextSearchContext, ITextSearchResult, QueryType } from 'vs/workbench/services/search/common/search'; import { getTextSearchMatchWithModelContext, editorMatchesToTextSearchResults } from 'vs/workbench/services/search/common/searchHelpers'; suite('SearchHelpers', () => { suite('editorMatchesToTextSearchResults', () => { ensureNoDisposablesAreLeakedInTestSuite(); - const mockTextModel: ITextModel = { + const mockTextModel = { getLineContent(lineNumber: number): string { return '' + lineNumber; } - }; + } as ITextModel; function assertRangesEqual(actual: ISearchRange | ISearchRange[], expected: ISearchRange[]) { if (!Array.isArray(actual)) { @@ -77,7 +77,7 @@ suite('SearchHelpers', () => { ensureNoDisposablesAreLeakedInTestSuite(); const MOCK_LINE_COUNT = 100; - const mockTextModel: ITextModel = { + const mockTextModel = { getLineContent(lineNumber: number): string { if (lineNumber < 1 || lineNumber > MOCK_LINE_COUNT) { throw new Error(`invalid line count: ${lineNumber}`); @@ -89,7 +89,7 @@ suite('SearchHelpers', () => { getLineCount(): number { return MOCK_LINE_COUNT; } - }; + } as ITextModel; function getQuery(beforeContext?: number, afterContext?: number): ITextQuery { return { @@ -123,20 +123,20 @@ suite('SearchHelpers', () => { }]; assert.deepStrictEqual(getTextSearchMatchWithModelContext(matches, mockTextModel, getQuery(1, 2)), [ - { + { text: '1', lineNumber: 1 }, ...matches, - { + { text: '3', lineNumber: 3 }, - { + { text: '4', lineNumber: 4 }, - ]); + ] satisfies ITextSearchResult[]); }); test('multiple matches next to each other', () => { diff --git a/src/vs/workbench/services/suggest/browser/simpleCompletionItem.ts b/src/vs/workbench/services/suggest/browser/simpleCompletionItem.ts index 052731e56539d..3aa43e4c72711 100644 --- a/src/vs/workbench/services/suggest/browser/simpleCompletionItem.ts +++ b/src/vs/workbench/services/suggest/browser/simpleCompletionItem.ts @@ -19,6 +19,10 @@ export interface ISimpleCompletion { * The completion's detail which appears on the right of the list. */ detail?: string; + /** + * The completion's completion text which is used to actually insert the completion. + */ + completionText?: string; } export class SimpleCompletionItem { diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts index 7aa0c00f8c1cc..ec6d4c95742f6 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModelManager.ts @@ -269,12 +269,17 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE await Promises.settled(modelsToRestore.map(async modelToRestore => { + // From this moment on, only operate on the canonical resource + // to fix a potential data loss issue: + // /~https://github.com/microsoft/vscode/issues/211374 + const target = this.uriIdentityService.asCanonicalUri(modelToRestore.target); + // restore the model at the target. if we have previous dirty content, we pass it // over to be used, otherwise we force a reload from disk. this is important // because we know the file has changed on disk after the move and the model might // have still existed with the previous state. this ensures that the model is not // tracking a stale state. - const restoredModel = await this.resolve(modelToRestore.target, { + const restoredModel = await this.resolve(target, { reload: { async: false }, // enforce a reload contents: modelToRestore.snapshot ? createTextBufferFactoryFromSnapshot(modelToRestore.snapshot) : undefined, encoding: modelToRestore.encoding @@ -287,7 +292,7 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE modelToRestore.languageId && modelToRestore.languageId !== PLAINTEXT_LANGUAGE_ID && restoredModel.getLanguageId() === PLAINTEXT_LANGUAGE_ID && - extname(modelToRestore.target) !== PLAINTEXT_EXTENSION + extname(target) !== PLAINTEXT_EXTENSION ) { restoredModel.updateTextEditorModel(undefined, modelToRestore.languageId); } diff --git a/src/vs/workbench/services/themes/common/workbenchThemeService.ts b/src/vs/workbench/services/themes/common/workbenchThemeService.ts index f6791507b6252..a7be26f1be154 100644 --- a/src/vs/workbench/services/themes/common/workbenchThemeService.ts +++ b/src/vs/workbench/services/themes/common/workbenchThemeService.ts @@ -60,12 +60,18 @@ export const COLOR_THEME_DARK_INITIAL_COLORS = { 'activityBar.background': '#181818', 'statusBar.background': '#181818', 'statusBar.noFolderBackground': '#1f1f1f', + 'titleBar.border': '#2B2B2B', + 'titleBar.inactiveBackground': '#ff0000', + 'titleBar.inactiveForeground': '#9D9D9D' }; export const COLOR_THEME_LIGHT_INITIAL_COLORS = { 'activityBar.background': '#f8f8f8', 'statusBar.background': '#f8f8f8', - 'statusBar.noFolderBackground': '#f8f8f8' + 'statusBar.noFolderBackground': '#f8f8f8', + 'titleBar.border': '#E5E5E5', + 'titleBar.inactiveBackground': '#F8F8F8', + 'titleBar.inactiveForeground': '#8B949E', }; export interface IWorkbenchTheme { diff --git a/src/vs/workbench/services/views/common/viewContainerModel.ts b/src/vs/workbench/services/views/common/viewContainerModel.ts index 60009d0ff794d..2aec0216c4b95 100644 --- a/src/vs/workbench/services/views/common/viewContainerModel.ts +++ b/src/vs/workbench/services/views/common/viewContainerModel.ts @@ -156,7 +156,7 @@ class ViewDescriptorsState extends Disposable { changedStates.push({ id, visible: !storedState.isHidden }); } } else { - const workspaceViewState = storedWorkspaceViewsStates[id]; + const workspaceViewState: IStoredWorkspaceViewState | undefined = storedWorkspaceViewsStates[id]; this.set(id, { active: false, visibleGlobal: !storedState.isHidden, @@ -256,7 +256,7 @@ class ViewDescriptorsState extends Disposable { } private parseStoredGlobalState(value: string): { state: Map; hasDuplicates: boolean } { - const storedValue = >JSON.parse(value); + const storedValue: Array = JSON.parse(value); let hasDuplicates = false; const state = storedValue.reduce((result, storedState) => { if (typeof storedState === 'string' /* migration */) { diff --git a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts index 2a34154580b39..f37d4af42b73f 100644 --- a/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewContainerModel.test.ts @@ -51,7 +51,7 @@ suite('ViewContainerModel', () => { let storageService: IStorageService; setup(() => { - const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposableStore); + const instantiationService: TestInstantiationService = workbenchInstantiationService(undefined, disposableStore); contextKeyService = disposableStore.add(instantiationService.createInstance(ContextKeyService)); instantiationService.stub(IContextKeyService, contextKeyService); storageService = instantiationService.get(IStorageService); diff --git a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts index 58103b06c38cb..31b8bb8a6a335 100644 --- a/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts +++ b/src/vs/workbench/services/views/test/browser/viewDescriptorService.test.ts @@ -31,7 +31,7 @@ suite('ViewDescriptorService', () => { let instantiationService: TestInstantiationService; setup(() => { - disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); + disposables.add(instantiationService = workbenchInstantiationService(undefined, disposables)); instantiationService.stub(IContextKeyService, disposables.add(instantiationService.createInstance(ContextKeyService))); }); diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts index 547244e4e6da7..f445e7d36ddef 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopyManager.ts @@ -411,12 +411,17 @@ export class StoredFileWorkingCopyManager await Promises.settled(workingCopiesToRestore.map(async workingCopyToRestore => { + // From this moment on, only operate on the canonical resource + // to fix a potential data loss issue: + // /~https://github.com/microsoft/vscode/issues/211374 + const target = this.uriIdentityService.asCanonicalUri(workingCopyToRestore.target); + // Restore the working copy at the target. if we have previous dirty content, we pass it // over to be used, otherwise we force a reload from disk. this is important // because we know the file has changed on disk after the move and the working copy might // have still existed with the previous state. this ensures that the working copy is not // tracking a stale state. - await this.resolve(workingCopyToRestore.target, { + await this.resolve(target, { reload: { async: false }, // enforce a reload contents: workingCopyToRestore.snapshot }); diff --git a/src/vs/workbench/test/browser/codeeditor.test.ts b/src/vs/workbench/test/browser/codeeditor.test.ts index 6bea2f87b2e2e..686bc17e88bec 100644 --- a/src/vs/workbench/test/browser/codeeditor.test.ts +++ b/src/vs/workbench/test/browser/codeeditor.test.ts @@ -39,7 +39,7 @@ suite('Editor - Range decorations', () => { setup(() => { disposables = new DisposableStore(); - instantiationService = workbenchInstantiationService(undefined, disposables); + instantiationService = workbenchInstantiationService(undefined, disposables); instantiationService.stub(IEditorService, new TestEditorService()); instantiationService.stub(ILanguageService, LanguageService); instantiationService.stub(IModelService, stubModelService(instantiationService)); diff --git a/src/vscode-dts/vscode.proposed.resolvers.d.ts b/src/vscode-dts/vscode.proposed.resolvers.d.ts index 3bf1f93eb7de3..4f49cdf7d4af8 100644 --- a/src/vscode-dts/vscode.proposed.resolvers.d.ts +++ b/src/vscode-dts/vscode.proposed.resolvers.d.ts @@ -141,7 +141,8 @@ declare module 'vscode' { export enum CandidatePortSource { None = 0, Process = 1, - Output = 2 + Output = 2, + Hybrid = 3 } export type ResolverResult = (ResolvedAuthority | ManagedResolvedAuthority) & ResolvedOptions & TunnelInformation; diff --git a/src/vscode-dts/vscode.proposed.speech.d.ts b/src/vscode-dts/vscode.proposed.speech.d.ts index 9ece68528b98c..4e0ad0031ce7c 100644 --- a/src/vscode-dts/vscode.proposed.speech.d.ts +++ b/src/vscode-dts/vscode.proposed.speech.d.ts @@ -5,8 +5,6 @@ declare module 'vscode' { - // todo@bpasero work in progress speech API - export interface SpeechToTextOptions { readonly language?: string; } @@ -28,6 +26,23 @@ declare module 'vscode' { readonly onDidChange: Event; } + export enum TextToSpeechStatus { + Started = 1, + Stopped = 2, + Error = 3 + } + + export interface TextToSpeechEvent { + readonly status: TextToSpeechStatus; + readonly text?: string; + } + + export interface TextToSpeechSession extends Disposable { + readonly onDidChange: Event; + + synthesize(text: string): void; + } + export enum KeywordRecognitionStatus { Recognized = 1, Stopped = 2 @@ -44,6 +59,7 @@ declare module 'vscode' { export interface SpeechProvider { provideSpeechToTextSession(token: CancellationToken, options?: SpeechToTextOptions): SpeechToTextSession; + provideTextToSpeechSession(token: CancellationToken): TextToSpeechSession; provideKeywordRecognitionSession(token: CancellationToken): KeywordRecognitionSession; } diff --git a/yarn.lock b/yarn.lock index 2f4c572131c40..a9ab6181dd6f2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1913,11 +1913,6 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@xterm/addon-canvas@0.8.0-beta.17": - version "0.8.0-beta.17" - resolved "https://registry.yarnpkg.com/@xterm/addon-canvas/-/addon-canvas-0.8.0-beta.17.tgz#309630c738aa2e44742cdce5ee88fb8079fa652c" - integrity sha512-1km1RE02rxdbJWp1sev6Um6T/4tWlpEhJ88OP7xwfUFuedhFEby0JXmKzP7qB0cFzEvFTCq1bOAHSA3DX8vlFQ== - "@xterm/addon-image@0.9.0-beta.17": version "0.9.0-beta.17" resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.17.tgz#343d0665a6060d4f893b4f2d32de6ccbbd00bb63" @@ -9555,10 +9550,10 @@ tar@^6.1.11: mkdirp "^1.0.3" yallist "^4.0.0" -tas-client-umd@0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.8.tgz#38bd32d49545417a0ea67fb618e646298e1b67cc" - integrity sha512-0jAAujLmjjGXf9PzrNpjOrr/6CTpSOp8jX80NOHK5nlOTWWpwaZ16EOyrPdHnm2bVfPHvT0/RAD0xyiQHGQvCQ== +tas-client-umd@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.2.0.tgz#b71cc28f4c9b14f7b62f1ca4669886aa197e390c" + integrity sha512-oezN7mJVm5qZDVEby7OzxCLKUpUN5of0rY4dvOWaDF2JZBlGpd3BXceFN8B53qlTaIkVSzP65aAMT0Vc+/N25Q== teex@^1.0.1: version "1.0.1"